diff --git a/v0/src/App.vue b/v0/src/App.vue new file mode 100644 index 00000000..39422761 --- /dev/null +++ b/v0/src/App.vue @@ -0,0 +1,21 @@ + + + diff --git a/v0/src/assets/constants/Navbar/NAVBAR_DATA.json b/v0/src/assets/constants/Navbar/NAVBAR_DATA.json new file mode 100644 index 00000000..98d70a94 --- /dev/null +++ b/v0/src/assets/constants/Navbar/NAVBAR_DATA.json @@ -0,0 +1,236 @@ +[ + { + "id": "1", + "text": "project", + "spanClass": "caret", + "dropdownItems": [ + { + "id": "1", + "item": "new_project", + "itemid": "newProject", + "attributes": [] + }, + { + "id": "2", + "item": "save_online", + "itemid": "save", + "attributes": [] + }, + { + "id": "3", + "item": "save_offline", + "itemid": "saveOffline", + "attributes": [] + }, + { + "id": "4", + "item": "open_offline", + "itemid": "createOpenLocalPrompt", + "attributes": [] + }, + { + "id": "5", + "item": "export_as_file", + "itemid": "ExportProject", + "attributes": [] + }, + { + "id": "6", + "item": "import_project", + "itemid": "ImportProject", + "attributes": [] + }, + { + "id": "7", + "item": "clear_project", + "itemid": "clearProject", + "attributes": [] + }, + { + "id": "8", + "item": "recover_project", + "itemid": "recoverProject", + "attributes": [] + }, + { + "id": "9", + "item": "preview_circuit", + "itemid": "fullViewOption", + "attributes": [] + }, + { + "id": "10", + "item": "view_previous_ui", + "itemid": "", + "attributes": [ + { + "name": "onclick", + "value": "old_ui_redirect()" + } + ] + } + ] + }, + { + "id": "2", + "text": "circuit", + "spanClass": "caret", + "dropdownItems": [ + { + "id": "1", + "item": "new_circuit", + "itemid": "createNewCircuitScope", + "attributes": [] + }, + { + "id": "2", + "item": "new_verilog_module_html", + "itemid": "newVerilogModule", + "attributes": [] + }, + { + "id": "3", + "item": "insert_subcircuit", + "itemid": "createSubCircuitPrompt", + "attributes": [] + } + ] + }, + { + "id": "3", + "text": "tools", + "spanClass": "caret", + "dropdownItems": [ + { + "id": "1", + "item": "combinational_analysis_html", + "itemid": "createCombinationalAnalysisPrompt", + "attributes": [] + }, + { + "id": "2", + "item": "hex_bin_dec_converter_html", + "itemid": "bitconverter", + "attributes": [] + }, + { + "id": "3", + "item": "download_image", + "itemid": "createSaveAsImgPrompt", + "attributes": [] + }, + { + "id": "4", + "item": "themes", + "itemid": "colorThemes", + "attributes": [] + }, + { + "id": "5", + "item": "custom_shortcut", + "itemid": "customShortcut", + "attributes": [] + }, + { + "id": "6", + "item": "export_verilog", + "itemid": "generateVerilog", + "attributes": [] + } + ] + }, + { + "id": "4", + "text": "help", + "spanClass": "caret", + "dropdownItems": [ + { + "id": "1", + "item": "tutorial_guide", + "itemid": "showTourGuide", + "attributes": [] + }, + { + "id": "2", + "item": "user_manual", + "itemid": "", + "attributes": [ + { + "name": "href", + "value": "https://docs.circuitverse.org" + }, + { + "name": "target", + "value": "_blank" + }, + { + "name": "role", + "value": "button" + }, + { + "name": "aria-haspopup", + "value": "true" + }, + { + "name": "aria-expanded", + "value": "false" + } + ] + }, + { + "id": "3", + "item": "learn_digital_logic", + "itemid": "", + "attributes": [ + { + "name": "href", + "value": "https://learn.circuitverse.org" + }, + { + "name": "target", + "value": "_blank" + }, + { + "name": "role", + "value": "button" + }, + { + "name": "aria-haspopup", + "value": "true" + }, + { + "name": "aria-expanded", + "value": "false" + } + ] + }, + { + "id": "4", + "item": "discussion_forum", + "itemid": "", + "attributes": [ + { + "name": "href", + "value": "https://circuitverse.org/forum" + }, + { + "name": "target", + "value": "_blank" + }, + { + "name": "role", + "value": "button" + }, + { + "name": "aria-haspopup", + "value": "true" + }, + { + "name": "aria-expanded", + "value": "false" + } + ] + } + ] + } +] diff --git a/v0/src/assets/constants/Navbar/USER_DATA.json b/v0/src/assets/constants/Navbar/USER_DATA.json new file mode 100644 index 00000000..4d317d1e --- /dev/null +++ b/v0/src/assets/constants/Navbar/USER_DATA.json @@ -0,0 +1,71 @@ +[ + { + "id": "1", + "item": "dashboard", + "itemid": "", + "attributes": [ + { + "name": "href", + "value": "/users/${:id}" + }, + { + "name": "role", + "value": "button" + }, + { + "name": "aria-haspopup", + "value": "true" + }, + { + "name": "aria-expanded", + "value": "false" + } + ] + }, + { + "id": "2", + "item": "my_groups", + "itemid": "", + "attributes": [ + { + "name": "href", + "value": "/users/${:id}/groups" + }, + { + "name": "role", + "value": "button" + }, + { + "name": "aria-haspopup", + "value": "true" + }, + { + "name": "aria-expanded", + "value": "false" + } + ] + }, + { + "id": "3", + "item": "notifications", + "itemid": "", + "attributes": [ + { + "name": "href", + "value": "/users/${:id}/notifications" + }, + { + "name": "role", + "value": "button" + }, + { + "name": "aria-haspopup", + "value": "true" + }, + { + "name": "aria-expanded", + "value": "false" + } + ] + } +] diff --git a/v0/src/assets/constants/Panels/TimingDiagramPanel/buttons.json b/v0/src/assets/constants/Panels/TimingDiagramPanel/buttons.json new file mode 100644 index 00000000..357a00fa --- /dev/null +++ b/v0/src/assets/constants/Panels/TimingDiagramPanel/buttons.json @@ -0,0 +1,79 @@ +[ + { + "title": "Decrease Size", + "icon": "fa-chevron-left", + "class": "timing-diagram-smaller", + "type": "primary", + "click": "smaller" + }, + { + "title": "Increase Size", + "icon": "fa-chevron-right", + "class": "timing-diagram-larger", + "type": "primary", + "click": "larger" + }, + { + "title": "Decrease Height", + "icon": "fa-chevron-up", + "class": "timing-diagram-small-height", + "type": "primary", + "click": "smallHeight" + }, + { + "title": "Increase Height", + "icon": "fa-chevron-down", + "class": "timing-diagram-large-height", + "type": "primary", + "click": "largeHeight" + }, + { + "title": "Download As Image", + "icon": "fa-download", + "class": "timing-diagram-download", + "type": "primary", + "click": "download" + }, + { + "title": "Reset Timing Diagram", + "icon": "fa-undo", + "class": "timing-diagram-reset", + "type": "tertiary", + "click": "reset" + }, + { + "title": "Autocalibrate Cycle Units", + "icon": "fa-magic", + "class": "timing-diagram-calibrate", + "type": "tertiary", + "click": "calibrate" + }, + { + "title": "Zoom In", + "icon": "fa-search-plus", + "class": "timing-diagram-zoom-in", + "type": "primary", + "click": "zoomIn" + }, + { + "title": "Zoom Out", + "icon": "fa-search-minus", + "class": "timing-diagram-zoom-out", + "type": "primary", + "click": "zoomOut" + }, + { + "title": "Resume auto-scroll", + "icon": "fa-play", + "class": "timing-diagram-resume", + "type": "primary", + "click": "resume" + }, + { + "title": "Pause auto-scroll", + "icon": "fa-pause", + "class": "timing-diagram-pause", + "type": "primary", + "click": "pause" + } +] diff --git a/v0/src/assets/constants/Panels/VerilogEditorPanel/THEMES.json b/v0/src/assets/constants/Panels/VerilogEditorPanel/THEMES.json new file mode 100644 index 00000000..4319f822 --- /dev/null +++ b/v0/src/assets/constants/Panels/VerilogEditorPanel/THEMES.json @@ -0,0 +1,24 @@ +[ + { + "label": "Light Themes", + "options": [ + { "value": "default" }, + { "value": "solarized" }, + { "value": "elegant" }, + { "value": "neat" }, + { "value": "idea" }, + { "value": "neo" } + ] + }, + { + "label": "Dark Themes", + "options": [ + { "value": "blackboard" }, + { "value": "cobalt" }, + { "value": "night" }, + { "value": "the-matrix" }, + { "value": "midnight" }, + { "value": "monokai" } + ] + } +] diff --git a/v0/src/assets/img/ALU.png b/v0/src/assets/img/ALU.png new file mode 100644 index 00000000..fa809425 Binary files /dev/null and b/v0/src/assets/img/ALU.png differ diff --git a/v0/src/assets/img/ALU.svg b/v0/src/assets/img/ALU.svg new file mode 100644 index 00000000..0db7c17a --- /dev/null +++ b/v0/src/assets/img/ALU.svg @@ -0,0 +1 @@ +BACTRCarryAnsALU \ No newline at end of file diff --git a/v0/src/assets/img/Adder.svg b/v0/src/assets/img/Adder.svg new file mode 100644 index 00000000..9249cff0 --- /dev/null +++ b/v0/src/assets/img/Adder.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/AndGate.svg b/v0/src/assets/img/AndGate.svg new file mode 100644 index 00000000..6273b83b --- /dev/null +++ b/v0/src/assets/img/AndGate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/Arrow.svg b/v0/src/assets/img/Arrow.svg new file mode 100644 index 00000000..c25f20a1 --- /dev/null +++ b/v0/src/assets/img/Arrow.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/AsyncCounter.jpeg b/v0/src/assets/img/AsyncCounter.jpeg new file mode 100644 index 00000000..88a40590 Binary files /dev/null and b/v0/src/assets/img/AsyncCounter.jpeg differ diff --git a/v0/src/assets/img/BitSelector.svg b/v0/src/assets/img/BitSelector.svg new file mode 100644 index 00000000..b0436573 --- /dev/null +++ b/v0/src/assets/img/BitSelector.svg @@ -0,0 +1 @@ +x \ No newline at end of file diff --git a/v0/src/assets/img/Buffer.svg b/v0/src/assets/img/Buffer.svg new file mode 100644 index 00000000..bf867043 --- /dev/null +++ b/v0/src/assets/img/Buffer.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/Button.svg b/v0/src/assets/img/Button.svg new file mode 100644 index 00000000..6c16a24e --- /dev/null +++ b/v0/src/assets/img/Button.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/Clock.svg b/v0/src/assets/img/Clock.svg new file mode 100644 index 00000000..653d29e0 --- /dev/null +++ b/v0/src/assets/img/Clock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/ConstantVal.svg b/v0/src/assets/img/ConstantVal.svg new file mode 100644 index 00000000..dcc41214 --- /dev/null +++ b/v0/src/assets/img/ConstantVal.svg @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/v0/src/assets/img/Control Sequencer.png b/v0/src/assets/img/Control Sequencer.png new file mode 100644 index 00000000..2189f0a7 Binary files /dev/null and b/v0/src/assets/img/Control Sequencer.png differ diff --git a/v0/src/assets/img/ControlledInverter.svg b/v0/src/assets/img/ControlledInverter.svg new file mode 100644 index 00000000..927dc8c8 --- /dev/null +++ b/v0/src/assets/img/ControlledInverter.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/Counter.svg b/v0/src/assets/img/Counter.svg new file mode 100644 index 00000000..ee2aa77b --- /dev/null +++ b/v0/src/assets/img/Counter.svg @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/v0/src/assets/img/Decoder.svg b/v0/src/assets/img/Decoder.svg new file mode 100644 index 00000000..9a48c394 --- /dev/null +++ b/v0/src/assets/img/Decoder.svg @@ -0,0 +1 @@ +01 \ No newline at end of file diff --git a/v0/src/assets/img/Demultiplexer.svg b/v0/src/assets/img/Demultiplexer.svg new file mode 100644 index 00000000..61cad635 --- /dev/null +++ b/v0/src/assets/img/Demultiplexer.svg @@ -0,0 +1 @@ +01 \ No newline at end of file diff --git a/v0/src/assets/img/DflipFlop.svg b/v0/src/assets/img/DflipFlop.svg new file mode 100644 index 00000000..49f4ab5f --- /dev/null +++ b/v0/src/assets/img/DflipFlop.svg @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/v0/src/assets/img/DigitalLed.svg b/v0/src/assets/img/DigitalLed.svg new file mode 100644 index 00000000..a9259c2f --- /dev/null +++ b/v0/src/assets/img/DigitalLed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/Dlatch.svg b/v0/src/assets/img/Dlatch.svg new file mode 100644 index 00000000..8aef7671 --- /dev/null +++ b/v0/src/assets/img/Dlatch.svg @@ -0,0 +1 @@ +3 \ No newline at end of file diff --git a/v0/src/assets/img/EEPROM.svg b/v0/src/assets/img/EEPROM.svg new file mode 100644 index 00000000..504f61b0 --- /dev/null +++ b/v0/src/assets/img/EEPROM.svg @@ -0,0 +1 @@ +EPROMADIWDO \ No newline at end of file diff --git a/v0/src/assets/img/Flag.svg b/v0/src/assets/img/Flag.svg new file mode 100644 index 00000000..87ef27f4 --- /dev/null +++ b/v0/src/assets/img/Flag.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/FlipFlop.jpeg b/v0/src/assets/img/FlipFlop.jpeg new file mode 100644 index 00000000..5c3d87ce Binary files /dev/null and b/v0/src/assets/img/FlipFlop.jpeg differ diff --git a/v0/src/assets/img/ForceGate.svg b/v0/src/assets/img/ForceGate.svg new file mode 100644 index 00000000..28b50f3f --- /dev/null +++ b/v0/src/assets/img/ForceGate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/Ground.svg b/v0/src/assets/img/Ground.svg new file mode 100644 index 00000000..70f453f6 --- /dev/null +++ b/v0/src/assets/img/Ground.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/HexDisplay.svg b/v0/src/assets/img/HexDisplay.svg new file mode 100644 index 00000000..10c89e13 --- /dev/null +++ b/v0/src/assets/img/HexDisplay.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/ImageAnnotation.svg b/v0/src/assets/img/ImageAnnotation.svg new file mode 100644 index 00000000..8fbd9f75 --- /dev/null +++ b/v0/src/assets/img/ImageAnnotation.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/Input.svg b/v0/src/assets/img/Input.svg new file mode 100644 index 00000000..42a626ff --- /dev/null +++ b/v0/src/assets/img/Input.svg @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/v0/src/assets/img/JKflipFlop.svg b/v0/src/assets/img/JKflipFlop.svg new file mode 100644 index 00000000..9e723197 --- /dev/null +++ b/v0/src/assets/img/JKflipFlop.svg @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/v0/src/assets/img/Keyboard.jpeg b/v0/src/assets/img/Keyboard.jpeg new file mode 100644 index 00000000..9922daff Binary files /dev/null and b/v0/src/assets/img/Keyboard.jpeg differ diff --git a/v0/src/assets/img/Keyboard.svg b/v0/src/assets/img/Keyboard.svg new file mode 100644 index 00000000..6e215f6f --- /dev/null +++ b/v0/src/assets/img/Keyboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/LSB.svg b/v0/src/assets/img/LSB.svg new file mode 100644 index 00000000..31148b47 --- /dev/null +++ b/v0/src/assets/img/LSB.svg @@ -0,0 +1 @@ +LSBEN \ No newline at end of file diff --git a/v0/src/assets/img/MSB.svg b/v0/src/assets/img/MSB.svg new file mode 100644 index 00000000..21997b94 --- /dev/null +++ b/v0/src/assets/img/MSB.svg @@ -0,0 +1 @@ +MSBEN \ No newline at end of file diff --git a/v0/src/assets/img/Main.png b/v0/src/assets/img/Main.png new file mode 100644 index 00000000..3bb25dac Binary files /dev/null and b/v0/src/assets/img/Main.png differ diff --git a/v0/src/assets/img/Multiplexer.svg b/v0/src/assets/img/Multiplexer.svg new file mode 100644 index 00000000..b1b8472d --- /dev/null +++ b/v0/src/assets/img/Multiplexer.svg @@ -0,0 +1 @@ +01 \ No newline at end of file diff --git a/v0/src/assets/img/NandGate.svg b/v0/src/assets/img/NandGate.svg new file mode 100644 index 00000000..04cddce2 --- /dev/null +++ b/v0/src/assets/img/NandGate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/NorGate.svg b/v0/src/assets/img/NorGate.svg new file mode 100644 index 00000000..55e9abc2 --- /dev/null +++ b/v0/src/assets/img/NorGate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/NotGate.svg b/v0/src/assets/img/NotGate.svg new file mode 100644 index 00000000..a0d40ad4 --- /dev/null +++ b/v0/src/assets/img/NotGate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/OrGate.svg b/v0/src/assets/img/OrGate.svg new file mode 100644 index 00000000..741ba9fe --- /dev/null +++ b/v0/src/assets/img/OrGate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/Output.svg b/v0/src/assets/img/Output.svg new file mode 100644 index 00000000..7d4298f6 --- /dev/null +++ b/v0/src/assets/img/Output.svg @@ -0,0 +1 @@ +x \ No newline at end of file diff --git a/v0/src/assets/img/Power.svg b/v0/src/assets/img/Power.svg new file mode 100644 index 00000000..a4619920 --- /dev/null +++ b/v0/src/assets/img/Power.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/PriorityEncoder.svg b/v0/src/assets/img/PriorityEncoder.svg new file mode 100644 index 00000000..417325c6 --- /dev/null +++ b/v0/src/assets/img/PriorityEncoder.svg @@ -0,0 +1 @@ +010EN \ No newline at end of file diff --git a/v0/src/assets/img/RAM.svg b/v0/src/assets/img/RAM.svg new file mode 100644 index 00000000..92fd8293 --- /dev/null +++ b/v0/src/assets/img/RAM.svg @@ -0,0 +1 @@ +RAMADIWDO \ No newline at end of file diff --git a/v0/src/assets/img/RGBLed.svg b/v0/src/assets/img/RGBLed.svg new file mode 100644 index 00000000..299657c8 --- /dev/null +++ b/v0/src/assets/img/RGBLed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/RGBLedMatrix.svg b/v0/src/assets/img/RGBLedMatrix.svg new file mode 100644 index 00000000..cbb52e83 --- /dev/null +++ b/v0/src/assets/img/RGBLedMatrix.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/Random.svg b/v0/src/assets/img/Random.svg new file mode 100644 index 00000000..4da0fa99 --- /dev/null +++ b/v0/src/assets/img/Random.svg @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/v0/src/assets/img/Rectangle.svg b/v0/src/assets/img/Rectangle.svg new file mode 100644 index 00000000..f2ac9773 --- /dev/null +++ b/v0/src/assets/img/Rectangle.svg @@ -0,0 +1 @@ +Rectangle \ No newline at end of file diff --git a/v0/src/assets/img/RippleCarry.jpeg b/v0/src/assets/img/RippleCarry.jpeg new file mode 100644 index 00000000..c1fbf61f Binary files /dev/null and b/v0/src/assets/img/RippleCarry.jpeg differ diff --git a/v0/src/assets/img/Rom.svg b/v0/src/assets/img/Rom.svg new file mode 100644 index 00000000..b72c1c26 --- /dev/null +++ b/v0/src/assets/img/Rom.svg @@ -0,0 +1 @@ +ADEn000000000000000000000000000000000004080c \ No newline at end of file diff --git a/v0/src/assets/img/SAP.jpeg b/v0/src/assets/img/SAP.jpeg new file mode 100644 index 00000000..79b76da0 Binary files /dev/null and b/v0/src/assets/img/SAP.jpeg differ diff --git a/v0/src/assets/img/SRflipFlop.svg b/v0/src/assets/img/SRflipFlop.svg new file mode 100644 index 00000000..c41ff746 --- /dev/null +++ b/v0/src/assets/img/SRflipFlop.svg @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/v0/src/assets/img/SevenSegDisplay.svg b/v0/src/assets/img/SevenSegDisplay.svg new file mode 100644 index 00000000..bb3d2ae8 --- /dev/null +++ b/v0/src/assets/img/SevenSegDisplay.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/SixteenSegDisplay.svg b/v0/src/assets/img/SixteenSegDisplay.svg new file mode 100644 index 00000000..ad05274b --- /dev/null +++ b/v0/src/assets/img/SixteenSegDisplay.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/Splitter.svg b/v0/src/assets/img/Splitter.svg new file mode 100644 index 00000000..fa4a969d --- /dev/null +++ b/v0/src/assets/img/Splitter.svg @@ -0,0 +1 @@ +0:11:2 \ No newline at end of file diff --git a/v0/src/assets/img/SquareRGBLed.svg b/v0/src/assets/img/SquareRGBLed.svg new file mode 100644 index 00000000..7900e614 --- /dev/null +++ b/v0/src/assets/img/SquareRGBLed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/Stepper.svg b/v0/src/assets/img/Stepper.svg new file mode 100644 index 00000000..f18aa4fa --- /dev/null +++ b/v0/src/assets/img/Stepper.svg @@ -0,0 +1 @@ +5f \ No newline at end of file diff --git a/v0/src/assets/img/T-clock.png b/v0/src/assets/img/T-clock.png new file mode 100644 index 00000000..66442d9e Binary files /dev/null and b/v0/src/assets/img/T-clock.png differ diff --git a/v0/src/assets/img/TB_Input.svg b/v0/src/assets/img/TB_Input.svg new file mode 100644 index 00000000..c1c42ead --- /dev/null +++ b/v0/src/assets/img/TB_Input.svg @@ -0,0 +1 @@ +Test1 [INPUT] Case:0 \ No newline at end of file diff --git a/v0/src/assets/img/TB_Output.svg b/v0/src/assets/img/TB_Output.svg new file mode 100644 index 00000000..b62e1993 --- /dev/null +++ b/v0/src/assets/img/TB_Output.svg @@ -0,0 +1 @@ +Test1 [OUTPUT] Paired \ No newline at end of file diff --git a/v0/src/assets/img/TTY.svg b/v0/src/assets/img/TTY.svg new file mode 100644 index 00000000..208e42b9 --- /dev/null +++ b/v0/src/assets/img/TTY.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/Text.svg b/v0/src/assets/img/Text.svg new file mode 100644 index 00000000..db86087a --- /dev/null +++ b/v0/src/assets/img/Text.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/TflipFlop.svg b/v0/src/assets/img/TflipFlop.svg new file mode 100644 index 00000000..50925f80 --- /dev/null +++ b/v0/src/assets/img/TflipFlop.svg @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/v0/src/assets/img/TriState.svg b/v0/src/assets/img/TriState.svg new file mode 100644 index 00000000..d251882c --- /dev/null +++ b/v0/src/assets/img/TriState.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/Tunnel.svg b/v0/src/assets/img/Tunnel.svg new file mode 100644 index 00000000..ff5b95bb --- /dev/null +++ b/v0/src/assets/img/Tunnel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/TwoComplement.svg b/v0/src/assets/img/TwoComplement.svg new file mode 100644 index 00000000..6baa7056 --- /dev/null +++ b/v0/src/assets/img/TwoComplement.svg @@ -0,0 +1 @@ +2' \ No newline at end of file diff --git a/v0/src/assets/img/VariableLed.svg b/v0/src/assets/img/VariableLed.svg new file mode 100644 index 00000000..e2829407 --- /dev/null +++ b/v0/src/assets/img/VariableLed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/XnorGate.svg b/v0/src/assets/img/XnorGate.svg new file mode 100644 index 00000000..4a8bae40 --- /dev/null +++ b/v0/src/assets/img/XnorGate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/XorGate.svg b/v0/src/assets/img/XorGate.svg new file mode 100644 index 00000000..639b4c0b --- /dev/null +++ b/v0/src/assets/img/XorGate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/assignment.png b/v0/src/assets/img/assignment.png new file mode 100644 index 00000000..c27df5bc Binary files /dev/null and b/v0/src/assets/img/assignment.png differ diff --git a/v0/src/assets/img/bus.png b/v0/src/assets/img/bus.png new file mode 100644 index 00000000..d37b30aa Binary files /dev/null and b/v0/src/assets/img/bus.png differ diff --git a/v0/src/assets/img/caret.jpg b/v0/src/assets/img/caret.jpg new file mode 100644 index 00000000..a382f8da Binary files /dev/null and b/v0/src/assets/img/caret.jpg differ diff --git a/v0/src/assets/img/circuitverse2.svg b/v0/src/assets/img/circuitverse2.svg new file mode 100644 index 00000000..23dd6118 --- /dev/null +++ b/v0/src/assets/img/circuitverse2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/circuitverse_black.svg b/v0/src/assets/img/circuitverse_black.svg new file mode 100644 index 00000000..eba77c2a --- /dev/null +++ b/v0/src/assets/img/circuitverse_black.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/circuitverse_logo.svg b/v0/src/assets/img/circuitverse_logo.svg new file mode 100644 index 00000000..27202ecb --- /dev/null +++ b/v0/src/assets/img/circuitverse_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/cross.png b/v0/src/assets/img/cross.png new file mode 100644 index 00000000..bb7126e2 Binary files /dev/null and b/v0/src/assets/img/cross.png differ diff --git a/v0/src/assets/img/cvlogo.svg b/v0/src/assets/img/cvlogo.svg new file mode 100644 index 00000000..ce4f7e59 --- /dev/null +++ b/v0/src/assets/img/cvlogo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/assets/img/default.png b/v0/src/assets/img/default.png new file mode 100644 index 00000000..8e183a8a Binary files /dev/null and b/v0/src/assets/img/default.png differ diff --git a/v0/src/assets/img/drag.mp4 b/v0/src/assets/img/drag.mp4 new file mode 100644 index 00000000..b211439c Binary files /dev/null and b/v0/src/assets/img/drag.mp4 differ diff --git a/v0/src/assets/img/edit_icon.png b/v0/src/assets/img/edit_icon.png new file mode 100644 index 00000000..32f99fb1 Binary files /dev/null and b/v0/src/assets/img/edit_icon.png differ diff --git a/v0/src/assets/img/embed.png b/v0/src/assets/img/embed.png new file mode 100644 index 00000000..520cee0b Binary files /dev/null and b/v0/src/assets/img/embed.png differ diff --git a/v0/src/assets/img/facebook.png b/v0/src/assets/img/facebook.png new file mode 100644 index 00000000..fc961175 Binary files /dev/null and b/v0/src/assets/img/facebook.png differ diff --git a/v0/src/assets/img/facebook_signin.png b/v0/src/assets/img/facebook_signin.png new file mode 100644 index 00000000..e78185f0 Binary files /dev/null and b/v0/src/assets/img/facebook_signin.png differ diff --git a/v0/src/assets/img/fullAdder.png b/v0/src/assets/img/fullAdder.png new file mode 100644 index 00000000..1150d9c6 Binary files /dev/null and b/v0/src/assets/img/fullAdder.png differ diff --git a/v0/src/assets/img/google.png b/v0/src/assets/img/google.png new file mode 100644 index 00000000..2accb4e6 Binary files /dev/null and b/v0/src/assets/img/google.png differ diff --git a/v0/src/assets/img/google_signin.png b/v0/src/assets/img/google_signin.png new file mode 100644 index 00000000..18bbd0b7 Binary files /dev/null and b/v0/src/assets/img/google_signin.png differ diff --git a/v0/src/assets/img/grading.png b/v0/src/assets/img/grading.png new file mode 100644 index 00000000..166ee57e Binary files /dev/null and b/v0/src/assets/img/grading.png differ diff --git a/v0/src/assets/img/groups.png b/v0/src/assets/img/groups.png new file mode 100644 index 00000000..e3619597 Binary files /dev/null and b/v0/src/assets/img/groups.png differ diff --git a/v0/src/assets/img/halfAdder.png b/v0/src/assets/img/halfAdder.png new file mode 100644 index 00000000..7c9f3f0c Binary files /dev/null and b/v0/src/assets/img/halfAdder.png differ diff --git a/v0/src/assets/img/help.png b/v0/src/assets/img/help.png new file mode 100644 index 00000000..5c72dbb7 Binary files /dev/null and b/v0/src/assets/img/help.png differ diff --git a/v0/src/assets/img/iDecoder.png b/v0/src/assets/img/iDecoder.png new file mode 100644 index 00000000..d55e9bfa Binary files /dev/null and b/v0/src/assets/img/iDecoder.png differ diff --git a/v0/src/assets/img/iiitb.png b/v0/src/assets/img/iiitb.png new file mode 100644 index 00000000..5a1c2fe3 Binary files /dev/null and b/v0/src/assets/img/iiitb.png differ diff --git a/v0/src/assets/img/implemented.png b/v0/src/assets/img/implemented.png new file mode 100644 index 00000000..6766aaf6 Binary files /dev/null and b/v0/src/assets/img/implemented.png differ diff --git a/v0/src/assets/img/logix.png b/v0/src/assets/img/logix.png new file mode 100644 index 00000000..2f6daac5 Binary files /dev/null and b/v0/src/assets/img/logix.png differ diff --git a/v0/src/assets/img/logixBanner.png b/v0/src/assets/img/logixBanner.png new file mode 100644 index 00000000..b9dad5d5 Binary files /dev/null and b/v0/src/assets/img/logixBanner.png differ diff --git a/v0/src/assets/img/logixBanner2.png b/v0/src/assets/img/logixBanner2.png new file mode 100644 index 00000000..9e7b044a Binary files /dev/null and b/v0/src/assets/img/logixBanner2.png differ diff --git a/v0/src/assets/img/logix_banner_new.png b/v0/src/assets/img/logix_banner_new.png new file mode 100644 index 00000000..13d7fc63 Binary files /dev/null and b/v0/src/assets/img/logix_banner_new.png differ diff --git a/v0/src/assets/img/multiselectionDrag.mp4 b/v0/src/assets/img/multiselectionDrag.mp4 new file mode 100644 index 00000000..6a193d8e Binary files /dev/null and b/v0/src/assets/img/multiselectionDrag.mp4 differ diff --git a/v0/src/assets/img/properties.mp4 b/v0/src/assets/img/properties.mp4 new file mode 100644 index 00000000..754f6c58 Binary files /dev/null and b/v0/src/assets/img/properties.mp4 differ diff --git a/v0/src/assets/img/properties.png b/v0/src/assets/img/properties.png new file mode 100644 index 00000000..167810f5 Binary files /dev/null and b/v0/src/assets/img/properties.png differ diff --git a/v0/src/assets/img/stats.png b/v0/src/assets/img/stats.png new file mode 100644 index 00000000..700bd7fb Binary files /dev/null and b/v0/src/assets/img/stats.png differ diff --git a/v0/src/assets/img/students.png b/v0/src/assets/img/students.png new file mode 100644 index 00000000..0be22e0e Binary files /dev/null and b/v0/src/assets/img/students.png differ diff --git a/v0/src/assets/img/super.png b/v0/src/assets/img/super.png new file mode 100644 index 00000000..4e3a0996 Binary files /dev/null and b/v0/src/assets/img/super.png differ diff --git a/v0/src/assets/img/wire.mp4 b/v0/src/assets/img/wire.mp4 new file mode 100644 index 00000000..8b246df9 Binary files /dev/null and b/v0/src/assets/img/wire.mp4 differ diff --git a/v0/src/assets/themes/ColorBlind.svg b/v0/src/assets/themes/ColorBlind.svg new file mode 100644 index 00000000..93bdf57a --- /dev/null +++ b/v0/src/assets/themes/ColorBlind.svg @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/v0/src/assets/themes/DefaultTheme.svg b/v0/src/assets/themes/DefaultTheme.svg new file mode 100644 index 00000000..f7979326 --- /dev/null +++ b/v0/src/assets/themes/DefaultTheme.svg @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/v0/src/assets/themes/GnW.svg b/v0/src/assets/themes/GnW.svg new file mode 100644 index 00000000..9d095029 --- /dev/null +++ b/v0/src/assets/themes/GnW.svg @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/v0/src/assets/themes/HighContrast.svg b/v0/src/assets/themes/HighContrast.svg new file mode 100644 index 00000000..d025b140 --- /dev/null +++ b/v0/src/assets/themes/HighContrast.svg @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/v0/src/assets/themes/LitebornSpring.svg b/v0/src/assets/themes/LitebornSpring.svg new file mode 100644 index 00000000..81b2bce4 --- /dev/null +++ b/v0/src/assets/themes/LitebornSpring.svg @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/v0/src/assets/themes/NightSky.svg b/v0/src/assets/themes/NightSky.svg new file mode 100644 index 00000000..718859b5 --- /dev/null +++ b/v0/src/assets/themes/NightSky.svg @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/v0/src/components/ContextMenu/ContextMenu.css b/v0/src/components/ContextMenu/ContextMenu.css new file mode 100644 index 00000000..e69de29b diff --git a/v0/src/components/ContextMenu/ContextMenu.vue b/v0/src/components/ContextMenu/ContextMenu.vue new file mode 100644 index 00000000..d060d4f6 --- /dev/null +++ b/v0/src/components/ContextMenu/ContextMenu.vue @@ -0,0 +1,82 @@ + + + diff --git a/v0/src/components/DialogBox/BooleanTable.vue b/v0/src/components/DialogBox/BooleanTable.vue new file mode 100644 index 00000000..528a6cd5 --- /dev/null +++ b/v0/src/components/DialogBox/BooleanTable.vue @@ -0,0 +1,23 @@ + + + diff --git a/v0/src/components/DialogBox/CombinationalAnalysis.vue b/v0/src/components/DialogBox/CombinationalAnalysis.vue new file mode 100644 index 00000000..3b4092a8 --- /dev/null +++ b/v0/src/components/DialogBox/CombinationalAnalysis.vue @@ -0,0 +1,708 @@ + + + + + + + diff --git a/v0/src/components/DialogBox/CustomShortcut.vue b/v0/src/components/DialogBox/CustomShortcut.vue new file mode 100644 index 00000000..ae67a842 --- /dev/null +++ b/v0/src/components/DialogBox/CustomShortcut.vue @@ -0,0 +1,275 @@ + + + + + + + diff --git a/v0/src/components/DialogBox/ExportProject.vue b/v0/src/components/DialogBox/ExportProject.vue new file mode 100644 index 00000000..0da58f88 --- /dev/null +++ b/v0/src/components/DialogBox/ExportProject.vue @@ -0,0 +1,117 @@ + + + + + + + + + diff --git a/v0/src/components/DialogBox/ExportVerilog.vue b/v0/src/components/DialogBox/ExportVerilog.vue new file mode 100644 index 00000000..d826ad35 --- /dev/null +++ b/v0/src/components/DialogBox/ExportVerilog.vue @@ -0,0 +1,98 @@ + + + diff --git a/v0/src/components/DialogBox/HexBinDec.vue b/v0/src/components/DialogBox/HexBinDec.vue new file mode 100644 index 00000000..e65ebf73 --- /dev/null +++ b/v0/src/components/DialogBox/HexBinDec.vue @@ -0,0 +1,253 @@ + + + + + diff --git a/v0/src/components/DialogBox/ImportProject.vue b/v0/src/components/DialogBox/ImportProject.vue new file mode 100644 index 00000000..caa1df60 --- /dev/null +++ b/v0/src/components/DialogBox/ImportProject.vue @@ -0,0 +1,250 @@ + + + + + + + + + + + diff --git a/v0/src/components/DialogBox/InsertSubcircuit.vue b/v0/src/components/DialogBox/InsertSubcircuit.vue new file mode 100644 index 00000000..f2c1b1a6 --- /dev/null +++ b/v0/src/components/DialogBox/InsertSubcircuit.vue @@ -0,0 +1,113 @@ + + + + + diff --git a/v0/src/components/DialogBox/OpenOffline.vue b/v0/src/components/DialogBox/OpenOffline.vue new file mode 100644 index 00000000..7bd3b94f --- /dev/null +++ b/v0/src/components/DialogBox/OpenOffline.vue @@ -0,0 +1,99 @@ + + + diff --git a/v0/src/components/DialogBox/SaveImage.vue b/v0/src/components/DialogBox/SaveImage.vue new file mode 100644 index 00000000..9cfaba08 --- /dev/null +++ b/v0/src/components/DialogBox/SaveImage.vue @@ -0,0 +1,157 @@ + + + diff --git a/v0/src/components/DialogBox/Themes/ApplyThemes.vue b/v0/src/components/DialogBox/Themes/ApplyThemes.vue new file mode 100644 index 00000000..7d564509 --- /dev/null +++ b/v0/src/components/DialogBox/Themes/ApplyThemes.vue @@ -0,0 +1,288 @@ + + + diff --git a/v0/src/components/Dropdown/DropDown.css b/v0/src/components/Dropdown/DropDown.css new file mode 100644 index 00000000..e69de29b diff --git a/v0/src/components/Dropdown/DropDown.vue b/v0/src/components/Dropdown/DropDown.vue new file mode 100644 index 00000000..89dc2b20 --- /dev/null +++ b/v0/src/components/Dropdown/DropDown.vue @@ -0,0 +1,58 @@ + + + diff --git a/v0/src/components/Extra.vue b/v0/src/components/Extra.vue new file mode 100644 index 00000000..e61d1597 --- /dev/null +++ b/v0/src/components/Extra.vue @@ -0,0 +1,323 @@ + + + \ No newline at end of file diff --git a/v0/src/components/Logo/Logo.css b/v0/src/components/Logo/Logo.css new file mode 100644 index 00000000..e69de29b diff --git a/v0/src/components/Logo/Logo.vue b/v0/src/components/Logo/Logo.vue new file mode 100644 index 00000000..56130b8c --- /dev/null +++ b/v0/src/components/Logo/Logo.vue @@ -0,0 +1,24 @@ + + + + + diff --git a/v0/src/components/MessageBox/messageBox.vue b/v0/src/components/MessageBox/messageBox.vue new file mode 100644 index 00000000..f008946f --- /dev/null +++ b/v0/src/components/MessageBox/messageBox.vue @@ -0,0 +1,82 @@ + + + + + diff --git a/v0/src/components/Navbar/Hamburger/Hamburger.css b/v0/src/components/Navbar/Hamburger/Hamburger.css new file mode 100644 index 00000000..e69de29b diff --git a/v0/src/components/Navbar/Hamburger/Hamburger.vue b/v0/src/components/Navbar/Hamburger/Hamburger.vue new file mode 100644 index 00000000..8c362ef8 --- /dev/null +++ b/v0/src/components/Navbar/Hamburger/Hamburger.vue @@ -0,0 +1,18 @@ + + + + + diff --git a/v0/src/components/Navbar/Navbar.css b/v0/src/components/Navbar/Navbar.css new file mode 100644 index 00000000..acdaef1a --- /dev/null +++ b/v0/src/components/Navbar/Navbar.css @@ -0,0 +1,306 @@ +@import url('/src/styles/color_theme.scss'); + +.navbar { + background-color: var(--white); + position: relative; + top: 0; + width: 100%; + z-index: 100; +} +.header { + background: var(--bg-navbar); +} + +.logo { + display: block; + max-width: 100%; + margin-bottom: 0px; +} +.logo { + background: url(/src/assets/logo.svg) center/cover; + height: 30px; + width: 105px; + display: inline-block; + margin-right: 36px; +} + +.navbar-nav > li > a { + padding: 7px 15px; +} + +.navbar-logo { + height: 70px; + padding-top: 5px; +} + +.navbar-search-icon-container { + height: 40px; + position: relative; + width: 40px; +} +.navbar-search-icon-container.search-icon { + cursor: pointer; +} + +.navbar-search-icon-oncollapse { + display: none; + margin-right: 0; + padding-right: 0; + padding-top: 5px; + text-align: right; +} + +.fa-search { + color: var(--navbar-dark-grey); + font-size: 20px; + margin-right: 12px; + vertical-align: middle; +} +.fa-search:hover { + color: var(--primary-green); + cursor: pointer; +} + +.fa-search.active { + color: var(--secondary-green); +} + +.fa-search:focus { + outline: none; +} + +.navbar-simulator-text { + border: 2px solid var(--primary-green); + color: var(--navbar-dark-grey); + font-weight: 500; + transition: all 0.3s ease-in; +} + +.navbar-simulator-text:hover { + background: var(--primary-green); + color: var(--white); +} + +.navbar-dropdown-toggle-hidden.dropdown-toggle::after { + display: none; +} + +.dropdown-toggle.navbar-user-dropdown { + cursor: pointer; +} +.dropdown-toggle.navbar-user-dropdown::after { + margin-left: 0; + padding-left: 0; + vertical-align: 0.5em; +} + +.dropdown-item:hover { + background-color: var(--primary-green); + color: var(--white); +} + +.navbar-text { + color: var(--navbar-dark-grey); + font-weight: 500; +} + +.navbar-text:hover { + color: var(--primary-green); +} + +.navbar-text.active { + color: var(--secondary-green); +} + +.navbar-text:focus { + outline: none; + text-decoration: underline; +} + +.navbar-search-active { + background-color: var(--card-green); + display: none; + margin-top: 0; + padding: 20px; + position: fixed; + top: 96px; + width: 100%; + z-index: 90; +} + +#navbar-dropdown-1, +#navbar-dropdown-2 :hover { + color: var(--primary-green); +} + +#navbar-dropdown-1, +#navbar-dropdown-2 [aria-expanded='true'] { + color: var(--secondary-green); +} + +#navbar-dropdown-1, +#navbar-dropdown-2 :focus { + outline: none; + text-decoration: underline; +} + +.navbar-username-truncate { + display: inline-block; + max-width: 70px; + overflow: hidden; + text-overflow: ellipsis; +} + +.affix { + box-shadow: 0 8px 6px -6px var(--shadow-grey); +} + +.navbar-search-bar-form { + display: flex; + margin-left: auto; + margin-right: auto; + width: 70%; +} + +.navbar-search-bar-input { + border: 1px solid var(--primary-green); + margin: 5px; + min-width: 150px; + padding: 5px; + width: 100%; +} + +.navbar-search-bar-select { + -moz-appearance: none; + -webkit-appearance: none; + appearance: none; + background-color: var(--card-green); + background-image: url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%23000000%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E'), + linear-gradient(to bottom, var(--white) 0%, var(--white) 100%); + background-position: right 0.3em top 50%, 0 0; + background-repeat: no-repeat, repeat; + background-size: 0.45em auto, 100%; + border: 1px solid var(--primary-green); + cursor: pointer; + font-size: 18px; + height: 40px; + margin-right: 6px; + margin-top: 5px; + max-width: 100px; + min-width: 100px; + padding-left: 5px; +} + +@media (max-width: 991px) { + .navbar-search-icon-oncollapse { + display: block; + } + + .navbar-search-icon-onexpand { + display: none; + } +} + +@media (max-width: 768px) { + .navbar-search-bar-form { + width: 90%; + } + + .navbar-search-bar-button { + padding-left: 10px; + padding-right: 10px; + } + + .navbar-search-bar-select { + width: 50px; + } +} + +@media (max-width: 400px) { + .navbar-search-bar-form { + width: 100%; + } + + .navbar-search-container { + padding-left: 0; + padding-right: 0; + } + + .navbar-search-active { + padding-left: 10px; + padding-right: 10px; + } + + .navbar-logo { + height: 60px; + padding-top: 10px; + } +} + +/* dropdown-menu styles */ + +.dropdown > ul { + border-radius: 5px; + text-align: center; + position: absolute; + left: 50%; + transform: translate(-50%, 13px); +} + +.draggable-panel-css { + border-radius: 5px; + z-index: 70; + transition: background 0.5s ease-out; + position: fixed; +} + +@supports (backdrop-filter: blur()) { + .dropdown > ul { + backdrop-filter: blur(5px); + } +} + +.mw-override { + min-width: 110px; +} + +.dropdown > ul::before { + background-color: transparent; + content: ''; + width: 10px; + display: inline-block; + height: 10px; + position: absolute; + transform: translate(-50%, -13px) rotate(-45deg); +} + +.dropdown > ul::after { + content: ''; + width: 11.5px; + display: inline-block; + height: 10px; + position: absolute; + transform: translate(-50%, -15.5px); + top: 14.5px; +} + +.dropdown-menu > li > a { + padding: 7px 0; + width: 90%; + margin: auto; + transition: all 0.2s ease-in-out; + text-align: left; + padding-left: 10px; +} + +.dropdown-menu > li > a:hover { + border-radius: 7px; + opacity: 1; +} + +@media (max-width: 991px) { + .navbar-nav .dropdown-menu { + position: absolute; + float: none; + } +} diff --git a/v0/src/components/Navbar/Navbar.vue b/v0/src/components/Navbar/Navbar.vue new file mode 100644 index 00000000..caeec961 --- /dev/null +++ b/v0/src/components/Navbar/Navbar.vue @@ -0,0 +1,51 @@ + + + + + diff --git a/v0/src/components/Navbar/NavbarLinks/NavbarLink/NavbarLink.css b/v0/src/components/Navbar/NavbarLinks/NavbarLink/NavbarLink.css new file mode 100644 index 00000000..e69de29b diff --git a/v0/src/components/Navbar/NavbarLinks/NavbarLink/NavbarLink.vue b/v0/src/components/Navbar/NavbarLinks/NavbarLink/NavbarLink.vue new file mode 100644 index 00000000..7f61cd7e --- /dev/null +++ b/v0/src/components/Navbar/NavbarLinks/NavbarLink/NavbarLink.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/v0/src/components/Navbar/NavbarLinks/NavbarLinks.css b/v0/src/components/Navbar/NavbarLinks/NavbarLinks.css new file mode 100644 index 00000000..e69de29b diff --git a/v0/src/components/Navbar/NavbarLinks/NavbarLinks.vue b/v0/src/components/Navbar/NavbarLinks/NavbarLinks.vue new file mode 100644 index 00000000..3c01a4dc --- /dev/null +++ b/v0/src/components/Navbar/NavbarLinks/NavbarLinks.vue @@ -0,0 +1,22 @@ + + + + + diff --git a/v0/src/components/Navbar/QuickButton/QuickButton.css b/v0/src/components/Navbar/QuickButton/QuickButton.css new file mode 100644 index 00000000..e69de29b diff --git a/v0/src/components/Navbar/QuickButton/QuickButton.vue b/v0/src/components/Navbar/QuickButton/QuickButton.vue new file mode 100644 index 00000000..aad07b93 --- /dev/null +++ b/v0/src/components/Navbar/QuickButton/QuickButton.vue @@ -0,0 +1,296 @@ + + + + + diff --git a/v0/src/components/Navbar/User/User.css b/v0/src/components/Navbar/User/User.css new file mode 100644 index 00000000..e69de29b diff --git a/v0/src/components/Navbar/User/User.vue b/v0/src/components/Navbar/User/User.vue new file mode 100644 index 00000000..839dae5e --- /dev/null +++ b/v0/src/components/Navbar/User/User.vue @@ -0,0 +1,55 @@ + + + + + diff --git a/v0/src/components/Panels/ElementsPanel/ElementsPanel.vue b/v0/src/components/Panels/ElementsPanel/ElementsPanel.vue new file mode 100644 index 00000000..e5c52896 --- /dev/null +++ b/v0/src/components/Panels/ElementsPanel/ElementsPanel.vue @@ -0,0 +1,240 @@ + + + + + diff --git a/v0/src/components/Panels/PropertiesPanel/LayoutProperty/LayoutProperty.vue b/v0/src/components/Panels/PropertiesPanel/LayoutProperty/LayoutProperty.vue new file mode 100644 index 00000000..49c7689f --- /dev/null +++ b/v0/src/components/Panels/PropertiesPanel/LayoutProperty/LayoutProperty.vue @@ -0,0 +1,151 @@ + + + + + diff --git a/v0/src/components/Panels/PropertiesPanel/ModuleProperty/ElementProperty/ElementProperty.vue b/v0/src/components/Panels/PropertiesPanel/ModuleProperty/ElementProperty/ElementProperty.vue new file mode 100644 index 00000000..fec2c571 --- /dev/null +++ b/v0/src/components/Panels/PropertiesPanel/ModuleProperty/ElementProperty/ElementProperty.vue @@ -0,0 +1,121 @@ + + + diff --git a/v0/src/components/Panels/PropertiesPanel/ModuleProperty/ModuleProperty.vue b/v0/src/components/Panels/PropertiesPanel/ModuleProperty/ModuleProperty.vue new file mode 100644 index 00000000..92d0c9aa --- /dev/null +++ b/v0/src/components/Panels/PropertiesPanel/ModuleProperty/ModuleProperty.vue @@ -0,0 +1,43 @@ + + + diff --git a/v0/src/components/Panels/PropertiesPanel/ModuleProperty/ProjectProperty/ProjectProperty.vue b/v0/src/components/Panels/PropertiesPanel/ModuleProperty/ProjectProperty/ProjectProperty.vue new file mode 100644 index 00000000..1fc88e1f --- /dev/null +++ b/v0/src/components/Panels/PropertiesPanel/ModuleProperty/ProjectProperty/ProjectProperty.vue @@ -0,0 +1,241 @@ + + + + + + + diff --git a/v0/src/components/Panels/PropertiesPanel/ModuleProperty/SubcircuitProperty/SubcircuitProperty.vue b/v0/src/components/Panels/PropertiesPanel/ModuleProperty/SubcircuitProperty/SubcircuitProperty.vue new file mode 100644 index 00000000..6ac03ece --- /dev/null +++ b/v0/src/components/Panels/PropertiesPanel/ModuleProperty/SubcircuitProperty/SubcircuitProperty.vue @@ -0,0 +1,62 @@ + + + diff --git a/v0/src/components/Panels/PropertiesPanel/PropertiesPanel.vue b/v0/src/components/Panels/PropertiesPanel/PropertiesPanel.vue new file mode 100644 index 00000000..368c5b79 --- /dev/null +++ b/v0/src/components/Panels/PropertiesPanel/PropertiesPanel.vue @@ -0,0 +1,75 @@ + + + diff --git a/v0/src/components/Panels/Shared/DropdownSelect.vue b/v0/src/components/Panels/Shared/DropdownSelect.vue new file mode 100644 index 00000000..f1983987 --- /dev/null +++ b/v0/src/components/Panels/Shared/DropdownSelect.vue @@ -0,0 +1,29 @@ + + + diff --git a/v0/src/components/Panels/Shared/HelpButton.vue b/v0/src/components/Panels/Shared/HelpButton.vue new file mode 100644 index 00000000..9956010b --- /dev/null +++ b/v0/src/components/Panels/Shared/HelpButton.vue @@ -0,0 +1,21 @@ + + + \ No newline at end of file diff --git a/v0/src/components/Panels/Shared/InputGroups.vue b/v0/src/components/Panels/Shared/InputGroups.vue new file mode 100644 index 00000000..628da797 --- /dev/null +++ b/v0/src/components/Panels/Shared/InputGroups.vue @@ -0,0 +1,81 @@ + + + diff --git a/v0/src/components/Panels/Shared/PanelHeader.vue b/v0/src/components/Panels/Shared/PanelHeader.vue new file mode 100644 index 00000000..212d5eab --- /dev/null +++ b/v0/src/components/Panels/Shared/PanelHeader.vue @@ -0,0 +1,22 @@ + + + + + diff --git a/v0/src/components/Panels/TimingDiagramPanel/TimingDiagramButtons.vue b/v0/src/components/Panels/TimingDiagramPanel/TimingDiagramButtons.vue new file mode 100644 index 00000000..e5b16ad4 --- /dev/null +++ b/v0/src/components/Panels/TimingDiagramPanel/TimingDiagramButtons.vue @@ -0,0 +1,26 @@ + + + diff --git a/v0/src/components/Panels/TimingDiagramPanel/TimingDiagramPanel.vue b/v0/src/components/Panels/TimingDiagramPanel/TimingDiagramPanel.vue new file mode 100644 index 00000000..70cbc244 --- /dev/null +++ b/v0/src/components/Panels/TimingDiagramPanel/TimingDiagramPanel.vue @@ -0,0 +1,107 @@ + + + + + + + diff --git a/v0/src/components/Panels/VerilogEditorPanel/VerilogEditorPanel.vue b/v0/src/components/Panels/VerilogEditorPanel/VerilogEditorPanel.vue new file mode 100644 index 00000000..5a2011fa --- /dev/null +++ b/v0/src/components/Panels/VerilogEditorPanel/VerilogEditorPanel.vue @@ -0,0 +1,103 @@ + + + + + diff --git a/v0/src/components/ReportIssue/ReportIssue.vue b/v0/src/components/ReportIssue/ReportIssue.vue new file mode 100644 index 00000000..14a9c288 --- /dev/null +++ b/v0/src/components/ReportIssue/ReportIssue.vue @@ -0,0 +1,274 @@ + + + + + diff --git a/v0/src/components/ReportIssue/ReportIssueButton.vue b/v0/src/components/ReportIssue/ReportIssueButton.vue new file mode 100644 index 00000000..128b6288 --- /dev/null +++ b/v0/src/components/ReportIssue/ReportIssueButton.vue @@ -0,0 +1,24 @@ + + + diff --git a/v0/src/components/TabsBar/TabsBar.vue b/v0/src/components/TabsBar/TabsBar.vue new file mode 100644 index 00000000..68c22aa4 --- /dev/null +++ b/v0/src/components/TabsBar/TabsBar.vue @@ -0,0 +1,368 @@ + + + + + + + diff --git a/v0/src/components/helpers/Helper.vue b/v0/src/components/helpers/Helper.vue new file mode 100644 index 00000000..14934a66 --- /dev/null +++ b/v0/src/components/helpers/Helper.vue @@ -0,0 +1,28 @@ + + + diff --git a/v0/src/components/helpers/confirmComponent/ConfirmComponent.vue b/v0/src/components/helpers/confirmComponent/ConfirmComponent.vue new file mode 100644 index 00000000..f10fe442 --- /dev/null +++ b/v0/src/components/helpers/confirmComponent/ConfirmComponent.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/v0/src/components/helpers/createNewProject/TextEditor.vue b/v0/src/components/helpers/createNewProject/TextEditor.vue new file mode 100644 index 00000000..62811a4c --- /dev/null +++ b/v0/src/components/helpers/createNewProject/TextEditor.vue @@ -0,0 +1,445 @@ + + + + + diff --git a/v0/src/components/helpers/createNewProject/UpdateProjectDetail.vue b/v0/src/components/helpers/createNewProject/UpdateProjectDetail.vue new file mode 100644 index 00000000..94327f0b --- /dev/null +++ b/v0/src/components/helpers/createNewProject/UpdateProjectDetail.vue @@ -0,0 +1,264 @@ + + + + + + + diff --git a/v0/src/components/helpers/deleteCircuit/DeleteCircuit.vue b/v0/src/components/helpers/deleteCircuit/DeleteCircuit.vue new file mode 100644 index 00000000..04b7d478 --- /dev/null +++ b/v0/src/components/helpers/deleteCircuit/DeleteCircuit.vue @@ -0,0 +1,151 @@ + + + + + diff --git a/v0/src/components/helpers/promptComponent/PromptComponent.vue b/v0/src/components/helpers/promptComponent/PromptComponent.vue new file mode 100644 index 00000000..fcedab76 --- /dev/null +++ b/v0/src/components/helpers/promptComponent/PromptComponent.vue @@ -0,0 +1,151 @@ + + + + + diff --git a/v0/src/env.d.ts b/v0/src/env.d.ts new file mode 100644 index 00000000..76a98121 --- /dev/null +++ b/v0/src/env.d.ts @@ -0,0 +1,8 @@ +/// + +declare module '*.vue' { + import type { DefineComponent } from 'vue' + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types + const component: DefineComponent<{}, {}, any> + export default component +} diff --git a/v0/src/globalVariables.ts b/v0/src/globalVariables.ts new file mode 100644 index 00000000..34a7a546 --- /dev/null +++ b/v0/src/globalVariables.ts @@ -0,0 +1,27 @@ +/*global ...*/ +/*eslint no-undef: "error"*/ + +declare const window: any + +import jQuery from 'jquery' +window.$ = window.jQuery = jQuery + +import Array from './simulator/src/arrayHelpers.js' +window.Array = Array +window.isUserLoggedIn = false +window.logixProjectId = undefined + +window.restrictedElements = [] +window.globalScope = undefined +window.lightMode = false // To be deprecated +window.projectId = undefined +window.id = undefined +window.loading = false // Flag - all assets are loaded + +window.embed = false + +window.width = undefined +window.height = undefined +window.DPR = window.devicePixelRatio || 1 // devicePixelRatio, 2 for retina displays, 1 for low resolution displays + +window.elementHierarchy = [] diff --git a/v0/src/locales/en.json b/v0/src/locales/en.json new file mode 100644 index 00000000..6681330a --- /dev/null +++ b/v0/src/locales/en.json @@ -0,0 +1,162 @@ +{ + "simulator": { + "save_online": "Save Online", + "save_offline": "Save Offline", + "preview_circuit": "Preview Circuit", + "export_verilog": "Export Verilog", + "insert_subcircuit": "Insert SubCircuit", + "undo": "Undo", + "report_issue": "Report an issue", + "restricted_elements_used": "Restricted elements used:", + "made_with_circuitverse": "Made With CircuitVerse", + "nav": { + "untitled_project": "Untitled", + "sign_out": "Sign Out", + "sign_in": "Sign In", + "user_dropdown": { + "dashboard": "Dashboard", + "my_groups": "My Groups", + "notifications": "Notifications" + }, + "project": { + "heading": "Project", + "project_page": "Project Page", + "new_project": "New Project", + "open_offline": "Open Offline", + "export_as_file": "Export as File", + "import_project": "Import Project", + "save_online": "Save Online", + "save_offline": "Save Offline", + "preview_circuit": "Preview Circuit", + "clear_project": "Clear Project", + "recover_project": "Recover Project", + "view_previous_ui": "View Previous UI" + }, + "circuit": { + "heading": "Circuit", + "new_circuit": "New Circuit +", + "insert_subcircuit": "Insert SubCircuit", + "new_verilog_module_html": "New Verilog\nModule" + }, + "tools": { + "heading": "Tools", + "combinational_analysis_html": "Combinational\nAnalysis", + "hex_bin_dec_converter_html": "Hex-Bin-Dec\nConverter", + "download_image": "Download Image", + "themes": "Themes", + "export_verilog": "Export Verilog", + "custom_shortcut": "Custom Shortcut" + }, + "help": { + "heading": "Help", + "tutorial_guide": "Tutorial Guide", + "user_manual": "User Manual", + "learn_digital_logic": "Learn Digital Logic", + "discussion_forum": "Discussion Forum" + } + }, + "panel_header": { + "circuit_elements": "Circuit Elements", + "layout_elements": "Layout Elements", + "timing_diagram": "Timing Diagram", + "verilog_module": "Verilog Module", + "properties": "Properties", + "layout": "Layout", + "keybinding_preference": "Keybinding Preference", + "render_image": "Render Image", + "select_theme": "Select Theme", + "boolean_logic_table": "BooleanLogicTable", + "open_project": "Open Project", + "bit_converter": "Dec-Bin-Hex-Converter" + }, + "panel_body": { + "circuit_elements": { + "search": "Search...", + "search_result": "No elements found...", + "expansion_panel_title": { + "Input": "Input", + "Output": "Output", + "Gates": "Gates", + "Decoders & Plexers": "Decoders & Plexers", + "Sequential Elements": "Sequential Elements", + "Annotation": "Annotation", + "Misc": "Misc" + } + }, + "timing_diagram": { + "one_cycle": "1 Cycle =", + "units": "Units" + }, + "verilog_module": { + "reset_code": "Reset Code", + "save_code": "Save Code", + "module_in_experiment_notice": "This is an experimental module. The code is not saved unless the \"Save Code\" button is clicked.", + "apply_themes": "Apply Themes", + "select_theme": "Select a theme:" + }, + "layout": { + "width": "Width", + "height": "Height", + "reset_all_nodes": "Reset all nodes:", + "title": "Title", + "title_enabled": "Title Enabled:", + "save": "Save", + "cancel": "Cancel" + }, + "render_image": { + "full_circuit_view": "Full Circuit View", + "current_view": "Current View", + "transparent_background": "Transparent Background", + "resolution": "Resolution:" + }, + "context_menu": { + "paste": "Paste", + "copy": "Copy", + "cut": "Cut", + "delete": "Delete", + "new_circuit": "New Circuit", + "center_focus": "Center Focus" + }, + "bit_converter": { + "decimal_value": "Decimal Value", + "binary_value": "Binary value", + "octal_value": "Octal value", + "hexadecimal_value": "Hexadecimal value" + }, + "custom_shortcut": { + "esc_cancel": "Press Desire Key Combination & press Enter", + "command": "Command", + "keymapping": "Keymapping", + "reset_to_default": "Reset to Default", + "save": "Save" + }, + "report_issue": { + "describe_issue": "Describe your issue:", + "email": "Email", + "optional": " [Optional]", + "report_btn": "Report", + "cancel_btn": "Cancel", + "close_btn": "Close" + } + }, + "tooltip": { + "delete_selected": "Delete Selected", + "download_as_image": "Download as Image", + "fit_to_screen": "Fit to Screen", + "redo": "Redo", + "decrease_size": "Decrease Size", + "increase_size": "Increase Size", + "decrease_height": "Decrease Height", + "increase_height": "Increase Height", + "reset_timing_diagram": "Reset Timing Diagram", + "autocalibrate_cycle_units": "Autocalibrate Cycle Units", + "zoom_in": "Zoom In", + "zoom_out": "Zoom Out", + "resume_timing_diagram": "Resume Timing Diagram", + "pause_timing_diagram": "Pause Timing Diagram", + "decrease_width": "Decrease Width", + "increase_width": "Increase Width", + "reset": "Reset" + } + } +} diff --git a/v0/src/locales/hi.json b/v0/src/locales/hi.json new file mode 100644 index 00000000..39086a16 --- /dev/null +++ b/v0/src/locales/hi.json @@ -0,0 +1,162 @@ +{ + "simulator": { + "save_online": "ऑनलाइन सेव करें", + "save_offline": "ऑफलाइन सेव करें", + "preview_circuit": "सर्किट प्रीव्यू करें", + "export_verilog": "वेरिलोग एक्सपोर्ट करें", + "insert_subcircuit": "सब-सर्किट इन्सर्ट करें", + "undo": "पूर्ववत् करें", + "report_issue": "मामले की रिपोर्ट करें", + "restricted_elements_used": "प्रतिबंधित एलिमेंट्स जिनका इस्तेमाल किया गया:", + "made_with_circuitverse": "सर्किटवर्स में बनाया गया", + "nav": { + "untitled_project": "शीर्षकहीन", + "sign_out": "साइन आउट", + "sign_in": "साइन इन करें", + "user_dropdown": { + "dashboard": "डैशबोर्ड", + "my_groups": "मेरे समूह", + "notifications": "सूचनाएं" + }, + "project": { + "heading": "परियोजना", + "project_page": "परियोजना का पेज", + "new_project": "नयी परियोजना", + "open_offline": "ऑफ़लाइन खोलें", + "save_online": "ऑनलाइन सहेजें", + "save_offline": "ऑफ़लाइन सहेजें", + "export_as_file": "फ़ाइल में निर्यात करें", + "import_project": "फ़ाइल से आयात करें", + "preview_circuit": "पूर्वावलोकन सर्किट", + "clear_project": "परियोजना क्लियर करें", + "recover_project": "परियोजना पुनर्प्राप्त करें", + "view_previous_ui": "पिछला UI देखें" + }, + "circuit": { + "heading": "सर्किट", + "new_circuit": "नया सर्किट +", + "insert_subcircuit": "सब-सर्किट डालें", + "new_verilog_module_html": "नया वेरिलोग\nमॉड्यूल" + }, + "tools": { + "heading": "उपकरण", + "combinational_analysis_html": "कॉम्बिनेशनल\nएनालिसिस", + "hex_bin_dec_converter_html": "Hex-Bin-Dec\nकनवर्टर", + "download_image": "छवि डाउनलोड करें", + "themes": "थीम", + "export_verilog": "निर्यात Verilog", + "custom_shortcut": "कस्टम शॉर्टकट" + }, + "help": { + "heading": "सहायता", + "tutorial_guide": "ट्यूटोरियल गाइड", + "user_manual": "उपयोगकर्ताओं के लिए मैन्युअल", + "learn_digital_logic": "डिजिटल लॉजिक सीखें", + "discussion_forum": "चर्चा के लिए मंच" + } + }, + "panel_header": { + "circuit_elements": "सर्किट के एलिमेंट्स", + "layout_elements": "लेआउट के एलिमेंट्स", + "timing_diagram": "टाइमिंग डायग्राम", + "verilog_module": "वेरिलोग मॉड्यूल", + "properties": "प्रॉपर्टीज", + "layout": "लेआउट", + "keybinding_preference": "कीबाइंडिंग परेफरेंस", + "render_image": "छवि प्रस्तुत करें", + "select_theme": "थीम चुनें", + "boolean_logic_table": "बूलियन लॉजिक टेबल", + "open_project": "परियोजना खोलें", + "bit_converter": "Dec-Bin-Hex-कनवर्टर" + }, + "panel_body": { + "circuit_elements": { + "search": "खोजें...", + "search_result": "कोई तत्व नहीं मिला...", + "expansion_panel_title": { + "Input": "इनपुट", + "Output": "उत्पादन", + "Gates": "गेट्स", + "Decoders & Plexers": "डिकोडर्स और प्लेक्सर्स", + "Sequential Elements": "अनुक्रमिक तत्व", + "Annotation": "टिप्पणी", + "Misc": "विविध" + } + }, + "timing_diagram": { + "one_cycle": "1 साइकिल =", + "units": "यूनिट्स" + }, + "verilog_module": { + "reset_code": "कोड रिसेट करें", + "save_code": "कोड सेव करें", + "module_in_experiment_notice": "यह एक प्रायोगिक मॉड्यूल है। कोड तब तक सेव नहीं होगा जब तक \"कोड सेव करें\" बटन क्लिक नहीं किया जाता।", + "apply_themes": "थीम लागू करें", + "select_theme": "थीम चुनें:" + }, + "layout": { + "width": "चौड़ाई", + "height": "ऊंचाई", + "reset_all_nodes": "सभी नोड्स रीसेट करें:", + "title": "शीर्षक", + "title_enabled": "शीर्षक इनेबल किया गया:", + "save": "सेव करें", + "cancel": "रद्द करें" + }, + "render_image": { + "full_circuit_view": "सर्किट का पूर्ण दृश्य देखें", + "current_view": "वर्तमान का दृश्य", + "transparent_background": "बैकग्राउंड ट्रांसपेरेंट करें", + "resolution": "रेजोलुएशन" + }, + "context_menu": { + "paste": "पेस्ट करें", + "copy": "कॉपी करें", + "cut": "कट करें", + "delete": "हटाएं", + "new_circuit": "नया सर्किट", + "center_focus": "सेंटर फोकस" + }, + "bit_converter": { + "decimal_value": "डेसीमल वैल्यू", + "binary_value": "बाइनरी वैल्यू", + "octal_value": "ऑक्टल वैल्यू", + "hexadecimal_value": "हेक्साडेसिमल वैल्यू" + }, + "custom_shortcut": { + "esc_cancel": "वांछित कुंजी संयोजन दबाएं और स्टोर करने के लिए enter दबाएं", + "command": "कमांड", + "keymapping": "कीमैपिंग", + "reset_to_default": "डिफ़ॉल्ट पर रीसेट करें", + "save": "सेव करें" + }, + "report_issue": { + "describe_issue": "अपनी समस्या का वर्णन करें:", + "email": "ईमेल", + "optional": " [वैकल्पिक]", + "report_btn": "रिपोर्ट करें", + "cancel_btn": "रद्द करें", + "close_btn": "बंद करें" + } + }, + "tooltip": { + "delete_selected": "चयनित हटाए", + "download_as_image": "छवि के रूप में डाउनलोड करें", + "fit_to_screen": "स्क्रीन में फिट", + "redo": "फिर से करें", + "decrease_size": "आकार घटाएं", + "increase_size": "आकार बढ़ाएँ", + "decrease_height": "ऊंचाई घटाएं", + "increase_height": "ऊँचाई बढ़ाएँ", + "reset_timing_diagram": "टाइमिंग डायग्राम रिसेट करें", + "autocalibrate_cycle_units": "साइक्ल यूनिट्स को ऑटोकैलिब्रेट करें", + "zoom_in": "ज़ूम इन", + "zoom_out": "ज़ूम आउट", + "resume_timing_diagram": "टाइमिंग डायग्राम को रिज्यूम करें", + "pause_timing_diagram": "टाइमिंग डायग्राम को पॉज करें", + "decrease_width": "चौड़ाई घटाएं", + "increase_width": "चौड़ाई बढ़ाएँ", + "reset": "रिसेट करें" + } + } +} diff --git a/v0/src/locales/i18n.ts b/v0/src/locales/i18n.ts new file mode 100644 index 00000000..d82d9d3f --- /dev/null +++ b/v0/src/locales/i18n.ts @@ -0,0 +1,16 @@ +import { createI18n } from 'vue-i18n' +import en from './en.json' +import hi from './hi.json' + +const i18n = createI18n({ + legacy: false, + locale: 'en', + globalInjection: true, + // messages + messages: { + en, + hi, + }, +}) + +export default i18n diff --git a/v0/src/main.ts b/v0/src/main.ts new file mode 100644 index 00000000..616edaa2 --- /dev/null +++ b/v0/src/main.ts @@ -0,0 +1,29 @@ +import { useActions } from './store/SimulatorStore/actions' +import { createApp } from 'vue' +import App from './App.vue' +import vuetify from './plugins/vuetify' +import router from './router/index' +import { createPinia } from 'pinia' +import { loadFonts } from './plugins/webfontloader' +import i18n from './locales/i18n' + +import 'bootstrap' + +import './globalVariables' + +import './styles/css/main.stylesheet.css' +import '../../node_modules/bootstrap/scss/bootstrap.scss' +import './styles/color_theme.scss' +import './styles/simulator.scss' +import './styles/tutorials.scss' +import '@fortawesome/fontawesome-free/css/all.css' + +loadFonts() + +const app = createApp(App) + +app.use(createPinia()) +app.use(vuetify) +app.use(router) +app.use(i18n) +app.mount('#app') diff --git a/v0/src/pages/embed.vue b/v0/src/pages/embed.vue new file mode 100644 index 00000000..18a68a24 --- /dev/null +++ b/v0/src/pages/embed.vue @@ -0,0 +1,317 @@ + + + + + diff --git a/v0/src/pages/simulator.vue b/v0/src/pages/simulator.vue new file mode 100644 index 00000000..98c3e17e --- /dev/null +++ b/v0/src/pages/simulator.vue @@ -0,0 +1,27 @@ + + + diff --git a/v0/src/pages/simulatorHandler.vue b/v0/src/pages/simulatorHandler.vue new file mode 100644 index 00000000..d9e935ed --- /dev/null +++ b/v0/src/pages/simulatorHandler.vue @@ -0,0 +1,96 @@ + + + + + diff --git a/v0/src/plugins/vuetify.ts b/v0/src/plugins/vuetify.ts new file mode 100644 index 00000000..d58e9e51 --- /dev/null +++ b/v0/src/plugins/vuetify.ts @@ -0,0 +1,9 @@ +// Styles +import '@mdi/font/css/materialdesignicons.css' +import 'vuetify/styles' + +// Vuetify +import { createVuetify } from 'vuetify' + +export default createVuetify() +// https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides diff --git a/v0/src/plugins/webfontloader.ts b/v0/src/plugins/webfontloader.ts new file mode 100644 index 00000000..f3287331 --- /dev/null +++ b/v0/src/plugins/webfontloader.ts @@ -0,0 +1,17 @@ +/** + * plugins/webfontloader.js + * + * webfontloader documentation: https://github.com/typekit/webfontloader + */ + +export async function loadFonts() { + const webFontLoader = await import( + /* webpackChunkName: "webfontloader" */ 'webfontloader' + ) + + webFontLoader.load({ + google: { + families: ['Roboto:100,300,400,500,700,900&display=swap'], + }, + }) +} diff --git a/v0/src/router/index.ts b/v0/src/router/index.ts new file mode 100644 index 00000000..730aea09 --- /dev/null +++ b/v0/src/router/index.ts @@ -0,0 +1,41 @@ +import { createRouter, createWebHistory } from 'vue-router' +import simulatorHandler from '../pages/simulatorHandler.vue' +import Embed from '../pages/embed.vue' + +const routes = [ + { + path: '/', + redirect: '/simulatorvue', // @TODO: update later back to /simulator + }, + { + path: '/simulatorvue', // @TODO: update later back to /simulator + name: 'simulator', + component: simulatorHandler, + children: [ + { + path: 'edit/:projectId', + name: 'simulator-edit', + component: simulatorHandler, + props: true, + }, + ], + }, + { + path: '/simulatorvue/:projectId', + name: 'simulator-view', + component: Embed, + props: true, + }, + { + path: '/simulatorvue/embed/:projectId', + name: 'simulator-embed', + component: Embed, + props: true, + }, +] +const router = createRouter({ + history: createWebHistory(), + routes, +}) + +export default router diff --git a/v0/src/shims-vuetify.d.ts b/v0/src/shims-vuetify.d.ts new file mode 100644 index 00000000..e4a5d5c5 --- /dev/null +++ b/v0/src/shims-vuetify.d.ts @@ -0,0 +1,3 @@ +declare module 'vuetify' +declare module 'vuetify/lib/components' +declare module 'vuetify/lib/directives' diff --git a/v0/src/simulator/spec/circuits/Decoders-plexers-circuitdata.json b/v0/src/simulator/spec/circuits/Decoders-plexers-circuitdata.json new file mode 100644 index 00000000..bf601345 --- /dev/null +++ b/v0/src/simulator/spec/circuits/Decoders-plexers-circuitdata.json @@ -0,0 +1,1109 @@ +{ + "name": "Decoders and Plexers", + "timePeriod": 500, + "clockEnabled": true, + "projectId": "RVvp1Qq4hf3eVcfUO7sE", + "focussedCircuit": 11597572508, + "orderedTabs": ["11597572508"], + "scopes": [ + { + "layout": { + "width": 100, + "height": 280, + "title_x": 50, + "title_y": 13, + "titleEnabled": true + }, + "verilogMetadata": { + "isVerilogCircuit": false, + "isMainCircuit": false, + "code": "// Write Some Verilog Code Here!", + "subCircuitScopeIds": [] + }, + "allNodes": [ + { + "x": -10, + "y": -10, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [14] + }, + { + "x": -10, + "y": 10, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [5] + }, + { + "x": 10, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [6] + }, + { + "x": 0, + "y": 20, + "type": 0, + "bitWidth": 1, + "label": "Control Signal", + "connections": [24] + }, + { + "x": 10, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [14] + }, + { + "x": 10, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [1] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [2] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [15] + }, + { + "x": -10, + "y": -10, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [12] + }, + { + "x": -10, + "y": 10, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [13] + }, + { + "x": 0, + "y": 20, + "type": 0, + "bitWidth": 1, + "label": "Control Signal", + "connections": [11] + }, + { + "x": 80, + "y": 510, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [10, 25] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [8] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [9] + }, + { + "x": -30, + "y": 130, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [0, 4, 15] + }, + { + "x": -30, + "y": 340, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [7, 14] + }, + { + "x": -20, + "y": 0, + "type": 0, + "bitWidth": 2, + "label": "Input", + "connections": [20] + }, + { + "x": 20, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "Output", + "connections": [19] + }, + { + "x": 0, + "y": 20, + "type": 0, + "bitWidth": 1, + "label": "Bit Selector", + "connections": [26] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [17] + }, + { + "x": 20, + "y": 0, + "type": 1, + "bitWidth": 2, + "label": "", + "connections": [16] + }, + { + "x": 10, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [22] + }, + { + "x": -180, + "y": 160, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [21, 23] + }, + { + "x": -180, + "y": 220, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [22, 24, 25] + }, + { + "x": 90, + "y": 220, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [3, 23] + }, + { + "x": -180, + "y": 510, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [11, 23, 27] + }, + { + "x": 70, + "y": 700, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [18, 27] + }, + { + "x": -180, + "y": 700, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [25, 26] + }, + { + "x": -10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [33] + }, + { + "x": 20, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [32] + }, + { + "x": 20, + "y": 20, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [31] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [30] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [29] + }, + { + "x": 10, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [28] + }, + { + "x": -10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [39] + }, + { + "x": 20, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [37] + }, + { + "x": 20, + "y": 20, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [38] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [35] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [36] + }, + { + "x": 10, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [34] + }, + { + "x": -10, + "y": -10, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [46] + }, + { + "x": -10, + "y": 10, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [47] + }, + { + "x": 30, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [45] + }, + { + "x": 10, + "y": 30, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [44] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [43] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [42] + }, + { + "x": 10, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [40] + }, + { + "x": 10, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [41] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [53] + }, + { + "x": -10, + "y": -10, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [51] + }, + { + "x": -10, + "y": 10, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [52] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [49] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [50] + }, + { + "x": 10, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [48] + } + ], + "id": 11597572508, + "name": "Main", + "Input": [ + { + "x": -70, + "y": 130, + "objectType": "Input", + "label": "inp1", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 0, + "customData": { + "nodes": { + "output1": 4 + }, + "values": { + "state": 1 + }, + "constructorParamaters": [ + "RIGHT", + 1, + { + "x": 0, + "y": 60, + "id": "cZW4OLLsTA1aBoRSmHxv" + } + ] + } + }, + { + "x": -70, + "y": 150, + "objectType": "Input", + "label": "inp2", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 0, + "customData": { + "nodes": { + "output1": 5 + }, + "values": { + "state": 1 + }, + "constructorParamaters": [ + "RIGHT", + 1, + { + "x": 0, + "y": 20, + "id": "OhBFKFzir02JVzBVOV44" + } + ] + } + }, + { + "x": -30, + "y": 630, + "objectType": "Input", + "label": "", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 0, + "customData": { + "nodes": { + "output1": 20 + }, + "values": { + "state": 3 + }, + "constructorParamaters": [ + "RIGHT", + 2, + { + "x": 0, + "y": 120, + "id": "Tci49l79hiLHSfxqSPhk" + } + ] + } + }, + { + "x": -360, + "y": 160, + "objectType": "Input", + "label": "s", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 0, + "customData": { + "nodes": { + "output1": 21 + }, + "values": { + "state": 0 + }, + "constructorParamaters": [ + "RIGHT", + 1, + { + "x": 0, + "y": 40, + "id": "2jYcmNNFpmoq6jcELh8X" + } + ] + } + }, + { + "x": -10, + "y": 840, + "objectType": "Input", + "label": "", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 0, + "customData": { + "nodes": { + "output1": 33 + }, + "values": { + "state": 0 + }, + "constructorParamaters": [ + "RIGHT", + 1, + { + "x": 0, + "y": 80, + "id": "wUbTZgbmrEStf17qxgOw" + } + ] + } + }, + { + "x": 0, + "y": 1030, + "objectType": "Input", + "label": "", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 0, + "customData": { + "nodes": { + "output1": 39 + }, + "values": { + "state": 1 + }, + "constructorParamaters": [ + "RIGHT", + 1, + { + "x": 0, + "y": 100, + "id": "hiFGtGajuArxucFOfydA" + } + ] + } + }, + { + "x": -550, + "y": 310, + "objectType": "Input", + "label": "", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 0, + "customData": { + "nodes": { + "output1": 46 + }, + "values": { + "state": 1 + }, + "constructorParamaters": [ + "RIGHT", + 1, + { + "x": 0, + "y": 140, + "id": "6br60gi40FN03qZRFG9W" + } + ] + } + }, + { + "x": -550, + "y": 330, + "objectType": "Input", + "label": "", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 0, + "customData": { + "nodes": { + "output1": 47 + }, + "values": { + "state": 1 + }, + "constructorParamaters": [ + "RIGHT", + 1, + { + "x": 0, + "y": 160, + "id": "EcNRmJVcoyFZdMUmGwQw" + } + ] + } + }, + { + "x": -600, + "y": 680, + "objectType": "Input", + "label": "", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 0, + "customData": { + "nodes": { + "output1": 53 + }, + "values": { + "state": 1 + }, + "constructorParamaters": [ + "RIGHT", + 1, + { + "x": 0, + "y": 180, + "id": "eAluIwGbrt2vmFD37xUb" + } + ] + } + } + ], + "Output": [ + { + "x": 250, + "y": 140, + "objectType": "Output", + "label": "out1", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 6 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 40, + "id": "PQtMRkU1V36zoiUZaKnC" + } + ] + } + }, + { + "x": 340, + "y": 330, + "objectType": "Output", + "label": "out3", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 12 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 20, + "id": "rl7YenrdGRoBr9BXA2Ee" + } + ] + } + }, + { + "x": 340, + "y": 350, + "objectType": "Output", + "label": "out4", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 13 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 60, + "id": "gPrg5glyUDxsQmGldTow" + } + ] + } + }, + { + "x": 260, + "y": 630, + "objectType": "Output", + "label": "", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 19 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 80, + "id": "qqnDrlsm0T8y1dyyiGlL" + } + ] + } + }, + { + "x": 270, + "y": 860, + "objectType": "Output", + "label": "", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 31 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 120, + "id": "2smYfkmptbhEZ2gFBMmf" + } + ] + } + }, + { + "x": 270, + "y": 840, + "objectType": "Output", + "label": "", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 32 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 100, + "id": "45NWNA9b3l54VZE5cFYS" + } + ] + } + }, + { + "x": 220, + "y": 1030, + "objectType": "Output", + "label": "", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 37 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 140, + "id": "COIaU2CIfnmcZ8h0Nxpq" + } + ] + } + }, + { + "x": 220, + "y": 1050, + "objectType": "Output", + "label": "", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 38 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 160, + "id": "bJ7CLsYt9Johbanua2d4" + } + ] + } + }, + { + "x": -460, + "y": 400, + "objectType": "Output", + "label": "", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 44 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 180, + "id": "Of7qPmfBvyW9mhiLkfpN" + } + ] + } + }, + { + "x": -290, + "y": 320, + "objectType": "Output", + "label": "", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 45 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 200, + "id": "LLwXovDSOe6rplHPIDhv" + } + ] + } + }, + { + "x": -310, + "y": 670, + "objectType": "Output", + "label": "", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 51 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 220, + "id": "Yo4Zo5P6gClWQJZWt5DF" + } + ] + } + }, + { + "x": -310, + "y": 690, + "objectType": "Output", + "label": "", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 52 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 240, + "id": "T86CxN5DtKbZtDaZXSMx" + } + ] + } + } + ], + "Multiplexer": [ + { + "x": 90, + "y": 140, + "objectType": "Multiplexer", + "label": "multiplexer", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 10, + "customData": { + "constructorParamaters": ["RIGHT", 1, 1], + "nodes": { + "inp": [0, 1], + "output1": 2, + "controlSignalInput": 3 + } + } + } + ], + "BitSelector": [ + { + "x": 70, + "y": 630, + "objectType": "BitSelector", + "label": "", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 10, + "customData": { + "nodes": { + "inp1": 16, + "output1": 17, + "bitSelectorInp": 18 + }, + "constructorParamaters": ["RIGHT", 2, 1] + } + } + ], + "Demultiplexer": [ + { + "x": 80, + "y": 340, + "objectType": "Demultiplexer", + "label": "demultiplexer", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 10, + "customData": { + "constructorParamaters": ["LEFT", 1, 1], + "nodes": { + "output1": [8, 9], + "input": 7, + "controlSignalInput": 10 + } + } + } + ], + "MSB": [ + { + "x": 70, + "y": 840, + "objectType": "MSB", + "label": "", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 10, + "customData": { + "nodes": { + "inp1": 28, + "output1": 29, + "enable": 30 + }, + "constructorParamaters": ["RIGHT", 1] + } + } + ], + "LSB": [ + { + "x": 70, + "y": 1030, + "objectType": "LSB", + "label": "", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 10, + "customData": { + "nodes": { + "inp1": 34, + "output1": 35, + "enable": 36 + }, + "constructorParamaters": ["RIGHT", 1] + } + } + ], + "PriorityEncoder": [ + { + "x": -480, + "y": 320, + "objectType": "PriorityEncoder", + "label": "", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 10, + "customData": { + "nodes": { + "inp1": [40, 41], + "output1": [42], + "enable": 43 + }, + "constructorParamaters": ["RIGHT", 1] + } + } + ], + "Decoder": [ + { + "x": -480, + "y": 680, + "objectType": "Decoder", + "label": "", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 10, + "customData": { + "constructorParamaters": ["LEFT", 1], + "nodes": { + "output1": [49, 50], + "input": 48 + } + } + } + ], + "restrictedCircuitElementsUsed": [], + "nodes": [11, 14, 15, 22, 23, 24, 25, 26, 27] + } + ] +} diff --git a/v0/src/simulator/spec/circuits/gates-circuitdata.json b/v0/src/simulator/spec/circuits/gates-circuitdata.json new file mode 100644 index 00000000..a5c96609 --- /dev/null +++ b/v0/src/simulator/spec/circuits/gates-circuitdata.json @@ -0,0 +1,710 @@ +{ + "name": "gates-circuitdata", + "timePeriod": 500, + "clockEnabled": true, + "projectId": "hCqg1Ns4JVckHsnyKQDi", + "focussedCircuit": 11597572508, + "orderedTabs": ["11597572508"], + "scopes": [ + { + "layout": { + "width": 100, + "height": 280, + "title_x": 50, + "title_y": 13, + "titleEnabled": true + }, + "verilogMetadata": { + "isVerilogCircuit": false, + "isMainCircuit": false, + "code": "// Write Some Verilog Code Here!", + "subCircuitScopeIds": [] + }, + "allNodes": [ + { + "x": -10, + "y": -10, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [29] + }, + { + "x": -10, + "y": 10, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [31] + }, + { + "x": 20, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [22] + }, + { + "x": -10, + "y": -10, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [30] + }, + { + "x": -10, + "y": 10, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [32] + }, + { + "x": 20, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [23] + }, + { + "x": -10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [35] + }, + { + "x": 20, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [25] + }, + { + "x": -20, + "y": -10, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [38] + }, + { + "x": -20, + "y": 10, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [40] + }, + { + "x": 20, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [27] + }, + { + "x": -10, + "y": -10, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [33] + }, + { + "x": -10, + "y": 10, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [34] + }, + { + "x": 30, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [24] + }, + { + "x": -10, + "y": -10, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [37] + }, + { + "x": -10, + "y": 10, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [39] + }, + { + "x": 30, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [28] + }, + { + "x": -20, + "y": -10, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [36] + }, + { + "x": -20, + "y": 10, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [41] + }, + { + "x": 30, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [26] + }, + { + "x": 10, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [29] + }, + { + "x": 10, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [31] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [2] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [5] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [13] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [7] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [19] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [10] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [16] + }, + { + "x": 260, + "y": -10, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [0, 20, 30] + }, + { + "x": 260, + "y": 130, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [29, 3, 33] + }, + { + "x": 210, + "y": 10, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [1, 21, 32] + }, + { + "x": 210, + "y": 150, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [31, 4, 34] + }, + { + "x": 260, + "y": 300, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [30, 11, 35] + }, + { + "x": 210, + "y": 320, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [32, 12, 41] + }, + { + "x": 260, + "y": 460, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [33, 6, 36] + }, + { + "x": 260, + "y": 600, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [35, 17, 38] + }, + { + "x": 260, + "y": 820, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [38, 14] + }, + { + "x": 260, + "y": 740, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [36, 37, 8] + }, + { + "x": 210, + "y": 840, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [15, 40] + }, + { + "x": 210, + "y": 760, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [39, 9, 41] + }, + { + "x": 210, + "y": 620, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [34, 40, 18] + } + ], + "id": 11597572508, + "name": "Main", + "Input": [ + { + "x": -30, + "y": -10, + "objectType": "Input", + "label": "inp1", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 0, + "customData": { + "nodes": { + "output1": 20 + }, + "values": { + "state": 1 + }, + "constructorParamaters": [ + "RIGHT", + 1, + { + "x": 0, + "y": 20, + "id": "55mKFwVFnZeU6ucfO8am" + } + ] + } + }, + { + "x": -30, + "y": 10, + "objectType": "Input", + "label": "inp2", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 0, + "customData": { + "nodes": { + "output1": 21 + }, + "values": { + "state": 1 + }, + "constructorParamaters": [ + "RIGHT", + 1, + { + "x": 0, + "y": 40, + "id": "2O2YfZk7yuqLrtiRb429" + } + ] + } + } + ], + "Output": [ + { + "x": 500, + "y": 0, + "objectType": "Output", + "label": "out1", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 22 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 20, + "id": "RarwxCcQgRygZftUFlMr" + } + ] + } + }, + { + "x": 510, + "y": 140, + "objectType": "Output", + "label": "out2", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 23 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 40, + "id": "N0agpzN04aqy9nLexP98" + } + ] + } + }, + { + "x": 520, + "y": 310, + "objectType": "Output", + "label": "out3", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 24 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 60, + "id": "2yO9fxrqrjPk1urH47Fc" + } + ] + } + }, + { + "x": 520, + "y": 460, + "objectType": "Output", + "label": "out4", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 25 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 80, + "id": "IN7sNsKhkByzBHRShVdu" + } + ] + } + }, + { + "x": 520, + "y": 610, + "objectType": "Output", + "label": "out5", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 26 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 100, + "id": "Dfr4VEfv27q3AOwkLqpx" + } + ] + } + }, + { + "x": 520, + "y": 750, + "objectType": "Output", + "label": "out6", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 27 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 120, + "id": "FGeZ7ip5nJo9MUlEyJ35" + } + ] + } + }, + { + "x": 530, + "y": 830, + "objectType": "Output", + "label": "out7", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 28 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 140, + "id": "oYVAxzNmrQgKjC7I3xOg" + } + ] + } + } + ], + "NotGate": [ + { + "x": 340, + "y": 460, + "objectType": "NotGate", + "label": "NOT GATE", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 10, + "customData": { + "constructorParamaters": ["RIGHT", 1], + "nodes": { + "output1": 7, + "inp1": 6 + } + } + } + ], + "OrGate": [ + { + "x": 330, + "y": 140, + "objectType": "OrGate", + "label": "OR GATE", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 10, + "customData": { + "constructorParamaters": ["RIGHT", 2, 1], + "nodes": { + "inp": [3, 4], + "output1": 5 + } + } + } + ], + "AndGate": [ + { + "x": 320, + "y": 0, + "objectType": "AndGate", + "label": "AND GATE", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 10, + "customData": { + "constructorParamaters": ["RIGHT", 2, 1], + "nodes": { + "inp": [0, 1], + "output1": 2 + } + } + } + ], + "NorGate": [ + { + "x": 330, + "y": 830, + "objectType": "NorGate", + "label": "NOR", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 10, + "customData": { + "constructorParamaters": ["RIGHT", 2, 1], + "nodes": { + "inp": [14, 15], + "output1": 16 + } + } + } + ], + "NandGate": [ + { + "x": 340, + "y": 310, + "objectType": "NandGate", + "label": "NAND GATE", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 10, + "customData": { + "constructorParamaters": ["RIGHT", 2, 1], + "nodes": { + "inp": [11, 12], + "output1": 13 + } + } + } + ], + "XorGate": [ + { + "x": 330, + "y": 750, + "objectType": "XorGate", + "label": "XOR", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 10, + "customData": { + "constructorParamaters": ["RIGHT", 2, 1], + "nodes": { + "inp": [8, 9], + "output1": 10 + } + } + } + ], + "XnorGate": [ + { + "x": 330, + "y": 610, + "objectType": "XnorGate", + "label": "XNOR GATE", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 10, + "customData": { + "constructorParamaters": ["RIGHT", 2, 1], + "nodes": { + "inp": [17, 18], + "output1": 19 + } + } + } + ], + "restrictedCircuitElementsUsed": [], + "nodes": [29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41] + } + ] +} diff --git a/v0/src/simulator/spec/data.spec.js b/v0/src/simulator/spec/data.spec.js new file mode 100644 index 00000000..f91a08c8 --- /dev/null +++ b/v0/src/simulator/spec/data.spec.js @@ -0,0 +1,121 @@ +/** + * @jest-environment jsdom + */ + +import CodeMirror from 'codemirror' +import { setup } from '../src/setup' +import load from '../src/data/load' +import gatesCircuitData from './circuits/gates-circuitdata.json' +import decoderCircuitData from './circuits/Decoders-plexers-circuitdata.json' +import { checkIfBackup, scheduleBackup } from '../src/data/backupCircuit' +import undo from '../src/data/undo' +import redo from '../src/data/redo' +import save from '../src/data/save' +import { + clearProject, + newProject, + recoverProject, + saveOffline, + openOffline, +} from '../src/data/project' +import createSaveAsImgPrompt from '../src/data/saveImage' + +jest.mock('codemirror') + +describe('data dir working', () => { + CodeMirror.fromTextArea.mockReturnValueOnce({ setValue: () => {} }) + window.confirm = jest.fn(() => true) + setup() + + test('load gates_circuitData without throwing error', () => { + expect(() => load(gatesCircuitData)).not.toThrow() + }) + + test('should load another circuit data decoder_circuitData', () => { + expect(() => load(decoderCircuitData)).not.toThrow() + }) + + test('schedule backup working', () => { + // toggle states of inputs a dn then run schedule backup + globalScope.Input.forEach((input) => { + input.state = input.state === 1 ? 0 : 1 + expect(() => scheduleBackup()).not.toThrow() + }) + }) + + test('check if backup performed', () => { + expect(() => checkIfBackup(globalScope)).toBeTruthy() + }) + + test('undo working', () => { + const beforeUndo = { + backups: globalScope.backups.length, + history: globalScope.history.length, + } + for (let i = 1; i < beforeUndo.backups; i++) { + undo() + const afterUndo = { + backups: globalScope.backups.length + i, + history: globalScope.history.length - i, + } + expect(afterUndo).toEqual(beforeUndo) + } + }) + + test('redo working', () => { + const beforeRedo = { + backups: globalScope.backups.length, + history: globalScope.history.length, + } + for (let i = 1; i < beforeRedo.history; i++) { + redo() + const afterRedo = { + backups: globalScope.backups.length - i, + history: globalScope.history.length + i, + } + expect(afterRedo).toEqual(beforeRedo) + } + }) + + test('save updated circuit_data', () => { + // save project + window.logixProjectId = decoderCircuitData.projectId + expect(() => save()).not.toThrow() + }) + + test('project working', () => { + // create new project + expect(() => newProject(true)).not.toThrow() + }) + + test('clear Project working', () => { + // clear project + expect(() => clearProject()).not.toThrow() + }) + + test('recover Project working', () => { + // recover project from localstorage + localStorage.setItem('recover', JSON.stringify(gatesCircuitData)) + expect(() => recoverProject()).not.toThrow() + }) + + test('SaveOffline working', () => { + // save offline gate project + expect(() => saveOffline()).not.toThrow() + }) + + test('OpenOffline working', () => { + // open dialog + openOffline() + // click on first input + $('#openProjectDialog input')[0].click() + // click on open button + $('#Open_offline_btn')[0].click() + // it should load the offline saved project + expect(globalScope.id).toBe(11597572508) + }) + + test('saveImage working', () => { + expect(() => createSaveAsImgPrompt()).not.toThrow() + }) +}) diff --git a/v0/src/simulator/spec/gates.spec.js b/v0/src/simulator/spec/gates.spec.js new file mode 100644 index 00000000..90c7b1bd --- /dev/null +++ b/v0/src/simulator/spec/gates.spec.js @@ -0,0 +1,57 @@ +/** + * @jest-environment jsdom + */ + +import CodeMirror from 'codemirror' +import { setup } from '../src/setup' + +import load from '../src/data/load' +import circuitData from './circuits/gates-circuitdata.json' +import testData from './testData/gates-testdata.json' +import { runAll } from '../src/testbench' + +jest.mock('codemirror') + +describe('Simulator Gates Testing', () => { + CodeMirror.fromTextArea.mockReturnValueOnce({ setValue: (text) => {} }) + setup() + + test('load circuitData', () => { + expect(() => load(circuitData)).not.toThrow() + }) + + test('AND gate testing', () => { + const result = runAll(testData.AndGate) + expect(result.summary.passed).toBe(4) + }) + + test('NAND gate testing', () => { + const result = runAll(testData.nandGate) + expect(result.summary.passed).toBe(4) + }) + + test('NOR gate testing', () => { + const result = runAll(testData.norGate) + expect(result.summary.passed).toBe(4) + }) + + test('NOT gate testing', () => { + const result = runAll(testData.notGate) + expect(result.summary.passed).toBe(2) + }) + + test('OR gate testing', () => { + const result = runAll(testData.OrGate) + expect(result.summary.passed).toBe(4) + }) + + test('XNOR gate testing', () => { + const result = runAll(testData.xnorGate) + expect(result.summary.passed).toBe(4) + }) + + test('XOR gate testing', () => { + const result = runAll(testData.xorGate) + expect(result.summary.passed).toBe(4) + }) +}) diff --git a/v0/src/simulator/spec/testData/gates-testdata.json b/v0/src/simulator/spec/testData/gates-testdata.json new file mode 100644 index 00000000..3152253b --- /dev/null +++ b/v0/src/simulator/spec/testData/gates-testdata.json @@ -0,0 +1,200 @@ +{ + "AndGate": { + "type": "comb", + "title": "AND Gate", + "groups": [ + { + "label": "Group 1", + "inputs": [ + { + "label": "inp1", + "bitWidth": 1, + "values": ["0", "0", "1", "1"] + }, + { + "label": "inp2", + "bitWidth": 1, + "values": ["0", "1", "0", "1"] + } + ], + "outputs": [ + { + "label": "out1", + "bitWidth": 1, + "values": ["0", "0", "0", "1"] + } + ], + "n": 4 + } + ] + }, + "OrGate": { + "type": "comb", + "title": "OR Gate", + "groups": [ + { + "label": "Group 1", + "inputs": [ + { + "label": "inp1", + "bitWidth": 1, + "values": ["0", "0", "1", "1"] + }, + { + "label": "inp2", + "bitWidth": 1, + "values": ["0", "1", "0", "1"] + } + ], + "outputs": [ + { + "label": "out2", + "bitWidth": 1, + "values": ["0", "1", "1", "1"] + } + ], + "n": 4 + } + ] + }, + "nandGate": { + "type": "comb", + "title": "NAND Gate", + "groups": [ + { + "label": "Group 1", + "inputs": [ + { + "label": "inp1", + "bitWidth": 1, + "values": ["0", "0", "1", "1"] + }, + { + "label": "inp2", + "bitWidth": 1, + "values": ["0", "1", "0", "1"] + } + ], + "outputs": [ + { + "label": "out3", + "bitWidth": 1, + "values": ["1", "1", "1", "0"] + } + ], + "n": 4 + } + ] + }, + "xorGate": { + "type": "comb", + "title": "XOR Gate", + "groups": [ + { + "label": "Group 1", + "inputs": [ + { + "label": "inp1", + "bitWidth": 1, + "values": ["0", "0", "1", "1"] + }, + { + "label": "inp2", + "bitWidth": 1, + "values": ["0", "1", "0", "1"] + } + ], + "outputs": [ + { + "label": "out6", + "bitWidth": 1, + "values": ["0", "1", "1", "0"] + } + ], + "n": 4 + } + ] + }, + "norGate": { + "type": "comb", + "title": "NOR Gate", + "groups": [ + { + "label": "Group 1", + "inputs": [ + { + "label": "inp1", + "bitWidth": 1, + "values": ["0", "0", "1", "1"] + }, + { + "label": "inp2", + "bitWidth": 1, + "values": ["0", "1", "0", "1"] + } + ], + "outputs": [ + { + "label": "out7", + "bitWidth": 1, + "values": ["1", "0", "0", "0"] + } + ], + "n": 4 + } + ] + }, + "notGate": { + "type": "comb", + "title": "NOT GAte", + "groups": [ + { + "label": "Group 1", + "inputs": [ + { + "label": "inp1", + "bitWidth": 1, + "values": ["0", "1"] + } + ], + "outputs": [ + { + "label": "out4", + "bitWidth": 1, + "values": ["1", "0"] + } + ], + "n": 2 + } + ] + }, + "xnorGate": { + "type": "comb", + "title": "XNOR GAte", + "groups": [ + { + "label": "Group 1", + "inputs": [ + { + "label": "inp1", + "bitWidth": 1, + "values": ["0", "0", "1", "1"] + }, + { + "label": "inp2", + "bitWidth": 1, + "values": ["0", "1", "0", "1"] + } + ], + "outputs": [ + { + "label": "out5", + "bitWidth": 1, + "values": ["1", "0", "0", "1"] + } + ], + "n": 4 + } + ] + } +} diff --git a/v0/src/simulator/src/Verilog2CV.js b/v0/src/simulator/src/Verilog2CV.js new file mode 100644 index 00000000..83e7641e --- /dev/null +++ b/v0/src/simulator/src/Verilog2CV.js @@ -0,0 +1,264 @@ +import { + createNewCircuitScope, + switchCircuit, + changeCircuitName, +} from './circuit' +import SubCircuit from './subcircuit' +import simulationArea from './simulationArea' +import CodeMirror from 'codemirror/lib/codemirror.js' +import 'codemirror/lib/codemirror.css' + +// Importing CodeMirror themes +import 'codemirror/theme/3024-day.css' +import 'codemirror/theme/solarized.css' +import 'codemirror/theme/elegant.css' +import 'codemirror/theme/neat.css' +import 'codemirror/theme/idea.css' +import 'codemirror/theme/neo.css' +import 'codemirror/theme/3024-night.css' +import 'codemirror/theme/blackboard.css' +import 'codemirror/theme/cobalt.css' +import 'codemirror/theme/the-matrix.css' +import 'codemirror/theme/night.css' +import 'codemirror/theme/monokai.css' +import 'codemirror/theme/midnight.css' + +import 'codemirror/addon/hint/show-hint.css' +import 'codemirror/mode/verilog/verilog.js' +import 'codemirror/addon/edit/closebrackets.js' +import 'codemirror/addon/hint/anyword-hint.js' +import 'codemirror/addon/hint/show-hint.js' +import 'codemirror/addon/display/autorefresh.js' +import { showError, showMessage } from './utils' +import { showProperties } from './ux' + +var editor +var verilogMode = false + +export async function createVerilogCircuit() { + const returned = await createNewCircuitScope( + undefined, + undefined, + true, + true + ) + if (returned) verilogModeSet(true) +} + +export function saveVerilogCode() { + var code = editor.getValue() + globalScope.verilogMetadata.code = code + generateVerilogCircuit(code) +} + +export function applyVerilogTheme(theme) { + localStorage.setItem('verilog-theme', theme) + editor.setOption('theme', theme) +} + +export function resetVerilogCode() { + editor.setValue(globalScope.verilogMetadata.code) +} + +export function hasVerilogCodeChanges() { + return editor.getValue() != globalScope.verilogMetadata.code +} + +export function verilogModeGet() { + return verilogMode +} + +export function verilogModeSet(mode) { + if (mode == verilogMode) return + verilogMode = mode + if (mode) { + document.getElementById('code-window').style.display = 'block' + document.querySelector('.elementPanel').style.display = 'none' + document.querySelector('.timing-diagram-panel').style.display = 'none' + document.querySelector('.quick-btn').style.display = 'none' + document.getElementById('verilogEditorPanel').style.display = 'block' + if (!embed) { + simulationArea.lastSelected = globalScope.root + showProperties(undefined) + showProperties(simulationArea.lastSelected) + } + resetVerilogCode() + } else { + document.getElementById('code-window').style.display = 'none' + document.querySelector('.elementPanel').style.display = '' + document.querySelector('.timing-diagram-panel').style.display = '' + document.querySelector('.quick-btn').style.display = '' + document.getElementById('verilogEditorPanel').style.display = 'none' + } +} + +import yosysTypeMap from './VerilogClasses' + +class verilogSubCircuit { + constructor(circuit) { + this.circuit = circuit + } + + getPort(portName) { + var numInputs = this.circuit.inputNodes.length + var numOutputs = this.circuit.outputNodes.length + + for (var i = 0; i < numInputs; i++) { + if (this.circuit.data.Input[i].label == portName) { + return this.circuit.inputNodes[i] + } + } + + for (var i = 0; i < numOutputs; i++) { + if (this.circuit.data.Output[i].label == portName) { + return this.circuit.outputNodes[i] + } + } + } +} + +export function YosysJSON2CV( + JSON, + parentScope = globalScope, + name = 'verilogCircuit', + subCircuitScope = {}, + root = false +) { + var parentID = parentScope.id + var subScope + if (root) { + subScope = parentScope + } else { + subScope = newCircuit(name, undefined, true, false) + } + var circuitDevices = {} + + for (var subCircuitName in JSON.subcircuits) { + var scope = YosysJSON2CV( + JSON.subcircuits[subCircuitName], + subScope, + subCircuitName, + subCircuitScope + ) + subCircuitScope[subCircuitName] = scope.id + } + + for (var device in JSON.devices) { + var deviceType = JSON.devices[device].type + if (deviceType == 'Subcircuit') { + var subCircuitName = JSON.devices[device].celltype + circuitDevices[device] = new verilogSubCircuit( + new SubCircuit( + 500, + 500, + undefined, + subCircuitScope[subCircuitName] + ) + ) + } else { + circuitDevices[device] = new yosysTypeMap[deviceType]( + JSON.devices[device] + ) + } + } + + for (var connection in JSON.connectors) { + var fromId = JSON.connectors[connection]['from']['id'] + var fromPort = JSON.connectors[connection]['from']['port'] + var toId = JSON.connectors[connection]['to']['id'] + var toPort = JSON.connectors[connection]['to']['port'] + + var fromObj = circuitDevices[fromId] + var toObj = circuitDevices[toId] + + var fromPortNode = fromObj.getPort(fromPort) + var toPortNode = toObj.getPort(toPort) + + fromPortNode.connect(toPortNode) + } + + if (!root) { + switchCircuit(parentID) + return subScope + } +} + +export default function generateVerilogCircuit( + verilogCode, + scope = globalScope +) { + var params = { code: verilogCode } + fetch('/api/v1/simulator/verilogcv', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(params), + }) + .then((response) => { + if (!response.ok) { + throw response + } + return response.json() + }) + .then((circuitData) => { + scope.initialize() + for (var id in scope.verilogMetadata.subCircuitScopeIds) + delete scopeList[id] + scope.verilogMetadata.subCircuitScopeIds = [] + scope.verilogMetadata.code = verilogCode + var subCircuitScope = {} + YosysJSON2CV( + circuitData, + globalScope, + 'verilogCircuit', + subCircuitScope, + true + ) + changeCircuitName(circuitData.name) + showMessage('Verilog Circuit Successfully Created') + document.getElementById('verilogOutput').innerHTML = '' + }) + .catch((error) => { + if (error.status == 500) { + showError('Could not connect to Yosys') + } else { + showError('There is some issue with the code') + error.json().then((errorMessage) => { + document.getElementById('verilogOutput').innerHTML = + errorMessage.message + }) + } + }) +} + +export function setupCodeMirrorEnvironment() { + var myTextarea = document.getElementById('codeTextArea') + + CodeMirror.commands.autocomplete = function (cm) { + cm.showHint({ hint: CodeMirror.hint.anyword }) + } + + editor = CodeMirror.fromTextArea(myTextarea, { + mode: 'verilog', + autoRefresh: true, + styleActiveLine: true, + lineNumbers: true, + autoCloseBrackets: true, + smartIndent: true, + indentWithTabs: true, + extraKeys: { 'Ctrl-Space': 'autocomplete' }, + }) + + if (!localStorage.getItem('verilog-theme')) { + localStorage.setItem('verilog-theme', 'default') + } else { + const prevtheme = localStorage.getItem('verilog-theme') + editor.setOption('theme', prevtheme) + } + + editor.setValue('// Write Some Verilog Code Here!') + setTimeout(function () { + editor.refresh() + }, 1) +} diff --git a/v0/src/simulator/src/VerilogClasses.js b/v0/src/simulator/src/VerilogClasses.js new file mode 100644 index 00000000..253748af --- /dev/null +++ b/v0/src/simulator/src/VerilogClasses.js @@ -0,0 +1,1507 @@ +import AndGate from './modules/AndGate' +import NandGate from './modules/NandGate' +import Multiplexer from './modules/Multiplexer' +import XorGate from './modules/XorGate' +import XnorGate from './modules/XnorGate' +import SevenSegDisplay from './modules/SevenSegDisplay' +import SixteenSegDisplay from './modules/SixteenSegDisplay' +import HexDisplay from './modules/HexDisplay' +import OrGate from './modules/OrGate' +import Stepper from './modules/Stepper' +import NotGate from './modules/NotGate' +import Text from './modules/Text' +import TriState from './modules/TriState' +import Buffer from './modules/Buffer' +import ControlledInverter from './modules/ControlledInverter' +import Adder from './modules/Adder' +import verilogMultiplier from './modules/verilogMultiplier' +import verilogDivider from './modules/verilogDivider' +import verilogPower from './modules/verilogPower' +import verilogShiftLeft from './modules/verilogShiftLeft' +import verilogShiftRight from './modules/verilogShiftRight' +import TwoComplement from './modules/TwoComplement' +import Splitter from './modules/Splitter' +import Ground from './modules/Ground' +import Power from './modules/Power' +import Input from './modules/Input' +import Output from './modules/Output' +import BitSelector from './modules/BitSelector' +import ConstantVal from './modules/ConstantVal' +import NorGate from './modules/NorGate' +import DigitalLed from './modules/DigitalLed' +import VariableLed from './modules/VariableLed' +import Button from './modules/Button' +import RGBLed from './modules/RGBLed' +import SquareRGBLed from './modules/SquareRGBLed' +import Demultiplexer from './modules/Demultiplexer' +import Decoder from './modules/Decoder' +import Flag from './modules/Flag' +import MSB from './modules/MSB' +import LSB from './modules/LSB' +import PriorityEncoder from './modules/PriorityEncoder' +import Tunnel from './modules/Tunnel' +import ALU from './modules/ALU' +import Rectangle from './modules/Rectangle' +import Arrow from './modules/Arrow' +import Counter from './modules/Counter' +import Random from './modules/Random' +import RGBLedMatrix from './modules/RGBLedMatrix' +import simulationArea from './simulationArea' +import TflipFlop from './sequential/TflipFlop' +import DflipFlop from './sequential/DflipFlop' +import Dlatch from './sequential/Dlatch' +import SRflipFlop from './sequential/SRflipFlop' +import JKflipFlop from './sequential/JKflipFlop' +import TTY from './sequential/TTY' +import Keyboard from './sequential/Keyboard' +import Clock from './sequential/Clock' +import RAM from './sequential/RAM' +import verilogRAM from './sequential/verilogRAM' +import EEPROM from './sequential/EEPROM' +import Rom from './sequential/Rom' +import TB_Input from './testbench/testbenchInput' +import TB_Output from './testbench/testbenchOutput' +import ForceGate from './testbench/ForceGate' +import { newCircuit, switchCircuit, changeCircuitName } from './circuit' +import SubCircuit from './subcircuit' + +function getBitWidth(bitsJSON) { + if (Number.isInteger(bitsJSON)) { + return bitsJSON + } else { + var ans = 1 + for (var i in bitsJSON) { + ans = Math.max(ans, bitsJSON[i]) + } + return ans + } +} + +class verilogUnaryGate { + constructor(deviceJSON) { + this.bitWidth = 1 + if (deviceJSON['bits']) { + this.bitWidth = getBitWidth(deviceJSON['bits']) + } + } + + getPort(portName) { + if (portName == 'in') { + return this.input + } + if (portName == 'out') { + return this.output + } + } +} + +class verilogInput extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + if (deviceJSON['net'] == 'clk' || deviceJSON['net'] == 'clock') { + this.element = new Clock(0, 0) + } else { + this.element = new Input(0, 0, undefined, undefined, this.bitWidth) + } + this.output = this.element.output1 + this.element.label = deviceJSON['net'] + } +} + +class verilogOutput extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + this.element = new Output(0, 0, undefined, undefined, this.bitWidth) + this.input = this.element.inp1 + this.element.label = deviceJSON['net'] + } +} + +class verilogClock extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + this.element = new Clock(0, 0) + this.output = this.element.output1 + } +} + +class verilogButton extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + this.element = new Button(0, 0) + this.output = this.element.output1 + } +} + +class verilogLamp extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + this.element = new DigitalLed(0, 0) + this.input = this.element.inp1 + } +} + +class verilogNotGate extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + this.element = new NotGate(0, 0, undefined, undefined, this.bitWidth) + this.input = this.element.inp1 + this.output = this.element.output1 + } +} + +class verilogRepeaterGate extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + this.element = new Buffer(0, 0, undefined, undefined, this.bitWidth) + this.input = this.element.inp1 + this.output = this.element.output1 + } +} + +class verilogConstantVal extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + this.bitWidth = deviceJSON['constant'].length + this.state = deviceJSON['constant'] + if (this.state[0] == 'x') { + this.state = undefined + } + this.element = new ConstantVal( + 0, + 0, + undefined, + undefined, + this.bitWidth, + this.state + ) + this.input = this.element.inp1 + this.output = this.element.output1 + } +} + +class verilogReduceAndGate extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + + this.bitWidthSplit = [] + for (var i = 0; i < this.bitWidth; i++) { + this.bitWidthSplit.push(1) + } + + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + this.bitWidthSplit + ) + this.andGate = new AndGate(0, 0, undefined, undefined, this.bitWidth, 1) + + for (var i = 0; i < this.bitWidth; i++) { + this.splitter.outputs[i].connect(this.andGate.inp[i]) + } + + this.input = this.splitter.inp1 + this.output = this.andGate.output1 + } +} + +class verilogReduceNandGate extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + + this.bitWidthSplit = [] + for (var i = 0; i < this.bitWidth; i++) { + this.bitWidthSplit.push(1) + } + + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + this.bitWidthSplit + ) + this.nandGate = new NandGate( + 0, + 0, + undefined, + undefined, + this.bitWidth, + 1 + ) + + for (var i = 0; i < this.bitWidth; i++) { + this.splitter.outputs[i].connect(this.nandGate.inp[i]) + } + + this.input = this.splitter.inp1 + this.output = this.nandGate.output1 + } +} + +class verilogReduceOrGate extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + + this.bitWidthSplit = [] + for (var i = 0; i < this.bitWidth; i++) { + this.bitWidthSplit.push(1) + } + + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + this.bitWidthSplit + ) + this.orGate = new OrGate(0, 0, undefined, undefined, this.bitWidth, 1) + + for (var i = 0; i < this.bitWidth; i++) { + this.splitter.outputs[i].connect(this.orGate.inp[i]) + } + + this.input = this.splitter.inp1 + this.output = this.orGate.output1 + } +} + +class verilogReduceNorGate extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + + this.bitWidthSplit = [] + for (var i = 0; i < this.bitWidth; i++) { + this.bitWidthSplit.push(1) + } + + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + this.bitWidthSplit + ) + this.norGate = new NorGate(0, 0, undefined, undefined, this.bitWidth, 1) + + for (var i = 0; i < this.bitWidth; i++) { + this.splitter.outputs[i].connect(this.norGate.inp[i]) + } + + this.input = this.splitter.inp1 + this.output = this.norGate.output1 + } +} + +class verilogReduceXorGate extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + + this.bitWidthSplit = [] + for (var i = 0; i < this.bitWidth; i++) { + this.bitWidthSplit.push(1) + } + + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + this.bitWidthSplit + ) + this.xorGate = new XorGate(0, 0, undefined, undefined, this.bitWidth, 1) + + for (var i = 0; i < this.bitWidth; i++) { + this.splitter.outputs[i].connect(this.xorGate.inp[i]) + } + + this.input = this.splitter.inp1 + this.output = this.xorGate.output1 + } +} + +class verilogReduceXnorGate extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + + this.bitWidthSplit = [] + for (var i = 0; i < this.bitWidth; i++) { + this.bitWidthSplit.push(1) + } + + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + this.bitWidthSplit + ) + this.xnorGate = new XnorGate( + 0, + 0, + undefined, + undefined, + this.bitWidth, + 1 + ) + + for (var i = 0; i < this.bitWidth; i++) { + this.splitter.outputs[i].connect(this.xnorGate.inp[i]) + } + + this.input = this.splitter.inp1 + this.output = this.xnorGate.output1 + } +} + +class verilogBusSlice extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + this.bitWidth = deviceJSON['slice']['total'] + + this.start = deviceJSON['slice']['first'] + this.count = deviceJSON['slice']['count'] + if (this.start == 0) { + if (this.count == this.bitWidth) { + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + [this.bitWidth] + ) + } else { + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + [this.count, this.bitWidth - this.count] + ) + } + + this.input = this.splitter.inp1 + this.output = this.splitter.outputs[0] + } else { + if (this.start + this.count == this.bitWidth) { + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + [this.start, this.count] + ) + } else { + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + [ + this.start, + this.count, + this.bitWidth - this.start - this.count, + ] + ) + } + this.input = this.splitter.inp1 + this.output = this.splitter.outputs[1] + } + } +} + +class verilogZeroExtend extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + + this.inputBitWidth = deviceJSON['extend']['input'] + this.outputBitWidth = deviceJSON['extend']['output'] + + var extraBits = this.outputBitWidth - this.inputBitWidth + + var zeroState = '' + for (var i = 0; i < extraBits; i++) { + zeroState += '0' + } + + this.zeroConstant = new ConstantVal( + 0, + 0, + undefined, + undefined, + extraBits, + zeroState + ) + + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.outputBitWidth, + [this.inputBitWidth, extraBits] + ) + + this.zeroConstant.output1.connect(this.splitter.outputs[1]) + this.input = this.splitter.outputs[0] + this.output = this.splitter.inp1 + } +} + +class verilogNegationGate extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + this.inputBitWidth = deviceJSON['bits']['in'] + + this.notGate = new NotGate(400, 0, undefined, undefined, this.bitWidth) + this.adder = new Adder(300, 0, undefined, undefined, this.bitWidth) + + if (this.inputBitWidth != this.bitWidth) { + var extraBits = this.bitWidth - this.inputBitWidth + this.splitter = new Splitter( + 600, + 600, + undefined, + undefined, + this.bitWidth, + [this.inputBitWidth, extraBits] + ) + + var zeroState = '' + for (var i = 0; i < extraBits; i++) { + zeroState += '0' + } + + this.zeroConstant = new ConstantVal( + 550, + 550, + undefined, + undefined, + extraBits, + zeroState + ) + + this.zeroConstant.output1.connect(this.splitter.outputs[1]) + this.splitter.inp1.connect(this.notGate.inp1) + + this.input = this.splitter.outputs[0] + } else { + this.input = this.notGate.inp1 + } + + var oneVal = '' + for (var i = 0; i < this.bitWidth - 1; i++) { + oneVal += '0' + } + oneVal += '1' + + this.oneConstant = new ConstantVal( + 0, + 0, + undefined, + undefined, + this.bitWidth, + oneVal + ) + + this.notGate.output1.connect(this.adder.inpA) + this.oneConstant.output1.connect(this.adder.inpB) + + this.output = this.adder.sum + } +} + +class verilogBinaryGate { + constructor(deviceJSON) { + this.bitWidth = 1 + if (deviceJSON['bits']) { + this.bitWidth = getBitWidth(deviceJSON['bits']) + } + } + + getPort(portName) { + if (portName == 'in1') { + return this.input[0] + } else if (portName == 'in2') { + return this.input[1] + } else if (portName == 'out') { + return this.output + } + } +} + +class verilogAndGate extends verilogBinaryGate { + constructor(deviceJSON) { + super(deviceJSON) + this.element = new AndGate( + 0, + 0, + undefined, + undefined, + undefined, + this.bitWidth + ) + this.input = [this.element.inp[0], this.element.inp[1]] + this.output = this.element.output1 + } +} + +class verilogNandGate extends verilogBinaryGate { + constructor(deviceJSON) { + super(deviceJSON) + this.element = new NandGate( + 0, + 0, + undefined, + undefined, + undefined, + this.bitWidth + ) + this.input = [this.element.inp[0], this.element.inp[1]] + this.output = this.element.output1 + } +} + +class verilogOrGate extends verilogBinaryGate { + constructor(deviceJSON) { + super(deviceJSON) + this.element = new OrGate( + 0, + 0, + undefined, + undefined, + undefined, + this.bitWidth + ) + this.input = [this.element.inp[0], this.element.inp[1]] + this.output = this.element.output1 + } +} + +class verilogNorGate extends verilogBinaryGate { + constructor(deviceJSON) { + super(deviceJSON) + this.element = new NorGate( + 0, + 0, + undefined, + undefined, + undefined, + this.bitWidth + ) + this.input = [this.element.inp[0], this.element.inp[1]] + this.output = this.element.output1 + } +} + +class verilogXorGate extends verilogBinaryGate { + constructor(deviceJSON) { + super(deviceJSON) + this.element = new XorGate( + 0, + 0, + undefined, + undefined, + undefined, + this.bitWidth + ) + this.input = [this.element.inp[0], this.element.inp[1]] + this.output = this.element.output1 + } +} + +class verilogXnorGate extends verilogBinaryGate { + constructor(deviceJSON) { + super(deviceJSON) + this.element = new XnorGate( + 0, + 0, + undefined, + undefined, + undefined, + this.bitWidth + ) + this.input = [this.element.inp[0], this.element.inp[1]] + this.output = this.element.output1 + } +} + +class verilogMathGate extends verilogBinaryGate { + constructor(deviceJSON, includeOutBitWidth) { + super(deviceJSON) + + this.bitWidth = Math.max( + deviceJSON['bits']['in1'], + deviceJSON['bits']['in2'] + ) + + if (includeOutBitWidth) { + this.bitWidth = Math.max(deviceJSON['bits']['out'], this.bitWidth) + } + + if (!Number.isInteger(deviceJSON['bits'])) { + this.in1BitWidth = deviceJSON['bits']['in1'] + this.in2BitWidth = deviceJSON['bits']['in2'] + } + + this.input = [] + + var extraBits = this.bitWidth - this.in1BitWidth + + if (extraBits != 0) { + this.in1Splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + [this.in1BitWidth, extraBits] + ) + + var zeroState = '' + for (var i = 0; i < extraBits; i++) { + zeroState += '0' + } + this.in1ZeroConstant = new ConstantVal( + 0, + 0, + undefined, + undefined, + extraBits, + zeroState + ) + this.in1ZeroConstant.output1.connect(this.in1Splitter.outputs[1]) + } else { + this.in1Splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + [this.bitWidth] + ) + } + + var extraBits = this.bitWidth - this.in2BitWidth + if (extraBits != 0) { + this.in2Splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + [this.in2BitWidth, extraBits] + ) + var zeroState = '' + for (var i = 0; i < extraBits; i++) { + zeroState += '0' + } + + this.in2ZeroConstant = new ConstantVal( + 0, + 0, + undefined, + undefined, + extraBits, + zeroState + ) + this.in2ZeroConstant.output1.connect(this.in2Splitter.outputs[1]) + } else { + this.in2Splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + [this.bitWidth] + ) + } + + this.input = [this.in1Splitter.outputs[0], this.in2Splitter.outputs[0]] + } +} + +class verilogEqGate extends verilogMathGate { + constructor(deviceJSON) { + super(deviceJSON, false) + + var bitWidthSplit = [] + + for (var i = 0; i < this.bitWidth; i++) { + bitWidthSplit.push(1) + } + + this.xnorGate = new XnorGate( + 0, + 0, + undefined, + undefined, + undefined, + this.bitWidth + ) + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + bitWidthSplit + ) + this.andGate = new AndGate(0, 0, undefined, undefined, this.bitWidth) + this.in1Splitter.inp1.connect(this.xnorGate.inp[0]) + this.in2Splitter.inp1.connect(this.xnorGate.inp[1]) + + this.xnorGate.output1.connect(this.splitter.inp1) + for (var i = 0; i < this.bitWidth; i++) { + this.splitter.outputs[i].connect(this.andGate.inp[i]) + } + + this.output = this.andGate.output1 + } +} + +class verilogNeGate extends verilogMathGate { + constructor(deviceJSON) { + super(deviceJSON, false) + + var bitWidthSplit = [] + + for (var i = 0; i < this.bitWidth; i++) { + bitWidthSplit.push(1) + } + + this.xnorGate = new XnorGate( + 0, + 0, + undefined, + undefined, + undefined, + this.bitWidth + ) + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + bitWidthSplit + ) + this.nandGate = new NandGate(0, 0, undefined, undefined, this.bitWidth) + + this.in1Splitter.inp1.connect(this.xnorGate.inp[0]) + this.in2Splitter.inp1.connect(this.xnorGate.inp[1]) + + this.xnorGate.output1.connect(this.splitter.inp1) + for (var i = 0; i < this.bitWidth; i++) { + this.splitter.outputs[i].connect(this.nandGate.inp[i]) + } + + this.output = this.nandGate.output1 + } +} + +class verilogLtGate extends verilogMathGate { + constructor(deviceJSON) { + super(deviceJSON, false) + this.constant7 = new ConstantVal(0, 0, undefined, undefined, 3, '111') + this.alu = new ALU(0, 0, undefined, undefined, this.bitWidth) + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + [1] + ) + + this.in1Splitter.inp1.connect(this.alu.inp1) + this.in2Splitter.inp1.connect(this.alu.inp2) + + this.constant7.output1.connect(this.alu.controlSignalInput) + this.alu.output.connect(this.splitter.inp1) + + this.output = this.splitter.outputs[0] + } +} + +class verilogGtGate extends verilogMathGate { + constructor(deviceJSON) { + super(deviceJSON, false) + this.constant7 = new ConstantVal(0, 0, undefined, undefined, 3, '111') + this.alu = new ALU(0, 0, undefined, undefined, this.bitWidth) + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + [1] + ) + + this.in1Splitter.inp1.connect(this.alu.inp1) + this.in2Splitter.inp1.connect(this.alu.inp2) + + this.constant7.output1.connect(this.alu.controlSignalInput) + this.alu.output.connect(this.splitter.inp1) + + this.output = this.splitter.outputs[0] + } +} + +class verilogGeGate extends verilogMathGate { + constructor(deviceJSON) { + super(deviceJSON, false) + this.constant7 = new ConstantVal(0, 0, undefined, undefined, 3, '111') + this.alu = new ALU(0, 0, undefined, undefined, this.bitWidth) + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + [1] + ) + this.notGate = new NotGate(0, 0) + + this.in1Splitter.inp1.connect(this.alu.inp1) + this.in2Splitter.inp1.connect(this.alu.inp2) + + this.constant7.output1.connect(this.alu.controlSignalInput) + this.alu.output.connect(this.splitter.inp1) + this.splitter.outputs[0].connect(this.notGate.inp1) + + this.output = this.notGate.output1 + } +} + +class verilogLeGate extends verilogMathGate { + constructor(deviceJSON) { + super(deviceJSON, false) + this.constant7 = new ConstantVal(0, 0, undefined, undefined, 3, '111') + this.alu = new ALU(0, 0, undefined, undefined, this.bitWidth) + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + [1] + ) + this.notGate = new NotGate(0, 0) + + this.in1Splitter.inp1.connect(this.alu.inp1) + this.in2Splitter.inp1.connect(this.alu.inp2) + + this.constant7.output1.connect(this.alu.controlSignalInput) + this.alu.output.connect(this.splitter.inp1) + this.splitter.outputs[0].connect(this.notGate.inp1) + + this.output = this.notGate.output1 + } +} + +class verilogAdditionGate extends verilogMathGate { + constructor(deviceJSON) { + super(deviceJSON, false) + + this.outBitWidth = deviceJSON['bits']['out'] + + this.adder = new Adder(0, 0, undefined, undefined, this.bitWidth) + + this.in1Splitter.inp1.connect(this.adder.inpA) + this.in2Splitter.inp1.connect(this.adder.inpB) + + if (this.outBitWidth == this.bitWidth) { + this.output = this.adder.sum + } else if (this.outBitWidth == this.bitWidth + 1) { + this.outputSplitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.outBitWidth, + [this.bitWidth, 1] + ) + this.adder.sum.connect(this.outputSplitter.outputs[0]) + this.adder.carryOut.connect(this.outputSplitter.outputs[1]) + this.output = this.outputSplitter.inp1 + } + } +} + +class verilogMultiplicationGate extends verilogMathGate { + constructor(deviceJSON) { + super(deviceJSON) + + this.outBitWidth = deviceJSON['bits']['out'] + + this.verilogMultiplier = new verilogMultiplier( + 300, + 300, + undefined, + undefined, + this.bitWidth, + this.outBitWidth + ) + + this.in1Splitter.inp1.connect(this.verilogMultiplier.inpA) + this.in2Splitter.inp1.connect(this.verilogMultiplier.inpB) + + this.output = this.verilogMultiplier.product + } +} + +class verilogDivisionGate extends verilogMathGate { + constructor(deviceJSON) { + super(deviceJSON) + + this.outBitWidth = deviceJSON['bits']['out'] + + this.verilogDivider = new verilogDivider( + 300, + 300, + undefined, + undefined, + this.bitWidth, + this.outBitWidth + ) + + this.in1Splitter.inp1.connect(this.verilogDivider.inpA) + this.in2Splitter.inp1.connect(this.verilogDivider.inpB) + + this.output = this.verilogDivider.quotient + } +} + +class verilogPowerGate extends verilogMathGate { + constructor(deviceJSON) { + super(deviceJSON) + + this.outBitWidth = deviceJSON['bits']['out'] + + this.verilogPower = new verilogPower( + 300, + 300, + undefined, + undefined, + this.bitWidth, + this.outBitWidth + ) + + this.in1Splitter.inp1.connect(this.verilogPower.inpA) + this.in2Splitter.inp1.connect(this.verilogPower.inpB) + + this.output = this.verilogPower.answer + } +} + +class verilogModuloGate extends verilogMathGate { + constructor(deviceJSON) { + super(deviceJSON) + + this.outBitWidth = deviceJSON['bits']['out'] + + this.verilogDivider = new verilogDivider( + 300, + 300, + undefined, + undefined, + this.bitWidth, + this.outBitWidth + ) + + this.in1Splitter.inp1.connect(this.verilogDivider.inpA) + this.in2Splitter.inp1.connect(this.verilogDivider.inpB) + + this.output = this.verilogDivider.remainder + } +} + +class verilogShiftLeftGate extends verilogMathGate { + constructor(deviceJSON) { + super(deviceJSON) + + this.outBitWidth = deviceJSON['bits']['out'] + + this.verilogShiftLeft = new verilogShiftLeft( + 300, + 300, + undefined, + undefined, + this.bitWidth, + this.outBitWidth + ) + + this.in1Splitter.inp1.connect(this.verilogShiftLeft.inp1) + this.in2Splitter.inp1.connect(this.verilogShiftLeft.shiftInp) + + this.output = this.verilogShiftLeft.output1 + } +} + +class verilogShiftRightGate extends verilogMathGate { + constructor(deviceJSON) { + super(deviceJSON) + + this.outBitWidth = deviceJSON['bits']['out'] + + this.verilogShiftRight = new verilogShiftRight( + 300, + 300, + undefined, + undefined, + this.bitWidth, + this.outBitWidth + ) + + this.in1Splitter.inp1.connect(this.verilogShiftRight.inp1) + this.in2Splitter.inp1.connect(this.verilogShiftRight.shiftInp) + + this.output = this.verilogShiftRight.output1 + } +} + +class verilogSubtractionGate extends verilogMathGate { + constructor(deviceJSON) { + super(deviceJSON, true) + + this.alu = new ALU(0, 0, undefined, undefined, this.bitWidth) + + this.controlConstant = new ConstantVal( + 0, + 0, + undefined, + undefined, + 3, + '110' + ) + this.alu.controlSignalInput.connect(this.controlConstant.output1) + + this.in1Splitter.inp1.connect(this.alu.inp1) + this.in2Splitter.inp1.connect(this.alu.inp2) + + this.output = this.alu.output + } +} + +class verilogDff { + constructor(deviceJSON) { + this.bitWidth = 1 + if (deviceJSON['bits']) { + this.bitWidth = getBitWidth(deviceJSON['bits']) + } + + this.dff = new DflipFlop(0, 0, undefined, undefined, this.bitWidth) + this.clockInput = this.dff.clockInp + this.arstInput = this.dff.reset + this.enableInput = this.dff.en + + this.clockPolarity = true + this.arstPolarity = true + this.enablePolarity = true + + if (deviceJSON['polarity']['clock'] != undefined) { + this.clockPolarity = deviceJSON['polarity']['clock'] + } + if (this.clockPolarity == false) { + this.notGateClock = new NotGate(0, 0) + this.notGateClock.output1.connect(this.dff.clockInp) + this.clockInput = this.notGateClock.inp1 + } + + if (deviceJSON['polarity']['enable'] != undefined) { + this.enablePolarity = deviceJSON['polarity']['enable'] + } + if (this.enablePolarity == false) { + this.notGateEnable = new NotGate(0, 0) + this.notGateEnable.output1.connect(this.dff.en) + this.enableInput = this.notGateEnable.inp1 + } + + if (deviceJSON['polarity']['arst'] != undefined) { + this.arstPolarity = deviceJSON['polarity']['arst'] + } + if (this.arstPolarity == false) { + this.notGateArst = new NotGate(0, 0) + this.notGateArst.output1.connect(this.dff.reset) + this.arstInput = this.notGateArst.inp1 + } + if (deviceJSON['arst_value'] != undefined) { + this.arst_value_constant = new ConstantVal( + 0, + 0, + undefined, + undefined, + this.bitWidth, + deviceJSON['arst_value'] + ) + this.arst_value_constant.output1.connect(this.dff.preset) + } + + this.dInput = this.dff.dInp + this.qOutput = this.dff.qOutput + } + + getPort(portName) { + if (portName == 'clk') { + return this.clockInput + } else if (portName == 'in') { + return this.dInput + } else if (portName == 'arst') { + return this.arstInput + } else if (portName == 'en') { + return this.enableInput + } else if (portName == 'out') { + return this.qOutput + } + } +} + +class verilogMultiplexer { + constructor(deviceJSON) { + this.bitWidth = 1 + this.selectBitWidth = undefined + if (deviceJSON['bits']['in'] != undefined) { + this.bitWidth = deviceJSON['bits']['in'] + } + + if (deviceJSON['bits']['sel'] != undefined) { + this.selectBitWidth = deviceJSON['bits']['sel'] + } + + this.multiplexer = new Multiplexer( + 0, + 0, + undefined, + undefined, + this.bitWidth, + this.selectBitWidth + ) + + this.input = this.multiplexer.inp + this.selectInput = this.multiplexer.controlSignalInput + this.output = this.multiplexer.output1 + } + + getPort(portName) { + if (portName == 'sel') { + return this.selectInput + } else if (portName == 'out') { + return this.output + } else { + var len = portName.length + var index = parseInt(portName.substring(2, len)) + + return this.input[index] + } + } +} + +class verilogMultiplexer1Hot { + constructor(deviceJSON) { + this.bitWidth = 1 + this.selectBitWidth = undefined + if (deviceJSON['bits']['in'] != undefined) { + this.bitWidth = deviceJSON['bits']['in'] + } + + if (deviceJSON['bits']['sel'] != undefined) { + this.selectBitWidth = deviceJSON['bits']['sel'] + } + + this.multiplexer = new Multiplexer( + 0, + 0, + undefined, + undefined, + this.bitWidth, + this.selectBitWidth + ) + this.lsb = new LSB(0, 0, undefined, undefined, this.selectBitWidth) + this.adder = new Adder(0, 0, undefined, undefined, this.selectBitWidth) + + var zeroState = '' + for (var i = 0; i < this.selectBitWidth - 1; i++) { + zeroState += '0' + } + this.zeroPadEnable = new ConstantVal( + 0, + 0, + undefined, + undefined, + this.selectBitWidth - 1, + zeroState + ) + + this.enbaleSplitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.selectBitWidth, + [1, this.selectBitWidth - 1] + ) + + this.lsb.enable.connect(this.enbaleSplitter.outputs[0]) + this.zeroPadEnable.output1.connect(this.enbaleSplitter.outputs[1]) + + this.adder.inpA.connect(this.lsb.output1) + this.adder.inpB.connect(this.enbaleSplitter.inp1) + + this.adder.sum.connect(this.multiplexer.controlSignalInput) + this.input = this.multiplexer.inp + this.selectInput = this.lsb.inp1 + this.output = this.multiplexer.output1 + } + + getPort(portName) { + if (portName == 'sel') { + return this.selectInput + } else if (portName == 'out') { + return this.output + } else { + var len = portName.length + var index = parseInt(portName.substring(2, len)) + + return this.input[index] + } + } +} + +class verilogBusGroup { + constructor(deviceJSON) { + this.bitWidth = 0 + this.bitWidthSplit = deviceJSON['groups'] + + for (var i = 0; i < this.bitWidthSplit.length; i++) { + this.bitWidth += this.bitWidthSplit[i] + } + + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + this.bitWidthSplit + ) + + this.input = this.splitter.outputs + this.output = this.splitter.inp1 + } + + getPort(portName) { + if (portName == 'out') { + return this.output + } else { + var len = portName.length + var index = parseInt(portName.substring(2, len)) + + return this.input[index] + } + } +} + +class verilogBusUngroup { + constructor(deviceJSON) { + this.bitWidth = 0 + this.bitWidthSplit = deviceJSON['groups'] + + for (var i = 0; i < this.bitWidthSplit.length; i++) { + this.bitWidth += this.bitWidthSplit[i] + } + + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + this.bitWidthSplit + ) + + this.input = this.splitter.inp1 + this.output = this.splitter.outputs + } + + getPort(portName) { + if (portName == 'in') { + return this.input + } else { + var len = portName.length + var index = parseInt(portName.substring(3, len)) + + return this.output[index] + } + } +} + +class verilogMemory { + constructor(deviceJSON) { + this.memData = deviceJSON['memdata'] + this.dataBitWidth = deviceJSON['bits'] + this.addressBitWidth = deviceJSON['abits'] + this.words = deviceJSON['words'] + + this.numRead = deviceJSON['rdports'].length + this.numWrite = deviceJSON['wrports'].length + + this.verilogRAM = new verilogRAM( + 0, + 0, + undefined, + undefined, + this.dataBitWidth, + this.addressBitWidth, + this.memData, + this.words, + this.numRead, + this.numWrite, + deviceJSON['rdports'], + deviceJSON['wrports'] + ) + + this.writeAddressInput = this.verilogRAM.writeAddress + this.readAddressInput = this.verilogRAM.readAddress + this.writeDataInput = this.verilogRAM.writeDataIn + this.writeEnableInput = this.verilogRAM.writeEnable + this.readDataOutput = this.verilogRAM.dataOut + this.readDffOut = this.verilogRAM.readDff + + for (var i = 0; i < this.numWrite; i++) { + var writeEnInput = new Input( + 0, + 0, + undefined, + undefined, + 1, + undefined + ) + writeEnInput.label = 'en' + i.toString() + writeEnInput.output1.connect(this.verilogRAM.writeEnable[i]) + } + } + + getPort(portName) { + var len = portName.length + var isPortAddr = portName.slice(len - 4, len) == 'addr' + var isPortData = portName.slice(len - 4, len) == 'data' + var isPortClk = portName.slice(len - 3, len) == 'clk' + var isPortEn = portName.slice(len - 2, len) == 'en' + if (portName.startsWith('rd')) { + if (isPortAddr) { + var portNum = portName.slice(2, len - 4) + portNum = parseInt(portNum) + + return this.readAddressInput[portNum] + } + if (isPortData) { + var portNum = portName.slice(2, len - 4) + portNum = parseInt(portNum) + return this.verilogRAM.readDffQOutput[portNum] + } + if (isPortClk) { + var portNum = portName.slice(2, len - 3) + portNum = parseInt(portNum) + + return this.verilogRAM.readDffClock[portNum] + } + if (isPortEn) { + var portNum = portName.slice(2, len - 2) + portNum = parseInt(portNum) + + return this.verilogRAM.readDffEn[portNum] + } + } else { + if (isPortAddr) { + var portNum = portName.slice(2, len - 4) + portNum = parseInt(portNum) + return this.writeAddressInput[portNum] + } + if (isPortData) { + var portNum = portName.slice(2, len - 4) + portNum = parseInt(portNum) + return this.writeDataInput[portNum] + } + if (isPortClk) { + var portNum = portName.slice(2, len - 3) + portNum = parseInt(portNum) + + return this.verilogRAM.writeDffClock[portNum] + } + if (isPortEn) { + var portNum = portName.slice(2, len - 2) + portNum = parseInt(portNum) + + return this.verilogRAM.writeDffEn[portNum] + } + } + } +} + +let yosysTypeMap = {} + +yosysTypeMap['Not'] = verilogNotGate +yosysTypeMap['Repeater'] = verilogRepeaterGate +yosysTypeMap['And'] = verilogAndGate +yosysTypeMap['Nand'] = verilogNandGate +yosysTypeMap['Or'] = verilogOrGate +yosysTypeMap['Nor'] = verilogNorGate +yosysTypeMap['Xor'] = verilogXorGate +yosysTypeMap['Xnor'] = verilogXnorGate +yosysTypeMap['Constant'] = verilogConstantVal +yosysTypeMap['Input'] = verilogInput +yosysTypeMap['Output'] = verilogOutput +yosysTypeMap['AndReduce'] = verilogReduceAndGate +yosysTypeMap['NandReduce'] = verilogReduceNandGate +yosysTypeMap['OrReduce'] = verilogReduceOrGate +yosysTypeMap['NorReduce'] = verilogReduceNorGate +yosysTypeMap['XorReduce'] = verilogReduceXorGate +yosysTypeMap['XnorReduce'] = verilogReduceXnorGate + +yosysTypeMap['Eq'] = verilogEqGate +yosysTypeMap['Ne'] = verilogNeGate + +yosysTypeMap['Lt'] = verilogLtGate +yosysTypeMap['Le'] = verilogLeGate +yosysTypeMap['Ge'] = verilogGeGate +yosysTypeMap['Gt'] = verilogGtGate + +yosysTypeMap['ZeroExtend'] = verilogZeroExtend +yosysTypeMap['Negation'] = verilogNegationGate + +yosysTypeMap['Dff'] = verilogDff +yosysTypeMap['Mux'] = verilogMultiplexer +yosysTypeMap['Mux1Hot'] = verilogMultiplexer1Hot +yosysTypeMap['BusSlice'] = verilogBusSlice +yosysTypeMap['BusGroup'] = verilogBusGroup +yosysTypeMap['BusUngroup'] = verilogBusUngroup + +yosysTypeMap['Addition'] = verilogAdditionGate +yosysTypeMap['Subtraction'] = verilogSubtractionGate +yosysTypeMap['Multiplication'] = verilogMultiplicationGate +yosysTypeMap['Division'] = verilogDivisionGate +yosysTypeMap['Modulo'] = verilogModuloGate +yosysTypeMap['Power'] = verilogPowerGate +yosysTypeMap['ShiftLeft'] = verilogShiftLeftGate +yosysTypeMap['ShiftRight'] = verilogShiftRightGate + +yosysTypeMap['Clock'] = verilogClock +yosysTypeMap['Lamp'] = verilogLamp +yosysTypeMap['Button'] = verilogButton + +yosysTypeMap['Memory'] = verilogMemory + +export default yosysTypeMap diff --git a/v0/src/simulator/src/app.js b/v0/src/simulator/src/app.js new file mode 100644 index 00000000..00b64e60 --- /dev/null +++ b/v0/src/simulator/src/app.js @@ -0,0 +1,213 @@ +import { setup } from './setup' +import Array from './arrayHelpers' + +document.addEventListener('DOMContentLoaded', () => { + setup() + var js = { + devices: { + dev0: { + type: 'Input', + net: 'clk', + order: 0, + bits: 1, + }, + dev1: { + type: 'Input', + net: 'addr', + order: 1, + bits: 4, + }, + dev2: { + type: 'Output', + net: 'data', + order: 2, + bits: 5, + }, + dev3: { + type: 'Input', + net: 'addr2', + order: 3, + bits: 4, + }, + dev4: { + type: 'Output', + net: 'data2', + order: 4, + bits: 5, + }, + dev5: { + type: 'Input', + net: 'wraddr', + order: 5, + bits: 4, + }, + dev6: { + type: 'Input', + net: 'wrdata', + order: 6, + bits: 5, + }, + dev7: { + type: 'Input', + net: 'wraddr2', + order: 7, + bits: 4, + }, + dev8: { + type: 'Input', + net: 'wrdata2', + order: 8, + bits: 5, + }, + dev9: { + label: 'mem', + type: 'Memory', + bits: 5, + abits: 4, + words: 16, + offset: 0, + rdports: [ + {}, + { + clock_polarity: true, + }, + ], + wrports: [ + { + clock_polarity: true, + }, + { + clock_polarity: true, + }, + ], + memdata: [13, '00001', 3, '11111'], + }, + }, + connectors: [ + { + to: { + id: 'dev9', + port: 'rd1clk', + }, + from: { + id: 'dev0', + port: 'out', + }, + name: 'clk', + }, + { + to: { + id: 'dev9', + port: 'wr0clk', + }, + from: { + id: 'dev0', + port: 'out', + }, + name: 'clk', + }, + { + to: { + id: 'dev9', + port: 'wr1clk', + }, + from: { + id: 'dev0', + port: 'out', + }, + name: 'clk', + }, + { + to: { + id: 'dev9', + port: 'rd0addr', + }, + from: { + id: 'dev1', + port: 'out', + }, + name: 'addr', + }, + { + to: { + id: 'dev2', + port: 'in', + }, + from: { + id: 'dev9', + port: 'rd0data', + }, + name: 'data', + }, + { + to: { + id: 'dev9', + port: 'rd1addr', + }, + from: { + id: 'dev3', + port: 'out', + }, + name: 'addr2', + }, + { + to: { + id: 'dev4', + port: 'in', + }, + from: { + id: 'dev9', + port: 'rd1data', + }, + name: 'data2', + }, + { + to: { + id: 'dev9', + port: 'wr0addr', + }, + from: { + id: 'dev5', + port: 'out', + }, + name: 'wraddr', + }, + { + to: { + id: 'dev9', + port: 'wr0data', + }, + from: { + id: 'dev6', + port: 'out', + }, + name: 'wrdata', + }, + { + to: { + id: 'dev9', + port: 'wr1addr', + }, + from: { + id: 'dev7', + port: 'out', + }, + name: 'wraddr2', + }, + { + to: { + id: 'dev9', + port: 'wr1data', + }, + from: { + id: 'dev8', + port: 'out', + }, + name: 'wrdata2', + }, + ], + subcircuits: {}, + } +}) + +window.Array = Array diff --git a/v0/src/simulator/src/arrayHelpers.js b/v0/src/simulator/src/arrayHelpers.js new file mode 100644 index 00000000..8d10917e --- /dev/null +++ b/v0/src/simulator/src/arrayHelpers.js @@ -0,0 +1,34 @@ +/* eslint-disable func-names */ +/* eslint-disable no-global-assign */ +/* eslint-disable no-extend-native */ +export default Array = window.Array + +Object.defineProperty(Array.prototype, 'clean', { + value: function (deleteValue) { + for (var i = 0; i < this.length; i++) { + if (this[i] === deleteValue) { + this.splice(i, 1) + i-- + } + } + return this + }, + enumerable: false, +}) + +Object.defineProperty(Array.prototype, 'extend', { + value: function (otherArray) { + /* you should include a test to check whether other_array really is an array */ + otherArray.forEach(function (v) { + this.push(v) + }, this) + }, + enumerable: false, +}) + +Object.defineProperty(Array.prototype, 'contains', { + value: function (value) { + return this.indexOf(value) > -1 + }, + enumerable: false, +}) diff --git a/v0/src/simulator/src/backgroundArea.js b/v0/src/simulator/src/backgroundArea.js new file mode 100644 index 00000000..49da21aa --- /dev/null +++ b/v0/src/simulator/src/backgroundArea.js @@ -0,0 +1,17 @@ +import { dots } from './canvasApi' + +var backgroundArea +export default backgroundArea = { + canvas: document.getElementById('backgroundArea'), + setup() { + this.canvas = document.getElementById('backgroundArea') + this.canvas.width = width + this.canvas.height = height + this.context = this.canvas.getContext('2d') + dots(true, false) + }, + clear() { + if (!this.context) return + this.context.clearRect(0, 0, this.canvas.width, this.canvas.height) + }, +} diff --git a/v0/src/simulator/src/canvas2svg.js b/v0/src/simulator/src/canvas2svg.js new file mode 100644 index 00000000..d6f5dc64 --- /dev/null +++ b/v0/src/simulator/src/canvas2svg.js @@ -0,0 +1,1433 @@ +/*!! + * Canvas 2 Svg v1.0.19 + * A low level canvas to SVG converter. Uses a mock canvas context to build an SVG document. + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/mit-license.php + * + * Author: + * Kerry Liu + * + * Copyright (c) 2014 Gliffy Inc. + */ + +/** + * CircuitVerse - Edited + * Latest npm package is 1.0.16 but we need 1.0.19 + */ + +'use strict' + +var STYLES, ctx, CanvasGradient, CanvasPattern, namedEntities + +//helper function to format a string +function format(str, args) { + var keys = Object.keys(args), + i + for (i = 0; i < keys.length; i++) { + str = str.replace( + new RegExp('\\{' + keys[i] + '\\}', 'gi'), + args[keys[i]] + ) + } + return str +} + +//helper function that generates a random string +function randomString(holder) { + var chars, randomstring, i + if (!holder) { + throw new Error( + 'cannot create a random attribute name for an undefined object' + ) + } + chars = 'ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz' + randomstring = '' + do { + randomstring = '' + for (i = 0; i < 12; i++) { + randomstring += chars[Math.floor(Math.random() * chars.length)] + } + } while (holder[randomstring]) + return randomstring +} + +//helper function to map named to numbered entities +function createNamedToNumberedLookup(items, radix) { + var i, + entity, + lookup = {}, + base10, + base16 + items = items.split(',') + radix = radix || 10 + // Map from named to numbered entities. + for (i = 0; i < items.length; i += 2) { + entity = '&' + items[i + 1] + ';' + base10 = parseInt(items[i], radix) + lookup[entity] = '&#' + base10 + ';' + } + //FF and IE need to create a regex from hex values ie   == \xa0 + lookup['\\xa0'] = ' ' + return lookup +} + +//helper function to map canvas-textAlign to svg-textAnchor +function getTextAnchor(textAlign) { + //TODO: support rtl languages + var mapping = { + left: 'start', + right: 'end', + center: 'middle', + start: 'start', + end: 'end', + } + return mapping[textAlign] || mapping.start +} + +//helper function to map canvas-textBaseline to svg-dominantBaseline +function getDominantBaseline(textBaseline) { + //INFO: not supported in all browsers + var mapping = { + alphabetic: 'alphabetic', + hanging: 'hanging', + top: 'text-before-edge', + bottom: 'text-after-edge', + middle: 'central', + } + return mapping[textBaseline] || mapping.alphabetic +} + +// Unpack entities lookup where the numbers are in radix 32 to reduce the size +// entity mapping courtesy of tinymce +namedEntities = createNamedToNumberedLookup( + '50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,' + + '5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,' + + '5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,' + + '5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,' + + '68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,' + + '6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,' + + '6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,' + + '75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,' + + '7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,' + + '7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,' + + 'sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,' + + 'st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,' + + 't9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,' + + 'tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,' + + 'u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,' + + '81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,' + + '8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,' + + '8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,' + + '8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,' + + '8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,' + + 'nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,' + + 'rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,' + + 'Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,' + + '80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,' + + '811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro', + 32 +) + +//Some basic mappings for attributes and default values. +STYLES = { + strokeStyle: { + svgAttr: 'stroke', //corresponding svg attribute + canvas: '#000000', //canvas default + svg: 'none', //svg default + apply: 'stroke', //apply on stroke() or fill() + }, + fillStyle: { + svgAttr: 'fill', + canvas: '#000000', + svg: null, //svg default is black, but we need to special case this to handle canvas stroke without fill + apply: 'fill', + }, + lineCap: { + svgAttr: 'stroke-linecap', + canvas: 'butt', + svg: 'butt', + apply: 'stroke', + }, + lineJoin: { + svgAttr: 'stroke-linejoin', + canvas: 'miter', + svg: 'miter', + apply: 'stroke', + }, + miterLimit: { + svgAttr: 'stroke-miterlimit', + canvas: 10, + svg: 4, + apply: 'stroke', + }, + lineWidth: { + svgAttr: 'stroke-width', + canvas: 1, + svg: 1, + apply: 'stroke', + }, + globalAlpha: { + svgAttr: 'opacity', + canvas: 1, + svg: 1, + apply: 'fill stroke', + }, + font: { + //font converts to multiple svg attributes, there is custom logic for this + canvas: '10px sans-serif', + }, + shadowColor: { + canvas: '#000000', + }, + shadowOffsetX: { + canvas: 0, + }, + shadowOffsetY: { + canvas: 0, + }, + shadowBlur: { + canvas: 0, + }, + textAlign: { + canvas: 'start', + }, + textBaseline: { + canvas: 'alphabetic', + }, + lineDash: { + svgAttr: 'stroke-dasharray', + canvas: [], + svg: null, + apply: 'stroke', + }, +} + +/** + * + * @param gradientNode - reference to the gradient + * @constructor + */ +CanvasGradient = function (gradientNode, ctx) { + this.__root = gradientNode + this.__ctx = ctx +} + +/** + * Adds a color stop to the gradient root + */ +CanvasGradient.prototype.addColorStop = function (offset, color) { + var stop = this.__ctx.__createElement('stop'), + regex, + matches + stop.setAttribute('offset', offset) + if (color.indexOf('rgba') !== -1) { + //separate alpha value, since webkit can't handle it + regex = + /rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d?\.?\d*)\s*\)/gi + matches = regex.exec(color) + stop.setAttribute( + 'stop-color', + format('rgb({r},{g},{b})', { + r: matches[1], + g: matches[2], + b: matches[3], + }) + ) + stop.setAttribute('stop-opacity', matches[4]) + } else { + stop.setAttribute('stop-color', color) + } + this.__root.appendChild(stop) +} + +CanvasPattern = function (pattern, ctx) { + this.__root = pattern + this.__ctx = ctx +} + +/** + * The mock canvas context + * @param o - options include: + * ctx - existing Context2D to wrap around + * width - width of your canvas (defaults to 500) + * height - height of your canvas (defaults to 500) + * enableMirroring - enables canvas mirroring (get image data) (defaults to false) + * document - the document object (defaults to the current document) + */ +ctx = function (o) { + var defaultOptions = { width: 500, height: 500, enableMirroring: false }, + options + + //keep support for this way of calling C2S: new C2S(width,height) + if (arguments.length > 1) { + options = defaultOptions + options.width = arguments[0] + options.height = arguments[1] + } else if (!o) { + options = defaultOptions + } else { + options = o + } + + if (!(this instanceof ctx)) { + //did someone call this without new? + return new ctx(options) + } + + //setup options + this.width = options.width || defaultOptions.width + this.height = options.height || defaultOptions.height + this.enableMirroring = + options.enableMirroring !== undefined + ? options.enableMirroring + : defaultOptions.enableMirroring + + this.canvas = this ///point back to this instance! + this.__document = options.document || document + + // allow passing in an existing context to wrap around + // if a context is passed in, we know a canvas already exist + if (options.ctx) { + this.__ctx = options.ctx + } else { + this.__canvas = this.__document.createElement('canvas') + this.__ctx = this.__canvas.getContext('2d') + } + + this.__setDefaultStyles() + this.__stack = [this.__getStyleState()] + this.__groupStack = [] + + //the root svg element + this.__root = this.__document.createElementNS( + 'http://www.w3.org/2000/svg', + 'svg' + ) + this.__root.setAttribute('version', 1.1) + this.__root.setAttribute('xmlns', 'http://www.w3.org/2000/svg') + this.__root.setAttributeNS( + 'http://www.w3.org/2000/xmlns/', + 'xmlns:xlink', + 'http://www.w3.org/1999/xlink' + ) + this.__root.setAttribute('width', this.width) + this.__root.setAttribute('height', this.height) + + //make sure we don't generate the same ids in defs + this.__ids = {} + + //defs tag + this.__defs = this.__document.createElementNS( + 'http://www.w3.org/2000/svg', + 'defs' + ) + this.__root.appendChild(this.__defs) + + //also add a group child. the svg element can't use the transform attribute + this.__currentElement = this.__document.createElementNS( + 'http://www.w3.org/2000/svg', + 'g' + ) + this.__root.appendChild(this.__currentElement) +} + +/** + * Creates the specified svg element + * @private + */ +ctx.prototype.__createElement = function (elementName, properties, resetFill) { + if (typeof properties === 'undefined') { + properties = {} + } + + var element = this.__document.createElementNS( + 'http://www.w3.org/2000/svg', + elementName + ), + keys = Object.keys(properties), + i, + key + if (resetFill) { + //if fill or stroke is not specified, the svg element should not display. By default SVG's fill is black. + element.setAttribute('fill', 'none') + element.setAttribute('stroke', 'none') + } + for (i = 0; i < keys.length; i++) { + key = keys[i] + element.setAttribute(key, properties[key]) + } + return element +} + +/** + * Applies default canvas styles to the context + * @private + */ +ctx.prototype.__setDefaultStyles = function () { + //default 2d canvas context properties see:http://www.w3.org/TR/2dcontext/ + var keys = Object.keys(STYLES), + i, + key + for (i = 0; i < keys.length; i++) { + key = keys[i] + this[key] = STYLES[key].canvas + } +} + +/** + * Applies styles on restore + * @param styleState + * @private + */ +ctx.prototype.__applyStyleState = function (styleState) { + var keys = Object.keys(styleState), + i, + key + for (i = 0; i < keys.length; i++) { + key = keys[i] + this[key] = styleState[key] + } +} + +/** + * Gets the current style state + * @return {Object} + * @private + */ +ctx.prototype.__getStyleState = function () { + var i, + styleState = {}, + keys = Object.keys(STYLES), + key + for (i = 0; i < keys.length; i++) { + key = keys[i] + styleState[key] = this[key] + } + return styleState +} + +/** + * Apples the current styles to the current SVG element. On "ctx.fill" or "ctx.stroke" + * @param type + * @private + */ +ctx.prototype.__applyStyleToCurrentElement = function (type) { + var currentElement = this.__currentElement + var currentStyleGroup = this.__currentElementsToStyle + if (currentStyleGroup) { + currentElement.setAttribute(type, '') + currentElement = currentStyleGroup.element + currentStyleGroup.children.forEach(function (node) { + node.setAttribute(type, '') + }) + } + + var keys = Object.keys(STYLES), + i, + style, + value, + id, + regex, + matches + for (i = 0; i < keys.length; i++) { + style = STYLES[keys[i]] + value = this[keys[i]] + if (style.apply) { + //is this a gradient or pattern? + if (value instanceof CanvasPattern) { + //pattern + if (value.__ctx) { + //copy over defs + while (value.__ctx.__defs.childNodes.length) { + id = value.__ctx.__defs.childNodes[0].getAttribute('id') + this.__ids[id] = id + this.__defs.appendChild( + value.__ctx.__defs.childNodes[0] + ) + } + } + currentElement.setAttribute( + style.apply, + format('url(#{id})', { + id: value.__root.getAttribute('id'), + }) + ) + } else if (value instanceof CanvasGradient) { + //gradient + currentElement.setAttribute( + style.apply, + format('url(#{id})', { + id: value.__root.getAttribute('id'), + }) + ) + } else if ( + style.apply.indexOf(type) !== -1 && + style.svg !== value + ) { + if ( + (style.svgAttr === 'stroke' || style.svgAttr === 'fill') && + value.indexOf('rgba') !== -1 + ) { + //separate alpha value, since illustrator can't handle it + regex = + /rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d?\.?\d*)\s*\)/gi + matches = regex.exec(value) + currentElement.setAttribute( + style.svgAttr, + format('rgb({r},{g},{b})', { + r: matches[1], + g: matches[2], + b: matches[3], + }) + ) + //should take globalAlpha here + var opacity = matches[4] + var globalAlpha = this.globalAlpha + if (globalAlpha != null) { + opacity *= globalAlpha + } + currentElement.setAttribute( + style.svgAttr + '-opacity', + opacity + ) + } else { + var attr = style.svgAttr + if (keys[i] === 'globalAlpha') { + attr = type + '-' + style.svgAttr + if (currentElement.getAttribute(attr)) { + //fill-opacity or stroke-opacity has already been set by stroke or fill. + continue + } + } + //otherwise only update attribute if right type, and not svg default + currentElement.setAttribute(attr, value) + } + } + } + } +} + +/** + * Will return the closest group or svg node. May return the current element. + * @private + */ +ctx.prototype.__closestGroupOrSvg = function (node) { + node = node || this.__currentElement + if (node.nodeName === 'g' || node.nodeName === 'svg') { + return node + } else { + return this.__closestGroupOrSvg(node.parentNode) + } +} + +/** + * Returns the serialized value of the svg so far + * @param fixNamedEntities - Standalone SVG doesn't support named entities, which document.createTextNode encodes. + * If true, we attempt to find all named entities and encode it as a numeric entity. + * @return serialized svg + */ +ctx.prototype.getSerializedSvg = function (fixNamedEntities) { + var serialized = new XMLSerializer().serializeToString(this.__root), + keys, + i, + key, + value, + regexp, + xmlns + + //IE search for a duplicate xmnls because they didn't implement setAttributeNS correctly + xmlns = + /xmlns="http:\/\/www\.w3\.org\/2000\/svg".+xmlns="http:\/\/www\.w3\.org\/2000\/svg/gi + if (xmlns.test(serialized)) { + serialized = serialized.replace( + 'xmlns="http://www.w3.org/2000/svg', + 'xmlns:xlink="http://www.w3.org/1999/xlink' + ) + } + + if (fixNamedEntities) { + keys = Object.keys(namedEntities) + //loop over each named entity and replace with the proper equivalent. + for (i = 0; i < keys.length; i++) { + key = keys[i] + value = namedEntities[key] + regexp = new RegExp(key, 'gi') + if (regexp.test(serialized)) { + serialized = serialized.replace(regexp, value) + } + } + } + + return serialized +} + +/** + * Returns the root svg + * @return svg + */ +ctx.prototype.getSvg = function () { + return this.__root +} +/** + * Will generate a group tag. + */ +ctx.prototype.save = function () { + var group = this.__createElement('g') + var parent = this.__closestGroupOrSvg() + this.__groupStack.push(parent) + parent.appendChild(group) + this.__currentElement = group + this.__stack.push(this.__getStyleState()) +} +/** + * Sets current element to parent, or just root if already root + */ +ctx.prototype.restore = function () { + this.__currentElement = this.__groupStack.pop() + this.__currentElementsToStyle = null + //Clearing canvas will make the poped group invalid, currentElement is set to the root group node. + if (!this.__currentElement) { + this.__currentElement = this.__root.childNodes[1] + } + var state = this.__stack.pop() + this.__applyStyleState(state) +} + +/** + * Helper method to add transform + * @private + */ +ctx.prototype.__addTransform = function (t) { + //if the current element has siblings, add another group + var parent = this.__closestGroupOrSvg() + if (parent.childNodes.length > 0) { + if (this.__currentElement.nodeName === 'path') { + if (!this.__currentElementsToStyle) + this.__currentElementsToStyle = { + element: parent, + children: [], + } + this.__currentElementsToStyle.children.push(this.__currentElement) + this.__applyCurrentDefaultPath() + } + + var group = this.__createElement('g') + parent.appendChild(group) + this.__currentElement = group + } + + var transform = this.__currentElement.getAttribute('transform') + if (transform) { + transform += ' ' + } else { + transform = '' + } + transform += t + this.__currentElement.setAttribute('transform', transform) +} + +/** + * scales the current element + */ +ctx.prototype.scale = function (x, y) { + if (y === undefined) { + y = x + } + this.__addTransform(format('scale({x},{y})', { x: x, y: y })) +} + +/** + * rotates the current element + */ +ctx.prototype.rotate = function (angle) { + var degrees = (angle * 180) / Math.PI + this.__addTransform( + format('rotate({angle},{cx},{cy})', { angle: degrees, cx: 0, cy: 0 }) + ) +} + +/** + * translates the current element + */ +ctx.prototype.translate = function (x, y) { + this.__addTransform(format('translate({x},{y})', { x: x, y: y })) +} + +/** + * applies a transform to the current element + */ +ctx.prototype.transform = function (a, b, c, d, e, f) { + this.__addTransform( + format('matrix({a},{b},{c},{d},{e},{f})', { + a: a, + b: b, + c: c, + d: d, + e: e, + f: f, + }) + ) +} + +/** + * Create a new Path Element + */ +ctx.prototype.beginPath = function () { + var path, parent + + // Note that there is only one current default path, it is not part of the drawing state. + // See also: https://html.spec.whatwg.org/multipage/scripting.html#current-default-path + this.__currentDefaultPath = '' + this.__currentPosition = {} + + path = this.__createElement('path', {}, true) + parent = this.__closestGroupOrSvg() + parent.appendChild(path) + this.__currentElement = path +} + +/** + * Helper function to apply currentDefaultPath to current path element + * @private + */ +ctx.prototype.__applyCurrentDefaultPath = function () { + var currentElement = this.__currentElement + if (currentElement.nodeName === 'path') { + currentElement.setAttribute('d', this.__currentDefaultPath) + } else { + console.error( + 'Attempted to apply path command to node', + currentElement.nodeName + ) + } +} + +/** + * Helper function to add path command + * @private + */ +ctx.prototype.__addPathCommand = function (command) { + this.__currentDefaultPath += ' ' + this.__currentDefaultPath += command +} + +/** + * Adds the move command to the current path element, + * if the currentPathElement is not empty create a new path element + */ +ctx.prototype.moveTo = function (x, y) { + if (this.__currentElement.nodeName !== 'path') { + this.beginPath() + } + + // creates a new subpath with the given point + this.__currentPosition = { x: x, y: y } + this.__addPathCommand(format('M {x} {y}', { x: x, y: y })) +} + +/** + * Closes the current path + */ +ctx.prototype.closePath = function () { + if (this.__currentDefaultPath) { + this.__addPathCommand('Z') + } +} + +/** + * Adds a line to command + */ +ctx.prototype.lineTo = function (x, y) { + this.__currentPosition = { x: x, y: y } + if (this.__currentDefaultPath.indexOf('M') > -1) { + this.__addPathCommand(format('L {x} {y}', { x: x, y: y })) + } else { + this.__addPathCommand(format('M {x} {y}', { x: x, y: y })) + } +} + +/** + * Add a bezier command + */ +ctx.prototype.bezierCurveTo = function (cp1x, cp1y, cp2x, cp2y, x, y) { + this.__currentPosition = { x: x, y: y } + this.__addPathCommand( + format('C {cp1x} {cp1y} {cp2x} {cp2y} {x} {y}', { + cp1x: cp1x, + cp1y: cp1y, + cp2x: cp2x, + cp2y: cp2y, + x: x, + y: y, + }) + ) +} + +/** + * Adds a quadratic curve to command + */ +ctx.prototype.quadraticCurveTo = function (cpx, cpy, x, y) { + this.__currentPosition = { x: x, y: y } + this.__addPathCommand( + format('Q {cpx} {cpy} {x} {y}', { cpx: cpx, cpy: cpy, x: x, y: y }) + ) +} + +/** + * Return a new normalized vector of given vector + */ +var normalize = function (vector) { + var len = Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1]) + return [vector[0] / len, vector[1] / len] +} + +/** + * Adds the arcTo to the current path + * + * @see http://www.w3.org/TR/2015/WD-2dcontext-20150514/#dom-context-2d-arcto + */ +ctx.prototype.arcTo = function (x1, y1, x2, y2, radius) { + // Let the point (x0, y0) be the last point in the subpath. + var x0 = this.__currentPosition && this.__currentPosition.x + var y0 = this.__currentPosition && this.__currentPosition.y + + // First ensure there is a subpath for (x1, y1). + if (typeof x0 == 'undefined' || typeof y0 == 'undefined') { + return + } + + // Negative values for radius must cause the implementation to throw an IndexSizeError exception. + if (radius < 0) { + throw new Error( + 'IndexSizeError: The radius provided (' + radius + ') is negative.' + ) + } + + // If the point (x0, y0) is equal to the point (x1, y1), + // or if the point (x1, y1) is equal to the point (x2, y2), + // or if the radius radius is zero, + // then the method must add the point (x1, y1) to the subpath, + // and connect that point to the previous point (x0, y0) by a straight line. + if ((x0 === x1 && y0 === y1) || (x1 === x2 && y1 === y2) || radius === 0) { + this.lineTo(x1, y1) + return + } + + // Otherwise, if the points (x0, y0), (x1, y1), and (x2, y2) all lie on a single straight line, + // then the method must add the point (x1, y1) to the subpath, + // and connect that point to the previous point (x0, y0) by a straight line. + var unit_vec_p1_p0 = normalize([x0 - x1, y0 - y1]) + var unit_vec_p1_p2 = normalize([x2 - x1, y2 - y1]) + if ( + unit_vec_p1_p0[0] * unit_vec_p1_p2[1] === + unit_vec_p1_p0[1] * unit_vec_p1_p2[0] + ) { + this.lineTo(x1, y1) + return + } + + // Otherwise, let The Arc be the shortest arc given by circumference of the circle that has radius radius, + // and that has one point tangent to the half-infinite line that crosses the point (x0, y0) and ends at the point (x1, y1), + // and that has a different point tangent to the half-infinite line that ends at the point (x1, y1), and crosses the point (x2, y2). + // The points at which this circle touches these two lines are called the start and end tangent points respectively. + + // note that both vectors are unit vectors, so the length is 1 + var cos = + unit_vec_p1_p0[0] * unit_vec_p1_p2[0] + + unit_vec_p1_p0[1] * unit_vec_p1_p2[1] + var theta = Math.acos(Math.abs(cos)) + + // Calculate origin + var unit_vec_p1_origin = normalize([ + unit_vec_p1_p0[0] + unit_vec_p1_p2[0], + unit_vec_p1_p0[1] + unit_vec_p1_p2[1], + ]) + var len_p1_origin = radius / Math.sin(theta / 2) + var x = x1 + len_p1_origin * unit_vec_p1_origin[0] + var y = y1 + len_p1_origin * unit_vec_p1_origin[1] + + // Calculate start angle and end angle + // rotate 90deg clockwise (note that y axis points to its down) + var unit_vec_origin_start_tangent = [-unit_vec_p1_p0[1], unit_vec_p1_p0[0]] + // rotate 90deg counter clockwise (note that y axis points to its down) + var unit_vec_origin_end_tangent = [unit_vec_p1_p2[1], -unit_vec_p1_p2[0]] + var getAngle = function (vector) { + // get angle (clockwise) between vector and (1, 0) + var x = vector[0] + var y = vector[1] + if (y >= 0) { + // note that y axis points to its down + return Math.acos(x) + } else { + return -Math.acos(x) + } + } + var startAngle = getAngle(unit_vec_origin_start_tangent) + var endAngle = getAngle(unit_vec_origin_end_tangent) + + // Connect the point (x0, y0) to the start tangent point by a straight line + this.lineTo( + x + unit_vec_origin_start_tangent[0] * radius, + y + unit_vec_origin_start_tangent[1] * radius + ) + + // Connect the start tangent point to the end tangent point by arc + // and adding the end tangent point to the subpath. + this.arc(x, y, radius, startAngle, endAngle) +} + +/** + * Sets the stroke property on the current element + */ +ctx.prototype.stroke = function () { + if (this.__currentElement.nodeName === 'path') { + this.__currentElement.setAttribute('paint-order', 'fill stroke markers') + } + this.__applyCurrentDefaultPath() + this.__applyStyleToCurrentElement('stroke') +} + +/** + * Sets fill properties on the current element + */ +ctx.prototype.fill = function () { + if (this.__currentElement.nodeName === 'path') { + this.__currentElement.setAttribute('paint-order', 'stroke fill markers') + } + this.__applyCurrentDefaultPath() + this.__applyStyleToCurrentElement('fill') +} + +/** + * Adds a rectangle to the path. + */ +ctx.prototype.rect = function (x, y, width, height) { + if (this.__currentElement.nodeName !== 'path') { + this.beginPath() + } + this.moveTo(x, y) + this.lineTo(x + width, y) + this.lineTo(x + width, y + height) + this.lineTo(x, y + height) + this.lineTo(x, y) + this.closePath() +} + +/** + * adds a rectangle element + */ +ctx.prototype.fillRect = function (x, y, width, height) { + var rect, parent + rect = this.__createElement( + 'rect', + { + x: x, + y: y, + width: width, + height: height, + }, + true + ) + parent = this.__closestGroupOrSvg() + parent.appendChild(rect) + this.__currentElement = rect + this.__applyStyleToCurrentElement('fill') +} + +/** + * Draws a rectangle with no fill + * @param x + * @param y + * @param width + * @param height + */ +ctx.prototype.strokeRect = function (x, y, width, height) { + var rect, parent + rect = this.__createElement( + 'rect', + { + x: x, + y: y, + width: width, + height: height, + }, + true + ) + parent = this.__closestGroupOrSvg() + parent.appendChild(rect) + this.__currentElement = rect + this.__applyStyleToCurrentElement('stroke') +} + +/** + * Clear entire canvas: + * 1. save current transforms + * 2. remove all the childNodes of the root g element + */ +ctx.prototype.__clearCanvas = function () { + var current = this.__closestGroupOrSvg(), + transform = current.getAttribute('transform') + var rootGroup = this.__root.childNodes[1] + var childNodes = rootGroup.childNodes + for (var i = childNodes.length - 1; i >= 0; i--) { + if (childNodes[i]) { + rootGroup.removeChild(childNodes[i]) + } + } + this.__currentElement = rootGroup + //reset __groupStack as all the child group nodes are all removed. + this.__groupStack = [] + if (transform) { + this.__addTransform(transform) + } +} + +/** + * "Clears" a canvas by just drawing a white rectangle in the current group. + */ +ctx.prototype.clearRect = function (x, y, width, height) { + //clear entire canvas + if (x === 0 && y === 0 && width === this.width && height === this.height) { + this.__clearCanvas() + return + } + var rect, + parent = this.__closestGroupOrSvg() + rect = this.__createElement( + 'rect', + { + x: x, + y: y, + width: width, + height: height, + fill: '#FFFFFF', + }, + true + ) + parent.appendChild(rect) +} + +/** + * Adds a linear gradient to a defs tag. + * Returns a canvas gradient object that has a reference to it's parent def + */ +ctx.prototype.createLinearGradient = function (x1, y1, x2, y2) { + var grad = this.__createElement( + 'linearGradient', + { + id: randomString(this.__ids), + x1: x1 + 'px', + x2: x2 + 'px', + y1: y1 + 'px', + y2: y2 + 'px', + gradientUnits: 'userSpaceOnUse', + }, + false + ) + this.__defs.appendChild(grad) + return new CanvasGradient(grad, this) +} + +/** + * Adds a radial gradient to a defs tag. + * Returns a canvas gradient object that has a reference to it's parent def + */ +ctx.prototype.createRadialGradient = function (x0, y0, r0, x1, y1, r1) { + var grad = this.__createElement( + 'radialGradient', + { + id: randomString(this.__ids), + cx: x1 + 'px', + cy: y1 + 'px', + r: r1 + 'px', + fx: x0 + 'px', + fy: y0 + 'px', + gradientUnits: 'userSpaceOnUse', + }, + false + ) + this.__defs.appendChild(grad) + return new CanvasGradient(grad, this) +} + +/** + * Parses the font string and returns svg mapping + * @private + */ +ctx.prototype.__parseFont = function () { + var regex = + /^\s*(?=(?:(?:[-a-z]+\s*){0,2}(italic|oblique))?)(?=(?:(?:[-a-z]+\s*){0,2}(small-caps))?)(?=(?:(?:[-a-z]+\s*){0,2}(bold(?:er)?|lighter|[1-9]00))?)(?:(?:normal|\1|\2|\3)\s*){0,3}((?:xx?-)?(?:small|large)|medium|smaller|larger|[.\d]+(?:\%|in|[cem]m|ex|p[ctx]))(?:\s*\/\s*(normal|[.\d]+(?:\%|in|[cem]m|ex|p[ctx])))?\s*([-,\'\"\sa-z0-9]+?)\s*$/i + var fontPart = regex.exec(this.font) + var data = { + style: fontPart[1] || 'normal', + size: fontPart[4] || '10px', + family: fontPart[6] || 'sans-serif', + weight: fontPart[3] || 'normal', + decoration: fontPart[2] || 'normal', + href: null, + } + + //canvas doesn't support underline natively, but we can pass this attribute + if (this.__fontUnderline === 'underline') { + data.decoration = 'underline' + } + + //canvas also doesn't support linking, but we can pass this as well + if (this.__fontHref) { + data.href = this.__fontHref + } + + return data +} + +/** + * Helper to link text fragments + * @param font + * @param element + * @return {*} + * @private + */ +ctx.prototype.__wrapTextLink = function (font, element) { + if (font.href) { + var a = this.__createElement('a') + a.setAttributeNS( + 'http://www.w3.org/1999/xlink', + 'xlink:href', + font.href + ) + a.appendChild(element) + return a + } + return element +} + +/** + * Fills or strokes text + * @param text + * @param x + * @param y + * @param action - stroke or fill + * @private + */ +ctx.prototype.__applyText = function (text, x, y, action) { + var font = this.__parseFont(), + parent = this.__closestGroupOrSvg(), + textElement = this.__createElement( + 'text', + { + 'font-family': font.family, + 'font-size': font.size, + 'font-style': font.style, + 'font-weight': font.weight, + 'text-decoration': font.decoration, + x: x, + y: y, + 'text-anchor': getTextAnchor(this.textAlign), + 'dominant-baseline': getDominantBaseline(this.textBaseline), + }, + true + ) + + textElement.appendChild(this.__document.createTextNode(text)) + this.__currentElement = textElement + this.__applyStyleToCurrentElement(action) + parent.appendChild(this.__wrapTextLink(font, textElement)) +} + +/** + * Creates a text element + * @param text + * @param x + * @param y + */ +ctx.prototype.fillText = function (text, x, y) { + this.__applyText(text, x, y, 'fill') +} + +/** + * Strokes text + * @param text + * @param x + * @param y + */ +ctx.prototype.strokeText = function (text, x, y) { + this.__applyText(text, x, y, 'stroke') +} + +/** + * No need to implement this for svg. + * @param text + * @return {TextMetrics} + */ +ctx.prototype.measureText = function (text) { + this.__ctx.font = this.font + return this.__ctx.measureText(text) +} + +/** + * Arc command! + */ +ctx.prototype.arc = function ( + x, + y, + radius, + startAngle, + endAngle, + counterClockwise +) { + // in canvas no circle is drawn if no angle is provided. + if (startAngle === endAngle) { + return + } + startAngle = startAngle % (2 * Math.PI) + endAngle = endAngle % (2 * Math.PI) + if (startAngle === endAngle) { + //circle time! subtract some of the angle so svg is happy (svg elliptical arc can't draw a full circle) + endAngle = + (endAngle + 2 * Math.PI - 0.001 * (counterClockwise ? -1 : 1)) % + (2 * Math.PI) + } + var endX = x + radius * Math.cos(endAngle), + endY = y + radius * Math.sin(endAngle), + startX = x + radius * Math.cos(startAngle), + startY = y + radius * Math.sin(startAngle), + sweepFlag = counterClockwise ? 0 : 1, + largeArcFlag = 0, + diff = endAngle - startAngle + + // https://github.com/gliffy/canvas2svg/issues/4 + if (diff < 0) { + diff += 2 * Math.PI + } + + if (counterClockwise) { + largeArcFlag = diff > Math.PI ? 0 : 1 + } else { + largeArcFlag = diff > Math.PI ? 1 : 0 + } + + this.lineTo(startX, startY) + this.__addPathCommand( + format( + 'A {rx} {ry} {xAxisRotation} {largeArcFlag} {sweepFlag} {endX} {endY}', + { + rx: radius, + ry: radius, + xAxisRotation: 0, + largeArcFlag: largeArcFlag, + sweepFlag: sweepFlag, + endX: endX, + endY: endY, + } + ) + ) + + this.__currentPosition = { x: endX, y: endY } +} + +/** + * Generates a ClipPath from the clip command. + */ +ctx.prototype.clip = function () { + var group = this.__closestGroupOrSvg(), + clipPath = this.__createElement('clipPath'), + id = randomString(this.__ids), + newGroup = this.__createElement('g') + + this.__applyCurrentDefaultPath() + group.removeChild(this.__currentElement) + clipPath.setAttribute('id', id) + clipPath.appendChild(this.__currentElement) + + this.__defs.appendChild(clipPath) + + //set the clip path to this group + group.setAttribute('clip-path', format('url(#{id})', { id: id })) + + //clip paths can be scaled and transformed, we need to add another wrapper group to avoid later transformations + // to this path + group.appendChild(newGroup) + + this.__currentElement = newGroup +} + +/** + * Draws a canvas, image or mock context to this canvas. + * Note that all svg dom manipulation uses node.childNodes rather than node.children for IE support. + * http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-drawimage + */ +ctx.prototype.drawImage = function () { + //convert arguments to a real array + var args = Array.prototype.slice.call(arguments), + image = args[0], + dx, + dy, + dw, + dh, + sx = 0, + sy = 0, + sw, + sh, + parent, + svg, + defs, + group, + currentElement, + svgImage, + canvas, + context, + id + + if (args.length === 3) { + dx = args[1] + dy = args[2] + sw = image.width + sh = image.height + dw = sw + dh = sh + } else if (args.length === 5) { + dx = args[1] + dy = args[2] + dw = args[3] + dh = args[4] + sw = image.width + sh = image.height + } else if (args.length === 9) { + sx = args[1] + sy = args[2] + sw = args[3] + sh = args[4] + dx = args[5] + dy = args[6] + dw = args[7] + dh = args[8] + } else { + throw new Error( + 'Inavlid number of arguments passed to drawImage: ' + + arguments.length + ) + } + + parent = this.__closestGroupOrSvg() + currentElement = this.__currentElement + var translateDirective = 'translate(' + dx + ', ' + dy + ')' + if (image instanceof ctx) { + //canvas2svg mock canvas context. In the future we may want to clone nodes instead. + //also I'm currently ignoring dw, dh, sw, sh, sx, sy for a mock context. + svg = image.getSvg().cloneNode(true) + if (svg.childNodes && svg.childNodes.length > 1) { + defs = svg.childNodes[0] + while (defs.childNodes.length) { + id = defs.childNodes[0].getAttribute('id') + this.__ids[id] = id + this.__defs.appendChild(defs.childNodes[0]) + } + group = svg.childNodes[1] + if (group) { + //save original transform + var originTransform = group.getAttribute('transform') + var transformDirective + if (originTransform) { + transformDirective = + originTransform + ' ' + translateDirective + } else { + transformDirective = translateDirective + } + group.setAttribute('transform', transformDirective) + parent.appendChild(group) + } + } + } else if (image.nodeName === 'CANVAS' || image.nodeName === 'IMG') { + //canvas or image + svgImage = this.__createElement('image') + svgImage.setAttribute('width', dw) + svgImage.setAttribute('height', dh) + svgImage.setAttribute('preserveAspectRatio', 'none') + + if (sx || sy || sw !== image.width || sh !== image.height) { + //crop the image using a temporary canvas + canvas = this.__document.createElement('canvas') + canvas.width = dw + canvas.height = dh + context = canvas.getContext('2d') + context.drawImage(image, sx, sy, sw, sh, 0, 0, dw, dh) + image = canvas + } + svgImage.setAttribute('transform', translateDirective) + svgImage.setAttributeNS( + 'http://www.w3.org/1999/xlink', + 'xlink:href', + image.nodeName === 'CANVAS' + ? image.toDataURL() + : image.getAttribute('src') + ) + parent.appendChild(svgImage) + } +} + +/** + * Generates a pattern tag + */ +ctx.prototype.createPattern = function (image, repetition) { + var pattern = this.__document.createElementNS( + 'http://www.w3.org/2000/svg', + 'pattern' + ), + id = randomString(this.__ids), + img + pattern.setAttribute('id', id) + pattern.setAttribute('width', image.width) + pattern.setAttribute('height', image.height) + if (image.nodeName === 'CANVAS' || image.nodeName === 'IMG') { + img = this.__document.createElementNS( + 'http://www.w3.org/2000/svg', + 'image' + ) + img.setAttribute('width', image.width) + img.setAttribute('height', image.height) + img.setAttributeNS( + 'http://www.w3.org/1999/xlink', + 'xlink:href', + image.nodeName === 'CANVAS' + ? image.toDataURL() + : image.getAttribute('src') + ) + pattern.appendChild(img) + this.__defs.appendChild(pattern) + } else if (image instanceof ctx) { + pattern.appendChild(image.__root.childNodes[1]) + this.__defs.appendChild(pattern) + } + return new CanvasPattern(pattern, this) +} + +ctx.prototype.setLineDash = function (dashArray) { + if (dashArray && dashArray.length > 0) { + this.lineDash = dashArray.join(',') + } else { + this.lineDash = null + } +} + +/** + * Not yet implemented + */ +ctx.prototype.drawFocusRing = function () {} +ctx.prototype.createImageData = function () {} +ctx.prototype.getImageData = function () {} +ctx.prototype.putImageData = function () {} +ctx.prototype.globalCompositeOperation = function () {} +ctx.prototype.setTransform = function () {} + +//add options for alternative namespace +// if (typeof window === "object") { +// window.C2S = ctx; +// } + +// CommonJS/Browserify +// if (typeof module === "object" && typeof module.exports === "object") { +// module.exports = ctx; +// } + +export default ctx diff --git a/v0/src/simulator/src/canvasApi.js b/v0/src/simulator/src/canvasApi.js new file mode 100644 index 00000000..08ee65d3 --- /dev/null +++ b/v0/src/simulator/src/canvasApi.js @@ -0,0 +1,624 @@ +/* eslint-disable no-param-reassign */ +import backgroundArea from './backgroundArea' +import simulationArea from './simulationArea' +import miniMapArea, { removeMiniMap, updatelastMinimapShown } from './minimap' +import { colors } from './themer/themer' + +var unit = 10 + +export function findDimensions(scope = globalScope) { + var totalObjects = 0 + simulationArea.minWidth = undefined + simulationArea.maxWidth = undefined + simulationArea.minHeight = undefined + simulationArea.maxHeight = undefined + for (var i = 0; i < updateOrder.length; i++) { + if (updateOrder[i] !== 'wires') { + for (var j = 0; j < scope[updateOrder[i]].length; j++) { + totalObjects += 1 + var obj = scope[updateOrder[i]][j] + if (totalObjects === 1) { + simulationArea.minWidth = obj.absX() + simulationArea.minHeight = obj.absY() + simulationArea.maxWidth = obj.absX() + simulationArea.maxHeight = obj.absY() + } + if (obj.objectType !== 'Node') { + if (obj.y - obj.upDimensionY < simulationArea.minHeight) { + simulationArea.minHeight = obj.y - obj.upDimensionY + } + if (obj.y + obj.downDimensionY > simulationArea.maxHeight) { + simulationArea.maxHeight = obj.y + obj.downDimensionY + } + if (obj.x - obj.leftDimensionX < simulationArea.minWidth) { + simulationArea.minWidth = obj.x - obj.leftDimensionX + } + if (obj.x + obj.rightDimensionX > simulationArea.maxWidth) { + simulationArea.maxWidth = obj.x + obj.rightDimensionX + } + } else { + if (obj.absY() < simulationArea.minHeight) { + simulationArea.minHeight = obj.absY() + } + if (obj.absY() > simulationArea.maxHeight) { + simulationArea.maxHeight = obj.absY() + } + if (obj.absX() < simulationArea.minWidth) { + simulationArea.minWidth = obj.absX() + } + if (obj.absX() > simulationArea.maxWidth) { + simulationArea.maxWidth = obj.absX() + } + } + } + } + } + simulationArea.objectList = updateOrder +} + +// Function used to change the zoom level wrt to a point +// fn to change scale (zoom) - It also shifts origin so that the position +// of the object in focus doesn't change +export function changeScale(delta, xx, yy, method = 1) { + // method = 3/2 - Zoom wrt center of screen + // method = 1 - Zoom wrt position of mouse + // Otherwise zoom wrt to selected object + + if (method === 3) { + xx = (width / 2 - globalScope.ox) / globalScope.scale + yy = (height / 2 - globalScope.oy) / globalScope.scale + } else if ( + xx === undefined || + yy === undefined || + xx === 'zoomButton' || + yy === 'zoomButton' + ) { + if ( + simulationArea.lastSelected && + simulationArea.lastSelected.objectType !== 'Wire' + ) { + // selected object + xx = simulationArea.lastSelected.x + yy = simulationArea.lastSelected.y + } else { + // mouse location + // eslint-disable-next-line no-lonely-if + if (method === 1) { + xx = simulationArea.mouseX + yy = simulationArea.mouseY + } else if (method === 2) { + xx = (width / 2 - globalScope.ox) / globalScope.scale + yy = (height / 2 - globalScope.oy) / globalScope.scale + } + } + } + + var oldScale = globalScope.scale + globalScope.scale = Math.max( + 0.5, + Math.min(4 * DPR, globalScope.scale + delta) + ) + globalScope.scale = Math.round(globalScope.scale * 10) / 10 + globalScope.ox -= Math.round(xx * (globalScope.scale - oldScale)) // Shift accordingly, so that we zoom wrt to the selected point + globalScope.oy -= Math.round(yy * (globalScope.scale - oldScale)) + // dots(true,false); + + // MiniMap + if (!embed && !lightMode) { + findDimensions(globalScope) + miniMapArea.setup() + $('#miniMap').show() + updatelastMinimapShown() + $('#miniMap').show() + setTimeout(removeMiniMap, 2000) + } +} +// fn to draw Dots on screen +// the function is called only when the zoom level or size of screen changes. +// Otherwise for normal panning, the canvas itself is moved to give the illusion of movement + +export function dots( + dots = true, + transparentBackground = false, + force = false +) { + const scale = unit * globalScope.scale + const ox = globalScope.ox % scale // offset + const oy = globalScope.oy % scale // offset + + const backgroundCtx = backgroundArea.context + if (!backgroundCtx) return + + const canvasWidth = backgroundArea.canvas.width // max X distance + const canvasHeight = backgroundArea.canvas.height // max Y distance + + backgroundArea.canvas.style.left = `${(ox - scale) / DPR}px` // adjust left position of canvas + backgroundArea.canvas.style.top = `${(oy - scale) / DPR}px` // adjust top position of canvas + + if (globalScope.scale === simulationArea.prevScale && !force) return + + simulationArea.prevScale = globalScope.scale // set the previous scale to current scale + + backgroundCtx.beginPath() + backgroundArea.clear() + + if (!transparentBackground) { + backgroundCtx.fillStyle = colors['canvas_fill'] + backgroundCtx.fillRect(0, 0, canvasWidth, canvasHeight) + } + + if (dots) { + backgroundCtx.fillStyle = colors['dot_fill'] + for (let i = 0; i < canvasWidth; i += scale) { + for (let j = 0; j < canvasHeight; j += scale) { + backgroundCtx.beginPath() + backgroundCtx.arc(i, j, scale / 10, 0, Math.PI * 2) + backgroundCtx.fill() + } + } + } + + backgroundCtx.strokeStyle = colors['canvas_stroke'] + backgroundCtx.lineWidth = 1 + + if (!embed) { + const correction = 0.5 * (backgroundCtx.lineWidth % 2) + for (let i = 0; i < canvasWidth; i += scale) { + backgroundCtx.moveTo(Math.round(i + correction) - correction, 0) + backgroundCtx.lineTo( + Math.round(i + correction) - correction, + canvasHeight + ) + } + for (let j = 0; j < canvasHeight; j += scale) { + backgroundCtx.moveTo(0, Math.round(j + correction) - correction) + backgroundCtx.lineTo( + canvasWidth, + Math.round(j + correction) - correction + ) + } + backgroundCtx.stroke() + } + + // Old Code + // function drawPixel(x, y, r, g, b, a) { + // var index = (x + y * canvasWidth) * 4; + // canvasData.data[index + 0] = r; + // canvasData.data[index + 1] = g; + // canvasData.data[index + 2] = b; + // canvasData.data[index + 3] = a; + // } + // if (dots) { + // var canvasData = ctx.getImageData(0, 0, canvasWidth, canvasHeight); + // + // + // + // for (var i = 0 + ox; i < canvasWidth; i += scale) + // for (var j = 0 + oy; j < canvasHeight; j += scale) + // drawPixel(i, j, 0, 0, 0, 255); + // ctx.putImageData(canvasData, 0, 0); + // } +} + +// Helper canvas API starts here +// All canvas functions are wrt to a center point (xx,yy), +// direction is used to abstract rotation of everything by a certain angle +// Possible values for direction = "RIGHT" (default), "LEFT", "UP", "DOWN" + +export function bezierCurveTo(x1, y1, x2, y2, x3, y3, xx, yy, dir) { + ;[x1, y1] = rotate(x1, y1, dir) + ;[x2, y2] = rotate(x2, y2, dir) + ;[x3, y3] = rotate(x3, y3, dir) + var { ox } = globalScope + var { oy } = globalScope + x1 *= globalScope.scale + y1 *= globalScope.scale + x2 *= globalScope.scale + y2 *= globalScope.scale + x3 *= globalScope.scale + y3 *= globalScope.scale + xx *= globalScope.scale + yy *= globalScope.scale + var ctx = simulationArea.context + ctx.bezierCurveTo( + Math.round(xx + ox + x1), + Math.round(yy + oy + y1), + Math.round(xx + ox + x2), + Math.round(yy + oy + y2), + Math.round(xx + ox + x3), + Math.round(yy + oy + y3) + ) +} + +export function moveTo(ctx, x1, y1, xx, yy, dir, bypass = false) { + var correction = 0.5 * (ctx.lineWidth % 2) + let newX + let newY + ;[newX, newY] = rotate(x1, y1, dir) + newX *= globalScope.scale + newY *= globalScope.scale + xx *= globalScope.scale + yy *= globalScope.scale + if (bypass) { + ctx.moveTo(xx + globalScope.ox + newX, yy + globalScope.oy + newY) + } else { + ctx.moveTo( + Math.round(xx + globalScope.ox + newX - correction) + correction, + Math.round(yy + globalScope.oy + newY - correction) + correction + ) + } +} + +export function lineTo(ctx, x1, y1, xx, yy, dir) { + let newX + let newY + + var correction = 0.5 * (ctx.lineWidth % 2) + ;[newX, newY] = rotate(x1, y1, dir) + newX *= globalScope.scale + newY *= globalScope.scale + xx *= globalScope.scale + yy *= globalScope.scale + ctx.lineTo( + Math.round(xx + globalScope.ox + newX - correction) + correction, + Math.round(yy + globalScope.oy + newY - correction) + correction + ) +} + +export function arc(ctx, sx, sy, radius, start, stop, xx, yy, dir) { + // ox-x of origin, xx- x of element , sx - shift in x from element + let Sx + let Sy + let newStart + let newStop + let counterClock + var correction = 0.5 * (ctx.lineWidth % 2) + ;[Sx, Sy] = rotate(sx, sy, dir) + Sx *= globalScope.scale + Sy *= globalScope.scale + xx *= globalScope.scale + yy *= globalScope.scale + radius *= globalScope.scale + ;[newStart, newStop, counterClock] = rotateAngle(start, stop, dir) + ctx.arc( + Math.round(xx + globalScope.ox + Sx + correction) - correction, + Math.round(yy + globalScope.oy + Sy + correction) - correction, + Math.round(radius), + newStart, + newStop, + counterClock + ) +} + +export function arc2(ctx, sx, sy, radius, start, stop, xx, yy, dir) { + // ox-x of origin, xx- x of element , sx - shift in x from element + let Sx + let Sy + let newStart + let newStop + let counterClock + var correction = 0.5 * (ctx.lineWidth % 2) + ;[Sx, Sy] = rotate(sx, sy, dir) + Sx *= globalScope.scale + Sy *= globalScope.scale + xx *= globalScope.scale + yy *= globalScope.scale + radius *= globalScope.scale + ;[newStart, newStop, counterClock] = rotateAngle(start, stop, dir) + var pi = 0 + if (counterClock) { + pi = Math.PI + } + ctx.arc( + Math.round(xx + globalScope.ox + Sx + correction) - correction, + Math.round(yy + globalScope.oy + Sy + correction) - correction, + Math.round(radius), + newStart + pi, + newStop + pi + ) +} + +export function drawCircle2(ctx, sx, sy, radius, xx, yy, dir) { + // ox-x of origin, xx- x of element , sx - shift in x from element + let Sx + let Sy + ;[Sx, Sy] = rotate(sx, sy, dir) + Sx *= globalScope.scale + Sy *= globalScope.scale + xx *= globalScope.scale + yy *= globalScope.scale + radius *= globalScope.scale + ctx.arc( + Math.round(xx + globalScope.ox + Sx), + Math.round(yy + globalScope.oy + Sy), + Math.round(radius), + 0, + 2 * Math.PI + ) +} + +export function rect(ctx, x1, y1, x2, y2) { + var correction = 0.5 * (ctx.lineWidth % 2) + x1 *= globalScope.scale + y1 *= globalScope.scale + x2 *= globalScope.scale + y2 *= globalScope.scale + ctx.rect( + Math.round(globalScope.ox + x1 - correction) + correction, + Math.round(globalScope.oy + y1 - correction) + correction, + Math.round(x2), + Math.round(y2) + ) +} + +export function drawImage(ctx, img, x1, y1, w_canvas, h_canvas) { + x1 *= globalScope.scale + y1 *= globalScope.scale + x1 += globalScope.ox + y1 += globalScope.oy + + w_canvas *= globalScope.scale + h_canvas *= globalScope.scale + ctx.drawImage(img, x1, y1, w_canvas, h_canvas) +} + +export function rect2(ctx, x1, y1, x2, y2, xx, yy, dir = 'RIGHT') { + var correction = 0.5 * (ctx.lineWidth % 2) + ;[x1, y1] = rotate(x1, y1, dir) + ;[x2, y2] = rotate(x2, y2, dir) + x1 *= globalScope.scale + y1 *= globalScope.scale + x2 *= globalScope.scale + y2 *= globalScope.scale + xx *= globalScope.scale + yy *= globalScope.scale + ctx.rect( + Math.round(globalScope.ox + xx + x1 - correction) + correction, + Math.round(globalScope.oy + yy + y1 - correction) + correction, + Math.round(x2), + Math.round(y2) + ) +} + +export function rotate(x1, y1, dir) { + if (dir === 'LEFT') { + return [-x1, y1] + } + if (dir === 'DOWN') { + return [y1, x1] + } + if (dir === 'UP') { + return [y1, -x1] + } + return [x1, y1] +} + +export function correctWidth(w) { + return Math.max(1, Math.round(w * globalScope.scale)) +} + +function rotateAngle(start, stop, dir) { + if (dir === 'LEFT') { + return [start, stop, true] + } + if (dir === 'DOWN') { + return [start - Math.PI / 2, stop - Math.PI / 2, true] + } + if (dir === 'UP') { + return [start - Math.PI / 2, stop - Math.PI / 2, false] + } + return [start, stop, false] +} + +export function drawLine(ctx, x1, y1, x2, y2, color, width) { + x1 *= globalScope.scale + y1 *= globalScope.scale + x2 *= globalScope.scale + y2 *= globalScope.scale + ctx.beginPath() + ctx.strokeStyle = color + ctx.lineCap = 'round' + ctx.lineWidth = correctWidth(width) //* globalScope.scale; + var correction = 0.5 * (ctx.lineWidth % 2) + var hCorrection = 0 + var vCorrection = 0 + if (y1 === y2) vCorrection = correction + if (x1 === x2) hCorrection = correction + ctx.moveTo( + Math.round(x1 + globalScope.ox + hCorrection) - hCorrection, + Math.round(y1 + globalScope.oy + vCorrection) - vCorrection + ) + ctx.lineTo( + Math.round(x2 + globalScope.ox + hCorrection) - hCorrection, + Math.round(y2 + globalScope.oy + vCorrection) - vCorrection + ) + ctx.stroke() +} + +// Checks if string color is a valid color using a hack +export function validColor(color) { + var $div = $('
') + $div.css('border', `1px solid ${color}`) + return $div.css('border-color') !== '' +} + +// Helper function to color "RED" to RGBA +export function colorToRGBA(color) { + var cvs + var ctx + cvs = document.createElement('canvas') + cvs.height = 1 + cvs.width = 1 + ctx = cvs.getContext('2d') + ctx.fillStyle = color + ctx.fillRect(0, 0, 1, 1) + return ctx.getImageData(0, 0, 1, 1).data +} + +export function drawCircle(ctx, x1, y1, r, color) { + x1 *= globalScope.scale + y1 *= globalScope.scale + ctx.beginPath() + ctx.fillStyle = color + ctx.arc( + Math.round(x1 + globalScope.ox), + Math.round(y1 + globalScope.oy), + Math.round(r * globalScope.scale), + 0, + Math.PI * 2, + false + ) + ctx.closePath() + ctx.fill() +} + +// To show message like values, node name etc +export function canvasMessage(ctx, str, x1, y1, fontSize = 10) { + if (!str || !str.length) return + + ctx.font = `${Math.round(fontSize * globalScope.scale)}px Raleway` + ctx.textAlign = 'center' + var width = ctx.measureText(str).width / globalScope.scale + 8 + var height = 13 + ctx.strokeStyle = 'black' + ctx.lineWidth = correctWidth(1) + ctx.fillStyle = 'yellow' + ctx.save() + ctx.beginPath() + rect(ctx, x1 - width / 2, y1 - height / 2 - 3, width, height) + ctx.shadowColor = '#999' + ctx.shadowBlur = 10 + ctx.shadowOffsetX = 3 + ctx.shadowOffsetY = 3 + ctx.stroke() + ctx.fill() + ctx.restore() + x1 *= globalScope.scale + y1 *= globalScope.scale + ctx.beginPath() + ctx.fillStyle = 'black' + ctx.fillText( + str, + Math.round(x1 + globalScope.ox), + Math.round(y1 + globalScope.oy) + ) + ctx.fill() +} + +export function fillText(ctx, str, x1, y1, fontSize = 20) { + x1 *= globalScope.scale + y1 *= globalScope.scale + ctx.font = `${Math.round(fontSize * globalScope.scale)}px Raleway` + ctx.fillText( + str, + Math.round(x1 + globalScope.ox), + Math.round(y1 + globalScope.oy) + ) +} + +export function fillText2(ctx, str, x1, y1, xx, yy, dir) { + var angle = { + RIGHT: 0, + LEFT: 0, + DOWN: Math.PI / 2, + UP: -Math.PI / 2, + } + x1 *= globalScope.scale + y1 *= globalScope.scale + ;[x1, y1] = rotate(x1, y1, dir) + xx *= globalScope.scale + yy *= globalScope.scale + + ctx.font = `${Math.round(14 * globalScope.scale)}px Raleway` + ctx.save() + ctx.translate( + Math.round(xx + x1 + globalScope.ox), + Math.round(yy + y1 + globalScope.oy) + ) + ctx.rotate(angle[dir]) + ctx.textAlign = 'center' + ctx.fillText( + str, + 0, + Math.round(4 * globalScope.scale) * (1 - 0 * +(dir === 'DOWN')) + ) + ctx.restore() +} + +export function fillText4( + ctx, + str, + x1, + y1, + xx, + yy, + dir, + fontSize = 14, + textAlign = 'center' +) { + var angle = { + RIGHT: 0, + LEFT: 0, + DOWN: Math.PI / 2, + UP: -Math.PI / 2, + } + x1 *= globalScope.scale + y1 *= globalScope.scale + ;[x1, y1] = rotate(x1, y1, dir) + xx *= globalScope.scale + yy *= globalScope.scale + + ctx.font = `${Math.round(fontSize * globalScope.scale)}px Raleway` + // ctx.font = 20+"px Raleway"; + ctx.textAlign = textAlign + ctx.fillText( + str, + xx + x1 + globalScope.ox, + yy + + y1 + + globalScope.oy + + Math.round((fontSize / 3) * globalScope.scale) + ) +} + +export function fillText3( + ctx, + str, + x1, + y1, + xx = 0, + yy = 0, + fontSize = 14, + font = 'Raleway', + textAlign = 'center' +) { + x1 *= globalScope.scale + y1 *= globalScope.scale + xx *= globalScope.scale + yy *= globalScope.scale + + ctx.font = `${Math.round(fontSize * globalScope.scale)}px ${font}` + ctx.textAlign = textAlign + ctx.fillText( + str, + Math.round(xx + x1 + globalScope.ox), + Math.round(yy + y1 + globalScope.oy) + ) +} + +export const oppositeDirection = { + RIGHT: 'LEFT', + LEFT: 'RIGHT', + DOWN: 'UP', + UP: 'DOWN', +} +export const fixDirection = { + right: 'LEFT', + left: 'RIGHT', + down: 'UP', + up: 'DOWN', + LEFT: 'LEFT', + RIGHT: 'RIGHT', + UP: 'UP', + DOWN: 'DOWN', +} diff --git a/v0/src/simulator/src/circuit.js b/v0/src/simulator/src/circuit.js new file mode 100644 index 00000000..f86429e3 --- /dev/null +++ b/v0/src/simulator/src/circuit.js @@ -0,0 +1,483 @@ +/* eslint-disable import/no-cycle */ +/* eslint-disable no-bitwise */ +/* eslint-disable guard-for-in */ +/* eslint-disable no-restricted-syntax */ +/* eslint-disable no-restricted-globals */ +/* eslint-disable consistent-return */ +/* eslint-disable func-names */ +/* eslint-disable array-callback-return */ +/* eslint-disable no-use-before-define */ +/* eslint-disable no-param-reassign */ +/* eslint-disable no-alert */ +import CircuitElement from './circuitElement' +import plotArea from './plotArea' +import simulationArea, { changeClockTime } from './simulationArea' +import { + stripTags, + uniq, + showMessage, + showError, + truncateString, +} from './utils' +import { findDimensions, dots } from './canvasApi' +import { updateRestrictedElementsList } from './restrictedElementDiv' +import { scheduleBackup } from './data/backupCircuit' +import { showProperties } from './ux' +import { + scheduleUpdate, + updateSimulationSet, + updateCanvasSet, + updateSubcircuitSet, + forceResetNodesSet, + changeLightMode, +} from './engine' +import { toggleLayoutMode, layoutModeGet } from './layoutMode' +import { setProjectName, getProjectName } from './data/save' +import { changeClockEnable } from './sequential' +import { changeInputSize } from './modules' +import { verilogModeGet, verilogModeSet } from './Verilog2CV' +import { updateTestbenchUI } from './testbench' +import { SimulatorStore } from '#/store/SimulatorStore/SimulatorStore' +import { toRef, toRefs } from 'vue' +import { provideCircuitName } from '#/components/helpers/promptComponent/PromptComponent.vue' +import { deleteCurrentCircuit } from '#/components/helpers/deleteCircuit/DeleteCircuit.vue' + +export const circuitProperty = { + toggleLayoutMode, + setProjectName, + changeCircuitName, + // changeClockTime, + deleteCurrentCircuit, + changeClockEnable, + changeInputSize, + changeLightMode, +} + +export var scopeList = {} +export function resetScopeList() { + const simulatorStore = SimulatorStore() + const { circuit_list } = toRefs(simulatorStore) + scopeList = {} + circuit_list.value = [] +} +/** + * Function used to change the current focusedCircuit + * Disables layoutMode if enabled + * Changes UI tab etc + * Sets flags to make updates, resets most of the things + * @param {string} id - identifier for circuit + * @category circuit + */ +export function switchCircuit(id) { + // TODO: fix tomorrow + const simulatorStore = SimulatorStore() + const { circuit_list } = toRefs(simulatorStore) + const { activeCircuit } = toRefs(simulatorStore) + + if (layoutModeGet()) { + toggleLayoutMode() + } + if (!scopeList[id].verilogMetadata.isVerilogCircuit) { + verilogModeSet(false) + } + + // globalScope.fixLayout(); + scheduleBackup() + if (id === globalScope.id) return + // $(`.circuits`).removeClass('current') + circuit_list.value.forEach((circuit) => + circuit.focussed ? (circuit.focussed = false) : null + ) + simulationArea.lastSelected = undefined + simulationArea.multipleObjectSelections = [] + simulationArea.copyList = [] + globalScope = scopeList[id] + if (globalScope.verilogMetadata.isVerilogCircuit) { + verilogModeSet(true) + } + if (globalScope.isVisible()) { + // $(`#${id}`).addClass('current') + const index = circuit_list.value.findIndex( + (circuit) => circuit.id == id + ) // TODO: add strict equality after typescript + circuit_list.value[index].focussed = true + activeCircuit.value.id = globalScope.id + activeCircuit.value.name = globalScope.name + } + updateSimulationSet(true) + updateSubcircuitSet(true) + forceResetNodesSet(true) + dots(false) + simulationArea.lastSelected = globalScope.root + if (!embed) { + showProperties(simulationArea.lastSelected) + updateTestbenchUI() + plotArea.reset() + } + updateCanvasSet(true) + scheduleUpdate() + + // to update the restricted elements information + updateRestrictedElementsList() +} + +export function getDependenciesList(scopeId) { + let scope = scopeList[scopeId] + if (scope == undefined) scope = scopeList[globalScope.id] + + let dependencies = '' + for (id in scopeList) { + if (id != scope.id && scopeList[id].checkDependency(scope.id)) { + if (dependencies === '') { + dependencies = scopeList[id].name + } else { + dependencies += `, ${scopeList[id].name}` + } + } + } + return dependencies +} + +// /** +// * Deletes the current circuit +// * Ensures that at least one circuit is there +// * Ensures that no circuit depends on the current circuit +// * Switched to a random circuit +// * @category circuit +// */ +// export function deleteCurrentCircuit(scopeId = globalScope.id) { +// const simulatorStore = SimulatorStore() +// const { circuit_list } = toRefs(simulatorStore) +// let scope = scopeList[scopeId] +// if (scope == undefined) scope = scopeList[globalScope.id] + +// if (scope.verilogMetadata.isVerilogCircuit) { +// scope.initialize() +// for (var id in scope.verilogMetadata.subCircuitScopeIds) +// delete scopeList[id] +// } +// // $(`#${scope.id}`).remove() +// const index = circuit_list.value.findIndex( +// (circuit) => circuit.id === scope.id +// ) +// circuit_list.value.splice(index, 1) +// delete scopeList[scope.id] +// if (scope.id == globalScope.id) { +// switchCircuit(Object.keys(scopeList)[0]) +// } +// showMessage('Circuit was successfully closed') +// } +/** + * Wrapper function around newCircuit to be called from + button on UI + */ +export async function createNewCircuitScope( + name, + id = undefined, + isVerilog = false, + isVerilogMain = false +) { + name = name ?? (await provideCircuitName()) + if (name instanceof Error) return // if user cancels the prompt + if (name.trim() == '') { + name = 'Untitled-Circuit' + } + simulationArea.lastSelected = undefined + newCircuit(name, id, isVerilog, isVerilogMain) + if (!embed) { + showProperties(simulationArea.lastSelected) + updateTestbenchUI() + plotArea.reset() + } + return true +} + +/** + * Function to create new circuit + * Function creates button in tab, creates scope and switches to this circuit + * @param {string} name - name of the new circuit + * @param {string} id - identifier for circuit + * @category circuit + */ +export function newCircuit(name, id, isVerilog = false, isVerilogMain = false) { + const simulatorStore = SimulatorStore() + const { circuit_list } = toRefs(simulatorStore) + const { activeCircuit } = toRefs(simulatorStore) + if (layoutModeGet()) { + toggleLayoutMode() + } + if (verilogModeGet()) { + verilogModeSet(false) + } + name = name || 'Untitled-Circuit' + name = stripTags(name) + if (!name) return + const scope = new Scope(name) + if (id) scope.id = id + scopeList[scope.id] = scope + let currCircuit = { + id: scope.id, + name: scope.name, // fix for tab name issue - vue - to be reviewed @devartstar + } + + circuit_list.value.push(currCircuit) + if (isVerilog) { + scope.verilogMetadata.isVerilogCircuit = true + // TODO: remove later if not required after fixing verilog code loading from saved file + circuit_list.value.forEach((circuit) => (circuit.isVerilog = false)) + circuit_list.value[circuit_list.value.length - 1].isVerilog = true + scope.verilogMetadata.isMainCircuit = isVerilogMain + } + globalScope = scope + // $('.circuits').removeClass('current') + circuit_list.value.forEach((circuit) => (circuit.focussed = false)) + circuit_list.value[circuit_list.value.length - 1].focussed = true + activeCircuit.value.id = scope.id + activeCircuit.value.name = scope.name + + if (!isVerilog || isVerilogMain) { + if (embed) { + // added calss - embed-tab using vue logic + // var html = `
${truncateString( + // name, + // 18 + // )}
` + // $('#tabsBar').append(html) + // $('#tabsBar').addClass('embed-tabs') + } else { + // logic implemented in vue + } + + // Remove listeners + //$('.circuits').off('click') + $('.circuitName').off('click') + //$('.tabsCloseButton').off('click') + + // switch circuit function moved inside vue component + + if (!embed) { + $('.circuitName').on('click', () => { + simulationArea.lastSelected = globalScope.root + setTimeout(() => { + // here link with the properties panel + document.getElementById('circname').select() + }, 100) + }) + } + // moved inside vue - component + // $('.tabsCloseButton').on('click', function (e) { + // e.stopPropagation() + // deleteCurrentCircuit(this.id) + // }) + + if (!embed) { + showProperties(scope.root) + } + dots(false) + } + return scope +} + +/** + * Used to change name of a circuit + * @param {string} name - new name + * @param {string} id - id of the circuit + * @category circuit + */ +export function changeCircuitName(name, id = globalScope.id) { + const simulatorStore = SimulatorStore() + const { circuit_list } = toRefs(simulatorStore) + // const { activeCircuit } = toRefs(simulatorStore) + name = name || 'Untitled' + name = stripTags(name) + scopeList[id].name = name + const index = circuit_list.value.findIndex((circuit) => circuit.id === id) + circuit_list.value[index].name = name + // activeCircuit.value.name = name // add later if necessary at current stage not important handled by projectProperty on switching circuit +} + +/** + * Class representing a Scope + * @class + * @param {string} name - name of the circuit + * @param {number=} id - a random id for the circuit + * @category circuit + */ +export default class Scope { + constructor(name = 'localScope', id = undefined) { + this.restrictedCircuitElementsUsed = [] + this.id = id || Math.floor(Math.random() * 100000000000 + 1) + this.CircuitElement = [] + this.name = name + + // root object for referring to main canvas - intermediate node uses this + this.root = new CircuitElement(0, 0, this, 'RIGHT', 1) + this.backups = [] + // maintaining a state (history) for redo function + this.history = [] + this.timeStamp = new Date().getTime() + this.verilogMetadata = { + isVerilogCircuit: false, + isMainCircuit: false, + code: '// Write Some Verilog Code Here!', + subCircuitScopeIds: [], + } + + this.ox = 0 + this.oy = 0 + this.scale = DPR + this.stack = [] + + this.initialize() + + // Setting default layout + this.layout = { + // default position + width: 100, + height: 40, + title_x: 50, + title_y: 13, + titleEnabled: true, + } + } + + isVisible() { + if (!this.verilogMetadata.isVerilogCircuit) return true + return this.verilogMetadata.isMainCircuit + } + + initialize() { + this.tunnelList = {} + this.pending = [] + this.nodes = [] // intermediate nodes only + this.allNodes = [] + this.wires = [] + + // Creating arrays for other module elements + for (let i = 0; i < moduleList.length; i++) { + this[moduleList[i]] = [] + } + } + + /** + * Resets all nodes recursively + */ + reset() { + for (let i = 0; i < this.allNodes.length; i++) { + this.allNodes[i].reset() + } + for (let i = 0; i < this.Splitter.length; i++) { + this.Splitter[i].reset() + } + for (let i = 0; i < this.SubCircuit.length; i++) { + this.SubCircuit[i].reset() + } + } + + /** + * Adds all inputs to simulationQueue + */ + addInputs() { + for (let i = 0; i < inputList.length; i++) { + for (var j = 0; j < this[inputList[i]].length; j++) { + simulationArea.simulationQueue.add(this[inputList[i]][j], 0) + } + } + + for (let i = 0; i < this.SubCircuit.length; i++) { + this.SubCircuit[i].addInputs() + } + } + + /** + * Ticks clocks recursively -- needs to be deprecated and synchronize all clocks with a global clock + */ + clockTick() { + for (let i = 0; i < this.Clock.length; i++) { + this.Clock[i].toggleState() + } // tick clock! + for (let i = 0; i < this.SubCircuit.length; i++) { + this.SubCircuit[i].localScope.clockTick() + } // tick clock! + } + + /** + * Checks if this circuit contains directly or indirectly scope with id + * Recursive nature + */ + checkDependency(id) { + if (id === this.id) return true + for (let i = 0; i < this.SubCircuit.length; i++) { + if (this.SubCircuit[i].id === id) return true + } + + for (let i = 0; i < this.SubCircuit.length; i++) { + if (scopeList[this.SubCircuit[i].id].checkDependency(id)) + return true + } + + return false + } + + /** + * Get dependency list - list of all circuits, this circuit depends on + */ + getDependencies() { + var list = [] + for (let i = 0; i < this.SubCircuit.length; i++) { + list.push(this.SubCircuit[i].id) + list.extend(scopeList[this.SubCircuit[i].id].getDependencies()) + } + return uniq(list) + } + + /** + * helper function to reduce layout size + */ + fixLayout() { + var maxY = 20 + for (let i = 0; i < this.Input.length; i++) { + maxY = Math.max(this.Input[i].layoutProperties.y, maxY) + } + for (let i = 0; i < this.Output.length; i++) { + maxY = Math.max(this.Output[i].layoutProperties.y, maxY) + } + if (maxY !== this.layout.height) { + this.layout.height = maxY + 10 + } + } + + /** + * Function which centers the circuit to the correct zoom level + */ + centerFocus(zoomIn = true) { + if (layoutModeGet()) return + findDimensions(this) + + var ytoolbarOffset = embed ? 0 : 60 * DPR // Some part ofcanvas is hidden behind the toolbar + + var minX = simulationArea.minWidth || 0 + var minY = simulationArea.minHeight || 0 + var maxX = simulationArea.maxWidth || 0 + var maxY = simulationArea.maxHeight || 0 + + var reqWidth = maxX - minX + 75 * DPR + var reqHeight = maxY - minY + 75 * DPR + + this.scale = Math.min( + width / reqWidth, + (height - ytoolbarOffset) / reqHeight + ) + + if (!zoomIn) { + this.scale = Math.min(this.scale, DPR) + } + this.scale = Math.max(this.scale, DPR / 10) + + this.ox = -minX * this.scale + (width - (maxX - minX) * this.scale) / 2 + this.oy = + -minY * this.scale + + (height - ytoolbarOffset - (maxY - minY) * this.scale) / 2 + } +} diff --git a/v0/src/simulator/src/circuitElement.js b/v0/src/simulator/src/circuitElement.js new file mode 100644 index 00000000..cd368c4d --- /dev/null +++ b/v0/src/simulator/src/circuitElement.js @@ -0,0 +1,1018 @@ +/* eslint-disable no-multi-assign */ +/* eslint-disable no-bitwise */ +import { scheduleUpdate } from './engine' +import simulationArea from './simulationArea' +import { + fixDirection, + fillText, + correctWidth, + rect2, + oppositeDirection, +} from './canvasApi' +import { colors } from './themer/themer' +import { layoutModeGet, tempBuffer } from './layoutMode' +import { fillSubcircuitElements } from './ux' +import { generateNodeName } from './verilogHelpers' + +/** + * Base class for circuit elements. + * @param {number} x - x coordinate of the element + * @param {number} y - y coordinate of the element + * @param {Scope} scope - The circuit on which circuit element is being drawn + * @param {string} dir - The direction of circuit element + * @param {number} bitWidth - the number of bits per node. + * @category circuitElement + */ +export default class CircuitElement { + constructor(x, y, scope, dir, bitWidth) { + // Data member initializations + this.x = x + this.y = y + this.hover = false + if (this.x === undefined || this.y === undefined) { + this.x = simulationArea.mouseX + this.y = simulationArea.mouseY + this.newElement = true + this.hover = true + } + this.deleteNodesWhenDeleted = true // FOR NOW - TO CHECK LATER + this.nodeList = [] + this.clicked = false + + this.oldx = x + this.oldy = y + + // The following attributes help in setting the touch area bound. They are the distances from the center. + // Note they are all positive distances from center. They will automatically be rotated when direction is changed. + // To stop the rotation when direction is changed, check overrideDirectionRotation attribute. + this.leftDimensionX = 10 + this.rightDimensionX = 10 + this.upDimensionY = 10 + this.downDimensionY = 10 + + this.label = '' + this.scope = scope + this.baseSetup() + + this.bitWidth = bitWidth || parseInt(prompt('Enter bitWidth'), 10) || 1 + this.direction = dir + this.directionFixed = false + this.labelDirectionFixed = false + this.labelDirection = oppositeDirection[dir] + this.orientationFixed = true + this.fixedBitWidth = false + + scheduleUpdate() + + this.queueProperties = { + inQueue: false, + time: undefined, + index: undefined, + } + + if (this.canShowInSubcircuit) { + this.subcircuitMetadata = { + showInSubcircuit: false, // if canShowInSubcircuit == true, showInSubcircuit determines wheter the user has added the element in the subcircuit + showLabelInSubcircuit: true, // determines whether the label of the element is to be showin the subcircuit + labelDirection: this.labelDirection, // determines the direction of the label of the element in the subcircuit + // coordinates of the element in the subcircuit relative to the subcircuit + x: 0, + y: 0, + } + } + } + + /** + * Function to flip bits + * @param {number} val - the value of flipped bits + * @returns {number} - The number of flipped bits + */ + flipBits(val) { + return ((~val >>> 0) << (32 - this.bitWidth)) >>> (32 - this.bitWidth) + } + + /** + * Function to get absolute value of x coordinate of the element + * @param {number} x - value of x coordinate of the element + * @return {number} - absolute value of x + */ + absX() { + return this.x + } + + /** + * Function to get absolute value of y coordinate of the element + * @param {number} y - value of y coordinate of the element + * @return {number} - absolute value of y + */ + absY() { + return this.y + } + + /** + * adds the element to scopeList + */ + baseSetup() { + this.scope[this.objectType].push(this) + } + + /** + * Function to copy the circuit element obj to a new circuit element + * @param {CircuitElement} obj - element to be copied from + */ + copyFrom(obj) { + var properties = ['label', 'labelDirection'] + for (let i = 0; i < properties.length; i++) { + if (obj[properties[i]] !== undefined) { + this[properties[i]] = obj[properties[i]] + } + } + } + + /** Methods to be Implemented for derivedClass + * saveObject(); //To generate JSON-safe data that can be loaded + * customDraw(); //This is to draw the custom design of the circuit(Optional) + * resolve(); // To execute digital logic(Optional) + * override isResolvable(); // custom logic for checking if module is ready + * override newDirection(dir) //To implement custom direction logic(Optional) + * newOrientation(dir) //To implement custom orientation logic(Optional) + */ + + // Method definitions + + /** + * Function to update the scope when a new element is added. + * @param {Scope} scope - the circuit in which we add element + */ + updateScope(scope) { + this.scope = scope + for (let i = 0; i < this.nodeList.length; i++) { + this.nodeList[i].scope = scope + } + } + + /** + * To generate JSON-safe data that can be loaded + * @memberof CircuitElement + * @return {JSON} - the data to be saved + */ + saveObject() { + var data = { + x: this.x, + y: this.y, + objectType: this.objectType, + label: this.label, + direction: this.direction, + labelDirection: this.labelDirection, + propagationDelay: this.propagationDelay, + customData: this.customSave(), + } + + if (this.canShowInSubcircuit) + data.subcircuitMetadata = this.subcircuitMetadata + return data + } + + /** + * Always overriden + * @memberof CircuitElement + * @return {JSON} - the data to be saved + */ + // eslint-disable-next-line class-methods-use-this + customSave() { + return { + values: {}, + nodes: {}, + constructorParamaters: [], + } + } + + /** + * check hover over the element + * @return {boolean} + */ + checkHover() { + if (simulationArea.mouseDown) return + for (let i = 0; i < this.nodeList.length; i++) { + this.nodeList[i].checkHover() + } + if (!simulationArea.mouseDown) { + if (simulationArea.hover === this) { + this.hover = this.isHover() + if (!this.hover) simulationArea.hover = undefined + } else if (!simulationArea.hover) { + this.hover = this.isHover() + if (this.hover) simulationArea.hover = this + } else { + this.hover = false + } + } + } + + /** + * This sets the width and height of the element if its rectangular + * and the reference point is at the center of the object. + * width and height define the X and Y distance from the center. + * Effectively HALF the actual width and height. + * NOT OVERRIDABLE + * @param {number} w - width + * @param {number} h - height + */ + setDimensions(width, height) { + this.leftDimensionX = this.rightDimensionX = width + this.downDimensionY = this.upDimensionY = height + } + + /** + * @memberof CircuitElement + * @param {number} w -width + */ + setWidth(width) { + this.leftDimensionX = this.rightDimensionX = width + } + + /** + * @param {number} h -height + */ + setHeight(height) { + this.downDimensionY = this.upDimensionY = height + } + + /** + * Helper Function to drag element to a new position + */ + startDragging() { + if (!layoutModeGet()) { + this.oldx = this.x + this.oldy = this.y + } else { + this.oldx = this.subcircuitMetadata.x + this.oldy = this.subcircuitMetadata.y + } + } + + /** + * Helper Function to drag element to a new position + * @memberof CircuitElement + */ + drag() { + if (!layoutModeGet()) { + this.x = + this.oldx + simulationArea.mouseX - simulationArea.mouseDownX + this.y = + this.oldy + simulationArea.mouseY - simulationArea.mouseDownY + } else { + this.subcircuitMetadata.x = + this.oldx + simulationArea.mouseX - simulationArea.mouseDownX + this.subcircuitMetadata.y = + this.oldy + simulationArea.mouseY - simulationArea.mouseDownY + } + } + + /** + * The update method is used to change the parameters of the object on mouse click and hover. + * Return Value: true if state has changed else false + * NOT OVERRIDABLE + */ + update() { + if (layoutModeGet()) { + return this.layoutUpdate() + } + let update = false + + update |= this.newElement + if (this.newElement) { + if (this.centerElement) { + this.x = + Math.round( + (simulationArea.mouseX - + (this.rightDimensionX - this.leftDimensionX) / 2) / + 10 + ) * 10 + this.y = + Math.round( + (simulationArea.mouseY - + (this.downDimensionY - this.upDimensionY) / 2) / + 10 + ) * 10 + } else { + this.x = simulationArea.mouseX + this.y = simulationArea.mouseY + } + + if (simulationArea.mouseDown) { + this.newElement = false + simulationArea.lastSelected = this + } else return update + } + + for (let i = 0; i < this.nodeList.length; i++) { + update |= this.nodeList[i].update() + } + + if (!simulationArea.hover || simulationArea.hover === this) { + this.hover = this.isHover() + } + + if (!simulationArea.mouseDown) this.hover = false + + if ((this.clicked || !simulationArea.hover) && this.isHover()) { + this.hover = true + simulationArea.hover = this + } else if ( + !simulationArea.mouseDown && + this.hover && + this.isHover() === false + ) { + if (this.hover) simulationArea.hover = undefined + this.hover = false + } + + if (simulationArea.mouseDown && this.clicked) { + this.drag() + if ( + !simulationArea.shiftDown && + simulationArea.multipleObjectSelections.contains(this) + ) { + for ( + let i = 0; + i < simulationArea.multipleObjectSelections.length; + i++ + ) { + simulationArea.multipleObjectSelections[i].drag() + } + } + + update |= true + } else if (simulationArea.mouseDown && !simulationArea.selected) { + this.startDragging() + if ( + !simulationArea.shiftDown && + simulationArea.multipleObjectSelections.contains(this) + ) { + for ( + let i = 0; + i < simulationArea.multipleObjectSelections.length; + i++ + ) { + simulationArea.multipleObjectSelections[i].startDragging() + } + } + simulationArea.selected = this.clicked = this.hover + + update |= this.clicked + } else { + if (this.clicked) simulationArea.selected = false + this.clicked = false + this.wasClicked = false + // If this is SubCircuit, then call releaseClick to recursively release clicks on each subcircuit object + if (this.objectType == 'SubCircuit') this.releaseClick() + } + + if (simulationArea.mouseDown && !this.wasClicked) { + if (this.clicked) { + this.wasClicked = true + if (this.click) this.click() + if (simulationArea.shiftDown) { + simulationArea.lastSelected = undefined + if ( + simulationArea.multipleObjectSelections.contains(this) + ) { + simulationArea.multipleObjectSelections.clean(this) + } else { + simulationArea.multipleObjectSelections.push(this) + } + } else { + simulationArea.lastSelected = this + } + } + } + + return update + } + + /** + * Used to update the state of the elements inside the subcircuit in layout mode + * Return Value: true if the state has changed, false otherwise + **/ + + layoutUpdate() { + var update = false + update |= this.newElement + if (this.newElement) { + this.subcircuitMetadata.x = simulationArea.mouseX + this.subcircuitMetadata.y = simulationArea.mouseY + + if (simulationArea.mouseDown) { + this.newElement = false + simulationArea.lastSelected = this + } else return + } + + if (!simulationArea.hover || simulationArea.hover == this) + this.hover = this.isHover() + + if ((this.clicked || !simulationArea.hover) && this.isHover()) { + this.hover = true + simulationArea.hover = this + } else if ( + !simulationArea.mouseDown && + this.hover && + this.isHover() == false + ) { + if (this.hover) simulationArea.hover = undefined + this.hover = false + } + + if (simulationArea.mouseDown && this.clicked) { + this.drag() + update |= true + } else if (simulationArea.mouseDown && !simulationArea.selected) { + this.startDragging() + simulationArea.selected = this.clicked = this.hover + update |= this.clicked + } else { + if (this.clicked) simulationArea.selected = false + this.clicked = false + this.wasClicked = false + } + + if (simulationArea.mouseDown && !this.wasClicked) { + if (this.clicked) { + this.wasClicked = true + simulationArea.lastSelected = this + } + } + + if (!this.clicked && !this.newElement) { + let x = this.subcircuitMetadata.x + let y = this.subcircuitMetadata.y + let yy = tempBuffer.layout.height + let xx = tempBuffer.layout.width + + let rX = this.layoutProperties.rightDimensionX + let lX = this.layoutProperties.leftDimensionX + let uY = this.layoutProperties.upDimensionY + let dY = this.layoutProperties.downDimensionY + + if (lX <= x && x + rX <= xx && y >= uY && y + dY <= yy) return + + this.subcircuitMetadata.showInSubcircuit = false + fillSubcircuitElements() + } + + return update + } + + /** + * Helper Function to correct the direction of element + */ + fixDirection() { + this.direction = fixDirection[this.direction] || this.direction + this.labelDirection = + fixDirection[this.labelDirection] || this.labelDirection + } + + /** + * The isHover method is used to check if the mouse is hovering over the object. + * Return Value: true if mouse is hovering over object else false + * NOT OVERRIDABLE + */ + isHover() { + var mX = simulationArea.mouseXf - this.x + var mY = this.y - simulationArea.mouseYf + + var rX = this.rightDimensionX + var lX = this.leftDimensionX + var uY = this.upDimensionY + var dY = this.downDimensionY + + if (layoutModeGet()) { + var mX = simulationArea.mouseXf - this.subcircuitMetadata.x + var mY = this.subcircuitMetadata.y - simulationArea.mouseYf + + var rX = this.layoutProperties.rightDimensionX + var lX = this.layoutProperties.leftDimensionX + var uY = this.layoutProperties.upDimensionY + var dY = this.layoutProperties.downDimensionY + } + + if (!this.directionFixed && !this.overrideDirectionRotation) { + if (this.direction === 'LEFT') { + lX = this.rightDimensionX + rX = this.leftDimensionX + } else if (this.direction === 'DOWN') { + lX = this.downDimensionY + rX = this.upDimensionY + uY = this.leftDimensionX + dY = this.rightDimensionX + } else if (this.direction === 'UP') { + lX = this.downDimensionY + rX = this.upDimensionY + dY = this.leftDimensionX + uY = this.rightDimensionX + } + } + + return -lX <= mX && mX <= rX && -dY <= mY && mY <= uY + } + + isSubcircuitHover(xoffset = 0, yoffset = 0) { + var mX = simulationArea.mouseXf - this.subcircuitMetadata.x - xoffset + var mY = yoffset + this.subcircuitMetadata.y - simulationArea.mouseYf + + var rX = this.layoutProperties.rightDimensionX + var lX = this.layoutProperties.leftDimensionX + var uY = this.layoutProperties.upDimensionY + var dY = this.layoutProperties.downDimensionY + + return -lX <= mX && mX <= rX && -dY <= mY && mY <= uY + } + + /** + * Helper Function to set label of an element. + * @memberof CircuitElement + * @param {string} label - the label for element + */ + setLabel(label) { + this.label = label || '' + } + + /** + * Method that draws the outline of the module and calls draw function on module Nodes. + * NOT OVERRIDABLE + */ + draw() { + // + var ctx = simulationArea.context + this.checkHover() + + if ( + this.x * this.scope.scale + this.scope.ox < + -this.rightDimensionX * this.scope.scale - 0 || + this.x * this.scope.scale + this.scope.ox > + width + this.leftDimensionX * this.scope.scale + 0 || + this.y * this.scope.scale + this.scope.oy < + -this.downDimensionY * this.scope.scale - 0 || + this.y * this.scope.scale + this.scope.oy > + height + 0 + this.upDimensionY * this.scope.scale + ) + return + + // Draws rectangle and highlights + if (this.rectangleObject) { + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.lineWidth = correctWidth(3) + ctx.beginPath() + rect2( + ctx, + -this.leftDimensionX, + -this.upDimensionY, + this.leftDimensionX + this.rightDimensionX, + this.upDimensionY + this.downDimensionY, + this.x, + this.y, + [this.direction, 'RIGHT'][+this.directionFixed] + ) + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + } + if (this.label !== '') { + var rX = this.rightDimensionX + var lX = this.leftDimensionX + var uY = this.upDimensionY + var dY = this.downDimensionY + if (!this.directionFixed) { + if (this.direction === 'LEFT') { + lX = this.rightDimensionX + rX = this.leftDimensionX + } else if (this.direction === 'DOWN') { + lX = this.downDimensionY + rX = this.upDimensionY + uY = this.leftDimensionX + dY = this.rightDimensionX + } else if (this.direction === 'UP') { + lX = this.downDimensionY + rX = this.upDimensionY + dY = this.leftDimensionX + uY = this.rightDimensionX + } + } + + if (this.labelDirection === 'LEFT') { + ctx.beginPath() + ctx.textAlign = 'right' + ctx.fillStyle = colors['text'] + fillText(ctx, this.label, this.x - lX - 10, this.y + 5, 14) + ctx.fill() + } else if (this.labelDirection === 'RIGHT') { + ctx.beginPath() + ctx.textAlign = 'left' + ctx.fillStyle = colors['text'] + fillText(ctx, this.label, this.x + rX + 10, this.y + 5, 14) + ctx.fill() + } else if (this.labelDirection === 'UP') { + ctx.beginPath() + ctx.textAlign = 'center' + ctx.fillStyle = colors['text'] + fillText(ctx, this.label, this.x, this.y + 5 - uY - 10, 14) + ctx.fill() + } else if (this.labelDirection === 'DOWN') { + ctx.beginPath() + ctx.textAlign = 'center' + ctx.fillStyle = colors['text'] + fillText(ctx, this.label, this.x, this.y + 5 + dY + 10, 14) + ctx.fill() + } + } + + // calls the custom circuit design + if (this.customDraw) { + this.customDraw() + } + + // draws nodes - Moved to renderCanvas + // for (let i = 0; i < this.nodeList.length; i++) + // this.nodeList[i].draw(); + } + + /** + Draws element in layout mode (inside the subcircuit) + @param {number} xOffset - x position of the subcircuit + @param {number} yOffset - y position of the subcircuit + + Called by subcirucit.js/customDraw() - for drawing as a part of another circuit + and layoutMode.js/renderLayout() - for drawing in layoutMode + **/ + drawLayoutMode(xOffset = 0, yOffset = 0) { + var ctx = simulationArea.context + if (layoutModeGet()) { + this.checkHover() + } + if ( + this.subcircuitMetadata.x * this.scope.scale + this.scope.ox < + -this.layoutProperties.rightDimensionX * this.scope.scale || + this.subcircuitMetadata.x * this.scope.scale + this.scope.ox > + width + + this.layoutProperties.leftDimensionX * this.scope.scale || + this.subcircuitMetadata.y * this.scope.scale + this.scope.oy < + -this.layoutProperties.downDimensionY * this.scope.scale || + this.subcircuitMetadata.y * this.scope.scale + this.scope.oy > + height + this.layoutProperties.upDimensionY * this.scope.scale + ) + return + + if (this.subcircuitMetadata.showLabelInSubcircuit) { + var rX = this.layoutProperties.rightDimensionX + var lX = this.layoutProperties.leftDimensionX + var uY = this.layoutProperties.upDimensionY + var dY = this.layoutProperties.downDimensionY + + // this.subcircuitMetadata.labelDirection + if (this.subcircuitMetadata.labelDirection == 'LEFT') { + ctx.beginPath() + ctx.textAlign = 'right' + ctx.fillStyle = 'black' + fillText( + ctx, + this.label, + this.subcircuitMetadata.x + xOffset - lX - 10, + this.subcircuitMetadata.y + yOffset + 5, + 10 + ) + ctx.fill() + } else if (this.subcircuitMetadata.labelDirection == 'RIGHT') { + ctx.beginPath() + ctx.textAlign = 'left' + ctx.fillStyle = 'black' + fillText( + ctx, + this.label, + this.subcircuitMetadata.x + xOffset + rX + 10, + this.subcircuitMetadata.y + yOffset + 5, + 10 + ) + ctx.fill() + } else if (this.subcircuitMetadata.labelDirection == 'UP') { + ctx.beginPath() + ctx.textAlign = 'center' + ctx.fillStyle = 'black' + fillText( + ctx, + this.label, + this.subcircuitMetadata.x + xOffset, + this.subcircuitMetadata.y + yOffset + 5 - uY - 10, + 10 + ) + ctx.fill() + } else if (this.subcircuitMetadata.labelDirection == 'DOWN') { + ctx.beginPath() + ctx.textAlign = 'center' + ctx.fillStyle = 'black' + fillText( + ctx, + this.label, + this.subcircuitMetadata.x + xOffset, + this.subcircuitMetadata.y + yOffset + 5 + dY + 10, + 10 + ) + ctx.fill() + } + } + // calls the subcircuitDraw function in the element to draw it to canvas + this.subcircuitDraw(xOffset, yOffset) + } + + // method to delete object + // OVERRIDE WITH CAUTION + delete() { + simulationArea.lastSelected = undefined + this.scope[this.objectType].clean(this) // CHECK IF THIS IS VALID + if (this.deleteNodesWhenDeleted) { + this.deleteNodes() + } else { + for (let i = 0; i < this.nodeList.length; i++) { + if (this.nodeList[i].connections.length) { + this.nodeList[i].converToIntermediate() + } else { + this.nodeList[i].delete() + } + } + } + this.deleted = true + } + + /** + * method to delete object + * OVERRIDE WITH CAUTION + * @memberof CircuitElement + */ + cleanDelete() { + this.deleteNodesWhenDeleted = true + this.delete() + } + + /** + * Helper Function to delete the element and all the node attached to it. + */ + deleteNodes() { + for (let i = 0; i < this.nodeList.length; i++) { + this.nodeList[i].delete() + } + } + + /** + * method to change direction + * OVERRIDE WITH CAUTION + * @param {string} dir - new direction + */ + newDirection(dir) { + if (this.direction === dir) return + // Leave this for now + if (this.directionFixed && this.orientationFixed) return + if (this.directionFixed) { + this.newOrientation(dir) + return // Should it return ? + } + + // if (obj.direction === undefined) return; + this.direction = dir + for (let i = 0; i < this.nodeList.length; i++) { + this.nodeList[i].refresh() + } + } + + /** + * Helper Function to change label direction of the element. + * @memberof CircuitElement + * @param {string} dir - new direction + */ + newLabelDirection(dir) { + if (layoutModeGet()) this.subcircuitMetadata.labelDirection = dir + else this.labelDirection = dir + } + + /** + * Method to check if object can be resolved + * OVERRIDE if necessary + * @return {boolean} + */ + isResolvable() { + if (this.alwaysResolve) return true + for (let i = 0; i < this.nodeList.length; i++) { + if ( + this.nodeList[i].type === 0 && + this.nodeList[i].value === undefined + ) + return false + } + return true + } + + /** + * Method to change object Bitwidth + * OVERRIDE if necessary + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + if (this.fixedBitWidth) return + if (this.bitWidth === undefined) return + if (this.bitWidth < 1) return + this.bitWidth = bitWidth + for (let i = 0; i < this.nodeList.length; i++) { + this.nodeList[i].bitWidth = bitWidth + } + } + + /** + * Method to change object delay + * OVERRIDE if necessary + * @param {number} delay - new delay + */ + changePropagationDelay(delay) { + if (this.propagationDelayFixed) return + if (delay === undefined) return + if (delay === '') return + var tmpDelay = parseInt(delay, 10) + if (tmpDelay < 0) return + this.propagationDelay = tmpDelay + } + + /** + * Dummy resolve function + * OVERRIDE if necessary + */ + resolve() {} + + /** + * Helper Function to process verilog + */ + processVerilog() { + // Output count used to sanitize output + var output_total = 0 + for (var i = 0; i < this.nodeList.length; i++) { + if ( + this.nodeList[i].type == NODE_OUTPUT && + this.nodeList[i].connections.length > 0 + ) + output_total++ + } + + var output_count = 0 + for (var i = 0; i < this.nodeList.length; i++) { + if (this.nodeList[i].type == NODE_OUTPUT) { + if ( + this.objectType != 'Input' && + this.objectType != 'Clock' && + this.nodeList[i].connections.length > 0 + ) { + this.nodeList[i].verilogLabel = generateNodeName( + this.nodeList[i], + output_count, + output_total + ) + + if ( + !this.scope.verilogWireList[ + this.nodeList[i].bitWidth + ].contains(this.nodeList[i].verilogLabel) + ) + this.scope.verilogWireList[ + this.nodeList[i].bitWidth + ].push(this.nodeList[i].verilogLabel) + output_count++ + } + this.scope.stack.push(this.nodeList[i]) + } + } + } + + /** + * Helper Function to check if verilog resolvable + * @return {boolean} + */ + isVerilogResolvable() { + var backupValues = [] + for (let i = 0; i < this.nodeList.length; i++) { + backupValues.push(this.nodeList[i].value) + this.nodeList[i].value = undefined + } + + for (let i = 0; i < this.nodeList.length; i++) { + if (this.nodeList[i].verilogLabel) { + this.nodeList[i].value = 1 + } + } + + var res = this.isResolvable() + + for (let i = 0; i < this.nodeList.length; i++) { + this.nodeList[i].value = backupValues[i] + } + + return res + } + + /** + * Helper Function to remove proporgation. + */ + removePropagation() { + for (let i = 0; i < this.nodeList.length; i++) { + if (this.nodeList[i].type === NODE_OUTPUT) { + if (this.nodeList[i].value !== undefined) { + this.nodeList[i].value = undefined + simulationArea.simulationQueue.add(this.nodeList[i]) + } + } + } + } + + /** + * Helper Function to name the verilog. + * @return {string} + */ + verilogName() { + return this.verilogType || this.objectType + } + + verilogBaseType() { + return this.verilogName() + } + + verilogParametrizedType() { + var type = this.verilogBaseType() + // Suffix bitwidth for multi-bit inputs + // Example: DflipFlop #(2) DflipFlop_0 + if (this.bitWidth != undefined && this.bitWidth > 1) + type += ' #(' + this.bitWidth + ')' + return type + } + + /** + * Helper Function to generate verilog + * @return {JSON} + */ + generateVerilog() { + // Example: and and_1(_out, _out, _Q[0]); + var inputs = [] + var outputs = [] + + for (var i = 0; i < this.nodeList.length; i++) { + if (this.nodeList[i].type == NODE_INPUT) { + inputs.push(this.nodeList[i]) + } else { + if (this.nodeList[i].connections.length > 0) + outputs.push(this.nodeList[i]) + else outputs.push('') // Don't create a wire + } + } + + var list = outputs.concat(inputs) + var res = this.verilogParametrizedType() + var moduleParams = list.map((x) => x.verilogLabel).join(', ') + res += ` ${this.verilogLabel}(${moduleParams});` + return res + } + + /** + * Toggles the visibility of the labels of subcircuit elements. Called by event handlers in ux.js + **/ + toggleLabelInLayoutMode() { + this.subcircuitMetadata.showLabelInSubcircuit = + !this.subcircuitMetadata.showLabelInSubcircuit + } +} + +CircuitElement.prototype.alwaysResolve = false +CircuitElement.prototype.propagationDelay = 10 +CircuitElement.prototype.tooltip = undefined +CircuitElement.prototype.propagationDelayFixed = false +CircuitElement.prototype.rectangleObject = true +CircuitElement.prototype.objectType = 'CircuitElement' +CircuitElement.prototype.canShowInSubcircuit = false // determines whether the element is supported to be shown inside a subcircuit +CircuitElement.prototype.subcircuitMetadata = {} // stores the coordinates and stuff for the elements in the subcircuit +CircuitElement.prototype.layoutProperties = { + rightDimensionX: 5, + leftDimensionX: 5, + upDimensionY: 5, + downDimensionY: 5, +} +CircuitElement.prototype.subcircuitMutableProperties = { + label: { + name: 'label: ', + type: 'text', + func: 'setLabel', + }, + 'show label': { + name: 'show label ', + type: 'checkbox', + func: 'toggleLabelInLayoutMode', + }, +} diff --git a/v0/src/simulator/src/combinationalAnalysis.js b/v0/src/simulator/src/combinationalAnalysis.js new file mode 100644 index 00000000..e45cb1b8 --- /dev/null +++ b/v0/src/simulator/src/combinationalAnalysis.js @@ -0,0 +1,667 @@ +/* eslint-disable import/no-cycle */ +/* eslint-disable guard-for-in */ +/* eslint-disable no-restricted-syntax */ +import Node from './node' +import { scheduleBackup } from './data/backupCircuit' +import BooleanMinimize from './quinMcCluskey' +import Input from './modules/Input' +import ConstantVal from './modules/ConstantVal' +import Output from './modules/Output' +import AndGate from './modules/AndGate' +import OrGate from './modules/OrGate' +import NotGate from './modules/NotGate' +import { stripTags } from './utils' +import simulationArea from './simulationArea' +import { findDimensions } from './canvasApi' +import { SimulatorStore } from '#/store/SimulatorStore/SimulatorStore' + +// var inputSample = 5 +// var dataSample = [ +// ['01---', '11110', '01---', '00000'], +// ['01110', '1-1-1', '----0'], +// ['01---', '11110', '01110', '1-1-1', '0---0'], +// ['----1'], +// ] + +// var sampleInputListNames = ['A', 'B'] +// var sampleOutputListNames = ['X'] + +/** + * The prompt for combinational analysis + * @param {Scope=} - the circuit in which we want combinational analysis + * @category combinationalAnalysis + */ +export function createCombinationalAnalysisPrompt(scope = globalScope) { + scheduleBackup() + SimulatorStore().dialogBox.combinationalanalysis_dialog = true + /* + $('#combinationalAnalysis').empty() + $('#combinationalAnalysis').append( + "

Enter Input names separated by commas:

" + ) + $('#combinationalAnalysis').append( + "

Enter Output names separated by commas:

" + ) + $('#combinationalAnalysis').append("

OR

") + $('#combinationalAnalysis').append( + "

Enter Boolean Function:

" + ) + $('#combinationalAnalysis').append( + "" + ) + $('#combinationalAnalysis').dialog({ + resizable: false, + width: 'auto', + buttons: [ + { + style: 'padding: 5px', + text: 'Next', + click() { + var inputList = stripTags($('#inputNameList').val()).split( + ',' + ) + var outputList = stripTags( + $('#outputNameList').val() + ).split(',') + var booleanExpression = $('#booleanExpression').val() + inputList = inputList.map((x) => x.trim()) + inputList = inputList.filter((e) => e) + outputList = outputList.map((x) => x.trim()) + outputList = outputList.filter((e) => e) + booleanExpression = booleanExpression.replace(/ /g, '') + booleanExpression = booleanExpression.toUpperCase() + var booleanInputVariables = [] + for (var i = 0; i < booleanExpression.length; i++) { + if ( + booleanExpression[i] >= 'A' && + booleanExpression[i] <= 'Z' + ) { + if ( + booleanExpression.indexOf( + booleanExpression[i] + ) == i + ) { + booleanInputVariables.push(booleanExpression[i]) + } + } + } + booleanInputVariables.sort() + if ( + inputList.length > 0 && + outputList.length > 0 && + booleanInputVariables.length == 0 + ) { + $(this).dialog('close') + createBooleanPrompt(inputList, outputList, null, scope) + } else if ( + booleanInputVariables.length > 0 && + inputList.length == 0 && + outputList.length == 0 + ) { + $(this).dialog('close') + var output = solveBooleanFunction( + booleanInputVariables, + booleanExpression + ) + if (output != null) { + createBooleanPrompt( + booleanInputVariables, + booleanExpression, + output, + scope + ) + } + } else if ( + (inputList.length == 0 || outputList.length == 0) && + booleanInputVariables == 0 + ) { + alert( + 'Enter Input / Output Variable(s) OR Boolean Function!' + ) + } else { + alert( + 'Use Either Combinational Analysis Or Boolean Function To Generate Circuit!' + ) + } + }, + }, + ], + }) + + $('#combinationalAnalysis').checkBo() + */ +} +// /** +// * This funciton hashes the output array and makes required JSON using +// * a BooleanMinimize class defined in Quin_Mcluskey.js var s which will +// * be output table is also initialied here +// * @param {Array} inputListNames - labels of input nodes +// * @param {Array} outputListNames - labels of output nodes +// * @param {Scope=} scope - h circuit +// * @category combinationalAnalysis +// */ +/* + function createBooleanPrompt( + inputListNames, + outputListNames, + output, + scope = globalScope + ) { + var inputListNames = + inputListNames || prompt('Enter inputs separated by commas').split(',') + var outputListNames = + outputListNames || + prompt('Enter outputs separated by commas').split(',') + var outputListNamesInteger = [] + if (output == null) { + for (var i = 0; i < outputListNames.length; i++) { + outputListNamesInteger[i] = 7 * i + 13 + } // assigning an integer to the value, 7*i + 13 is random + } else { + outputListNamesInteger = [13] + } + var s = '' + s += '' + s += '' + if ($('#decimalColumnBox').is(':checked')) { + s += '' + } + for (var i = 0; i < inputListNames.length; i++) { + s += `` + } + if (output == null) { + for (var i = 0; i < outputListNames.length; i++) { + s += `` + } + } else { + s += `` + } + s += '' + + var matrix = [] + for (var i = 0; i < inputListNames.length; i++) { + matrix[i] = new Array(1 << inputListNames.length) + } + + for (var i = 0; i < inputListNames.length; i++) { + for (var j = 0; j < 1 << inputListNames.length; j++) { + matrix[i][j] = +((j & (1 << (inputListNames.length - i - 1))) != 0) + } + } + + for (var j = 0; j < 1 << inputListNames.length; j++) { + s += '' + if ($('#decimalColumnBox').is(':checked')) { + s += `` + } + for (var i = 0; i < inputListNames.length; i++) { + s += `` + } + for (var i = 0; i < outputListNamesInteger.length; i++) { + if (output == null) { + s += + `' + // using hash values as they'll be used in the generateBooleanTableData function + } + } + if (output != null) { + s += + `' + } + s += '' + } + s += '' + s += '
' + 'dec' + '${inputListNames[i]}${outputListNames[i]}${outputListNames}
${j}${matrix[i][j]}` + + 'x' + + '` + + `${output[j]}` + + '
' + $('#combinationalAnalysis').empty() + $('#combinationalAnalysis').append(s) + $('#combinationalAnalysis').dialog({ + resizable: false, + width: 'auto', + buttons: [ + { + style: 'padding: 6px', + text: 'Generate Circuit', + click() { + $(this).dialog('close') + var data = generateBooleanTableData(outputListNamesInteger) + // passing the hash values to avoid spaces being passed which is causing a problem + var minimizedCircuit = [] + let inputCount = inputListNames.length + for (const output in data) { + let oneCount = data[output][1].length // Number of ones + let zeroCount = data[output][0].length // Number of zeroes + if (oneCount == 0) { + // Hardcode to 0 as output + minimizedCircuit.push([ + '-'.repeat(inputCount) + '0', + ]) + } else if (zeroCount == 0) { + // Hardcode to 1 as output + minimizedCircuit.push([ + '-'.repeat(inputCount) + '1', + ]) + } else { + // Perform KMap like minimzation + const temp = new BooleanMinimize( + inputListNames.length, + data[output][1].map(Number), + data[output].x.map(Number) + ) + minimizedCircuit.push(temp.result) + } + } + if (output == null) { + drawCombinationalAnalysis( + minimizedCircuit, + inputListNames, + outputListNames, + scope + ) + } else { + drawCombinationalAnalysis( + minimizedCircuit, + inputListNames, + [`${outputListNames}`], + scope + ) + } + }, + }, + { + style: 'padding: 6px', + text: 'Print Truth Table', + click() { + var sTable = document.getElementById( + 'combinationalAnalysis' + ).innerHTML + var style = + '' + var win = window.open('', '', 'height=700,width=700') + var htmlBody = ` + \ + Boolean Logic Table\ + ${style}\ + \ + \ +
${sTable}
\ + + ` + win.document.write(htmlBody) + win.document.close() + win.print() + }, + }, + ], + }) + + $('.output').on('click', function () { + var v = $(this).html() + if (v == 0) v = $(this).html(1) + else if (v == 1) v = $(this).html('x') + else if (v == 'x') v = $(this).html(0) + }) + } +*/ + +// function generateBooleanTableData(outputListNames) { +// var data = {} +// for (var i = 0; i < outputListNames.length; i++) { +// data[outputListNames[i]] = { +// x: [], +// 1: [], +// 0: [], +// } +// var rows = $(`.${outputListNames[i]}`) +// for (let j = 0; j < rows.length; j++) { +// data[outputListNames[i]][rows[j].innerHTML].push(rows[j].id) +// } +// } +// return data +// } + +// function drawCombinationalAnalysis( +// combinationalData, +// inputList, +// outputListNames, +// scope = globalScope +// ) { +// findDimensions(scope) +// var inputCount = inputList.length +// var maxTerms = 0 +// for (var i = 0; i < combinationalData.length; i++) { +// maxTerms = Math.max(maxTerms, combinationalData[i].length) +// } + +// var startPosX = 200 +// var startPosY = 200 + +// var currentPosY = 300 + +// if (simulationArea.maxWidth && simulationArea.maxHeight) { +// if (simulationArea.maxHeight + currentPosY > simulationArea.maxWidth) { +// startPosX += simulationArea.maxWidth +// } else { +// startPosY += simulationArea.maxHeight +// currentPosY += simulationArea.maxHeight +// } +// } +// var andPosX = startPosX + inputCount * 40 + 40 + 40 +// var orPosX = andPosX + Math.floor(maxTerms / 2) * 10 + 80 +// var outputPosX = orPosX + 60 +// var inputObjects = [] + +// var logixNodes = [] + +// // Appending constant input to the end of inputObjects +// for (var i = 0; i <= inputCount; i++) { +// if (i < inputCount) { +// // Regular Input +// inputObjects.push( +// new Input(startPosX + i * 40, startPosY, scope, 'DOWN', 1) +// ) +// inputObjects[i].setLabel(inputList[i]) +// } else { +// // Constant Input +// inputObjects.push( +// new ConstantVal( +// startPosX + i * 40, +// startPosY, +// scope, +// 'DOWN', +// 1, +// '1' +// ) +// ) +// inputObjects[i].setLabel('_C_') +// } + +// inputObjects[i].newLabelDirection('UP') +// var v1 = new Node(startPosX + i * 40, startPosY + 20, 2, scope.root) +// inputObjects[i].output1.connect(v1) +// var v2 = new Node( +// startPosX + i * 40 + 20, +// startPosY + 20, +// 2, +// scope.root +// ) +// v1.connect(v2) +// var notG = new NotGate( +// startPosX + i * 40 + 20, +// startPosY + 40, +// scope, +// 'DOWN', +// 1 +// ) +// notG.inp1.connect(v2) +// logixNodes.push(v1) +// logixNodes.push(notG.output1) +// } + +// function countTerm(s) { +// var c = 0 +// for (var i = 0; i < s.length; i++) { +// if (s[i] !== '-') c++ +// } +// return c +// } + +// for (var i = 0; i < combinationalData.length; i++) { +// var andGateNodes = [] +// for (var j = 0; j < combinationalData[i].length; j++) { +// var c = countTerm(combinationalData[i][j]) +// if (c > 1) { +// var andGate = new AndGate( +// andPosX, +// currentPosY, +// scope, +// 'RIGHT', +// c, +// 1 +// ) +// andGateNodes.push(andGate.output1) +// var misses = 0 +// for (var k = 0; k < combinationalData[i][j].length; k++) { +// if (combinationalData[i][j][k] == '-') { +// misses++ +// continue +// } +// var index = 2 * k + (combinationalData[i][j][k] == 0) +// var v = new Node( +// logixNodes[index].absX(), +// andGate.inp[k - misses].absY(), +// 2, +// scope.root +// ) +// logixNodes[index].connect(v) +// logixNodes[index] = v +// v.connect(andGate.inp[k - misses]) +// } +// } else { +// for (var k = 0; k < combinationalData[i][j].length; k++) { +// if (combinationalData[i][j][k] == '-') continue +// var index = 2 * k + (combinationalData[i][j][k] == 0) +// var andGateSubstituteNode = new Node( +// andPosX, +// currentPosY, +// 2, +// scope.root +// ) +// var v = new Node( +// logixNodes[index].absX(), +// andGateSubstituteNode.absY(), +// 2, +// scope.root +// ) +// logixNodes[index].connect(v) +// logixNodes[index] = v +// v.connect(andGateSubstituteNode) +// andGateNodes.push(andGateSubstituteNode) +// } +// } +// currentPosY += c * 10 + 30 +// } + +// var andGateCount = andGateNodes.length +// var midWay = Math.floor(andGateCount / 2) +// var orGatePosY = +// (andGateNodes[midWay].absY() + +// andGateNodes[Math.floor((andGateCount - 1) / 2)].absY()) / +// 2 +// if (orGatePosY % 10 == 5) { +// orGatePosY += 5 +// } // To make or gate fall in grid +// if (andGateCount > 1) { +// var o = new OrGate( +// orPosX, +// orGatePosY, +// scope, +// 'RIGHT', +// andGateCount, +// 1 +// ) +// if (andGateCount % 2 == 1) +// andGateNodes[midWay].connect(o.inp[midWay]) +// for (var j = 0; j < midWay; j++) { +// var v = new Node( +// andPosX + 30 + (midWay - j) * 10, +// andGateNodes[j].absY(), +// 2, +// scope.root +// ) +// v.connect(andGateNodes[j]) +// var v2 = new Node( +// andPosX + 30 + (midWay - j) * 10, +// o.inp[j].absY(), +// 2, +// scope.root +// ) +// v2.connect(v) +// o.inp[j].connect(v2) + +// var v = new Node( +// andPosX + 30 + (midWay - j) * 10, +// andGateNodes[andGateCount - j - 1].absY(), +// 2, +// scope.root +// ) +// v.connect(andGateNodes[andGateCount - j - 1]) +// var v2 = new Node( +// andPosX + 30 + (midWay - j) * 10, +// o.inp[andGateCount - j - 1].absY(), +// 2, +// scope.root +// ) +// v2.connect(v) +// o.inp[andGateCount - j - 1].connect(v2) +// } +// var out = new Output(outputPosX, o.y, scope, 'LEFT', 1) +// out.inp1.connect(o.output1) +// } else { +// var out = new Output( +// outputPosX, +// andGateNodes[0].absY(), +// scope, +// 'LEFT', +// 1 +// ) +// out.inp1.connect(andGateNodes[0]) +// } +// out.setLabel(outputListNames[i]) +// out.newLabelDirection('RIGHT') +// } +// for (var i = 0; i < logixNodes.length; i++) { +// if (logixNodes[i].absY() != currentPosY) { +// var v = new Node(logixNodes[i].absX(), currentPosY, 2, scope.root) +// logixNodes[i].connect(v) +// } +// } +// globalScope.centerFocus() +// } + +// /** +// * This function solves passed boolean expression and returns +// * output array which contains solution of the truth table +// * of given boolean expression +// * @param {Array} inputListNames - labels for input nodes +// * @param {String} booleanExpression - boolean expression which is to be solved +// */ +// function solveBooleanFunction(inputListNames, booleanExpression) { +// let i +// let j +// let output = [] + +// if ( +// booleanExpression.match( +// /[^ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01+'() ]/g +// ) != null +// ) { +// alert('One of the characters is not allowed.') +// return +// } + +// if (inputListNames.length > 8) { +// alert('You can only have 8 variables at a time.') +// return +// } + +// var s = '' +// s += '' +// s += '' +// if ($('#decimalColumnBox').is(':checked')) { +// s += '' +// } +// for (i = 0; i < inputListNames.length; i++) { +// s += `` +// } +// s += `` +// s += '' +// var matrix = [] +// for (i = 0; i < inputListNames.length; i++) { +// matrix[i] = new Array(inputListNames.length) +// } + +// for (i = 0; i < inputListNames.length; i++) { +// for (j = 0; j < 1 << inputListNames.length; j++) { +// matrix[i][j] = +((j & (1 << (inputListNames.length - i - 1))) != 0) +// } +// } +// // generate equivalent expression by replacing input vars with possible combinations of o and 1 +// for (i = 0; i < 2 ** inputListNames.length; i++) { +// const data = [] +// for (j = 0; j < inputListNames.length; j++) { +// data[j] = +// Math.floor(i / Math.pow(2, inputListNames.length - j - 1)) % 2 +// } +// let equation = booleanExpression +// for (j = 0; j < inputListNames.length; j++) { +// equation = equation.replace( +// new RegExp(inputListNames[j], 'g'), +// data[j] +// ) +// } + +// output[i] = solve(equation) +// } + +// for (j = 0; j < 1 << inputListNames.length; j++) { +// s += '' +// if ($('#decimalColumnBox').is(':checked')) { +// s += `` +// } +// for (i = 0; i < inputListNames.length; i++) { +// s += `` +// } + +// s += `' +// s += '' +// } + +// s += '' +// s += '
' + 'dec' + '${inputListNames[i]}${booleanExpression}
${j}${matrix[i][j]}` + `${output[j]}` + '
' +// // generates solution for the truth table of booleanexpression +// function solve(equation) { +// while (equation.indexOf('(') != -1) { +// const start = equation.lastIndexOf('(') +// const end = equation.indexOf(')', start) +// if (start != -1) { +// equation = +// equation.substring(0, start) + +// solve(equation.substring(start + 1, end)) + +// equation.substring(end + 1) +// } +// } +// equation = equation.replace(/''/g, '') +// equation = equation.replace(/0'/g, '1') +// equation = equation.replace(/1'/g, '0') +// for (let i = 0; i < equation.length - 1; i++) { +// if ( +// (equation[i] == '0' || equation[i] == '1') && +// (equation[i + 1] == '0' || equation[i + 1] == '1') +// ) { +// equation = +// equation.substring(0, i + 1) + +// '*' + +// equation.substring(i + 1, equation.length) +// } +// } +// try { +// const safeEval = eval +// const answer = safeEval(equation) +// if (answer == 0) { +// return 0 +// } +// if (answer > 0) { +// return 1 +// } +// return '' +// } catch (e) { +// return '' +// } +// } + +// return output +// } diff --git a/v0/src/simulator/src/data.js b/v0/src/simulator/src/data.js new file mode 100644 index 00000000..4e573c21 --- /dev/null +++ b/v0/src/simulator/src/data.js @@ -0,0 +1,62 @@ +import { fullView } from './ux' +import { createSubCircuitPrompt } from './subcircuit' +import save from './data/save' +import load from './data/load' +import createSaveAsImgPrompt from './data/saveImage' +import { + clearProject, + newProject, + saveOffline, + openOffline, + recoverProject, +} from './data/project' +import { newCircuit, createNewCircuitScope } from './circuit' +import { createCombinationalAnalysisPrompt } from './combinationalAnalysis' +import { colorThemes } from './themer/themer' +import { showTourGuide } from './tutorials' +import { + createVerilogCircuit, + // saveVerilogCode, + // resetVerilogCode, + // applyVerilogTheme, +} from './Verilog2CV' +import { generateVerilog } from './verilog' +import { bitConverterDialog } from './utils' +import { keyBinder } from '#/components/DialogBox/CustomShortcut.vue' +import { ExportProject } from '#/components/DialogBox/ExportProject.vue' +import { ImportProject } from '#/components/DialogBox/ImportProject.vue' + +const logixFunction = {} +logixFunction.save = save +logixFunction.load = load +logixFunction.createSaveAsImgPrompt = createSaveAsImgPrompt +logixFunction.clearProject = clearProject +logixFunction.newProject = newProject +logixFunction.saveOffline = saveOffline +// logixFunction.newCircuit = newCircuit +logixFunction.createOpenLocalPrompt = openOffline +logixFunction.recoverProject = recoverProject +logixFunction.createSubCircuitPrompt = createSubCircuitPrompt +logixFunction.createCombinationalAnalysisPrompt = + createCombinationalAnalysisPrompt +logixFunction.fullViewOption = fullView +logixFunction.colorThemes = colorThemes +logixFunction.showTourGuide = showTourGuideHelper +logixFunction.newVerilogModule = createVerilogCircuit +// logixFunction.saveVerilogCode = saveVerilogCode +// logixFunction.resetVerilogCode = resetVerilogCode +logixFunction.generateVerilog = generateVerilog +// logixFunction.applyVerilogTheme = applyVerilogTheme +logixFunction.bitconverter = bitConverterDialog +logixFunction.createNewCircuitScope = createNewCircuitScope +logixFunction.customShortcut = keyBinder +logixFunction.ExportProject = ExportProject +logixFunction.ImportProject = ImportProject +export default logixFunction + +// Hack to restart tour guide +function showTourGuideHelper() { + setTimeout(() => { + showTourGuide() + }, 100) +} diff --git a/v0/src/simulator/src/data/backupCircuit.js b/v0/src/simulator/src/data/backupCircuit.js new file mode 100644 index 00000000..c17a93c3 --- /dev/null +++ b/v0/src/simulator/src/data/backupCircuit.js @@ -0,0 +1,83 @@ +import { projectSavedSet } from './project' +/* eslint-disable no-param-reassign */ +function extract(obj) { + return obj.saveObject() +} + +// Check if there is anything to backup - to be deprecated +/** + * Check if backup is available + * @param {Scope} scope + * @return {boolean} + * @category data + */ +export function checkIfBackup(scope) { + for (let i = 0; i < updateOrder.length; i++) { + if (scope[updateOrder[i]].length) return true + } + return false +} + +export function backUp(scope = globalScope) { + // Disconnection of subcircuits are needed because these are the connections between nodes + // in current scope and those in the subcircuit's scope + for (let i = 0; i < scope.SubCircuit.length; i++) { + scope.SubCircuit[i].removeConnections() + } + + var data = {} + + // Storing layout + data.layout = scope.layout + + // Storing Verilog Properties + data.verilogMetadata = scope.verilogMetadata + + // Storing all nodes + data.allNodes = scope.allNodes.map(extract) + + // Storing test attached to scope + data.testbenchData = scope.testbenchData + + // Storing other details + data.id = scope.id + data.name = scope.name + + // Storing details of all module objects + for (let i = 0; i < moduleList.length; i++) { + if (scope[moduleList[i]].length) { + data[moduleList[i]] = scope[moduleList[i]].map(extract) + } + } + + // Adding restricted circuit elements used in the save data + data.restrictedCircuitElementsUsed = scope.restrictedCircuitElementsUsed + + // Storing intermediate nodes (nodes in wires) + data.nodes = [] + for (let i = 0; i < scope.nodes.length; i++) { + data.nodes.push(scope.allNodes.indexOf(scope.nodes[i])) + } + + // Restoring the connections + for (let i = 0; i < scope.SubCircuit.length; i++) { + scope.SubCircuit[i].makeConnections() + } + + return data +} + +export function scheduleBackup(scope = globalScope) { + var backup = JSON.stringify(backUp(scope)) + if ( + scope.backups.length === 0 || + scope.backups[scope.backups.length - 1] !== backup + ) { + scope.backups.push(backup) + scope.history = [] + scope.timeStamp = new Date().getTime() + projectSavedSet(false) + } + + return backup +} diff --git a/v0/src/simulator/src/data/load.js b/v0/src/simulator/src/data/load.js new file mode 100644 index 00000000..f9d7b89b --- /dev/null +++ b/v0/src/simulator/src/data/load.js @@ -0,0 +1,294 @@ +import { resetScopeList, newCircuit, switchCircuit } from '../circuit' +import { setProjectName } from './save' +import { + scheduleUpdate, + update, + updateSimulationSet, + updateCanvasSet, + gridUpdateSet, +} from '../engine' +import { updateRestrictedElementsInScope } from '../restrictedElementDiv' +import simulationArea from '../simulationArea' + +import { loadSubCircuit } from '../subcircuit' +import { scheduleBackup } from './backupCircuit' +import { showProperties } from '../ux' +import { constructNodeConnections, loadNode, replace } from '../node' +import { generateId } from '../utils' +import modules from '../modules' +import { oppositeDirection } from '../canvasApi' +import plotArea from '../plotArea' +import { updateTestbenchUI, TestbenchData } from '../testbench' +import { SimulatorStore } from '#/store/SimulatorStore/SimulatorStore' +import { toRefs } from 'vue' +/** + * Backward compatibility - needs to be deprecated + * @param {CircuitElement} obj - the object to be rectified + * @category data + */ +function rectifyObjectType(obj) { + const rectify = { + FlipFlop: 'DflipFlop', + Ram: 'Rom', + } + return rectify[obj] || obj +} + +/** + * Function to load CircuitElements + * @param {JSON} data - JSOn data + * @param {Scope} scope - circuit in which we want to load modules + * @category data + */ +function loadModule(data, scope) { + // Create circuit element + var obj = new modules[rectifyObjectType(data.objectType)]( + data.x, + data.y, + scope, + ...(data.customData.constructorParamaters || []) + ) + // Sets directions + obj.label = data.label + obj.labelDirection = + data.labelDirection || oppositeDirection[fixDirection[obj.direction]] + + // Sets delay + obj.propagationDelay = data.propagationDelay || obj.propagationDelay + obj.fixDirection() + + // Restore other values + if (data.customData.values) { + for (var prop in data.customData.values) { + obj[prop] = data.customData.values[prop] + } + } + + // Replace new nodes with the correct old nodes (with connections) + if (data.customData.nodes) { + for (const node in data.customData.nodes) { + const n = data.customData.nodes[node] + if (n instanceof Array) { + for (let i = 0; i < n.length; i++) { + obj[node][i] = replace(obj[node][i], n[i]) + } + } else { + obj[node] = replace(obj[node], n) + } + } + } + if (data.subcircuitMetadata) + obj.subcircuitMetadata = data['subcircuitMetadata'] +} + +/** + * This function shouldn't ideally exist. But temporary fix + * for some issues while loading nodes. + * @category data + */ +function removeBugNodes(scope = globalScope) { + let x = scope.allNodes.length + for (let i = 0; i < x; i++) { + if ( + scope.allNodes[i].type !== 2 && + scope.allNodes[i].parent.objectType === 'CircuitElement' + ) { + scope.allNodes[i].delete() + } + if (scope.allNodes.length !== x) { + i = 0 + x = scope.allNodes.length + } + } +} + +/** + * Function to load a full circuit + * @param {Scope} scope + * @param {JSON} data + * @category data + */ +export function loadScope(scope, data) { + const ML = moduleList.slice() // Module List copy + scope.restrictedCircuitElementsUsed = data.restrictedCircuitElementsUsed + + // Load all nodes + data.allNodes.map((x) => loadNode(x, scope)) + + // Make all connections + for (let i = 0; i < data.allNodes.length; i++) { + constructNodeConnections(scope.allNodes[i], data.allNodes[i]) + } + // Load all modules + for (let i = 0; i < ML.length; i++) { + if (data[ML[i]]) { + if (ML[i] === 'SubCircuit') { + // Load subcircuits differently + for (let j = 0; j < data[ML[i]].length; j++) { + loadSubCircuit(data[ML[i]][j], scope) + } + } else { + // Load everything else similarly + for (let j = 0; j < data[ML[i]].length; j++) { + loadModule(data[ML[i]][j], scope) + } + } + } + } + // Update wires according + scope.wires.map((x) => { + x.updateData(scope) + }) + removeBugNodes(scope) // To be deprecated + + // If Verilog Circuit Metadata exists, then restore + if (data.verilogMetadata) { + scope.verilogMetadata = data.verilogMetadata + } + + // If Test exists, then restore + if (data.testbenchData) { + globalScope.testbenchData = new TestbenchData( + data.testbenchData.testData, + data.testbenchData.currentGroup, + data.testbenchData.currentCase + ) + } + + // If layout exists, then restore + if (data.layout) { + scope.layout = data.layout + } else { + // Else generate new layout according to how it would have been otherwise (backward compatibility) + scope.layout = {} + scope.layout.width = 100 + scope.layout.height = + Math.max(scope.Input.length, scope.Output.length) * 20 + 20 + scope.layout.title_x = 50 + scope.layout.title_y = 13 + for (let i = 0; i < scope.Input.length; i++) { + scope.Input[i].layoutProperties = { + x: 0, + y: + scope.layout.height / 2 - + scope.Input.length * 10 + + 20 * i + + 10, + id: generateId(), + } + } + for (let i = 0; i < scope.Output.length; i++) { + scope.Output[i].layoutProperties = { + x: scope.layout.width, + y: + scope.layout.height / 2 - + scope.Output.length * 10 + + 20 * i + + 10, + id: generateId(), + } + } + } + // Backward compatibility + if (scope.layout.titleEnabled === undefined) { + scope.layout.titleEnabled = true + } +} + +// Function to load project from data +/** + * loads a saved project + * @param {JSON} data - the json data of the + * @category data + * @exports load + */ +export default function load(data) { + // If project is new and no data is there, then just set project name + const simulatorStore = SimulatorStore() + const { circuit_list } = toRefs(simulatorStore) + + if (!data) { + setProjectName(__projectName) + return + } + + var { projectId } = data + setProjectName(data.name) + + globalScope = undefined + resetScopeList() // Remove default scope + // $('.circuits').remove() // Delete default scope + + // Load all according to the dependency order + for (let i = 0; i < data.scopes.length; i++) { + var isVerilogCircuit = false + var isMainCircuit = false + if (data.scopes[i].verilogMetadata) { + isVerilogCircuit = data.scopes[i].verilogMetadata.isVerilogCircuit + isMainCircuit = data.scopes[i].verilogMetadata.isMainCircuit + } + // Create new circuit + const scope = newCircuit( + data.scopes[i].name || 'Untitled', + data.scopes[i].id, + isVerilogCircuit, + isMainCircuit + ) + + // Load circuit data + loadScope(scope, data.scopes[i]) + + // Focus circuit + globalScope = scope + + // Center circuit + if (embed) { + globalScope.centerFocus(true) + } else { + globalScope.centerFocus(false) + } + + // update and backup circuit once + update(globalScope, true) + + // Updating restricted element list initially on loading + updateRestrictedElementsInScope() + + scheduleBackup() + } + + // Restore clock + simulationArea.changeClockTime(data.timePeriod || 500) + simulationArea.clockEnabled = + data.clockEnabled === undefined ? true : data.clockEnabled + + if (!embed) { + showProperties(simulationArea.lastSelected) + } + + // Reorder tabs according to the saved order + if (data.orderedTabs) { + // var unorderedTabs = $('.circuits').detach() + // var plusButton = $('#tabsBar').children().detach() + // for (const tab of data.orderedTabs) { + // $('#tabsBar').append(unorderedTabs.filter(`#${tab}`)) + // } + // $('#tabsBar').append(plusButton) + circuit_list.value.sort((a, b) => { + return data.orderedTabs.indexOf(String(a.id)) - data.orderedTabs.indexOf(String(b.id)); + }) + } + + // Switch to last focussedCircuit + if (data.focussedCircuit) switchCircuit(String(data.focussedCircuit)) + + // Update the testbench UI + updateTestbenchUI() + + updateSimulationSet(true) + updateCanvasSet(true) + gridUpdateSet(true) + // Reset Timing + if (!embed) plotArea.reset() + scheduleUpdate(1) +} diff --git a/v0/src/simulator/src/data/project.js b/v0/src/simulator/src/data/project.js new file mode 100644 index 00000000..5a88fdd5 --- /dev/null +++ b/v0/src/simulator/src/data/project.js @@ -0,0 +1,175 @@ +/* eslint-disable guard-for-in */ +/* eslint-disable no-bitwise */ +/* eslint-disable import/no-cycle */ +/* eslint-disable no-restricted-globals */ +/* eslint-disable no-alert */ +import { resetScopeList, scopeList, newCircuit } from '../circuit' +import { showMessage, showError, generateId } from '../utils' +import { checkIfBackup } from './backupCircuit' +import { generateSaveData, getProjectName, setProjectName } from './save' +import load from './load' +import { SimulatorStore } from '#/store/SimulatorStore/SimulatorStore' +import { confirmOption } from '#/components/helpers/confirmComponent/ConfirmComponent.vue' + +/** + * Helper function to recover unsaved data + * @category data + */ +export async function recoverProject() { + if (localStorage.getItem('recover')) { + var data = JSON.parse(localStorage.getItem('recover')) + if (await confirmOption(`Would you like to recover: ${data.name}`)) { + load(data) + } + localStorage.removeItem('recover') + } else { + showError('No recover project found') + } +} + +/** + * Prompt to restore from localStorage + * @category data + */ +export function openOffline() { + const simulatorStore = SimulatorStore() + simulatorStore.dialogBox.open_project_dialog = true + /* + $('#openProjectDialog').empty() + const projectList = JSON.parse(localStorage.getItem('projectList')) + let flag = true + for (id in projectList) { + flag = false + $('#openProjectDialog').append( + `` + ) + } + if (flag) + $('#openProjectDialog').append( + '

Looks like no circuit has been saved yet. Create a new one and save it!

' + ) + $('#openProjectDialog').dialog({ + resizable: false, + width: 'auto', + buttons: !flag + ? [ + { + id: 'Open_offline_btn', + text: 'Open Project', + click() { + if (!$('input[name=projectId]:checked').val()) return + load( + JSON.parse( + localStorage.getItem( + $('input[name=projectId]:checked').val() + ) + ) + ) + window.projectId = $( + 'input[name=projectId]:checked' + ).val() + $(this).dialog('close') + }, + }, + ] + : [], + }) + */ +} +/** + * Flag for project saved or not + * @type {boolean} + * @category data + */ +var projectSaved = true +export function projectSavedSet(param) { + projectSaved = param +} + +/** + * Helper function to store to localStorage -- needs to be deprecated/removed + * @category data + */ +export async function saveOffline() { + const data = await generateSaveData() + if (data instanceof Error) return + localStorage.setItem(projectId, data) + const temp = JSON.parse(localStorage.getItem('projectList')) || {} + temp[projectId] = getProjectName() + localStorage.setItem('projectList', JSON.stringify(temp)) + showMessage( + `We have saved your project: ${getProjectName()} in your browser's localStorage` + ) +} + +/** + * Checks if any circuit has unsaved data + * @category data + */ +function checkToSave() { + let saveFlag = false + // eslint-disable-next-line no-restricted-syntax + for (id in scopeList) { + saveFlag |= checkIfBackup(scopeList[id]) + } + return saveFlag +} + +/** + * Prompt user to save data if unsaved + * @category data + */ +window.onbeforeunload = async function () { + if (projectSaved || embed) return + + if (!checkToSave()) return + + alert( + 'You have unsaved changes on this page. Do you want to leave this page and discard your changes or stay on this page?' + ) + // await confirmSingleOption( + // 'You have unsaved changes on this page. Do you want to leave this page and discard your changes or stay on this page?' + // ) + const data = await generateSaveData('Untitled') + localStorage.setItem('recover', await data) + // eslint-disable-next-line consistent-return + return 'Are u sure u want to leave? Any unsaved changes may not be recoverable' +} + +/** + * Function to clear project + * @category data + */ +export async function clearProject() { + if (await confirmOption('Would you like to clear the project?')) { + globalScope = undefined + resetScopeList() + // $('.circuits').remove() + newCircuit('main') + showMessage('Your project is as good as new!') + } +} + +/** + Function used to start a new project while prompting confirmation from the user + * @param {boolean} verify - flag to verify a new project + * @category data + */ +export async function newProject(verify) { + if ( + verify || + projectSaved || + !checkToSave() || + (await confirmOption( + 'What you like to start a new project? Any unsaved changes will be lost.' + )) + ) { + clearProject() + localStorage.removeItem('recover') + window.location = '/simulator' + + setProjectName(undefined) + projectId = generateId() + showMessage('New Project has been created!') + } +} diff --git a/v0/src/simulator/src/data/redo.js b/v0/src/simulator/src/data/redo.js new file mode 100644 index 00000000..bc252a98 --- /dev/null +++ b/v0/src/simulator/src/data/redo.js @@ -0,0 +1,47 @@ +/* eslint-disable import/no-cycle */ +/** + * Function to restore copy from backup + * @param {Scope=} scope - The circuit on which redo is called + * @category data + */ +import { layoutModeGet } from '../layoutMode' +import Scope, { scopeList } from '../circuit' +import { loadScope } from './load' +import { updateRestrictedElementsInScope } from '../restrictedElementDiv' +import { forceResetNodesSet } from '../engine' +/** + * Function called to generate a prompt to save an image + * @param {Scope=} - the circuit in which we want to call redo + * @category data + * @exports redo + */ +export default function redo(scope = globalScope) { + if (layoutModeGet()) return + if (scope.history.length === 0) return + const backupOx = globalScope.ox + const backupOy = globalScope.oy + const backupScale = globalScope.scale + globalScope.ox = 0 + globalScope.oy = 0 + const tempScope = new Scope(scope.name) + loading = true + const redoData = scope.history.pop() + scope.backups.push(redoData) + loadScope(tempScope, JSON.parse(redoData)) + tempScope.backups = scope.backups + tempScope.history = scope.history + tempScope.id = scope.id + tempScope.name = scope.name + tempScope.testbenchData = scope.testbenchData + scopeList[scope.id] = tempScope + globalScope = tempScope + globalScope.ox = backupOx + globalScope.oy = backupOy + globalScope.scale = backupScale + loading = false + forceResetNodesSet(true) + + // Updated restricted elements + updateRestrictedElementsInScope() +} +// for html file diff --git a/v0/src/simulator/src/data/save.js b/v0/src/simulator/src/data/save.js new file mode 100644 index 00000000..3ffb7788 --- /dev/null +++ b/v0/src/simulator/src/data/save.js @@ -0,0 +1,514 @@ +import { scopeList } from '../circuit' +import { resetup } from '../setup' +import { update } from '../engine' +import { stripTags, showMessage } from '../utils' +import { backUp } from './backupCircuit' +import simulationArea from '../simulationArea' +import backgroundArea from '../backgroundArea' +import { findDimensions } from '../canvasApi' +import { projectSavedSet } from './project' +import { colors } from '../themer/themer' +import { layoutModeGet, toggleLayoutMode } from '../layoutMode' +import { verilogModeGet } from '../Verilog2CV' +import domtoimage from 'dom-to-image' +import '../../vendor/canvas2svg' +import { useProjectStore } from '#/store/projectStore' +import { provideProjectName } from '#/components/helpers/promptComponent/PromptComponent.vue' +import { UpdateProjectDetail } from '#/components/helpers/createNewProject/UpdateProjectDetail.vue' +import { confirmOption } from '#/components/helpers/confirmComponent/ConfirmComponent.vue' +import { getToken } from '#/pages/simulatorHandler.vue' + +// var projectName = undefined + +/** + * Function to set the name of project. + * @param {string} name - name for project + * @category data + */ +export function setProjectName(name) { + const projectStore = useProjectStore() + if (name == undefined) { + // $('#projectName').html('Untitled') + return + } + name = stripTags(name) + // projectName = name + // $('#projectName').html(name) + projectStore.setProjectName(name) +} + +/** + * Function to set the name of project. + * @param {string} name - name for project + * @category data + */ +export function getProjectName() { + const projectStore = useProjectStore() + if (projectStore.getProjectNameDefined) + return projectStore.getProjectName.trim() + else return undefined +} +/** + * Helper function to save canvas as image based on image type + * @param {string} name -name of the circuit + * @param {string} imgType - image type ex: png,jpg etc. + * @category data + */ +function downloadAsImg(name, imgType) { + const gh = simulationArea.canvas.toDataURL(`image/${imgType}`) + const anchor = document.createElement('a') + anchor.href = gh + anchor.download = `${name}.${imgType}` + anchor.click() +} + +/** + * Returns the order of tabs in the project + */ +export function getTabsOrder() { + var tabs = document.getElementById('tabsBar').firstChild.children + var order = [] + for (let i = 0; i < tabs.length; i++) { + order.push(tabs[i].id) + } + return order +} + +/** + * Generates JSON of the entire project + * @param {string} name - the name of project + * @return {JSON} + * @category data + */ +export async function generateSaveData(name, setName = true) { + let data = {} + + // Prompts for name, defaults to Untitled + name = getProjectName() || name || (await provideProjectName()) + if (name instanceof Error) { + return new Error('cancel') + // throw 'save has been canceled' + } else if (name == '') { + name = 'Untitled' + } + data.name = stripTags(name) + if (setName) setProjectName(data.name) + + // Save project details + data.timePeriod = simulationArea.timePeriod + data.clockEnabled = simulationArea.clockEnabled + data.projectId = projectId + data.focussedCircuit = globalScope.id + data.orderedTabs = getTabsOrder() + + // Project Circuits, each scope is one circuit + data.scopes = [] + const dependencyList = {} + const completed = {} + // Getting list of dependencies for each circuit + for (id in scopeList) { + dependencyList[id] = scopeList[id].getDependencies() + } + + // Helper function to save Scope + // Recursively saves inner subcircuits first, before saving parent circuits + function saveScope(id) { + if (completed[id]) return + + for (let i = 0; i < dependencyList[id].length; i++) { + // Save inner subcircuits + saveScope(dependencyList[id][i]) + } + + completed[id] = true + update(scopeList[id], true) // For any pending integrity checks on subcircuits + data.scopes.push(backUp(scopeList[id])) + } + + // Save all circuits + for (let id in scopeList) { + saveScope(id) + } + + // convert to text + data = JSON.stringify(data) + return data +} + +// Helper function to download text +function download(filename, text) { + var pom = document.createElement('a') + pom.setAttribute( + 'href', + 'data:text/plain;charset=utf-8,' + encodeURIComponent(text) + ) + pom.setAttribute('download', filename) + + if (document.createEvent) { + var event = document.createEvent('MouseEvents') + event.initEvent('click', true, true) + pom.dispatchEvent(event) + } else { + pom.click() + } +} + +/** + * Function to generate image for the circuit + * @param {string} imgType - ex: png,jpg etc. + * @param {string} view - view type ex: full + * @param {boolean} transparent - tranparent bg or not + * @param {number} resolution - resolution of the image + * @param {boolean=} down - will download if true + * @category data + */ +export function generateImage( + imgType, + view, + transparent, + resolution, + down = true +) { + // Backup all data + const backUpOx = globalScope.ox + const backUpOy = globalScope.oy + const backUpWidth = width + const backUpHeight = height + const backUpScale = globalScope.scale + const backUpContextBackground = backgroundArea.context + const backUpContextSimulation = simulationArea.context + + backgroundArea.context = simulationArea.context + + globalScope.ox *= 1 / backUpScale + globalScope.oy *= 1 / backUpScale + + // If SVG, create SVG context - using canvas2svg here + if (imgType === 'svg') { + simulationArea.context = new C2S(width, height) + resolution = 1 + } else if (imgType !== 'png') { + transparent = false + } + + globalScope.scale = resolution + + const scope = globalScope + + // Focus circuit + var flag = 1 + if (flag) { + if (view === 'full') { + findDimensions() + const minX = simulationArea.minWidth + const minY = simulationArea.minHeight + const maxX = simulationArea.maxWidth + const maxY = simulationArea.maxHeight + width = (maxX - minX + 100) * resolution + height = (maxY - minY + 100) * resolution + + globalScope.ox = (-minX + 50) * resolution + globalScope.oy = (-minY + 50) * resolution + } else { + globalScope.ox *= resolution + globalScope.oy *= resolution + width = (width * resolution) / backUpScale + height = (height * resolution) / backUpScale + } + } + + globalScope.ox = Math.round(globalScope.ox) + globalScope.oy = Math.round(globalScope.oy) + + simulationArea.canvas.width = width + simulationArea.canvas.height = height + backgroundArea.canvas.width = width + backgroundArea.canvas.height = height + + backgroundArea.context = simulationArea.context + + simulationArea.clear() + + // Background + if (!transparent) { + simulationArea.context.fillStyle = colors['canvas_fill'] + simulationArea.context.rect(0, 0, width, height) + simulationArea.context.fill() + } + + // Draw circuits, why is it updateOrder and not renderOrder? + for (let i = 0; i < renderOrder.length; i++) { + for (let j = 0; j < scope[renderOrder[i]].length; j++) { + scope[renderOrder[i]][j].draw() + } + } + + let returnData + // If circuit is to be downloaded, download, other wise return dataURL + if (down) { + if (imgType === 'svg') { + const mySerializedSVG = simulationArea.context.getSerializedSvg() // true here, if you need to convert named to numbered entities. + download(`${globalScope.name}.svg`, mySerializedSVG) + } else { + downloadAsImg(globalScope.name, imgType) + } + } else { + returnData = simulationArea.canvas.toDataURL(`image/${imgType}`) + } + + // Restore everything + width = backUpWidth + height = backUpHeight + simulationArea.canvas.width = width + simulationArea.canvas.height = height + backgroundArea.canvas.width = width + backgroundArea.canvas.height = height + globalScope.scale = backUpScale + backgroundArea.context = backUpContextBackground + simulationArea.context = backUpContextSimulation + globalScope.ox = backUpOx + globalScope.oy = backUpOy + + resetup() + + if (!down) return returnData +} + +async function crop(dataURL, w, h) { + //get empty second canvas + var myCanvas = document.createElement('CANVAS') + myCanvas.width = w + myCanvas.height = h + var myContext = myCanvas.getContext('2d') + var myImage + var img = new Image() + return new Promise(function (resolved, rejected) { + img.src = dataURL + img.onload = () => { + myContext.drawImage(img, 0, 0, w, h, 0, 0, w, h) + myContext.save() + + //create a new data URL + myImage = myCanvas.toDataURL('image/jpeg') + resolved(myImage) + } + }) +} + +/** + * Function that is used to save image for display in the website + * @return {JSON} + * @category data + */ +async function generateImageForOnline() { + // Verilog Mode -> Different logic + // Fix aspect ratio to 1.6 + // Ensure image is approximately 700 x 440 + var ratio = 1.6 + if (verilogModeGet()) { + var node = document.getElementsByClassName('CodeMirror')[0] + // var node = document.getElementsByClassName('CodeMirror')[0]; + var prevHeight = $(node).css('height') + var prevWidth = $(node).css('width') + var baseWidth = 500 + var baseHeight = Math.round(baseWidth / ratio) + $(node).css('height', baseHeight) + $(node).css('width', baseWidth) + + var data = await domtoimage.toJpeg(node) + $(node).css('width', prevWidth) + $(node).css('height', prevHeight) + data = await crop(data, baseWidth, baseHeight) + return data + } + + simulationArea.lastSelected = undefined // Unselect any selections + + // Fix aspect ratio to 1.6 + if (width > height * ratio) { + height = width / ratio + } else { + width = height * 1.6 + } + + // Center circuits + globalScope.centerFocus() + + // Ensure image is approximately 700 x 440 + const resolution = Math.min( + 700 / (simulationArea.maxWidth - simulationArea.minWidth), + 440 / (simulationArea.maxHeight - simulationArea.minHeight) + ) + + data = generateImage('jpeg', 'current', false, resolution, false) + + // Restores Focus + globalScope.centerFocus(false) + return data +} +/** + * Function called when you save acircuit online + * @category data + * @exports save + */ +export default async function save() { + if (layoutModeGet()) toggleLayoutMode() + + projectSavedSet(true) + + const data = await generateSaveData() + if (data instanceof Error) return + $('.loadingIcon').fadeIn() + + const projectName = getProjectName() + var imageData = await generateImageForOnline() + + const headers = { + 'Content-Type': 'application/json', + 'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content'), + Authorization: `Token ${getToken('cvt')}`, + } + + if (!window.isUserLoggedIn) { + // user not signed in, save locally temporarily and force user to sign in + localStorage.setItem('recover_login', data) + // Asking user whether they want to login. + if ( + await confirmOption( + 'You have to login to save the project, you will be redirected to the login page.' + ) + ) + window.location.href = '/users/sign_in' + else $('.loadingIcon').fadeOut() + // eslint-disable-next-line camelcase + } else if ([0, undefined, null, '', '0'].includes(window.logixProjectId)) { + // Create new project - this part needs to be improved and optimised + // const form = $('
', { + // action: '/api/v1/simulator/create', + // method: 'post', + // }) + // form.append( + // $('', { + // type: 'hidden', + // name: 'authenticity_token', + // value: $('meta[name="csrf-token"]').attr('content'), + // }) + // ) + // form.append( + // $('', { + // type: 'text', + // name: 'data', + // value: data, + // }) + // ) + // form.append( + // $('', { + // type: 'text', + // name: 'image', + // value: imageData, + // }) + // ) + // form.append( + // $('', { + // type: 'text', + // name: 'name', + // value: projectName, + // }) + // ) + // $('body').append(form) + // form.submit() + + fetch('/api/v1/projects', { + method: 'POST', + headers, + body: JSON.stringify({ + data, + image: imageData, + name: projectName, + }), + }) + .then((response) => { + if (response.ok) { + showMessage( + `We have Created a new project: ${projectName} in our servers.` + ) + $('.loadingIcon').fadeOut() + localStorage.removeItem('recover') + const responseJson = response.json() + responseJson.then((data) => { + UpdateProjectDetail(data) + }) + } + }) + .catch((error) => { + console.error('Error:', error) + }) + } else { + // updates project - this part needs to be improved and optimised + // $.ajax({ + // url: '/api/v1/simulator/update', + // type: 'PATCH', + // contentType: 'application/json', + // beforeSend(xhr) { + // xhr.setRequestHeader( + // 'X-CSRF-Token', + // $('meta[name="csrf-token"]').attr('content') + // ) + // }, + // data: JSON.stringify({ + // data, + // id: logixProjectId, + // image: imageData, + // name: projectName, + // }), + // success(response) { + // showMessage( + // `We have saved your project: ${projectName} in our servers.` + // ) + // $('.loadingIcon').fadeOut() + // localStorage.removeItem('recover') + // }, + // failure(err) { + // showMessage( + // "There was an error, we couldn't save to our servers" + // ) + // $('.loadingIcon').fadeOut() + // }, + // }) + // function getCookie(name) { + // const value = `; ${document.cookie}`; + // const parts = value.split(`; ${name}=`); + // if (parts.length === 2) return parts.pop().split(';').shift(); + // } + + fetch('/api/v1/projects/update_circuit', { + method: 'PATCH', + headers, + body: JSON.stringify({ + data, + id: window.logixProjectId, + image: imageData, + name: projectName, + }), + }) + .then((response) => { + if (response.ok) { + showMessage( + `We have saved your project: ${projectName} in our servers.` + ) + localStorage.removeItem('recover') + } else { + showMessage( + "There was an error, we couldn't save to our servers" + ) + } + $('.loadingIcon').fadeOut() + }) + .catch((error) => { + console.error('Error:', error) + }) + } + + // Restore everything + resetup() +} diff --git a/v0/src/simulator/src/data/saveImage.js b/v0/src/simulator/src/data/saveImage.js new file mode 100644 index 00000000..8e2b813d --- /dev/null +++ b/v0/src/simulator/src/data/saveImage.js @@ -0,0 +1,18 @@ +/** + * Helper function to show prompt to save image + * Options - resolution, image type, view + * @param {Scope=} scope - useless though + * @category data + */ +import { SimulatorStore } from '#/store/SimulatorStore/SimulatorStore' + +/** + * Function called to generate a prompt to save an image + * @category data + * @param {Scope=} - circuit whose image we want + * @exports createSaveAsImgPrompt + */ +export default function createSaveAsImgPrompt(scope = globalScope) { + const simulatorStore = SimulatorStore() + simulatorStore.dialogBox.saveimage_dialog = true +} diff --git a/v0/src/simulator/src/data/undo.js b/v0/src/simulator/src/data/undo.js new file mode 100644 index 00000000..67f22005 --- /dev/null +++ b/v0/src/simulator/src/data/undo.js @@ -0,0 +1,51 @@ +/* eslint-disable import/no-cycle */ +/** + * Function to restore copy from backup + * @param {Scope=} scope - The circuit on which undo is called + * @category data + */ +import { layoutModeGet } from '../layoutMode' +import Scope, { scopeList } from '../circuit' +import { loadScope } from './load' +import { updateRestrictedElementsInScope } from '../restrictedElementDiv' +import { forceResetNodesSet } from '../engine' +/** + * Function called to generate a prompt to save an image + * @param {Scope=} - the circuit in which we want to call undo + * @category data + * @exports undo + */ +export default function undo(scope = globalScope) { + if (layoutModeGet()) return + if (scope.backups.length < 2) return + const backupOx = globalScope.ox + const backupOy = globalScope.oy + const backupScale = globalScope.scale + globalScope.ox = 0 + globalScope.oy = 0 + const tempScope = new Scope(scope.name) + loading = true + const undoData = scope.backups.pop() + scope.history.push(undoData) + scope.backups.length !== 0 && + loadScope( + tempScope, + JSON.parse(scope.backups[scope.backups.length - 1]) + ) + tempScope.backups = scope.backups + tempScope.history = scope.history + tempScope.id = scope.id + tempScope.name = scope.name + tempScope.testbenchData = scope.testbenchData + scopeList[scope.id] = tempScope + globalScope = tempScope + globalScope.ox = backupOx + globalScope.oy = backupOy + globalScope.scale = backupScale + loading = false + forceResetNodesSet(true) + + // Updated restricted elements + updateRestrictedElementsInScope() +} +// for html file diff --git a/v0/src/simulator/src/doc/Circuit2Verilog documentation.md b/v0/src/simulator/src/doc/Circuit2Verilog documentation.md new file mode 100644 index 00000000..42d6b89d --- /dev/null +++ b/v0/src/simulator/src/doc/Circuit2Verilog documentation.md @@ -0,0 +1,70 @@ +## Circuit2Verilog Module + +**Primary Contributors:** + +1. James H - J Yeh, Ph.D. +2. Satvik Ramaprasad + +## Introduction + +This is an experimental module that generates Verilog netlist (structural +Verilog) given the circuit. Currently, the module generates fully functional +Verilog code for basic circuits. For a complex circuit, additional (manual) work +may need to be done in order to make it work. We are continuously improving this +module to work with more and more complex circuits. + +# Algorithm + +The basic algorithm is fairly straightforward. We have the circuit graph in +memory. We just need to convert this graph into Verilog netlist. It is done by +performing a DFS on the circuit graph. The DFS involves the following steps + +1. Creating Verilog wires as and when required +2. Connecting Verilog wires in element instantiations + +## Some background information + +The different sub-circuits form a DAG (Directed Acyclic Graph) or dependency +graph. Each sub-circuit itself (called scope internally) is actually a (cyclic) +graph on its own. Therefore the Verilog generation is done in a 2 step DFS +approach. The first DFS is performed on the dependency graph. The second DFS is +done on an individual sub-circuit (scope). + +## Code/Algorithm workflow + +1. `exportVerilog()` - entry point +2. `exportVerilogScope()` - DFS(1) on Sub Circuits Dependency Graph + 1. Set Verilog Labels for all elements + 2. `generateHeader()` - Generates Module Header + 3. `generateOutputList()` - Output Output List + 4. `generateInputList()` - Generates Input List + 5. `processGraph()` - DFS(2) on individual subcircuit/scope + 1. DFS starts from inputs + 2. Calls `processVerilog()` on all circuit elements (graph nodes) - resolves label names and adds neighbors to DFS stack. + 3. Calls `generateVerilog()` on all circuit elements to get the final Verilog. + 6. Generate Wire initializations + +## Functions + +**Verilog Module Functions:** + +1. `verilog.exportVerilog()` - Entry point +1. `verilog.exportVerilogScope()` - Recursive DFS function on subcircuit graph +1. `verilog.processGraph()` - Iterative DFS function on subcircuit scope +1. `verilog.resetLabels()` - Resets labels in scope +1. `verilog.setLabels()` - Sets labels in scope +1. `verilog.generateHeader()` - Generates Verilog Module Header +1. `verilog.generateInputList()` - Generates Verilog Module Input List +1. `verilog.generateOutputList()` - Generates Verilog Module Output List +1. `sanitizeLabel()` - Sanitizes label for node/wire +1. `verilog.generateNodeName()` - Helper function to resolve node/wire name + +**CircuitElement Functions:** + +These functions can be overridden by derived classes. + +1. `CircuitElement.prototype.processVerilog()` - Graph algorithm to resolve Verilog wire labels +1. `CircuitElement.prototype.verilogName()` - Generate Verilog name +1. `CircuitElement.prototype.generateVerilog()` - Generate final Verilog code +1. `CircuitElement.prototype.verilogType` - Verilog type name +1. `CircuitElement.moduleVerilog` - Custom module Verilog for elements diff --git a/v0/src/simulator/src/doc/images/CircuitVerse Timing Diagram Size Spec.svg b/v0/src/simulator/src/doc/images/CircuitVerse Timing Diagram Size Spec.svg new file mode 100644 index 00000000..d92c7cda --- /dev/null +++ b/v0/src/simulator/src/doc/images/CircuitVerse Timing Diagram Size Spec.svg @@ -0,0 +1,3 @@ + + +
TimeLine
TimeLi...
timeLineHeight
time...
padding
padd...
padding
padd...
padding
padd...
padding
p...
flagLabelWidth
flagLabelWidth
plotHeight
plot...
plotHeight
plot...
cycleWidth
cycleW...
cycleWidth
cycleWidth
waveFormHeight
wave...
waveFormPadding
wave...
waveFormPadding
wave...
plotHeight
plot...
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/v0/src/simulator/src/drag.ts b/v0/src/simulator/src/drag.ts new file mode 100644 index 00000000..2e14abca --- /dev/null +++ b/v0/src/simulator/src/drag.ts @@ -0,0 +1,87 @@ +import interact from 'interactjs' + +interface Position { + x: number + y: number +} + +function updatePosition( + element: HTMLElement, + dx: number, + dy: number, + positions: WeakMap +): void { + if (!element) return // Check if the element is valid + + // If the element does not exist in the positions WeakMap, create it + if (!positions.has(element)) { + positions.set(element, { x: 0, y: 0 }) + } + + // Update the element's x and y position + const currentPosition = positions.get(element) + if (!currentPosition) return // Check if the currentPosition is valid + currentPosition.x += dx + currentPosition.y += dy + + // Apply the new position to the element using the CSS transform property + element.style.transform = `translate(${currentPosition.x}px, ${currentPosition.y}px)` +} + +function disableSelection(element: HTMLElement): void { + element.setAttribute('unselectable', 'on') + element.style.userSelect = 'none' + element.style.webkitUserSelect = 'none' + element.style.MozUserSelect = 'none' + element.style.msUserSelect = 'none' + element.style.OUserSelect = 'none' + element.onselectstart = () => false +} + +/** + * Make an element draggable within a specified container. + * @param {HTMLElement} targetEl - Element that triggers the drag event. + * @param {HTMLElement} DragEl - Element to be dragged. + */ +export function dragging(targetEl: HTMLElement, DragEl: HTMLElement): void { + // WeakMap to store the position of each dragged element + const positions = new WeakMap() + + // Initialize the interact.js library with the draggable element selector + interact(DragEl).draggable({ + // Specify the element that triggers the drag event + allowFrom: targetEl, + // Set up event listeners for the draggable element + listeners: { + // Update the element's position when the move event is triggered + move(event) { + updatePosition( + event.target as HTMLElement, + event.dx, + event.dy, + positions + ) + }, + }, + // Set up modifiers to apply constraints to the draggable element + modifiers: [ + interact.modifiers.restrictRect({ + // Restrict the draggable element within its parent container + restriction: 'body', + }), + ], + }) + + $(DragEl).on('mousedown', () => { + $(`.draggable-panel:not(${DragEl})`).css('z-index', '99') + $(DragEl).css('z-index', '99') + }) + + let panelElements = document.querySelectorAll( + '.elementPanel, .layoutElementPanel, #moduleProperty, #layoutDialog, #verilogEditorPanel, .timing-diagram-panel, .testbench-manual-panel, .quick-btn' + ) + + panelElements.forEach((element) => { + disableSelection(element as HTMLElement) + }) +} diff --git a/v0/src/simulator/src/embed.js b/v0/src/simulator/src/embed.js new file mode 100644 index 00000000..b17e82ba --- /dev/null +++ b/v0/src/simulator/src/embed.js @@ -0,0 +1,132 @@ +// /* eslint-disable import/no-cycle */ +// // Helper functions for when circuit is embedded +// import { scopeList, circuitProperty } from './circuit' +// import simulationArea from './simulationArea' +// import { +// scheduleUpdate, +// wireToBeCheckedSet, +// updateCanvasSet, +// gridUpdateSet, +// } from './engine' +// import { prevPropertyObjGet, prevPropertyObjSet } from './ux' +// import { ZoomIn, ZoomOut } from './listeners' + +// // circuitProperty.toggleFullScreen = toggleFullScreen; +// $(document).ready(() => { +// // Clock features +// $('#clockProperty').append( +// " " +// ) +// $('#clockProperty').append( +// `
Time:
` +// ) +// $('#clockProperty').append( +// `
Clock:
` +// ) + +// // Following codes need to be removed +// $('.objectPropertyAttributeEmbed').on( +// 'change keyup paste click', +// function () { +// scheduleUpdate() +// updateCanvasSet(true) +// wireToBeCheckedSet(1) +// if ( +// simulationArea.lastSelected && +// simulationArea.lastSelected[this.name] +// ) { +// prevPropertyObjSet( +// simulationArea.lastSelected[this.name](this.value) +// ) || prevPropertyObjGet() +// } else { +// circuitProperty[this.name](this.value) +// } +// } +// ) + +// // Following codes need to be removed +// $('.objectPropertyAttributeEmbedChecked').on( +// 'change keyup paste click', +// function () { +// scheduleUpdate() +// updateCanvasSet(true) +// wireToBeCheckedSet(1) +// if ( +// simulationArea.lastSelected && +// simulationArea.lastSelected[this.name] +// ) { +// prevPropertyObjSet( +// simulationArea.lastSelected[this.name](this.value) +// ) || prevPropertyObjGet() +// } else { +// circuitProperty[this.name](this.checked) +// } +// } +// ) + +// $('#zoom-in-embed').on('click', () => ZoomIn()) + +// $('#zoom-out-embed').on('click', () => ZoomOut()) +// }) + +// // Full screen toggle helper function +// function toggleFullScreen(value) { +// if (!getfullscreenelement()) { +// GoInFullscreen(document.documentElement) +// } else { +// GoOutFullscreen() +// } +// } +// // Center focus accordingly +// function exitHandler() { +// setTimeout(() => { +// Object.keys(scopeList).forEach((id) => { +// scopeList[id].centerFocus(true) +// }) +// gridUpdateSet(true) +// scheduleUpdate() +// }, 100) +// } + +// function GoInFullscreen(element) { +// if (element.requestFullscreen) { +// element.requestFullscreen() +// } else if (element.mozRequestFullScreen) { +// element.mozRequestFullScreen() +// } else if (element.webkitRequestFullscreen) { +// element.webkitRequestFullscreen() +// } else if (element.msRequestFullscreen) { +// element.msRequestFullscreen() +// } +// } + +// function GoOutFullscreen() { +// if (document.exitFullscreen) { +// document.exitFullscreen() +// } else if (document.mozCancelFullScreen) { +// document.mozCancelFullScreen() +// } else if (document.webkitExitFullscreen) { +// document.webkitExitFullscreen() +// } else if (document.msExitFullscreen) { +// document.msExitFullscreen() +// } +// } + +// function getfullscreenelement() { +// return ( +// document.fullscreenElement || +// document.webkitFullscreenElement || +// document.mozFullScreenElement || +// document.msFullscreenElement +// ) +// } + +// // Full screen Listeners +// if (document.addEventListener) { +// document.addEventListener('webkitfullscreenchange', exitHandler, false) +// document.addEventListener('mozfullscreenchange', exitHandler, false) +// document.addEventListener('fullscreenchange', exitHandler, false) +// document.addEventListener('MSFullscreenChange', exitHandler, false) +// } diff --git a/v0/src/simulator/src/embedListeners.js b/v0/src/simulator/src/embedListeners.js new file mode 100644 index 00000000..e7cb2d0a --- /dev/null +++ b/v0/src/simulator/src/embedListeners.js @@ -0,0 +1,259 @@ +/* eslint-disable import/no-cycle */ +// Listeners when circuit is embedded +// Refer listeners.js +import simulationArea from './simulationArea' +import { + scheduleUpdate, + update, + updateSelectionsAndPane, + wireToBeCheckedSet, + updatePositionSet, + updateSimulationSet, + updateCanvasSet, + gridUpdateSet, + errorDetectedSet, +} from './engine' +import { changeScale } from './canvasApi' +import { copy, paste } from './events' +import { ZoomIn, ZoomOut } from './listeners' + +var unit = 10 + +export default function startListeners() { + window.addEventListener('keyup', (e) => { + scheduleUpdate(1) + if (e.keyCode == 16) { + simulationArea.shiftDown = false + } + if (e.key == 'Meta' || e.key == 'Control') { + simulationArea.controlDown = false + } + }) + + document + .getElementById('simulationArea') + .addEventListener('mousedown', (e) => { + errorDetectedSet(false) + updateSimulationSet(true) + updatePositionSet(true) + updateCanvasSet(true) + + simulationArea.lastSelected = undefined + simulationArea.selected = false + simulationArea.hover = undefined + var rect = simulationArea.canvas.getBoundingClientRect() + simulationArea.mouseDownRawX = (e.clientX - rect.left) * DPR + simulationArea.mouseDownRawY = (e.clientY - rect.top) * DPR + simulationArea.mouseDownX = + Math.round( + (simulationArea.mouseDownRawX - globalScope.ox) / + globalScope.scale / + unit + ) * unit + simulationArea.mouseDownY = + Math.round( + (simulationArea.mouseDownRawY - globalScope.oy) / + globalScope.scale / + unit + ) * unit + simulationArea.mouseDown = true + simulationArea.oldx = globalScope.ox + simulationArea.oldy = globalScope.oy + + e.preventDefault() + scheduleUpdate(1) + }) + + document + .getElementById('simulationArea') + .addEventListener('mousemove', () => { + var ele = document.getElementById('elementName') + if (globalScope && simulationArea && simulationArea.objectList) { + var { objectList } = simulationArea + objectList = objectList.filter((val) => val !== 'wires') + + for (var i = 0; i < objectList.length; i++) { + for ( + var j = 0; + j < globalScope[objectList[i]].length; + j++ + ) { + if (globalScope[objectList[i]][j].isHover()) { + ele.style.display = 'block' + if (objectList[i] === 'SubCircuit') { + ele.innerHTML = `Subcircuit: ${globalScope.SubCircuit[j].data.name}` + } else { + ele.innerHTML = `CircuitElement: ${objectList[i]}` + } + return + } + } + } + } + + ele.style.display = 'none' + document.getElementById('elementName').innerHTML = '' + }) + + window.addEventListener('mousemove', (e) => { + var rect = simulationArea.canvas.getBoundingClientRect() + simulationArea.mouseRawX = (e.clientX - rect.left) * DPR + simulationArea.mouseRawY = (e.clientY - rect.top) * DPR + simulationArea.mouseXf = + (simulationArea.mouseRawX - globalScope.ox) / globalScope.scale + simulationArea.mouseYf = + (simulationArea.mouseRawY - globalScope.oy) / globalScope.scale + simulationArea.mouseX = Math.round(simulationArea.mouseXf / unit) * unit + simulationArea.mouseY = Math.round(simulationArea.mouseYf / unit) * unit + + updateCanvasSet(true) + if (simulationArea.lastSelected == globalScope.root) { + updateCanvasSet(true) + var fn + fn = function () { + updateSelectionsAndPane() + } + scheduleUpdate(0, 20, fn) + } else { + scheduleUpdate(0, 200) + } + }) + window.addEventListener('keydown', (e) => { + errorDetectedSet(false) + updateSimulationSet(true) + updatePositionSet(true) + + // zoom in (+) + if (e.key == 'Meta' || e.key == 'Control') { + simulationArea.controlDown = true + } + + if ( + simulationArea.controlDown && + (e.keyCode == 187 || e.KeyCode == 171) + ) { + e.preventDefault() + ZoomIn() + } + + // zoom out (-) + if ( + simulationArea.controlDown && + (e.keyCode == 189 || e.Keycode == 173) + ) { + e.preventDefault() + ZoomOut() + } + + if ( + simulationArea.mouseRawX < 0 || + simulationArea.mouseRawY < 0 || + simulationArea.mouseRawX > width || + simulationArea.mouseRawY > height + ) + return + + scheduleUpdate(1) + updateCanvasSet(true) + + if ( + simulationArea.lastSelected && + simulationArea.lastSelected.keyDown + ) { + if ( + e.key.toString().length == 1 || + e.key.toString() == 'Backspace' + ) { + simulationArea.lastSelected.keyDown(e.key.toString()) + return + } + } + if ( + simulationArea.lastSelected && + simulationArea.lastSelected.keyDown2 + ) { + if (e.key.toString().length == 1) { + simulationArea.lastSelected.keyDown2(e.key.toString()) + return + } + } + + // if (simulationArea.lastSelected && simulationArea.lastSelected.keyDown3) { + // if (e.key.toString() != "Backspace" && e.key.toString() != "Delete") { + // simulationArea.lastSelected.keyDown3(e.key.toString()); + // return; + // } + + // } + + if (e.key == 'T' || e.key == 't') { + simulationArea.changeClockTime(prompt('Enter Time:')) + } + }) + document + .getElementById('simulationArea') + .addEventListener('dblclick', (e) => { + scheduleUpdate(2) + if ( + simulationArea.lastSelected && + simulationArea.lastSelected.dblclick !== undefined + ) { + simulationArea.lastSelected.dblclick() + } + }) + + window.addEventListener('mouseup', (e) => { + simulationArea.mouseDown = false + errorDetectedSet(false) + updateSimulationSet(true) + updatePositionSet(true) + updateCanvasSet(true) + gridUpdateSet(true) + wireToBeCheckedSet(1) + + scheduleUpdate(1) + }) + window.addEventListener('mousedown', function (e) { + this.focus() + }) + + document + .getElementById('simulationArea') + .addEventListener('mousewheel', MouseScroll) + document + .getElementById('simulationArea') + .addEventListener('DOMMouseScroll', MouseScroll) + + function MouseScroll(event) { + updateCanvasSet(true) + + event.preventDefault() + var deltaY = event.wheelDelta ? event.wheelDelta : -event.detail + var scrolledUp = deltaY < 0 + var scrolledDown = deltaY > 0 + + if (event.ctrlKey) { + if (scrolledUp && globalScope.scale > 0.5 * DPR) { + changeScale(-0.1 * DPR) + } + if (scrolledDown && globalScope.scale < 4 * DPR) { + changeScale(0.1 * DPR) + } + } else { + if (scrolledUp && globalScope.scale < 4 * DPR) { + changeScale(0.1 * DPR) + } + if (scrolledDown && globalScope.scale > 0.5 * DPR) { + changeScale(-0.1 * DPR) + } + } + + updateCanvasSet(true) + gridUpdateSet(true) + update() // Schedule update not working, this is INEFFICENT + } +} + +var isIe = + navigator.userAgent.toLowerCase().indexOf('msie') != -1 || + navigator.userAgent.toLowerCase().indexOf('trident') != -1 diff --git a/v0/src/simulator/src/engine.js b/v0/src/simulator/src/engine.js new file mode 100644 index 00000000..2f74df6e --- /dev/null +++ b/v0/src/simulator/src/engine.js @@ -0,0 +1,559 @@ +/* eslint-disable import/no-cycle */ +/* eslint-disable no-use-before-define */ +/* eslint-disable no-continue */ +/* eslint-disable no-param-reassign */ +/* eslint-disable no-bitwise */ +import { layoutModeGet, layoutUpdate } from './layoutMode' +import plotArea from './plotArea' +import simulationArea from './simulationArea' +import { dots, canvasMessage, findDimensions, rect2 } from './canvasApi' +import { showProperties, prevPropertyObjGet } from './ux' +import { showError } from './utils' +import miniMapArea from './minimap' +import { resetup } from './setup' +import { verilogModeGet } from './Verilog2CV' + +/** + * Core of the simulation and rendering algorithm. + */ + +/** + * @type {number} engine + * @category engine + */ +var wireToBeChecked = 0 + +/** + * Used to set wireChecked boolean which updates wires in UI if true (or 1). 2 if some problem and it is handled. + * @param {number} param - value of wirechecked + * @category engine + */ +export function wireToBeCheckedSet(param) { + wireToBeChecked = param +} + +/** + * scheduleUpdate() will be called if true + * @type {boolean} + * @category engine + */ +var willBeUpdated = false + +/** + * used to set willBeUpdated variable + * @type {boolean} + * @category engine + * @category engine + */ +export function willBeUpdatedSet(param) { + willBeUpdated = param +} + +/** + * true if we have an element selected and + * is used when we are paning the grid. + * @type {boolean} + * @category engine + */ +var objectSelection = false + +/** + * used to set the value of object selection, + * @param {boolean} param + * @category engine + */ +export function objectSelectionSet(param) { + objectSelection = param +} + +/** + * Flag for updating position + * @type {boolean} + * @category engine + */ +var updatePosition = true + +/** + * used to set the value of updatePosition. + * @param {boolean} param + * @category engine + */ +export function updatePositionSet(param) { + updatePosition = param +} + +/** + * Flag for updating simulation + * @type {boolean} + * @category engine + */ +var updateSimulation = true + +/** + * used to set the value of updateSimulation. + * @param {boolean} param + * @category engine + */ +export function updateSimulationSet(param) { + updateSimulation = param +} +/** + * Flag for rendering + * @type {boolean} + * @category engine + */ +var updateCanvas = true + +/** + * used to set the value of updateCanvas. + * @param {boolean} param + * @category engine + */ +export function updateCanvasSet(param) { + updateCanvas = param +} + +/** + * Flag for updating grid + * @type {boolean} + * @category engine + */ +var gridUpdate = true + +/** + * used to set gridUpdate + * @param {boolean} param + * @category engine + */ +export function gridUpdateSet(param) { + gridUpdate = param +} + +/** + * used to get gridUpdate + * @return {boolean} + * @category engine + */ +export function gridUpdateGet() { + return gridUpdate +} +/** + * Flag for updating grid + * @type {boolean} + * @category engine + */ +var forceResetNodes = true + +/** + * used to set forceResetNodes + * @param {boolean} param + * @category engine + */ +export function forceResetNodesSet(param) { + forceResetNodes = param +} +/** + * Flag for updating grid + * @type {boolean} + * @category engine + */ +var errorDetected = false + +/** + * used to set errorDetected + * @param {boolean} param + * @category engine + */ +export function errorDetectedSet(param) { + errorDetected = param +} + +/** + * used to set errorDetected + * @returns {boolean} errorDetected + * @category engine + */ +export function errorDetectedGet() { + return errorDetected +} + +/** + * details of where and what canvas message has to be shown. + * @type {Object} + * @property {number} x - x cordinate of message + * @property {number} y - x cordinate of message + * @property {number} string - the message + * @category engine + */ +export var canvasMessageData = { + x: undefined, + y: undefined, + string: undefined, +} + +/** + * Flag for updating subCircuits + * @type {boolean} + * @category engine + */ +var updateSubcircuit = true + +/** + * used to set updateSubcircuit + * @param {boolean} param + * @category engine + */ +export function updateSubcircuitSet(param) { + if (updateSubcircuit != param) { + updateSubcircuit = param + return true + } + updateSubcircuit = param + return false +} + +/** + * turn light mode on + * @param {boolean} val -- new value for light mode + * @category engine + */ +export function changeLightMode(val) { + if (!val && lightMode) { + lightMode = false + DPR = window.devicePixelRatio || 1 + globalScope.scale *= DPR + } else if (val && !lightMode) { + lightMode = true + globalScope.scale /= DPR + DPR = 1 + $('#miniMap').fadeOut('fast') + } + resetup() +} + +/** + * Function to render Canvas according th renderupdate order + * @param {Scope} scope - The circuit whose canvas we want to render + * @category engine + */ +export function renderCanvas(scope) { + if (layoutModeGet() || verilogModeGet()) { + // Different Algorithm + return + } + var ctx = simulationArea.context + // Reset canvas + simulationArea.clear() + // Update Grid + if (gridUpdate) { + gridUpdateSet(false) + dots() + } + canvasMessageData = { + x: undefined, + y: undefined, + string: undefined, + } // Globally set in draw fn () + // Render objects + for (let i = 0; i < renderOrder.length; i++) { + for (var j = 0; j < scope[renderOrder[i]].length; j++) { + scope[renderOrder[i]][j].draw() + } + } + // Show any message + if (canvasMessageData.string !== undefined) { + canvasMessage( + ctx, + canvasMessageData.string, + canvasMessageData.x, + canvasMessageData.y + ) + } + // If multiple object selections are going on, show selected area + if (objectSelection) { + ctx.beginPath() + ctx.lineWidth = 2 + ctx.strokeStyle = 'black' + ctx.fillStyle = 'rgba(0,0,0,0.1)' + rect2( + ctx, + simulationArea.mouseDownX, + simulationArea.mouseDownY, + simulationArea.mouseX - simulationArea.mouseDownX, + simulationArea.mouseY - simulationArea.mouseDownY, + 0, + 0, + 'RIGHT' + ) + ctx.stroke() + ctx.fill() + } + if (simulationArea.hover !== undefined) { + simulationArea.canvas.style.cursor = 'pointer' + } else if (simulationArea.mouseDown) { + simulationArea.canvas.style.cursor = 'grabbing' + } else { + simulationArea.canvas.style.cursor = 'default' + } +} + +/** + * Function to move multiple objects and panes window + * deselected using dblclick right now (PR open for esc key) + * @param {Scope=} scope - the circuit in which we are selecting stuff + * @category engine + */ +export function updateSelectionsAndPane(scope = globalScope) { + if (!simulationArea.selected && simulationArea.mouseDown) { + simulationArea.selected = true + simulationArea.lastSelected = scope.root + simulationArea.hover = scope.root + // Selecting multiple objects + if (simulationArea.shiftDown) { + objectSelectionSet(true) + } else if (!embed) { + findDimensions(scope) + miniMapArea.setup() + $('#miniMap').show() + } + } else if ( + simulationArea.lastSelected === scope.root && + simulationArea.mouseDown + ) { + // pane canvas to give an idea of grid moving + if (!objectSelection) { + globalScope.ox = + simulationArea.mouseRawX - + simulationArea.mouseDownRawX + + simulationArea.oldx + globalScope.oy = + simulationArea.mouseRawY - + simulationArea.mouseDownRawY + + simulationArea.oldy + globalScope.ox = Math.round(globalScope.ox) + globalScope.oy = Math.round(globalScope.oy) + gridUpdateSet(true) + if (!embed && !lightMode) miniMapArea.setup() + } else { + // idea: kind of empty + } + } else if (simulationArea.lastSelected === scope.root) { + /* + Select multiple objects by adding them to the array + simulationArea.multipleObjectSelections when we select + using shift + mouse movement to select an area but + not shift + click + */ + simulationArea.lastSelected = undefined + simulationArea.selected = false + simulationArea.hover = undefined + if (objectSelection) { + objectSelectionSet(false) + var x1 = simulationArea.mouseDownX + var x2 = simulationArea.mouseX + var y1 = simulationArea.mouseDownY + var y2 = simulationArea.mouseY + // Sort those four points to make a selection pane + if (x1 > x2) { + const temp = x1 + x1 = x2 + x2 = temp + } + if (y1 > y2) { + const temp = y1 + y1 = y2 + y2 = temp + } + // Select the objects, push them into a list + for (let i = 0; i < updateOrder.length; i++) { + for (var j = 0; j < scope[updateOrder[i]].length; j++) { + var obj = scope[updateOrder[i]][j] + if (simulationArea.multipleObjectSelections.contains(obj)) + continue + var x + var y + if (obj.objectType === 'Node') { + x = obj.absX() + y = obj.absY() + } else if (obj.objectType !== 'Wire') { + x = obj.x + y = obj.y + } else { + continue + } + if (x > x1 && x < x2 && y > y1 && y < y2) { + simulationArea.multipleObjectSelections.push(obj) + } + } + } + } + } +} + +/** + * Main fn that resolves circuit using event driven simulation + * All inputs are added to a scope using scope.addinput() and + * the simulation starts to play. + * @param {Scope=} scope - the circuit we want to simulate + * @param {boolean} resetNodes - boolean to reset all nodes + * @category engine + */ +export function play(scope = globalScope, resetNodes = false) { + if (errorDetected) return // Don't simulate until error is fixed + if (loading === true) return // Don't simulate until loaded + + simulationArea.simulationQueue.reset() + plotArea.setExecutionTime() // Waveform thing + // Reset Nodes if required + if (resetNodes || forceResetNodes) { + scope.reset() + simulationArea.simulationQueue.reset() + forceResetNodesSet(false) + } + + // To store list of circuitselements that have shown contention but kept temporarily + // Mainly to resolve tristate bus issues + simulationArea.contentionPending = [] + // add inputs to the simulation queue + scope.addInputs() + // to check if we have infinite loop in circuit + let stepCount = 0 + let elem + while (!simulationArea.simulationQueue.isEmpty()) { + if (errorDetected) { + simulationArea.simulationQueue.reset() + return + } + elem = simulationArea.simulationQueue.pop() + elem.resolve() + stepCount++ + if (stepCount > 1000000) { + // Cyclic or infinite Circuit Detection + showError( + 'Simulation Stack limit exceeded: maybe due to cyclic paths or contention' + ) + errorDetectedSet(true) + forceResetNodesSet(true) + } + } + // Check for TriState Contentions + if (simulationArea.contentionPending.length) { + showError('Contention at TriState') + forceResetNodesSet(true) + errorDetectedSet(true) + } +} + +/** + * Function to check for any UI update, it is throttled by time + * @param {number=} count - this is used to force update + * @param {number=} time - the time throttling parameter + * @param {function} fn - function to run before updating UI + * @category engine + */ +export function scheduleUpdate(count = 0, time = 100, fn) { + if (lightMode) time *= 5 + var updateFn = layoutModeGet() ? layoutUpdate : update + if (count) { + // Force update + updateFn() + for (let i = 0; i < count; i++) { + setTimeout(updateFn, 10 + 50 * i) + } + } + if (willBeUpdated) return // Throttling + willBeUpdatedSet(true) + // Call a function before update .. + if (fn) { + setTimeout(() => { + fn() + updateFn() + }, time) + } else setTimeout(updateFn, time) +} + +/** + * fn that calls update on everything else. If any change + * is there, it resolves the circuit and draws it again. + * Also updates simulations, selection, minimap, resolves + * circuit and redraws canvas if required. + * @param {Scope=} scope - the circuit to be updated + * @param {boolean=} updateEverything - if true we update the wires, nodes and modules + * @category engine + */ +export function update(scope = globalScope, updateEverything = false) { + willBeUpdatedSet(false) + if (loading === true || layoutModeGet()) return + var updated = false + simulationArea.hover = undefined + // Update wires + if (wireToBeChecked || updateEverything) { + if (wireToBeChecked === 2) + wireToBeChecked = 0 // this required due to timing issues + else wireToBeChecked++ + // WHY IS THIS REQUIRED ???? we are checking inside wire ALSO + // Idea: we can just call length again instead of doing it during loop. + var prevLength = scope.wires.length + for (let i = 0; i < scope.wires.length; i++) { + scope.wires[i].checkConnections() + if (scope.wires.length !== prevLength) { + prevLength-- + i-- + } + } + scheduleUpdate() + } + // Update subcircuits + if (updateSubcircuit || updateEverything) { + for (let i = 0; i < scope.SubCircuit.length; i++) { + scope.SubCircuit[i].reset() + } + updateSubcircuitSet(false) + } + // Update UI position + if (updatePosition || updateEverything) { + for (let i = 0; i < updateOrder.length; i++) { + for (let j = 0; j < scope[updateOrder[i]].length; j++) { + updated |= scope[updateOrder[i]][j].update() + } + } + } + // Updates multiple objectselections and panes window + if (updatePosition || updateEverything) { + updateSelectionsAndPane(scope) + } + // Update MiniMap + if ( + !embed && + simulationArea.mouseDown && + simulationArea.lastSelected && + simulationArea.lastSelected !== globalScope.root + ) { + if (!lightMode) { + $('#miniMap').fadeOut('fast') + } + } + // Run simulation + if (updateSimulation) { + play() + } + // Show properties of selected element + if (!embed && prevPropertyObjGet() !== simulationArea.lastSelected) { + if ( + simulationArea.lastSelected && + simulationArea.lastSelected.objectType !== 'Wire' + ) { + // ideas: why show properties of project in Nodes but not wires? + showProperties(simulationArea.lastSelected) + } else { + // hideProperties(); + } + } + // Draw, render everything + if (updateCanvas) { + renderCanvas(scope) + } + updateSimulationSet(false) + updateCanvas = false + updatePositionSet(false) +} diff --git a/v0/src/simulator/src/eventQueue.js b/v0/src/simulator/src/eventQueue.js new file mode 100644 index 00000000..f40110e1 --- /dev/null +++ b/v0/src/simulator/src/eventQueue.js @@ -0,0 +1,116 @@ +/** + * Event Queue is simply a priority Queue, basic implementation O(n^2). + * @category eventQueue + */ +export default class EventQueue { + constructor(size) { + this.size = size + this.queue = new Array(size) + this.frontIndex = 0 + this.time = 0 + } + + /** + * @param {CircuitElement} obj - the elemnt to be added + * @param {number} delay - the delay in adding an object to queue + */ + add(obj, delay) { + if (obj.queueProperties.inQueue) { + obj.queueProperties.time = + this.time + (delay || obj.propagationDelay) + let i = obj.queueProperties.index + while ( + i > 0 && + obj.queueProperties.time > + this.queue[i - 1].queueProperties.time + ) { + this.swap(i, i - 1) + i-- + } + i = obj.queueProperties.index + while ( + i < this.frontIndex - 1 && + obj.queueProperties.time < + this.queue[i + 1].queueProperties.time + ) { + this.swap(i, i + 1) + i++ + } + return + } + + if (this.frontIndex == this.size) throw 'EventQueue size exceeded' + this.queue[this.frontIndex] = obj + // obj.queueProperties.time=obj.propagationDelay; + obj.queueProperties.time = this.time + (delay || obj.propagationDelay) + obj.queueProperties.index = this.frontIndex + this.frontIndex++ + obj.queueProperties.inQueue = true + let i = obj.queueProperties.index + while ( + i > 0 && + obj.queueProperties.time > this.queue[i - 1].queueProperties.time + ) { + this.swap(i, i - 1) + i-- + } + } + + /** + * To add without any delay. + * @param {CircuitElement} obj - the object to be added + */ + addImmediate(obj) { + this.queue[this.frontIndex] = obj + obj.queueProperties.time = this.time + obj.queueProperties.index = this.frontIndex + obj.queueProperties.inQueue = true + this.frontIndex++ + } + + /** + * Function to swap two objects in queue. + * @param {number} v1 + * @param {number} v2 + */ + swap(v1, v2) { + const obj1 = this.queue[v1] + obj1.queueProperties.index = v2 + + const obj2 = this.queue[v2] + obj2.queueProperties.index = v1 + + this.queue[v1] = obj2 + this.queue[v2] = obj1 + } + + /** + * function to pop element from queue. + */ + pop() { + if (this.isEmpty()) throw 'Queue Empty' + + this.frontIndex-- + const obj = this.queue[this.frontIndex] + this.time = obj.queueProperties.time + obj.queueProperties.inQueue = false + return obj + } + + /** + * function to reset queue. + */ + reset() { + for (let i = 0; i < this.frontIndex; i++) + this.queue[i].queueProperties.inQueue = false + this.time = 0 + this.frontIndex = 0 + } + + /** + * function to check if empty queue. + */ + isEmpty() { + return this.frontIndex == 0 + } +} diff --git a/v0/src/simulator/src/events.js b/v0/src/simulator/src/events.js new file mode 100644 index 00000000..c3c0edca --- /dev/null +++ b/v0/src/simulator/src/events.js @@ -0,0 +1,338 @@ +/* eslint-disable import/no-cycle */ +import Scope, { scopeList, switchCircuit, newCircuit } from './circuit' + +import { loadScope } from './data/load' +import { + scheduleUpdate, + updateSimulationSet, + updateSubcircuitSet, + forceResetNodesSet, +} from './engine' +import { backUp } from './data/backupCircuit' +import { getNextPosition } from './modules' +import { generateId } from './utils' +import simulationArea from './simulationArea' +import { TestbenchData } from './testbench' + +/** + * Helper function to paste + * @param {JSON} copyData - the data to be pasted + * @category events + */ +export function paste(copyData) { + if (copyData === undefined) return + var data = JSON.parse(copyData) + if (!data.logixClipBoardData) return + + var currentScopeId = globalScope.id + for (let i = 0; i < data.scopes.length; i++) { + if (scopeList[data.scopes[i].id] === undefined) { + var isVerilogCircuit = false + var isMainCircuit = false + if (data.scopes[i].verilogMetadata) { + isVerilogCircuit = + data.scopes[i].verilogMetadata.isVerilogCircuit + isMainCircuit = data.scopes[i].verilogMetadata.isMainCircuit + } + var scope = newCircuit( + data.scopes[i].name, + data.scopes[i].id, + isVerilogCircuit, + isMainCircuit + ) + loadScope(scope, data.scopes[i]) + scopeList[data.scopes[i].id] = scope + } + } + + switchCircuit(currentScopeId) + var tempScope = new Scope(globalScope.name, globalScope.id) + var oldOx = globalScope.ox + var oldOy = globalScope.oy + var oldScale = globalScope.scale + loadScope(tempScope, data) + + var prevLength = tempScope.allNodes.length + for (let i = 0; i < tempScope.allNodes.length; i++) { + tempScope.allNodes[i].checkDeleted() + if (tempScope.allNodes.length != prevLength) { + prevLength-- + i-- + } + } + + var approxX = 0 + var approxY = 0 + var count = 0 + + for (let i = 0; i < updateOrder.length; i++) { + for (let j = 0; j < tempScope[updateOrder[i]].length; j++) { + const obj = tempScope[updateOrder[i]][j] + obj.updateScope(globalScope) + if (obj.objectType != 'Wire') { + approxX += obj.x + approxY += obj.y + count++ + } + } + } + + for (let j = 0; j < tempScope.CircuitElement.length; j++) { + const obj = tempScope.CircuitElement[j] + obj.updateScope(globalScope) + } + + approxX /= count + approxY /= count + + approxX = Math.round(approxX / 10) * 10 + approxY = Math.round(approxY / 10) * 10 + + for (let i = 0; i < updateOrder.length; i++) { + for (let j = 0; j < tempScope[updateOrder[i]].length; j++) { + const obj = tempScope[updateOrder[i]][j] + if (obj.objectType !== 'Wire') { + obj.x += simulationArea.mouseX - approxX + obj.y += simulationArea.mouseY - approxY + } + } + } + + Object.keys(tempScope).forEach((l) => { + if ( + tempScope[l] instanceof Array && + l !== 'objects' && + l !== 'CircuitElement' + ) { + globalScope[l].extend(tempScope[l]) + } + }) + for (let i = 0; i < tempScope.Input.length; i++) { + tempScope.Input[i].layoutProperties.y = getNextPosition(0, globalScope) + tempScope.Input[i].layoutProperties.id = generateId() + } + for (let i = 0; i < tempScope.Output.length; i++) { + tempScope.Output[i].layoutProperties.x = globalScope.layout.width + tempScope.Output[i].layoutProperties.id = generateId() + tempScope.Output[i].layoutProperties.y = getNextPosition( + globalScope.layout.width, + globalScope + ) + } + var canvasUpdate = true + updateSimulationSet(true) + updateSubcircuitSet(true) + scheduleUpdate() + globalScope.ox = oldOx + globalScope.oy = oldOy + globalScope.scale = oldScale + + forceResetNodesSet(true) +} +/** + * Helper function for cut + * @param {JSON} copyList - The selected elements + * @category events + */ +export function cut(copyList) { + if (copyList.length === 0) return + var tempScope = new Scope(globalScope.name, globalScope.id) + var oldOx = globalScope.ox + var oldOy = globalScope.oy + var oldScale = globalScope.scale + d = backUp(globalScope) + loadScope(tempScope, d) + scopeList[tempScope.id] = tempScope + + for (let i = 0; i < copyList.length; i++) { + const obj = copyList[i] + if (obj.objectType === 'Node') obj.objectType = 'allNodes' + for (let j = 0; j < tempScope[obj.objectType].length; j++) { + if ( + tempScope[obj.objectType][j].x === obj.x && + tempScope[obj.objectType][j].y === obj.y && + (obj.objectType != 'Node' || obj.type === 2) + ) { + tempScope[obj.objectType][j].delete() + break + } + } + } + tempScope.backups = globalScope.backups + for (let i = 0; i < updateOrder.length; i++) { + let prevLength = globalScope[updateOrder[i]].length // LOL length of list will reduce automatically when deletion starts + for (let j = 0; j < globalScope[updateOrder[i]].length; j++) { + const obj = globalScope[updateOrder[i]][j] + if (obj.objectType != 'Wire') { + // }&&obj.objectType!='CircuitElement'){//}&&(obj.objectType!='Node'||obj.type==2)){ + if (!copyList.contains(globalScope[updateOrder[i]][j])) { + globalScope[updateOrder[i]][j].cleanDelete() + } + } + + if (globalScope[updateOrder[i]].length != prevLength) { + prevLength-- + j-- + } + } + } + + var prevLength = globalScope.wires.length + for (let i = 0; i < globalScope.wires.length; i++) { + globalScope.wires[i].checkConnections() + if (globalScope.wires.length != prevLength) { + prevLength-- + i-- + } + } + + updateSimulationSet(true) + + var data = backUp(globalScope) + data.logixClipBoardData = true + var dependencyList = globalScope.getDependencies() + data.dependencies = {} + Object.keys(dependencyList).forEach((dependency) => { + data.dependencies[dependency] = backUp(scopeList[dependency]) + }) + data.logixClipBoardData = true + data = JSON.stringify(data) + + simulationArea.multipleObjectSelections = [] // copyList.slice(); + simulationArea.copyList = [] // copyList.slice(); + var canvasUpdate = true + updateSimulationSet(true) + globalScope = tempScope + scheduleUpdate() + globalScope.ox = oldOx + globalScope.oy = oldOy + globalScope.scale = oldScale + forceResetNodesSet(true) + // eslint-disable-next-line consistent-return + return data +} +/** + * Helper function for copy + * @param {JSON} copyList - The data to copied + * @param {boolean} cutflag - flase if we want to copy + * @category events + */ +export function copy(copyList, cutflag = false) { + if (copyList.length === 0) return + var tempScope = new Scope(globalScope.name, globalScope.id) + var oldOx = globalScope.ox + var oldOy = globalScope.oy + var oldScale = globalScope.scale + var d = backUp(globalScope) + const oldTestbenchData = globalScope.testbenchData + + loadScope(tempScope, d) + scopeList[tempScope.id] = tempScope + + if (cutflag) { + for (let i = 0; i < copyList.length; i++) { + const obj = copyList[i] + if (obj.objectType === 'Node') obj.objectType = 'allNodes' + for (let j = 0; j < tempScope[obj.objectType].length; j++) { + if ( + tempScope[obj.objectType][j].x === obj.x && + tempScope[obj.objectType][j].y === obj.y && + (obj.objectType != 'Node' || obj.type === 2) + ) { + tempScope[obj.objectType][j].delete() + break + } + } + } + } + tempScope.backups = globalScope.backups + for (let i = 0; i < updateOrder.length; i++) { + let prevLength = globalScope[updateOrder[i]].length // LOL length of list will reduce automatically when deletion starts + for (let j = 0; j < globalScope[updateOrder[i]].length; j++) { + const obj = globalScope[updateOrder[i]][j] + if (obj.objectType != 'Wire') { + // }&&obj.objectType!='CircuitElement'){//}&&(obj.objectType!='Node'||obj.type==2)){ + if (!copyList.contains(globalScope[updateOrder[i]][j])) { + globalScope[updateOrder[i]][j].cleanDelete() + } + } + + if (globalScope[updateOrder[i]].length != prevLength) { + prevLength-- + j-- + } + } + } + + var prevLength = globalScope.wires.length + for (let i = 0; i < globalScope.wires.length; i++) { + globalScope.wires[i].checkConnections() + if (globalScope.wires.length != prevLength) { + prevLength-- + i-- + } + } + + updateSimulationSet(true) + + var data = backUp(globalScope) + data.scopes = [] + var dependencyList = {} + var requiredDependencies = globalScope.getDependencies() + var completed = {} + Object.keys(scopeList).forEach((id) => { + dependencyList[id] = scopeList[id].getDependencies() + }) + function saveScope(id) { + if (completed[id]) return + for (let i = 0; i < dependencyList[id].length; i++) { + saveScope(dependencyList[id][i]) + } + completed[id] = true + data.scopes.push(backUp(scopeList[id])) + } + for (let i = 0; i < requiredDependencies.length; i++) { + saveScope(requiredDependencies[i]) + } + data.logixClipBoardData = true + data.testbenchData = undefined // Don't copy testbench data + data = JSON.stringify(data) + simulationArea.multipleObjectSelections = [] // copyList.slice(); + simulationArea.copyList = [] // copyList.slice(); + var canvasUpdate = true + updateSimulationSet(true) + globalScope = tempScope + scheduleUpdate() + globalScope.ox = oldOx + globalScope.oy = oldOy + globalScope.scale = oldScale + // Restore testbench data + if (oldTestbenchData) { + globalScope.testbenchData = new TestbenchData( + oldTestbenchData.testData, + oldTestbenchData.currentGroup, + oldTestbenchData.currentCase + ) + } + + forceResetNodesSet(true) + // needs to be fixed + // eslint-disable-next-line consistent-return + return data +} + +/** + * Function selects all the elements from the scope + * @category events + */ +export function selectAll(scope = globalScope) { + moduleList.forEach((val, _, __) => { + if (scope.hasOwnProperty(val)) { + simulationArea.multipleObjectSelections.push(...scope[val]) + } + }) + + if (scope.nodes) { + simulationArea.multipleObjectSelections.push(...scope.nodes) + } +} diff --git a/v0/src/simulator/src/file/Open.js b/v0/src/simulator/src/file/Open.js new file mode 100644 index 00000000..c371be16 --- /dev/null +++ b/v0/src/simulator/src/file/Open.js @@ -0,0 +1,96 @@ +/* **************************************************************************************************** */ +/* Implemented in ImportProject.vue Kept for reference in case any bugs occur */ +/* TODO: Remove this file after testing */ +/* **************************************************************************************************** */ + +// import load from '../data/load' +// import { generateSaveData } from '../data/save' +// import { escapeHtml } from '../ux' + +// const scopeSchema = [ +// 'layout', +// 'verilogMetadata', +// 'allNodes', +// 'id', +// 'name', +// 'restrictedCircuitElementsUsed', +// 'nodes', +// ] +// const JSONSchema = [ +// 'name', +// 'timePeriod', +// 'clockEnabled', +// 'projectId', +// 'focussedCircuit', +// 'orderedTabs', +// 'scopes', +// ] + +// var circuitData = null +// const GetDialogData = () => +// '

Browse files or Drag & Drop files here
No file chosen!!
' + +// const ImportCircuitFiles = () => { +// $('#ImportCircuitFilesDialog').empty() +// $('#ImportCircuitFilesDialog').append(GetDialogData()) +// $('#ImportCircuitFilesDialog').dialog({ +// resizable: false, +// close() { +// if (circuitData) load(circuitData) +// }, +// buttons: [ +// { +// text: 'Close', +// click() { +// $(this).dialog('close') +// }, +// }, +// ], +// }) +// $('#ImportCircuitFilesDialog').focus() + +// function ValidateData(fileData) { +// try { +// const parsedFileDate = JSON.parse(fileData) +// if ( +// JSON.stringify(Object.keys(parsedFileDate)) !== +// JSON.stringify(JSONSchema) +// ) +// throw new Error('Invalid JSON data') +// parsedFileDate.scopes.forEach((scope) => { +// const keys = Object.keys(scope) // get scope keys +// scopeSchema.forEach((key) => { +// if (!keys.includes(key)) +// throw new Error('Invalid Scope data') +// }) +// }) +// load(parsedFileDate) +// return true +// } catch (error) { +// $('#message').text('Invalid file format') +// return false +// } +// } + +// function receivedText(e) { +// // backUp data +// const backUp = JSON.parse( +// generateSaveData(escapeHtml($('#projectName').text()).trim(), false) +// ) +// const valid = ValidateData(e.target.result) // validate data +// if (!valid) { +// // fallback +// load(backUp) +// } else { +// $('#ImportCircuitFilesDialog').dialog('close') +// } +// } +// $('#CircuitDataFile').on('change', (event) => { +// var File = event.target.files[0] +// var fr = new FileReader() +// fr.onload = receivedText +// fr.readAsText(File) +// }) +// } + +// export default ImportCircuitFiles diff --git a/v0/src/simulator/src/file/SaveAs.js b/v0/src/simulator/src/file/SaveAs.js new file mode 100644 index 00000000..d94696a7 --- /dev/null +++ b/v0/src/simulator/src/file/SaveAs.js @@ -0,0 +1,54 @@ +/* **************************************************************************************************** */ +/* Implemented in ExportProject.vue Kept for reference in case any bugs occur */ +/* TODO: Remove this file after testing */ +/* **************************************************************************************************** */ + +// import { download, generateSaveData } from '../data/save' +// import { escapeHtml } from '../ux' + +// const GetDialogData = () => { +// const fileName = `${$('#projectName').text().trim()}.cv` +// const Input = document.createElement('input') +// Input.type = 'text' +// Input.name = 'fileName' +// Input.setAttribute('placeholder', fileName) +// Input.id = 'filename' +// Input.defaultValue = fileName +// const Label = document.createElement('label') +// Label.setAttribute('for', 'filename') +// Label.textContent = 'File Name' +// const container = document.createElement('div') +// container.appendChild(Label) +// container.appendChild(Input) +// return container +// } + +// /** +// * To Export Circuit Files +// */ +// const ExportCircuitFiles = () => { +// $('#ExportCircuitFilesDialog').empty() +// $('#ExportCircuitFilesDialog').append(GetDialogData()) +// $('#ExportCircuitFilesDialog').dialog({ +// resizable: false, +// buttons: [ +// { +// text: 'Save', +// click() { +// var fileName = +// escapeHtml($('#filename').val()) || 'untitled' +// const circuitData = generateSaveData( +// fileName.split('.')[0], +// false +// ) +// fileName = `${fileName.split('.')[0]}.cv` +// download(fileName, circuitData) +// $(this).dialog('close') +// }, +// }, +// ], +// }) +// $('#ExportCircuitFilesDialog').focus() +// } + +// export default ExportCircuitFiles diff --git a/v0/src/simulator/src/hotkey_binder/defaultKeys.js b/v0/src/simulator/src/hotkey_binder/defaultKeys.js new file mode 100644 index 00000000..0529ce5f --- /dev/null +++ b/v0/src/simulator/src/hotkey_binder/defaultKeys.js @@ -0,0 +1,29 @@ +/**Add more elements here, along with a valid value for key + * Elements keys must have the same name as their ID + **/ + +export const defaultKeys = { + 'New Circuit': 'Shift + N', + 'Save Online': 'Ctrl + S', + 'Save Offline': 'Ctrl + Alt + S', + 'Download as Image': 'Ctrl + D', + 'Open Offline': 'Ctrl + O', + 'Insert Sub-circuit': 'Shift + C', + 'Combinational Analysis': 'Shift + A', + // "Start Plot": "Ctrl + P", + 'Direction Up': 'Up', + 'Direction Down': 'Down', + 'Direction Left': 'Left', + 'Direction Right': 'Right', + 'Insert Label': 'Ctrl + L', + 'Label Direction Up': 'Alt + Up', + 'Label Direction Down': 'Alt + Down', + 'Label Direction Left': 'Alt + Left', + 'Label Direction Right': 'Alt + Right', + 'Move Element Up': 'Shift + Up', + 'Move Element Down': 'Shift + Down', + 'Move Element Left': 'Shift + Left', + 'Move Element Right': 'Shift + Right', + 'Hotkey Preference': 'F8', + 'Open Documentation': 'F1', +} diff --git a/v0/src/simulator/src/hotkey_binder/documentation.txt b/v0/src/simulator/src/hotkey_binder/documentation.txt new file mode 100644 index 00000000..f4af3be9 --- /dev/null +++ b/v0/src/simulator/src/hotkey_binder/documentation.txt @@ -0,0 +1,45 @@ +The feature includes two libraries: +1. shortcuts.plugin.js: It consists of a few function(s) + a. Add + b. Remove + c. RemoveAll + +a. function Add takes 3 arguments: + shortcut_combination: eg "Ctrl + X" + callback: reference to a function, which will be invoked when the above combination is detected + opt: some optional parameters such as propagation, type of keyevent, etc + +b. function Remove: removes a particular shortcut provided in the param. +c. function RemoveAll: takes no param removes all shortcuts. + +supported format: + * Modifier + * Modifier + key + * Single key + +2. normalizer.plugin.js: is used in keyBinder.js (line 73), to detect the shortcut_combination in string format + which is then passed to the shortcuts plugin. + +MAIN FILEs: + keyBinder.js is the controller, it performs operation that are defined in other files (model, view) + It consist of the keydown listener which detects while user is customizing the keys from the preference panel, + * checks for restrictions + * warns override + * restricts non-overridable combinations + * Others + +USAGE: + TO ADD SHORCUTS: + #1 add the shortcut in the defaultKeys.json file + #2 Assign the callback in addShorcut.js: + * the callback should be a func reference,if function to be invoked is already defined in the codebase just set it as it is. (for some func reference may not work, refer next step) + + *for callback not present in the codebase, or is present but doesn't work as intended, defined the function that will invoke the required callback in action.js, note that callback must be func reference hence define it as a higher-order func if necessary in action.js + TO REMOVE SHORCUTS: + #1. Remove the option from defaultKeys.json + #2. Remove the case from addShortcut.js + RESTRICTION: + To add restriction to any combination, mention it in utils.js -> checkRestricted func, which contains an array that consists of restricted combination, push to the array. + +*The feature make uses of localstorage for persistence, only defaultkeys else userkeys will be present in localstorage not both. +On load IFFE func on keyBinder.js (line 111) will check for userKeys, if found it it will be set, else defaultkeys will be set. \ No newline at end of file diff --git a/v0/src/simulator/src/hotkey_binder/keyBinder.js b/v0/src/simulator/src/hotkey_binder/keyBinder.js new file mode 100644 index 00000000..f4653fb7 --- /dev/null +++ b/v0/src/simulator/src/hotkey_binder/keyBinder.js @@ -0,0 +1,151 @@ +// import { +// editPanel, +// heading, +// markUp, +// closeEdit, +// submit, +// updateHTML, +// override, +// } from './view/panel.ui' +// import { setDefault, checkUpdate, addKeys, warnOverride } from './model/actions' +// import { KeyCode } from './model/normalize/normalizer.plugin.js' +// import { checkRestricted } from './model/utils.js' +// import { SimulatorStore } from '#/store/SimulatorStore/SimulatorStore' + +//** keyBinder dialog */ +// export function keyBinder() { + // const simulatorStore = SimulatorStore() + // simulatorStore.dialogBox.customshortcut_dialog = true + // $('#customShortcutDialog').append(editPanel) + // $('#customShortcutDialog').append(heading) + // $('#customShortcutDialog').append(markUp) + // $('#customShortcut').on('click', () => { + // closeEdit() + // $('#customShortcutDialog').dialog({ + // resizable: false, + // buttons: [ + // { + // text: 'Reset to default', + // click: () => { + // if ( + // confirm( + // 'Remove all custom keys & set the default keys?' + // ) + // ) + // setDefault() + // }, + // id: 'resetDefault', + // }, + // { + // text: 'Save', + // click: () => { + // submit() + // $('#customShortcutDialog').dialog('close') + // }, + // id: 'submitBtn', + // }, + // ], + // }) + // $('#customShortcutDialog').css('display', 'flex') + // }) + + // //** targetPref is assigned to the target key option to be edited */ + // let targetPref = null + // $('#preference').on('click', (e) => { + // $('#pressedKeys').text('') + // $('#warning').text('') + // $('#edit').css('border', 'none') + // $('#edit').css('display', 'block') + // $($('#edit')).focus() + // ;[, targetPref] = e.target.closest('div').children + // }) + + // //*** Modifiers restriction enabled here */ + // //*** below fn works in the edit panel where user enters key combo, + // //*** responsible for checking duplicate entries, overriding entries, checking restricted keys, arranging keys in + // //*** proper order, validating key combos */ + + // $('#edit').keydown((e) => { + // e = e || window.event + // e.stopPropagation() + // e.preventDefault() + // var k = KeyCode + // let modifiers = ['CTRL', 'ALT', 'SHIFT', 'META'] + // $('#edit').css('animation', 'none') + // $('#warning').text('') + // if (e.keyCode === 27) closeEdit() + // if (e.keyCode === 13) { + // if ($('#pressedKeys').text() === '') { + // $('#warning').text('Please enter some key(s)') + // $('#edit').css('animation', 'shake .3s linear') + // return + // } + // if (!checkRestricted($('#pressedKeys').text())) { + // override($('#pressedKeys').text()) + // targetPref.innerText = $('#pressedKeys').text() + // $('#pressedKeys').text('') + // $('#edit').css('display', 'none') + // } else { + // $('#warning').text('Please enter different key(s).') + // $('#edit').css('animation', 'shake .3s linear') + // $('#pressedKeys').text('') + // } + // } + // const currentKey = + // k.hot_key(k.translate_event(e)).split('+').join(' + ') !== 'Enter' + // ? k.hot_key(k.translate_event(e)).split('+').join(' + ') + // : '' + // if ( + // $('#pressedKeys').text().split(' + ').length === 2 && + // !modifiers.includes(currentKey) && + // modifiers.includes($('#pressedKeys').text().split(' + ')[1]) + // ) { + // $('#pressedKeys').append(` + ${currentKey}`) + // } else if (modifiers.includes($('#pressedKeys').text())) { + // modifiers = modifiers.filter( + // (mod) => mod === $('#pressedKeys').text() + // ) + // if (!modifiers.includes(currentKey)) { + // $('#pressedKeys').append(` + ${currentKey}`) + // } + // } else { + // $('#pressedKeys').text('') + // $('#pressedKeys').text(currentKey) + // } + // if (!$('#pressedKeys').text()) { + // $('#pressedKeys').text(currentKey) + // } + // if ( + // ($('#pressedKeys').text().split(' + ').length === 2 && + // ['Ctrl', 'Meta'].includes( + // $('#pressedKeys').text().split(' + ')[1] + // )) || + // ($('#pressedKeys').text().split(' + ')[0] === 'Alt' && + // $('#pressedKeys').text().split(' + ')[1] === 'Shift') + // ) { + // $('#pressedKeys').text( + // $('#pressedKeys').text().split(' + ').reverse().join(' + ') + // ) + // } + // warnOverride($('#pressedKeys').text(), targetPref) + // if (checkRestricted($('#pressedKeys').text())) { + // $('#warning').text( + // 'The above combination is a system default shortcut & cannot be set.' + // ) + // } + // }) + + // //** if users closes hotkey dialog by making changes & not saving them, fn will fallback to previous state */ + + // $('div#customShortcutDialog').on('dialogclose', function (event) { + // if (localStorage.userKeys) { + // updateHTML('user') + // } else updateHTML('default') + // }) + + // // Set up shortcuts + // if (localStorage.userKeys) { + // checkUpdate() + // addKeys('user') + // } else setDefault() +// } diff --git a/v0/src/simulator/src/hotkey_binder/model/actions.js b/v0/src/simulator/src/hotkey_binder/model/actions.js new file mode 100644 index 00000000..102209ee --- /dev/null +++ b/v0/src/simulator/src/hotkey_binder/model/actions.js @@ -0,0 +1,205 @@ +import { defaultKeys } from '../defaultKeys' +import { addShortcut } from './addShortcut' +import { updateHTML } from '../view/panel.ui' +import simulationArea from '../../simulationArea' +import { + scheduleUpdate, + update, + updateSelectionsAndPane, + wireToBeCheckedSet, + updatePositionSet, + updateSimulationSet, + updateCanvasSet, + gridUpdateSet, + errorDetectedSet, +} from '../../engine' + +import { getOS } from './utils.js' +import { shortcut } from './shortcuts.plugin.js' +/** + * Function used to add or change keys user or default + * grabs the keycombo from localstorage & + * calls the addShortcut function in a loop to bind them + * @param {string} mode - user custom keys or default keys + */ +export const addKeys = (mode) => { + shortcut.removeAll() + if (mode === 'user') { + localStorage.removeItem('defaultKeys') + let userKeys = localStorage.get('userKeys') + for (let pref in userKeys) { + let key = userKeys[pref] + key = key.split(' ').join('') + addShortcut(key, pref) + } + updateHTML('user') + } else if (mode == 'default') { + if (localStorage.userKeys) localStorage.removeItem('userKeys') + let defaultKeys = localStorage.get('defaultKeys') + for (let pref in defaultKeys) { + let key = defaultKeys[pref] + key = key.split(' ').join('') + addShortcut(key, pref) + } + updateHTML('default') + } +} +/** + * Function used to check if new keys are added, adds missing keys if added + */ +export const checkUpdate = () => { + const userK = localStorage.get('userKeys') + if (Object.size(userK) !== Object.size(defaultKeys)) { + for (const [key, value] of Object.entries(defaultKeys)) { + if (!Object.keys(userK).includes(key)) { + userK[key] = value + } + } + localStorage.set('userKeys', userK) + } else { + return + } +} +/** + * Function used to set userKeys, grabs the keycombo from the panel UI + * sets it to the localStorage & cals addKeys + * removes the defaultkeys from localStorage + */ +export const setUserKeys = () => { + if (localStorage.defaultKeys) localStorage.removeItem('defaultKeys') + let userKeys = {} + let x = 0 + while ($('#preference').children()[x]) { + userKeys[ + $('#preference').children()[x].children[1].children[0].innerText + ] = $('#preference').children()[x].children[1].children[1].innerText + x++ + } + localStorage.set('userKeys', userKeys) + addKeys('user') +} +/** + * Function used to set defaultKeys, grabs the keycombo from the defaultkeys metadata + * sets it to the localStorage & cals addKeys + * removes the userkeys from localStorage if present + * also checks for OS type + */ +export const setDefault = () => { + if (localStorage.userKeys) localStorage.removeItem('userKeys') + if (getOS() === 'MacOS') { + const macDefaultKeys = {} + for (let [key, value] of Object.entries(defaultKeys)) { + if (value.split(' + ')[0] == 'Ctrl'); + macDefaultKeys[key] = + value.split(' + ')[0] == 'Ctrl' + ? value.replace('Ctrl', 'Meta') + : value + localStorage.set('defaultKeys', macDefaultKeys) + } + } else { + localStorage.set('defaultKeys', defaultKeys) //TODO add a confirmation alert + } + addKeys('default') +} +/** + * function to check if user entered keys are already assigned to other key + * gives a warning message if keys already assigned + * @param {string} combo the key combo + * @param {string} target the target option of the panel + */ +export const warnOverride = (combo, target, warning) => { + let x = 0 + while ($('#preference').children()[x]) { + if ( + $('#preference').children()[x].children[1].children[1].innerText === + combo && + $('#preference').children()[x].children[1].children[0].innerText !== + target.previousElementSibling.innerText + ) { + const assignee = + $('#preference').children()[x].children[1].children[0].innerText + // $('#warning').text( + // `This key(s) is already assigned to: ${assignee}, press Enter to override.` + // ) + warning.value = `This key(s) is already assigned to: ${assignee}, press Enter to override.` + $('#edit').css('border', '1.5px solid #dc5656') + return + } else { + $('#edit').css('border', 'none') + } + x++ + } +} + +export const elementDirection = (direct) => () => { + if (simulationArea.lastSelected) { + simulationArea.lastSelected.newDirection(direct.toUpperCase()) + $("select[name |= 'newDirection']").val(direct.toUpperCase()) + updateSystem() + } +} + +export const labelDirection = (direct) => () => { + if ( + simulationArea.lastSelected && + !simulationArea.lastSelected.labelDirectionFixed + ) { + simulationArea.lastSelected.labelDirection = direct.toUpperCase() + $("select[name |= 'newLabelDirection']").val(direct.toUpperCase()) + updateSystem() + } +} + +export const insertLabel = () => { + if (simulationArea.lastSelected) { + $("input[name |= 'setLabel']").focus() + $("input[name |= 'setLabel']").val().length + ? null + : $("input[name |= 'setLabel']").val('Untitled') + $("input[name |= 'setLabel']").select() + updateSystem() + } +} + +export const moveElement = (direct) => () => { + if (simulationArea.lastSelected) { + switch (direct) { + case 'up': + simulationArea.lastSelected.y -= 10 + break + case 'down': + simulationArea.lastSelected.y += 10 + break + case 'left': + simulationArea.lastSelected.x -= 10 + break + case 'right': + simulationArea.lastSelected.x += 10 + break + } + updateSystem() + } +} + +export const openHotkey = () => $('#customShortcut').trigger('click') + +export const createNewCircuitScopeCall = () => + $('#createNewCircuitScope').trigger('click') // TODO: remove later + +export const openDocumentation = () => { + if ( + simulationArea.lastSelected == undefined || + simulationArea.lastSelected.helplink == undefined + ) { + // didn't select any element or documentation not found + window.open('https://docs.circuitverse.org/', '_blank') + } else { + window.open(simulationArea.lastSelected.helplink, '_blank') + } +} + +function updateSystem() { + updateCanvasSet(true) + wireToBeCheckedSet(1) + scheduleUpdate(1) +} diff --git a/v0/src/simulator/src/hotkey_binder/model/addShortcut.js b/v0/src/simulator/src/hotkey_binder/model/addShortcut.js new file mode 100644 index 00000000..27834ab1 --- /dev/null +++ b/v0/src/simulator/src/hotkey_binder/model/addShortcut.js @@ -0,0 +1,102 @@ +// import { shortcut } from './Shortcuts.plugin'; +// import createSaveAsImgPrompt from '../../data/saveImage'; +//Assign the callback func for the keymap here +import { + createNewCircuitScopeCall, + elementDirection, + insertLabel, + labelDirection, + openHotkey, + moveElement, + openDocumentation, +} from './actions' +import save from '../../data/save' +import { saveOffline, openOffline } from '../../data/project' +import createSaveAsImgPrompt from '../../data/saveImage' +import { createSubCircuitPrompt } from '../../subcircuit' +import { createCombinationalAnalysisPrompt } from '../../combinationalAnalysis' +import { shortcut } from './shortcuts.plugin.js' + +export const addShortcut = (keys, action) => { + let callback + switch (action) { + case 'New Circuit': + callback = createNewCircuitScopeCall // TODO: directly call rather than using dom click + break + case 'Save Online': + callback = save + break + case 'Save Offline': + callback = saveOffline + break + case 'Download as Image': + callback = createSaveAsImgPrompt + break + case 'Open Offline': + callback = openOffline + break + case 'Insert Sub-circuit': + callback = createSubCircuitPrompt + break + case 'Combinational Analysis': + callback = createCombinationalAnalysisPrompt + break //bug + // case "Start Plot": + // callback = startPlot; + // break; + case 'Direction Up': + callback = elementDirection('up') + break + case 'Direction Down': + callback = elementDirection('down') + break + case 'Direction Left': + callback = elementDirection('left') + break + case 'Direction Right': + callback = elementDirection('right') + break + case 'Insert Label': + callback = insertLabel + break + case 'Label Direction Up': + callback = labelDirection('up') + break + case 'Label Direction Down': + callback = labelDirection('down') + break + case 'Label Direction Left': + callback = labelDirection('left') + break + case 'Label Direction Right': + callback = labelDirection('right') + break + case 'Move Element Up': + callback = moveElement('up') + break + case 'Move Element Down': + callback = moveElement('down') + break + case 'Move Element Left': + callback = moveElement('left') + break + case 'Move Element Right': + callback = moveElement('right') + break + case 'Hotkey Preference': + callback = openHotkey + break + case 'Open Documentation': + callback = openDocumentation + break + default: + callback = () => console.log('No shortcut found..') + break + } + shortcut.add(keys, callback, { + type: 'keydown', + propagate: false, + target: document, + disable_in_input: true, + }) +} diff --git a/v0/src/simulator/src/hotkey_binder/model/normalize/normalizer.plugin.js b/v0/src/simulator/src/hotkey_binder/model/normalize/normalizer.plugin.js new file mode 100644 index 00000000..cc9a7def --- /dev/null +++ b/v0/src/simulator/src/hotkey_binder/model/normalize/normalizer.plugin.js @@ -0,0 +1,389 @@ +/** + * This plugin has been modified to support metakeys + */ + +/* + * Library to normalize key codes across browsers. This works with keydown + * events; keypress events are not fired for all keys, and the codes are + * different for them. It returns an object with the following fields: + * { int code, bool shift, bool alt, bool ctrl }. The normalized keycodes + * obey the following rules: + * + * For alphabetic characters, the ASCII code of the uppercase version + * + * For codes that are identical across all browsers (this includes all + * modifiers, esc, delete, arrows, etc.), the common keycode + * + * For numeric keypad keys, the value returned by numkey(). + * (Usually 96 + the number) + * + * For symbols, the ASCII code of the character that appears when shift + * is not held down, EXCEPT for '" => 222 (conflicts with right-arrow/pagedown), + * .> => 190 (conflicts with Delete) and `~ => 126 (conflicts with Num0). + * + * Basic usage: + * document.onkeydown = function(e) { + * do_something_with(KeyCode.translateEvent(e) + * }; + * + * The naming conventions for functions use 'code' to represent an integer + * keycode, 'key' to represent a key description (specified above), and 'e' + * to represent an event object. + * + * There's also functionality to track and detect which keys are currently + * being held down: install 'key_up' and 'key_down' on their respective event + * handlers, and then check with 'is_down'. + * + * @fileoverview + * @author Jonathan Tang + * @version 0.9 + * @license BSD + */ + +/* +Copyright (c) 2008 Jonathan Tang +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +var modifiers = ['ctrl', 'alt', 'shift', 'meta'], + KEY_MAP = {}, + shifted_symbols = { + 58: 59, // : -> ; + 43: 61, // = -> + + 60: 44, // < -> , + 95: 45, // _ -> - + 62: 46, // > -> . + 63: 47, // ? -> / + 96: 192, // ` -> ~ + 124: 92, // | -> \ + 39: 222, // ' -> 222 + 34: 222, // " -> 222 + 33: 49, // ! -> 1 + 64: 50, // @ -> 2 + 35: 51, // # -> 3 + 36: 52, // $ -> 4 + 37: 53, // % -> 5 + 94: 54, // ^ -> 6 + 38: 55, // & -> 7 + 42: 56, // * -> 8 + 40: 57, // ( -> 9 + 41: 58, // ) -> 0 + 123: 91, // { -> [ + 125: 93, // } -> ] + } + +function isLower(ascii) { + return ascii >= 97 && ascii <= 122 +} +function capitalize(str) { + return str.substr(0, 1).toUpperCase() + str.substr(1).toLowerCase() +} + +var is_gecko = navigator.userAgent.indexOf('Gecko') != -1, + is_ie = navigator.userAgent.indexOf('MSIE') != -1, + is_windows = navigator.platform.indexOf('Win') != -1, + is_opera = window.opera && window.opera.version() < 9.5, + is_konqueror = navigator.vendor && navigator.vendor.indexOf('KDE') != -1, + is_icab = navigator.vendor && navigator.vendor.indexOf('iCab') != -1 + +var GECKO_IE_KEYMAP = { + 186: 59, // ;: in IE + 187: 61, // =+ in IE + 188: 44, // ,< + 109: 95, // -_ in Mozilla + 107: 61, // =+ in Mozilla + 189: 95, // -_ in IE + 190: 62, // .> + 191: 47, // /? + 192: 126, // `~ + 219: 91, // {[ + 220: 92, // \| + 221: 93, // }] +} + +var OPERA_KEYMAP = {} + +// Browser detection taken from quirksmode.org +if (is_opera && is_windows) { + KEY_MAP = OPERA_KEYMAP +} else if (is_opera || is_konqueror || is_icab) { + var unshift = [ + 33, 64, 35, 36, 37, 94, 38, 42, 40, 41, 58, 43, 60, 95, 62, 63, 124, 34, + ] + KEY_MAP = OPERA_KEYMAP + for (var i = 0; i < unshift.length; ++i) { + KEY_MAP[unshift[i]] = shifted_symbols[unshift[i]] + } +} else { + // IE and Gecko are close enough that we can use the same map for both, + // and the rest of the world (eg. Opera 9.50) seems to be standardizing + // on them + KEY_MAP = GECKO_IE_KEYMAP +} + +if (is_konqueror) { + KEY_MAP[0] = 45 + KEY_MAP[127] = 46 + KEY_MAP[45] = 95 +} + +var key_names = { + 32: 'SPACE', + 13: 'ENTER', + 9: 'TAB', + 8: 'BACKSPACE', + 16: 'SHIFT', + 17: 'CTRL', + 18: 'ALT', + 20: 'CAPS_LOCK', + 144: 'NUM_LOCK', + 145: 'SCROLL_LOCK', + 37: 'LEFT', + 38: 'UP', + 39: 'RIGHT', + 40: 'DOWN', + 33: 'PAGE_UP', + 34: 'PAGE_DOWN', + 36: 'HOME', + 35: 'END', + 45: 'INSERT', + 46: 'DELETE', + 27: 'ESCAPE', + 19: 'PAUSE', + 222: "'", + 91: 'META', +} +function fn_name(code) { + if (code >= 112 && code <= 123) return 'F' + (code - 111) + return false +} +function num_name(code) { + if (code >= 96 && code < 106) return 'Num' + (code - 96) + switch (code) { + case 106: + return 'Num*' + case 111: + return 'Num/' + case 110: + return 'Num.' + default: + return false + } +} + +var current_keys = { + codes: {}, + ctrl: false, + alt: false, + shift: false, + meta: false, +} + +function update_current_modifiers(key) { + current_keys.ctrl = key.ctrl + current_keys.alt = key.alt + current_keys.shift = key.shift + current_keys.meta = key.meta +} + +function same_modifiers(key1, key2) { + return ( + key1.ctrl === key2.ctrl && + key1.alt === key2.alt && + key1.shift === key2.shift && + key1.meta === key2.meta + ) +} + +if (typeof window.KeyCode != 'undefined') { + var _KeyCode = window.KeyCode +} + +export const KeyCode = { + no_conflict: function () { + window.KeyCode = _KeyCode + return KeyCode + }, + + /** Generates a function key code from a number between 1 and 12 */ + fkey: function (num) { + return 111 + num + }, + + /** + * Generates a numeric keypad code from a number between 0 and 9. + * Also works for (some) arithmetic operators. The mappings are: + * + * *: 106, /: 111, .: 110 + * + * + and - are not supported because the keycodes generated by Mozilla + * conflict with the non-keypad codes. The same applies to all the + * arithmetic keypad keys on Konqueror and early Opera. + */ + numkey: function (num) { + switch (num) { + case '*': + return 106 + case '/': + return 111 + case '.': + return 110 + default: + return 96 + num + } + }, + + /** + * Generates a key code from the ASCII code of (the first character of) a + * string. + */ + key: function (str) { + var c = str.charCodeAt(0) + if (isLower(c)) return c - 32 + return shifted_symbols[c] || c + }, + + /** Checks if two key objects are equal. */ + key_equals: function (key1, key2) { + return key1.code == key2.code && same_modifiers(key1, key2) + }, + + /** Translates a keycode to its normalized value. */ + translate_key_code: function (code) { + return KEY_MAP[code] || code + }, + + /** + * Translates a keyDown event to a normalized key event object. The + * object has the following fields: + * { int code; boolean shift, boolean alt, boolean ctrl } + */ + translate_event: function (e) { + e = e || window.event + var code = e.which || e.keyCode + return { + code: KeyCode.translate_key_code(code), + shift: e.shiftKey, + alt: e.altKey, + ctrl: e.ctrlKey, + meta: e.metaKey, + } + }, + + /** + * Keydown event listener to update internal state of which keys are + * currently pressed. + */ + + key_down: function (e) { + var key = KeyCode.translate_event(e) + current_keys.codes[key.code] = key.code + update_current_modifiers(key) + }, + + /** + * Keyup event listener to update internal state. + */ + key_up: function (e) { + var key = KeyCode.translate_event(e) + delete current_keys.codes[key.code] + update_current_modifiers(key) + }, + + /** + * Returns true if the key spec (as returned by translate_event) is + * currently held down. + */ + is_down: function (key) { + var code = key.code + if (code == KeyCode.CTRL) return current_keys.ctrl + if (code == KeyCode.ALT) return current_keys.alt + if (code == KeyCode.SHIFT) return current_keys.shift + + return ( + current_keys.codes[code] !== undefined && + same_modifiers(key, current_keys) + ) + }, + + /** + * Returns a string representation of a key event suitable for the + * shortcut.js or JQuery HotKeys plugins. Also makes a decent UI display. + */ + hot_key: function (key) { + var pieces = [] + for (var i = 0; i < modifiers.length; ++i) { + var modifier = modifiers[i] + if ( + key[modifier] && + modifier.toUpperCase() != key_names[key.code] + ) { + pieces.push(capitalize(modifier)) + } + } + + var c = key.code + var key_name = + key_names[c] || fn_name(c) || num_name(c) || String.fromCharCode(c) + pieces.push(capitalize(key_name)) + return pieces.join('+') + }, +} + +// Add key constants +for (var code in key_names) { + KeyCode[key_names[code]] = code +} + +// var fields = ['charCode', 'keyCode', 'which', 'type', 'timeStamp', +// 'altKey', 'ctrlKey', 'shiftKey', 'metaKey']; +// var types = ['keydown', 'keypress', 'keyup']; + +// function show_event(type) { +// return function(e) { +// var lines = []; +// for(var i = 0; i < fields.length; ++i) { +// lines.push('' + fields[i] + ': ' + e[fields[i]] + '
'); +// } +// document.getElementByI(type).innerHTML = lines.join('\n'); +// return false; +// } +// }; + +// function show_is_key_down(id, code, ctrl, alt, shift) { +// document.getElementById(id + '_down').innerHTML = KeyCode.is_down({ +// code: code, +// ctrl: ctrl, +// alt: alt, +// shift: shift +// }); +// }; + +// function update_key_downs() { +// show_is_key_down('x', KeyCode.key('x'), false, false, false); +// show_is_key_down('shift_x', KeyCode.key('x'), false, false, true); +// show_is_key_down('shift_c', KeyCode.key('c'), false, false, true); +// show_is_key_down('ctrl_a', KeyCode.key('a'), true, false, false); +// }; diff --git a/v0/src/simulator/src/hotkey_binder/model/shortcuts.plugin.js b/v0/src/simulator/src/hotkey_binder/model/shortcuts.plugin.js new file mode 100644 index 00000000..f80d37bd --- /dev/null +++ b/v0/src/simulator/src/hotkey_binder/model/shortcuts.plugin.js @@ -0,0 +1,250 @@ +/** + * http://www.openjs.com/scripts/events/keyboard_shortcuts/ + * Version : 2.01.B + * By Binny V A + * License : BSD + */ + +/** + * Restrictions: + * The shortcut key combination should be specified in this format ... Modifier[+Modifier..]+Key. + * Can have a single key without Modifier .. Key, not Key + Key + * These restrictions must be be hardcoded to not let users input invalid key combo + * There is no way to override Ctrl+N, Ctrl+T, or Ctrl+W in Google Chrome since version 4 of Chrome (shipped in 2010). + * + **/ + +//*! This plugin has been modified + +export const shortcut = { + all_shortcuts: {}, //All the shortcuts are stored in this array ex. download : keycombo; + add: function (shortcut_combination, callback, opt) { + //Provide a set of default options + var default_options = { + type: 'keydown', + propagate: false, + disable_in_input: true, + target: document, + keycode: false, + } + + if (!opt) opt = default_options + else { + for (var dfo in default_options) { + if (typeof opt[dfo] == 'undefined') + opt[dfo] = default_options[dfo] + } + } + + var ele = opt.target + if (typeof opt.target == 'string') + ele = document.getElementById(opt.target) + var ths = this + shortcut_combination = shortcut_combination.toLowerCase() + + //The function to be called at keypress + var func = function (e) { + e = e || window.event + if (opt['disable_in_input']) { + //Don't enable shortcut keys in Input, Textarea fields + var element + if (e.target) element = e.target + else if (e.srcElement) element = e.srcElement + if (element.nodeType == 3) element = element.parentNode + + if (element.tagName == 'INPUT' || element.tagName == 'TEXTAREA') + return + } + + let code = '' + //Find Which key is pressed + if (e.keyCode) code = e.keyCode + else if (e.which) code = e.which + var character = String.fromCharCode(code).toLowerCase() + // e.preventDefault(); + + if (code == 188) character = ',' //If the user presses , when the type is onkeydown + if (code == 190) character = '.' //If the user presses , when the type is onkeydown + + var keys = shortcut_combination.split('+') + //Key Pressed - counts the number of valid keypresses - if it is same as the number of keys, the shortcut function is invoked + var kp = 0 + + //Work around for stupid Shift key bug created by using lowercase - as a result the shift+num combination was broken + var shift_nums = { + '`': '~', + 1: '!', + 2: '@', + 3: '#', + 4: '$', + 5: '%', + 6: '^', + 7: '&', + 8: '*', + 9: '(', + 0: ')', + '-': '_', + '=': '+', + ';': ':', + "'": '"', + ',': '<', + '.': '>', + '/': '?', + '\\': '|', + } + //Special Keys - and their codes + var special_keys = { + esc: 27, + escape: 27, + tab: 9, + space: 32, + return: 13, + enter: 13, + backspace: 8, + + scrolllock: 145, + scroll_lock: 145, + scroll: 145, + capslock: 20, + caps_lock: 20, + caps: 20, + numlock: 144, + num_lock: 144, + num: 144, + + pause: 19, + break: 19, + + insert: 45, + home: 36, + delete: 46, + end: 35, + + pageup: 33, + page_up: 33, + pu: 33, + + pagedown: 34, + page_down: 34, + pd: 34, + + left: 37, + up: 38, + right: 39, + down: 40, + + f1: 112, + f2: 113, + f3: 114, + f4: 115, + f5: 116, + f6: 117, + f7: 118, + f8: 119, + f9: 120, + f10: 121, + f11: 122, + f12: 123, + } + + var modifiers = { + shift: { wanted: false, pressed: false }, + ctrl: { wanted: false, pressed: false }, + alt: { wanted: false, pressed: false }, + meta: { wanted: false, pressed: false }, //Meta is Mac specific + } + + if (e.ctrlKey) modifiers.ctrl.pressed = true + if (e.shiftKey) modifiers.shift.pressed = true + if (e.altKey) modifiers.alt.pressed = true + if (e.metaKey) modifiers.meta.pressed = true + + let k + for (var i = 0; (k = keys[i]), i < keys.length; i++) { + //Modifiers + if (k == 'ctrl' || k == 'control') { + kp++ + modifiers.ctrl.wanted = true + } else if (k == 'shift') { + kp++ + modifiers.shift.wanted = true + } else if (k == 'alt') { + kp++ + modifiers.alt.wanted = true + } else if (k == 'meta') { + kp++ + modifiers.meta.wanted = true + } else if (k.length > 1) { + //If it is a special key + if (special_keys[k] == code) kp++ + } else if (opt['keycode']) { + if (opt['keycode'] == code) kp++ + } else { + //The special keys did not match + if (character == k) kp++ + else { + if (shift_nums[character] && e.shiftKey) { + //Stupid Shift key bug created by using lowercase + character = shift_nums[character] + if (character == k) kp++ + } + } + } + } + + if ( + kp == keys.length && + modifiers.ctrl.pressed == modifiers.ctrl.wanted && + modifiers.shift.pressed == modifiers.shift.wanted && + modifiers.alt.pressed == modifiers.alt.wanted && + modifiers.meta.pressed == modifiers.meta.wanted + ) { + callback(e) + + if (!opt['propagate']) { + //Stop the event + //e.cancelBubble is supported by IE - this will kill the bubbling process. + e.cancelBubble = true + e.returnValue = false + + //e.stopPropagation works in Firefox. + if (e.stopPropagation) { + e.stopPropagation() + e.preventDefault() + } + return false + } + } + } + this.all_shortcuts[shortcut_combination] = { + callback: func, + target: ele, + event: opt['type'], + } + //Attach the function with the event + if (ele.addEventListener) ele.addEventListener(opt['type'], func, false) + else if (ele.attachEvent) ele.attachEvent('on' + opt['type'], func) + else ele['on' + opt['type']] = func + }, + + //Remove the shortcut - just specify the shortcut and I will remove the binding + remove: function (shortcut_combination) { + shortcut_combination = shortcut_combination.toLowerCase() + var binding = this.all_shortcuts[shortcut_combination] + delete this.all_shortcuts[shortcut_combination] + if (!binding) return + var type = binding['event'] + var ele = binding['target'] + var callback = binding['callback'] + + if (ele.detachEvent) ele.detachEvent('on' + type, callback) + else if (ele.removeEventListener) + ele.removeEventListener(type, callback, false) + else ele['on' + type] = false + }, + removeAll: function () { + for (let x in this.all_shortcuts) { + this.remove(x) + } + }, +} diff --git a/v0/src/simulator/src/hotkey_binder/model/utils.js b/v0/src/simulator/src/hotkey_binder/model/utils.js new file mode 100644 index 00000000..8a2a8a8b --- /dev/null +++ b/v0/src/simulator/src/hotkey_binder/model/utils.js @@ -0,0 +1,67 @@ +Storage.prototype.set = function (key, obj) { + return this.setItem(key, JSON.stringify(obj)) +} + +Storage.prototype.get = function (key) { + return JSON.parse(this.getItem(key)) +} + +Object.size = function (obj) { + var size = 0, + key + for (key in obj) { + if (obj.hasOwnProperty(key)) size++ + } + return size +} + +export const getKey = (obj, val) => + Object.keys(obj).find((key) => obj[key] === val) + +export const getOS = () => { + let OSName = '' + if (navigator.appVersion.indexOf('Win') != -1) OSName = 'Windows' + if (navigator.appVersion.indexOf('Mac') != -1) OSName = 'MacOS' + if (navigator.appVersion.indexOf('X11') != -1) OSName = 'UNIX' + if (navigator.appVersion.indexOf('Linux') != -1) OSName = 'Linux' + return OSName +} + +export const checkRestricted = (key) => { + const restrictedKeys = [ + 'Ctrl + N', + 'Ctrl + W', + 'Ctrl + T', + 'Ctrl + C', + 'Ctrl + V', + 'Ctrl + Delete', + 'Ctrl + Backspace', + 'Ctrl + /', + 'Ctrl + \\', + 'Ctrl + ]', + "Ctrl + '", + 'Ctrl + `', + 'Ctrl + [', + 'Ctrl + ~', + 'Ctrl + Num1', + 'Ctrl + Num2', + 'Ctrl + Num3', + 'Ctrl + Num4', + 'Ctrl + Num5', + 'Ctrl + Num6', + 'Ctrl + Num*', + 'Ctrl + Num/', + 'Ctrl + Num.', + 'Ctrl + Num0', + ] + if (getOS == 'macOS') { + restrictedKeys.forEach((value, i) => { + if (value.split(' + ')[0] == 'Ctrl'); + restrictedKeys[i] = + value.split(' + ')[0] == 'Ctrl' + ? value.replace('Ctrl', 'Meta') + : value + }) + } + return restrictedKeys.includes(key) +} diff --git a/v0/src/simulator/src/hotkey_binder/view/panel.ui.js b/v0/src/simulator/src/hotkey_binder/view/panel.ui.js new file mode 100644 index 00000000..06222901 --- /dev/null +++ b/v0/src/simulator/src/hotkey_binder/view/panel.ui.js @@ -0,0 +1,60 @@ +import { setUserKeys } from '../model/actions' + +/** + * fn to update the htokey panel UI with the currently set configuration + * @param {string} mode user prefered if present, or default keys configuration + */ +export const updateHTML = (mode) => { + let x = 0 + if (mode == 'user') { + const userKeys = localStorage.get('userKeys') + while ($('#preference').children()[x]) { + $('#preference').children()[x].children[1].children[1].innerText = + userKeys[ + $('#preference').children()[ + x + ].children[1].children[0].innerText + ] + x++ + } + } else if (mode == 'default') { + while ($('#preference').children()[x]) { + const defaultKeys = localStorage.get('defaultKeys') + $('#preference').children()[x].children[1].children[1].innerText = + defaultKeys[ + $('#preference').children()[ + x + ].children[1].children[0].innerText + ] + x++ + } + } +} +/** + * fn to override key of duplicate entries + * old entry will be left blank & keys will be assigned to the new target + * @param {*} combo + */ +export const override = (combo) => { + let x = 0 + while ($('#preference').children()[x]) { + if ( + $('#preference').children()[x].children[1].children[1].innerText === + combo + ) + $('#preference').children()[x].children[1].children[1].innerText = + '' + x++ + } +} + +export const closeEdit = () => { + $('#pressedKeys').text('') + $('#edit').css('display', 'none') +} + +export const submit = () => { + $('#edit').css('display', 'none') + setUserKeys() + updateHTML('user') +} diff --git a/v0/src/simulator/src/i18n.js b/v0/src/simulator/src/i18n.js new file mode 100644 index 00000000..602f4c51 --- /dev/null +++ b/v0/src/simulator/src/i18n.js @@ -0,0 +1,17 @@ +import Banana from 'banana-i18n' + +const banana = new Banana() +banana.setLocale(window.locale) +const { locale } = banana +const finalFallback = 'en' +// object with default language preloaded +const messages = { + [finalFallback]: require(`./i18n/${finalFallback}.json`), +} +try { + messages[locale] = require(`./i18n/${locale}.json`) +} catch (err) { + // If Asynchronous loading for current locale failed, load default locale +} +banana.load(messages) +export default banana diff --git a/v0/src/simulator/src/i18n/en.json b/v0/src/simulator/src/i18n/en.json new file mode 100644 index 00000000..e9fe2e17 --- /dev/null +++ b/v0/src/simulator/src/i18n/en.json @@ -0,0 +1,8 @@ +{ + "@metadata": { + "authors": ["Pavan"], + "last-updated": "2021-07-06", + "locale": "en", + "message-documentation": "qqq" + } +} diff --git a/v0/src/simulator/src/i18n/hi.json b/v0/src/simulator/src/i18n/hi.json new file mode 100644 index 00000000..96f4543e --- /dev/null +++ b/v0/src/simulator/src/i18n/hi.json @@ -0,0 +1,8 @@ +{ + "@metadata": { + "authors": ["Pavan"], + "last-updated": "2021-07-06", + "locale": "hi", + "message-documentation": "qqq" + } +} diff --git a/v0/src/simulator/src/img/ALU.png b/v0/src/simulator/src/img/ALU.png new file mode 100644 index 00000000..fa809425 Binary files /dev/null and b/v0/src/simulator/src/img/ALU.png differ diff --git a/v0/src/simulator/src/img/ALU.svg b/v0/src/simulator/src/img/ALU.svg new file mode 100644 index 00000000..0db7c17a --- /dev/null +++ b/v0/src/simulator/src/img/ALU.svg @@ -0,0 +1 @@ +BACTRCarryAnsALU \ No newline at end of file diff --git a/v0/src/simulator/src/img/Adder.svg b/v0/src/simulator/src/img/Adder.svg new file mode 100644 index 00000000..9249cff0 --- /dev/null +++ b/v0/src/simulator/src/img/Adder.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/AndGate.svg b/v0/src/simulator/src/img/AndGate.svg new file mode 100644 index 00000000..6273b83b --- /dev/null +++ b/v0/src/simulator/src/img/AndGate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/Arrow.svg b/v0/src/simulator/src/img/Arrow.svg new file mode 100644 index 00000000..c25f20a1 --- /dev/null +++ b/v0/src/simulator/src/img/Arrow.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/AsyncCounter.jpeg b/v0/src/simulator/src/img/AsyncCounter.jpeg new file mode 100644 index 00000000..88a40590 Binary files /dev/null and b/v0/src/simulator/src/img/AsyncCounter.jpeg differ diff --git a/v0/src/simulator/src/img/BitSelector.svg b/v0/src/simulator/src/img/BitSelector.svg new file mode 100644 index 00000000..b0436573 --- /dev/null +++ b/v0/src/simulator/src/img/BitSelector.svg @@ -0,0 +1 @@ +x \ No newline at end of file diff --git a/v0/src/simulator/src/img/Buffer.svg b/v0/src/simulator/src/img/Buffer.svg new file mode 100644 index 00000000..bf867043 --- /dev/null +++ b/v0/src/simulator/src/img/Buffer.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/Button.svg b/v0/src/simulator/src/img/Button.svg new file mode 100644 index 00000000..6c16a24e --- /dev/null +++ b/v0/src/simulator/src/img/Button.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/Clock.svg b/v0/src/simulator/src/img/Clock.svg new file mode 100644 index 00000000..653d29e0 --- /dev/null +++ b/v0/src/simulator/src/img/Clock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/ConstantVal.svg b/v0/src/simulator/src/img/ConstantVal.svg new file mode 100644 index 00000000..dcc41214 --- /dev/null +++ b/v0/src/simulator/src/img/ConstantVal.svg @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/v0/src/simulator/src/img/Control Sequencer.png b/v0/src/simulator/src/img/Control Sequencer.png new file mode 100644 index 00000000..2189f0a7 Binary files /dev/null and b/v0/src/simulator/src/img/Control Sequencer.png differ diff --git a/v0/src/simulator/src/img/ControlledInverter.svg b/v0/src/simulator/src/img/ControlledInverter.svg new file mode 100644 index 00000000..927dc8c8 --- /dev/null +++ b/v0/src/simulator/src/img/ControlledInverter.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/Counter.svg b/v0/src/simulator/src/img/Counter.svg new file mode 100644 index 00000000..ee2aa77b --- /dev/null +++ b/v0/src/simulator/src/img/Counter.svg @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/v0/src/simulator/src/img/Decoder.svg b/v0/src/simulator/src/img/Decoder.svg new file mode 100644 index 00000000..9a48c394 --- /dev/null +++ b/v0/src/simulator/src/img/Decoder.svg @@ -0,0 +1 @@ +01 \ No newline at end of file diff --git a/v0/src/simulator/src/img/Demultiplexer.svg b/v0/src/simulator/src/img/Demultiplexer.svg new file mode 100644 index 00000000..61cad635 --- /dev/null +++ b/v0/src/simulator/src/img/Demultiplexer.svg @@ -0,0 +1 @@ +01 \ No newline at end of file diff --git a/v0/src/simulator/src/img/DflipFlop.svg b/v0/src/simulator/src/img/DflipFlop.svg new file mode 100644 index 00000000..49f4ab5f --- /dev/null +++ b/v0/src/simulator/src/img/DflipFlop.svg @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/v0/src/simulator/src/img/DigitalLed.svg b/v0/src/simulator/src/img/DigitalLed.svg new file mode 100644 index 00000000..a9259c2f --- /dev/null +++ b/v0/src/simulator/src/img/DigitalLed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/Dlatch.svg b/v0/src/simulator/src/img/Dlatch.svg new file mode 100644 index 00000000..8aef7671 --- /dev/null +++ b/v0/src/simulator/src/img/Dlatch.svg @@ -0,0 +1 @@ +3 \ No newline at end of file diff --git a/v0/src/simulator/src/img/EEPROM.svg b/v0/src/simulator/src/img/EEPROM.svg new file mode 100644 index 00000000..504f61b0 --- /dev/null +++ b/v0/src/simulator/src/img/EEPROM.svg @@ -0,0 +1 @@ +EPROMADIWDO \ No newline at end of file diff --git a/v0/src/simulator/src/img/Flag.svg b/v0/src/simulator/src/img/Flag.svg new file mode 100644 index 00000000..87ef27f4 --- /dev/null +++ b/v0/src/simulator/src/img/Flag.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/FlipFlop.jpeg b/v0/src/simulator/src/img/FlipFlop.jpeg new file mode 100644 index 00000000..5c3d87ce Binary files /dev/null and b/v0/src/simulator/src/img/FlipFlop.jpeg differ diff --git a/v0/src/simulator/src/img/ForceGate.svg b/v0/src/simulator/src/img/ForceGate.svg new file mode 100644 index 00000000..28b50f3f --- /dev/null +++ b/v0/src/simulator/src/img/ForceGate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/Ground.svg b/v0/src/simulator/src/img/Ground.svg new file mode 100644 index 00000000..70f453f6 --- /dev/null +++ b/v0/src/simulator/src/img/Ground.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/HexDisplay.svg b/v0/src/simulator/src/img/HexDisplay.svg new file mode 100644 index 00000000..10c89e13 --- /dev/null +++ b/v0/src/simulator/src/img/HexDisplay.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/ImageAnnotation.svg b/v0/src/simulator/src/img/ImageAnnotation.svg new file mode 100644 index 00000000..8fbd9f75 --- /dev/null +++ b/v0/src/simulator/src/img/ImageAnnotation.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/Input.svg b/v0/src/simulator/src/img/Input.svg new file mode 100644 index 00000000..42a626ff --- /dev/null +++ b/v0/src/simulator/src/img/Input.svg @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/v0/src/simulator/src/img/JKflipFlop.svg b/v0/src/simulator/src/img/JKflipFlop.svg new file mode 100644 index 00000000..9e723197 --- /dev/null +++ b/v0/src/simulator/src/img/JKflipFlop.svg @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/v0/src/simulator/src/img/Keyboard.jpeg b/v0/src/simulator/src/img/Keyboard.jpeg new file mode 100644 index 00000000..9922daff Binary files /dev/null and b/v0/src/simulator/src/img/Keyboard.jpeg differ diff --git a/v0/src/simulator/src/img/Keyboard.svg b/v0/src/simulator/src/img/Keyboard.svg new file mode 100644 index 00000000..6e215f6f --- /dev/null +++ b/v0/src/simulator/src/img/Keyboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/LSB.svg b/v0/src/simulator/src/img/LSB.svg new file mode 100644 index 00000000..31148b47 --- /dev/null +++ b/v0/src/simulator/src/img/LSB.svg @@ -0,0 +1 @@ +LSBEN \ No newline at end of file diff --git a/v0/src/simulator/src/img/MSB.svg b/v0/src/simulator/src/img/MSB.svg new file mode 100644 index 00000000..21997b94 --- /dev/null +++ b/v0/src/simulator/src/img/MSB.svg @@ -0,0 +1 @@ +MSBEN \ No newline at end of file diff --git a/v0/src/simulator/src/img/Main.png b/v0/src/simulator/src/img/Main.png new file mode 100644 index 00000000..3bb25dac Binary files /dev/null and b/v0/src/simulator/src/img/Main.png differ diff --git a/v0/src/simulator/src/img/Multiplexer.svg b/v0/src/simulator/src/img/Multiplexer.svg new file mode 100644 index 00000000..b1b8472d --- /dev/null +++ b/v0/src/simulator/src/img/Multiplexer.svg @@ -0,0 +1 @@ +01 \ No newline at end of file diff --git a/v0/src/simulator/src/img/NandGate.svg b/v0/src/simulator/src/img/NandGate.svg new file mode 100644 index 00000000..04cddce2 --- /dev/null +++ b/v0/src/simulator/src/img/NandGate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/NorGate.svg b/v0/src/simulator/src/img/NorGate.svg new file mode 100644 index 00000000..55e9abc2 --- /dev/null +++ b/v0/src/simulator/src/img/NorGate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/NotGate.svg b/v0/src/simulator/src/img/NotGate.svg new file mode 100644 index 00000000..a0d40ad4 --- /dev/null +++ b/v0/src/simulator/src/img/NotGate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/OrGate.svg b/v0/src/simulator/src/img/OrGate.svg new file mode 100644 index 00000000..741ba9fe --- /dev/null +++ b/v0/src/simulator/src/img/OrGate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/Output.svg b/v0/src/simulator/src/img/Output.svg new file mode 100644 index 00000000..7d4298f6 --- /dev/null +++ b/v0/src/simulator/src/img/Output.svg @@ -0,0 +1 @@ +x \ No newline at end of file diff --git a/v0/src/simulator/src/img/Power.svg b/v0/src/simulator/src/img/Power.svg new file mode 100644 index 00000000..a4619920 --- /dev/null +++ b/v0/src/simulator/src/img/Power.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/PriorityEncoder.svg b/v0/src/simulator/src/img/PriorityEncoder.svg new file mode 100644 index 00000000..417325c6 --- /dev/null +++ b/v0/src/simulator/src/img/PriorityEncoder.svg @@ -0,0 +1 @@ +010EN \ No newline at end of file diff --git a/v0/src/simulator/src/img/RAM.svg b/v0/src/simulator/src/img/RAM.svg new file mode 100644 index 00000000..92fd8293 --- /dev/null +++ b/v0/src/simulator/src/img/RAM.svg @@ -0,0 +1 @@ +RAMADIWDO \ No newline at end of file diff --git a/v0/src/simulator/src/img/RGBLed.svg b/v0/src/simulator/src/img/RGBLed.svg new file mode 100644 index 00000000..299657c8 --- /dev/null +++ b/v0/src/simulator/src/img/RGBLed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/RGBLedMatrix.svg b/v0/src/simulator/src/img/RGBLedMatrix.svg new file mode 100644 index 00000000..cbb52e83 --- /dev/null +++ b/v0/src/simulator/src/img/RGBLedMatrix.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/Random.svg b/v0/src/simulator/src/img/Random.svg new file mode 100644 index 00000000..4da0fa99 --- /dev/null +++ b/v0/src/simulator/src/img/Random.svg @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/v0/src/simulator/src/img/Rectangle.svg b/v0/src/simulator/src/img/Rectangle.svg new file mode 100644 index 00000000..f2ac9773 --- /dev/null +++ b/v0/src/simulator/src/img/Rectangle.svg @@ -0,0 +1 @@ +Rectangle \ No newline at end of file diff --git a/v0/src/simulator/src/img/RippleCarry.jpeg b/v0/src/simulator/src/img/RippleCarry.jpeg new file mode 100644 index 00000000..c1fbf61f Binary files /dev/null and b/v0/src/simulator/src/img/RippleCarry.jpeg differ diff --git a/v0/src/simulator/src/img/Rom.svg b/v0/src/simulator/src/img/Rom.svg new file mode 100644 index 00000000..b72c1c26 --- /dev/null +++ b/v0/src/simulator/src/img/Rom.svg @@ -0,0 +1 @@ +ADEn000000000000000000000000000000000004080c \ No newline at end of file diff --git a/v0/src/simulator/src/img/SAP.jpeg b/v0/src/simulator/src/img/SAP.jpeg new file mode 100644 index 00000000..79b76da0 Binary files /dev/null and b/v0/src/simulator/src/img/SAP.jpeg differ diff --git a/v0/src/simulator/src/img/SRflipFlop.svg b/v0/src/simulator/src/img/SRflipFlop.svg new file mode 100644 index 00000000..c41ff746 --- /dev/null +++ b/v0/src/simulator/src/img/SRflipFlop.svg @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/v0/src/simulator/src/img/SevenSegDisplay.svg b/v0/src/simulator/src/img/SevenSegDisplay.svg new file mode 100644 index 00000000..bb3d2ae8 --- /dev/null +++ b/v0/src/simulator/src/img/SevenSegDisplay.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/SixteenSegDisplay.svg b/v0/src/simulator/src/img/SixteenSegDisplay.svg new file mode 100644 index 00000000..ad05274b --- /dev/null +++ b/v0/src/simulator/src/img/SixteenSegDisplay.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/Splitter.svg b/v0/src/simulator/src/img/Splitter.svg new file mode 100644 index 00000000..fa4a969d --- /dev/null +++ b/v0/src/simulator/src/img/Splitter.svg @@ -0,0 +1 @@ +0:11:2 \ No newline at end of file diff --git a/v0/src/simulator/src/img/SquareRGBLed.svg b/v0/src/simulator/src/img/SquareRGBLed.svg new file mode 100644 index 00000000..7900e614 --- /dev/null +++ b/v0/src/simulator/src/img/SquareRGBLed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/Stepper.svg b/v0/src/simulator/src/img/Stepper.svg new file mode 100644 index 00000000..f18aa4fa --- /dev/null +++ b/v0/src/simulator/src/img/Stepper.svg @@ -0,0 +1 @@ +5f \ No newline at end of file diff --git a/v0/src/simulator/src/img/T-clock.png b/v0/src/simulator/src/img/T-clock.png new file mode 100644 index 00000000..66442d9e Binary files /dev/null and b/v0/src/simulator/src/img/T-clock.png differ diff --git a/v0/src/simulator/src/img/TB_Input.svg b/v0/src/simulator/src/img/TB_Input.svg new file mode 100644 index 00000000..c1c42ead --- /dev/null +++ b/v0/src/simulator/src/img/TB_Input.svg @@ -0,0 +1 @@ +Test1 [INPUT] Case:0 \ No newline at end of file diff --git a/v0/src/simulator/src/img/TB_Output.svg b/v0/src/simulator/src/img/TB_Output.svg new file mode 100644 index 00000000..b62e1993 --- /dev/null +++ b/v0/src/simulator/src/img/TB_Output.svg @@ -0,0 +1 @@ +Test1 [OUTPUT] Paired \ No newline at end of file diff --git a/v0/src/simulator/src/img/TTY.svg b/v0/src/simulator/src/img/TTY.svg new file mode 100644 index 00000000..208e42b9 --- /dev/null +++ b/v0/src/simulator/src/img/TTY.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/Text.svg b/v0/src/simulator/src/img/Text.svg new file mode 100644 index 00000000..db86087a --- /dev/null +++ b/v0/src/simulator/src/img/Text.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/TflipFlop.svg b/v0/src/simulator/src/img/TflipFlop.svg new file mode 100644 index 00000000..50925f80 --- /dev/null +++ b/v0/src/simulator/src/img/TflipFlop.svg @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/v0/src/simulator/src/img/TriState.svg b/v0/src/simulator/src/img/TriState.svg new file mode 100644 index 00000000..d251882c --- /dev/null +++ b/v0/src/simulator/src/img/TriState.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/Tunnel.svg b/v0/src/simulator/src/img/Tunnel.svg new file mode 100644 index 00000000..ff5b95bb --- /dev/null +++ b/v0/src/simulator/src/img/Tunnel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/TwoComplement.svg b/v0/src/simulator/src/img/TwoComplement.svg new file mode 100644 index 00000000..6baa7056 --- /dev/null +++ b/v0/src/simulator/src/img/TwoComplement.svg @@ -0,0 +1 @@ +2' \ No newline at end of file diff --git a/v0/src/simulator/src/img/VariableLed.svg b/v0/src/simulator/src/img/VariableLed.svg new file mode 100644 index 00000000..e2829407 --- /dev/null +++ b/v0/src/simulator/src/img/VariableLed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/XnorGate.svg b/v0/src/simulator/src/img/XnorGate.svg new file mode 100644 index 00000000..4a8bae40 --- /dev/null +++ b/v0/src/simulator/src/img/XnorGate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/XorGate.svg b/v0/src/simulator/src/img/XorGate.svg new file mode 100644 index 00000000..639b4c0b --- /dev/null +++ b/v0/src/simulator/src/img/XorGate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/assignment.png b/v0/src/simulator/src/img/assignment.png new file mode 100644 index 00000000..c27df5bc Binary files /dev/null and b/v0/src/simulator/src/img/assignment.png differ diff --git a/v0/src/simulator/src/img/bus.png b/v0/src/simulator/src/img/bus.png new file mode 100644 index 00000000..d37b30aa Binary files /dev/null and b/v0/src/simulator/src/img/bus.png differ diff --git a/v0/src/simulator/src/img/caret.jpg b/v0/src/simulator/src/img/caret.jpg new file mode 100644 index 00000000..a382f8da Binary files /dev/null and b/v0/src/simulator/src/img/caret.jpg differ diff --git a/v0/src/simulator/src/img/circuitverse2.svg b/v0/src/simulator/src/img/circuitverse2.svg new file mode 100644 index 00000000..23dd6118 --- /dev/null +++ b/v0/src/simulator/src/img/circuitverse2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/circuitverse_black.svg b/v0/src/simulator/src/img/circuitverse_black.svg new file mode 100644 index 00000000..eba77c2a --- /dev/null +++ b/v0/src/simulator/src/img/circuitverse_black.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/circuitverse_logo.svg b/v0/src/simulator/src/img/circuitverse_logo.svg new file mode 100644 index 00000000..27202ecb --- /dev/null +++ b/v0/src/simulator/src/img/circuitverse_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/cross.png b/v0/src/simulator/src/img/cross.png new file mode 100644 index 00000000..bb7126e2 Binary files /dev/null and b/v0/src/simulator/src/img/cross.png differ diff --git a/v0/src/simulator/src/img/cvlogo.svg b/v0/src/simulator/src/img/cvlogo.svg new file mode 100644 index 00000000..ce4f7e59 --- /dev/null +++ b/v0/src/simulator/src/img/cvlogo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/simulator/src/img/default.png b/v0/src/simulator/src/img/default.png new file mode 100644 index 00000000..8e183a8a Binary files /dev/null and b/v0/src/simulator/src/img/default.png differ diff --git a/v0/src/simulator/src/img/drag.mp4 b/v0/src/simulator/src/img/drag.mp4 new file mode 100644 index 00000000..b211439c Binary files /dev/null and b/v0/src/simulator/src/img/drag.mp4 differ diff --git a/v0/src/simulator/src/img/edit_icon.png b/v0/src/simulator/src/img/edit_icon.png new file mode 100644 index 00000000..32f99fb1 Binary files /dev/null and b/v0/src/simulator/src/img/edit_icon.png differ diff --git a/v0/src/simulator/src/img/embed.png b/v0/src/simulator/src/img/embed.png new file mode 100644 index 00000000..54a7d78d Binary files /dev/null and b/v0/src/simulator/src/img/embed.png differ diff --git a/v0/src/simulator/src/img/facebook.png b/v0/src/simulator/src/img/facebook.png new file mode 100644 index 00000000..fc961175 Binary files /dev/null and b/v0/src/simulator/src/img/facebook.png differ diff --git a/v0/src/simulator/src/img/facebook_signin.png b/v0/src/simulator/src/img/facebook_signin.png new file mode 100644 index 00000000..e78185f0 Binary files /dev/null and b/v0/src/simulator/src/img/facebook_signin.png differ diff --git a/v0/src/simulator/src/img/fullAdder.png b/v0/src/simulator/src/img/fullAdder.png new file mode 100644 index 00000000..1150d9c6 Binary files /dev/null and b/v0/src/simulator/src/img/fullAdder.png differ diff --git a/v0/src/simulator/src/img/google.png b/v0/src/simulator/src/img/google.png new file mode 100644 index 00000000..2accb4e6 Binary files /dev/null and b/v0/src/simulator/src/img/google.png differ diff --git a/v0/src/simulator/src/img/google_signin.png b/v0/src/simulator/src/img/google_signin.png new file mode 100644 index 00000000..18bbd0b7 Binary files /dev/null and b/v0/src/simulator/src/img/google_signin.png differ diff --git a/v0/src/simulator/src/img/grading.png b/v0/src/simulator/src/img/grading.png new file mode 100644 index 00000000..166ee57e Binary files /dev/null and b/v0/src/simulator/src/img/grading.png differ diff --git a/v0/src/simulator/src/img/groups.png b/v0/src/simulator/src/img/groups.png new file mode 100644 index 00000000..e3619597 Binary files /dev/null and b/v0/src/simulator/src/img/groups.png differ diff --git a/v0/src/simulator/src/img/halfAdder.png b/v0/src/simulator/src/img/halfAdder.png new file mode 100644 index 00000000..7c9f3f0c Binary files /dev/null and b/v0/src/simulator/src/img/halfAdder.png differ diff --git a/v0/src/simulator/src/img/help.png b/v0/src/simulator/src/img/help.png new file mode 100644 index 00000000..5c72dbb7 Binary files /dev/null and b/v0/src/simulator/src/img/help.png differ diff --git a/v0/src/simulator/src/img/iDecoder.png b/v0/src/simulator/src/img/iDecoder.png new file mode 100644 index 00000000..d55e9bfa Binary files /dev/null and b/v0/src/simulator/src/img/iDecoder.png differ diff --git a/v0/src/simulator/src/img/iiitb.png b/v0/src/simulator/src/img/iiitb.png new file mode 100644 index 00000000..5a1c2fe3 Binary files /dev/null and b/v0/src/simulator/src/img/iiitb.png differ diff --git a/v0/src/simulator/src/img/implemented.png b/v0/src/simulator/src/img/implemented.png new file mode 100644 index 00000000..6766aaf6 Binary files /dev/null and b/v0/src/simulator/src/img/implemented.png differ diff --git a/v0/src/simulator/src/img/logix.png b/v0/src/simulator/src/img/logix.png new file mode 100644 index 00000000..2f6daac5 Binary files /dev/null and b/v0/src/simulator/src/img/logix.png differ diff --git a/v0/src/simulator/src/img/logixBanner.png b/v0/src/simulator/src/img/logixBanner.png new file mode 100644 index 00000000..b9dad5d5 Binary files /dev/null and b/v0/src/simulator/src/img/logixBanner.png differ diff --git a/v0/src/simulator/src/img/logixBanner2.png b/v0/src/simulator/src/img/logixBanner2.png new file mode 100644 index 00000000..9e7b044a Binary files /dev/null and b/v0/src/simulator/src/img/logixBanner2.png differ diff --git a/v0/src/simulator/src/img/logix_banner_new.png b/v0/src/simulator/src/img/logix_banner_new.png new file mode 100644 index 00000000..13d7fc63 Binary files /dev/null and b/v0/src/simulator/src/img/logix_banner_new.png differ diff --git a/v0/src/simulator/src/img/multiselectionDrag.mp4 b/v0/src/simulator/src/img/multiselectionDrag.mp4 new file mode 100644 index 00000000..6a193d8e Binary files /dev/null and b/v0/src/simulator/src/img/multiselectionDrag.mp4 differ diff --git a/v0/src/simulator/src/img/properties.mp4 b/v0/src/simulator/src/img/properties.mp4 new file mode 100644 index 00000000..754f6c58 Binary files /dev/null and b/v0/src/simulator/src/img/properties.mp4 differ diff --git a/v0/src/simulator/src/img/properties.png b/v0/src/simulator/src/img/properties.png new file mode 100644 index 00000000..167810f5 Binary files /dev/null and b/v0/src/simulator/src/img/properties.png differ diff --git a/v0/src/simulator/src/img/stats.png b/v0/src/simulator/src/img/stats.png new file mode 100644 index 00000000..700bd7fb Binary files /dev/null and b/v0/src/simulator/src/img/stats.png differ diff --git a/v0/src/simulator/src/img/students.png b/v0/src/simulator/src/img/students.png new file mode 100644 index 00000000..0be22e0e Binary files /dev/null and b/v0/src/simulator/src/img/students.png differ diff --git a/v0/src/simulator/src/img/super.png b/v0/src/simulator/src/img/super.png new file mode 100644 index 00000000..4e3a0996 Binary files /dev/null and b/v0/src/simulator/src/img/super.png differ diff --git a/v0/src/simulator/src/img/wire.mp4 b/v0/src/simulator/src/img/wire.mp4 new file mode 100644 index 00000000..8b246df9 Binary files /dev/null and b/v0/src/simulator/src/img/wire.mp4 differ diff --git a/v0/src/simulator/src/layout/layoutBuffer.js b/v0/src/simulator/src/layout/layoutBuffer.js new file mode 100644 index 00000000..1c339d28 --- /dev/null +++ b/v0/src/simulator/src/layout/layoutBuffer.js @@ -0,0 +1,88 @@ +import LayoutNode from './layoutNode' +/** + * Buffer object to store changes so that you can reset changes + * @class + * @param {Scope=} scope + * @category layout + */ +export default class LayoutBuffer { + constructor(scope = globalScope) { + var w = 300 * DPR + var h = 50 * DPR + + globalScope.ox = w + globalScope.oy = h + + // Assign layout if exist or create new one + this.layout = { ...scope.layout } // Object.create(scope.layout); + + // Push Input Nodes + this.Input = [] + for (let i = 0; i < scope.Input.length; i++) + this.Input.push( + new LayoutNode( + scope.Input[i].layoutProperties.x, + scope.Input[i].layoutProperties.y, + scope.Input[i].layoutProperties.id, + scope.Input[i].label, + scope.Input[i].type, + scope.Input[i] + ) + ) + + // Push Output Nodes + this.Output = [] + for (let i = 0; i < scope.Output.length; i++) + this.Output.push( + new LayoutNode( + scope.Output[i].layoutProperties.x, + scope.Output[i].layoutProperties.y, + scope.Output[i].layoutProperties.id, + scope.Output[i].label, + scope.Output[i].type, + scope.Output[i] + ) + ) + + // holds subcircuit elements + this.subElements = [] + } + + /** + * @memberof layoutBuffer + * Check if position is on the boundaries of subcircuit + * if the desired width and heiht is allowed + */ + isAllowed(x, y) { + if (x < 0 || x > this.layout.width || y < 0 || y > this.layout.height) + return false + if (x > 0 && x < this.layout.width && y > 0 && y < this.layout.height) + return false + + if ( + (x === 0 && y === 0) || + (x === 0 && y === this.layout.height) || + (x === this.layout.width && y === 0) || + (x === this.layout.width && y === this.layout.height) + ) + return false + + return true + } + + /** + * @memberof layoutBuffer + * Check if node is already at a position + * Function is called while decreasing height to + * check if it is possible without moving other node + */ + isNodeAt(x, y) { + for (let i = 0; i < this.Input.length; i++) { + if (this.Input[i].x === x && this.Input[i].y === y) return true + } + for (let i = 0; i < this.Output.length; i++) { + if (this.Output[i].x === x && this.Output[i].y === y) return true + } + return false + } +} diff --git a/v0/src/simulator/src/layout/layoutNode.js b/v0/src/simulator/src/layout/layoutNode.js new file mode 100644 index 00000000..ddf199b3 --- /dev/null +++ b/v0/src/simulator/src/layout/layoutNode.js @@ -0,0 +1,124 @@ +import { drawCircle } from '../canvasApi' +import simulationArea from '../simulationArea' +import { tempBuffer } from '../layoutMode' + +/** + * @class + * @param {number} x - x coord of node + * @param {number} y - y coord of node + * @param {strng} id - id for node + * @param {string=} label - label for the node + * @param {number} xx - parent x + * @param {number} yy - parent y + * @param {number} type - input or output node + * @param {CircuitElement} parent parent of the node + * @category layout + */ +export default class LayoutNode { + constructor(x, y, id, label = '', type, parent) { + this.type = type + this.id = id + + this.label = label + + this.prevx = undefined + this.prevy = undefined + this.x = x // Position of node wrt to parent + this.y = y // Position of node wrt to parent + + this.radius = 5 + this.clicked = false + this.hover = false + this.wasClicked = false + this.prev = 'a' + this.count = 0 + this.parent = parent + this.objectType = 'Layout Node' + } + + absX() { + return this.x + } + + absY() { + return this.y + } + + update() { + // Code copied from node.update() - Some code is redundant - needs to be removed + + if (this === simulationArea.hover) simulationArea.hover = undefined + this.hover = this.isHover() + + if (!simulationArea.mouseDown) { + if (this.absX() !== this.prevx || this.absY() !== this.prevy) { + // Store position before clicked + this.prevx = this.absX() + this.prevy = this.absY() + } + } + + if (this.hover) { + simulationArea.hover = this + } + + if ( + simulationArea.mouseDown && + ((this.hover && !simulationArea.selected) || + simulationArea.lastSelected === this) + ) { + simulationArea.selected = true + simulationArea.lastSelected = this + this.clicked = true + } else { + this.clicked = false + } + + if (!this.wasClicked && this.clicked) { + this.wasClicked = true + this.prev = 'a' + simulationArea.lastSelected = this + } else if (this.wasClicked && this.clicked) { + // Check if valid position and update accordingly + if ( + tempBuffer.isAllowed( + simulationArea.mouseX, + simulationArea.mouseY + ) && + !tempBuffer.isNodeAt( + simulationArea.mouseX, + simulationArea.mouseY + ) + ) { + this.x = simulationArea.mouseX + this.y = simulationArea.mouseY + } + } + } + + /** + * @memberof layoutNode + * this function is used to draw the nodes + */ + draw() { + var ctx = simulationArea.context + drawCircle( + ctx, + this.absX(), + this.absY(), + 3, + ['green', 'red'][+(simulationArea.lastSelected === this)] + ) + } + + /** + * @memberof layoutNode + * this function is used to check if hover + */ + isHover() { + return ( + this.absX() === simulationArea.mouseX && + this.absY() === simulationArea.mouseY + ) + } +} diff --git a/v0/src/simulator/src/layoutMode.js b/v0/src/simulator/src/layoutMode.js new file mode 100644 index 00000000..c400cb71 --- /dev/null +++ b/v0/src/simulator/src/layoutMode.js @@ -0,0 +1,544 @@ +/* eslint-disable import/no-cycle */ +/* eslint-disable no-continue */ +import { dots, correctWidth, fillText, rect2 } from './canvasApi' +import LayoutBuffer from './layout/layoutBuffer' +import simulationArea from './simulationArea' +import { + hideProperties, + fillSubcircuitElements, + prevPropertyObjGet, + prevPropertyObjSet, + showProperties, +} from './ux' +import { + update, + scheduleUpdate, + willBeUpdatedSet, + gridUpdateSet, + gridUpdateGet, +} from './engine' +import miniMapArea from './minimap' +import { showMessage } from './utils' +import * as metadata from './metadata.json' +import { verilogModeGet, verilogModeSet } from './Verilog2CV' + +/** + * Layout.js - all subcircuit layout related code is here + * You can edit how your subcircuit for a circuit will look by + * clicking edit layout in properties for a ciruit + * @category layoutMode + */ + +var layoutMode = false + +export function layoutModeSet(param) { + layoutMode = param +} + +export function layoutModeGet(param) { + return layoutMode +} + +/** + * @type {LayoutBuffer} - used to temporartily store all changes. + * @category layoutMode + */ +export var tempBuffer + +/** + * Helper function to determine alignment and position of nodes for rendering + * @param {number} x - width of label + * @param {number} y - height of label + * @category layoutMode + */ +export function determineLabel(x, y) { + if (x === 0) return ['left', 5, 5] + if (x === tempBuffer.layout.width) return ['right', -5, 5] + if (y === 0) return ['center', 0, 13] + return ['center', 0, -6] +} + +/** + * Used to move the grid in the layout mode + * @param {Scope} scope - the circuit whose subcircuit we are editing + * @category layoutMode + */ +export function paneLayout(scope = globalScope) { + if (!simulationArea.selected && simulationArea.mouseDown) { + simulationArea.selected = true + simulationArea.lastSelected = scope.root + simulationArea.hover = scope.root + } else if ( + simulationArea.lastSelected === scope.root && + simulationArea.mouseDown + ) { + // pane canvas + if (true) { + globalScope.ox = + simulationArea.mouseRawX - + simulationArea.mouseDownRawX + + simulationArea.oldx + globalScope.oy = + simulationArea.mouseRawY - + simulationArea.mouseDownRawY + + simulationArea.oldy + globalScope.ox = Math.round(globalScope.ox) + globalScope.oy = Math.round(globalScope.oy) + gridUpdateSet(true) + if (!embed && !lightMode) miniMapArea.setup() + } + } else if (simulationArea.lastSelected === scope.root) { + // Select multiple objects + + simulationArea.lastSelected = undefined + simulationArea.selected = false + simulationArea.hover = undefined + } +} + +/** + * Function to render layout on screen in layoutMode + * @param {Scope=} scope + * @category layoutMode + */ +export function renderLayout(scope = globalScope) { + if (!layoutModeGet()) return + var ctx = simulationArea.context + simulationArea.clear() + ctx.strokeStyle = 'black' + ctx.fillStyle = 'white' + ctx.lineWidth = correctWidth(3) + // Draw base rectangle + ctx.beginPath() + rect2( + ctx, + 0, + 0, + tempBuffer.layout.width, + tempBuffer.layout.height, + 0, + 0, + 'RIGHT' + ) + ctx.fill() + ctx.stroke() + ctx.beginPath() + ctx.textAlign = 'center' + ctx.fillStyle = 'black' + if (tempBuffer.layout.titleEnabled) { + fillText( + ctx, + scope.name, + tempBuffer.layout.title_x, + tempBuffer.layout.title_y, + 11 + ) + } + + // Draw labels + var info + for (let i = 0; i < tempBuffer.Input.length; i++) { + if (!tempBuffer.Input[i].label) continue + info = determineLabel( + tempBuffer.Input[i].x, + tempBuffer.Input[i].y, + scope + ) + ;[ctx.textAlign] = info + fillText( + ctx, + tempBuffer.Input[i].label, + tempBuffer.Input[i].x + info[1], + tempBuffer.Input[i].y + info[2], + 12 + ) + } + for (let i = 0; i < tempBuffer.Output.length; i++) { + if (!tempBuffer.Output[i].label) continue + info = determineLabel( + tempBuffer.Output[i].x, + tempBuffer.Output[i].y, + scope + ) + ;[ctx.textAlign] = info + fillText( + ctx, + tempBuffer.Output[i].label, + tempBuffer.Output[i].x + info[1], + tempBuffer.Output[i].y + info[2], + 12 + ) + } + ctx.fill() + + // Draw points + for (let i = 0; i < tempBuffer.Input.length; i++) { + tempBuffer.Input[i].draw() + } + for (let i = 0; i < tempBuffer.Output.length; i++) { + tempBuffer.Output[i].draw() + } + + if (gridUpdateGet()) { + dots() + } + + // Update UI position + for (let i = 0; i < tempBuffer.subElements.length; i++) { + tempBuffer.subElements[i].update() + + // element nodes + for (let j = 0; j < tempBuffer.subElements[i].nodeList.length; j++) + tempBuffer.subElements[i].nodeList[j].update() + } + + // Show properties of selected element + if (!embed && prevPropertyObjGet() != simulationArea.lastSelected) { + if (simulationArea.lastSelected) { + showProperties(simulationArea.lastSelected) + } + } + // Render objects + for (let i = 0; i < circuitElementList.length; i++) { + if (globalScope[circuitElementList[i]].length === 0) continue + if (!globalScope[circuitElementList[i]][0].canShowInSubcircuit) continue + + let elementName = circuitElementList[i] + + for (let j = 0; j < globalScope[elementName].length; j++) { + if ( + globalScope[elementName][j].subcircuitMetadata.showInSubcircuit + ) { + globalScope[elementName][j].drawLayoutMode() + } + } + } +} + +/** + * Update UI, positions of inputs and outputs + * @param {Scope} scope - the circuit whose subcircuit we are editing + * @category layoutMode + */ +export function layoutUpdate(scope = globalScope) { + if (!layoutModeGet()) return + willBeUpdatedSet(false) + for (let i = 0; i < tempBuffer.Input.length; i++) { + tempBuffer.Input[i].update() + } + for (let i = 0; i < tempBuffer.Output.length; i++) { + tempBuffer.Output[i].update() + } + + for (let i = 0; i < circuitElementList.length; i++) { + if (globalScope[circuitElementList[i]].length === 0) continue + if (!globalScope[circuitElementList[i]][0].canShowInSubcircuit) continue + let elementName = circuitElementList[i] + + for (let j = 0; j < globalScope[elementName].length; j++) { + if ( + globalScope[elementName][j].subcircuitMetadata.showInSubcircuit + ) { + globalScope[elementName][j].layoutUpdate() + } + } + } + paneLayout(scope) + renderLayout(scope) +} + +/** + * Helper function to reset all nodes to original default positions + * @category layoutMode + */ +export function layoutResetNodes() { + tempBuffer.layout.width = 100 + tempBuffer.layout.height = + Math.max(tempBuffer.Input.length, tempBuffer.Output.length) * 20 + 20 + for (let i = 0; i < tempBuffer.Input.length; i++) { + tempBuffer.Input[i].x = 0 + tempBuffer.Input[i].y = i * 20 + 20 + } + for (let i = 0; i < tempBuffer.Output.length; i++) { + tempBuffer.Output[i].x = tempBuffer.layout.width + tempBuffer.Output[i].y = i * 20 + 20 + } +} + +/** + * Increase width, and move all nodes + * @category layoutMode + */ +export function increaseLayoutWidth() { + for (let i = 0; i < tempBuffer.Input.length; i++) { + if (tempBuffer.Input[i].x === tempBuffer.layout.width) { + tempBuffer.Input[i].x += 10 + } + } + for (let i = 0; i < tempBuffer.Output.length; i++) { + if (tempBuffer.Output[i].x === tempBuffer.layout.width) { + tempBuffer.Output[i].x += 10 + } + } + tempBuffer.layout.width += 10 +} + +/** + * Increase Height, and move all nodes + * @category layoutMode + */ +export function increaseLayoutHeight() { + for (let i = 0; i < tempBuffer.Input.length; i++) { + if (tempBuffer.Input[i].y === tempBuffer.layout.height) { + tempBuffer.Input[i].y += 10 + } + } + for (let i = 0; i < tempBuffer.Output.length; i++) { + if (tempBuffer.Output[i].y === tempBuffer.layout.height) { + tempBuffer.Output[i].y += 10 + } + } + tempBuffer.layout.height += 10 +} + +/** + * Decrease Width, and move all nodes, check if space is there + * @category layoutMode + */ +export function decreaseLayoutWidth() { + if (tempBuffer.layout.width < 30) return + for (let i = 0; i < tempBuffer.Input.length; i++) { + if (tempBuffer.Input[i].x === tempBuffer.layout.width - 10) { + showMessage('No space. Move or delete some nodes to make space.') + return + } + } + for (let i = 0; i < tempBuffer.Output.length; i++) { + if (tempBuffer.Output[i].x === tempBuffer.layout.width - 10) { + showMessage('No space. Move or delete some nodes to make space.') + return + } + } + + for (let i = 0; i < tempBuffer.Input.length; i++) { + if (tempBuffer.Input[i].x === tempBuffer.layout.width) { + tempBuffer.Input[i].x -= 10 + } + } + for (let i = 0; i < tempBuffer.Output.length; i++) { + if (tempBuffer.Output[i].x === tempBuffer.layout.width) { + tempBuffer.Output[i].x -= 10 + } + } + tempBuffer.layout.width -= 10 +} + +/** + * Decrease Height, and move all nodes, check if space is there + * @category layoutMode + */ +export function decreaseLayoutHeight() { + if (tempBuffer.layout.height < 30) return + for (let i = 0; i < tempBuffer.Input.length; i++) { + if (tempBuffer.Input[i].y === tempBuffer.layout.height - 10) { + showMessage('No space. Move or delete some nodes to make space.') + return + } + } + for (let i = 0; i < tempBuffer.Output.length; i++) { + if (tempBuffer.Output[i].y === tempBuffer.layout.height - 10) { + showMessage('No space. Move or delete some nodes to make space.') + return + } + } + + for (let i = 0; i < tempBuffer.Input.length; i++) { + if (tempBuffer.Input[i].y === tempBuffer.layout.height) { + tempBuffer.Input[i].y -= 10 + } + } + for (let i = 0; i < tempBuffer.Output.length; i++) { + if (tempBuffer.Output[i].y === tempBuffer.layout.height) { + tempBuffer.Output[i].y -= 10 + } + } + tempBuffer.layout.height -= 10 +} + +/** + * Helper functions to move the titles + * @category layoutMode + */ +export function layoutTitleUp() { + tempBuffer.layout.title_y -= 5 +} + +/** + * Helper functions to move the titles + * @category layoutMode + */ +export function layoutTitleDown() { + tempBuffer.layout.title_y += 5 +} + +/** + * Helper functions to move the titles + * @category layoutMode + */ +export function layoutTitleRight() { + tempBuffer.layout.title_x += 5 +} + +/** + * Helper functions to move the titles + * @category layoutMode + */ +export function layoutTitleLeft() { + tempBuffer.layout.title_x -= 5 +} + +/** + * Helper functions to move the titles + * @category layoutMode + */ +export function toggleLayoutTitle() { + tempBuffer.layout.titleEnabled = !tempBuffer.layout.titleEnabled +} + +/** + * just toggles back to normal mode + * @category layoutMode + */ +export function cancelLayout() { + if (layoutModeGet()) { + // eslint-disable-next-line no-use-before-define + toggleLayoutMode() + } +} + +/** + * Store all data into layout and exit + * @category layoutMode + */ +export function saveLayout() { + if (layoutModeGet()) { + for (let i = 0; i < tempBuffer.Input.length; i++) { + tempBuffer.Input[i].parent.layoutProperties.x = + tempBuffer.Input[i].x + tempBuffer.Input[i].parent.layoutProperties.y = + tempBuffer.Input[i].y + } + for (let i = 0; i < tempBuffer.Output.length; i++) { + tempBuffer.Output[i].parent.layoutProperties.x = + tempBuffer.Output[i].x + tempBuffer.Output[i].parent.layoutProperties.y = + tempBuffer.Output[i].y + } + globalScope.layout = { ...tempBuffer.layout } + // eslint-disable-next-line no-use-before-define + toggleLayoutMode() + } +} + +/** + * Function to toggle between layoutMode and normal Mode + * the sidebar is disabled and n properties are shown. + * @category layoutMode + */ +export function toggleLayoutMode() { + // hideProperties() + // lines from hideProperty function() <--- + prevPropertyObjSet(undefined) + $('.objectPropertyAttribute').unbind('change keyup paste click') + + if (layoutModeGet()) { + layoutModeSet(false) + $('#layoutDialog').fadeOut() + $('.layoutElementPanel').fadeOut() + $('.elementPanel').fadeIn() + $('.timing-diagram-panel').fadeIn() + $('.testbench-manual-panel').fadeIn() + globalScope.centerFocus(false) + if (globalScope.verilogMetadata.isVerilogCircuit) verilogModeSet(true) + dots() + } else { + layoutModeSet(true) + verilogModeSet(false) + $('#layoutDialog').fadeIn() + $('.layoutElementPanel').fadeIn() + $('.elementPanel').fadeOut() + $('.timing-diagram-panel').fadeOut() + $('.testbench-manual-panel').fadeOut() + fillSubcircuitElements() + + globalScope.ox = 0 + globalScope.oy = 0 + globalScope.scale = DPR * 1.3 + dots() + tempBuffer = new LayoutBuffer() + // $('#toggleLayoutTitle')[0].checked = tempBuffer.layout.titleEnabled + } + update(globalScope, true) + scheduleUpdate() +} + +export const layoutFunctions = { + decreaseLayoutWidth, + increaseLayoutWidth, + decreaseLayoutHeight, + increaseLayoutHeight, + layoutResetNodes, + layoutTitleUp, + layoutTitleDown, + layoutTitleLeft, + layoutTitleRight, + toggleLayoutTitle, + cancelLayout, + saveLayout, + toggleLayoutMode, +} + +// export function setupLayoutModePanelListeners() { +// $('#decreaseLayoutWidth').on('click', () => { +// decreaseLayoutWidth() +// }) +// $('#increaseLayoutWidth').on('click', () => { +// increaseLayoutWidth() +// }) +// $('#decreaseLayoutHeight').on('click', () => { +// decreaseLayoutHeight() +// }) +// $('#increaseLayoutHeight').on('click', () => { +// increaseLayoutHeight() +// }) +// $('#layoutResetNodes').on('click', () => { +// layoutResetNodes() +// }) +// $('#layoutTitleUp').on('click', () => { +// layoutTitleUp() +// }) +// $('#layoutTitleDown').on('click', () => { +// layoutTitleDown() +// }) +// $('#layoutTitleLeft').on('click', () => { +// layoutTitleLeft() +// }) +// $('#layoutTitleRight').on('click', () => { +// layoutTitleRight() +// }) +// $('#toggleLayoutTitle').on('click', () => { +// toggleLayoutTitle() +// }) +// $('#saveLayout').on('click', () => { +// saveLayout() +// }) +// $('#cancelLayout').on('click', () => { +// cancelLayout() +// }) +// $('#layoutDialog button').on('click', () => { +// scheduleUpdate() +// }) +// $('#layoutDialog input').on('click', () => { +// scheduleUpdate() +// }) +// } diff --git a/v0/src/simulator/src/listeners.js b/v0/src/simulator/src/listeners.js new file mode 100644 index 00000000..460be076 --- /dev/null +++ b/v0/src/simulator/src/listeners.js @@ -0,0 +1,761 @@ +// Most Listeners are stored here +import { + layoutModeGet, + tempBuffer, + layoutUpdate, + // setupLayoutModePanelListeners, +} from './layoutMode' +import simulationArea from './simulationArea' +import { + scheduleUpdate, + update, + updateSelectionsAndPane, + wireToBeCheckedSet, + updatePositionSet, + updateSimulationSet, + updateCanvasSet, + gridUpdateSet, + errorDetectedSet, +} from './engine' +import { changeScale } from './canvasApi' +import { scheduleBackup } from './data/backupCircuit' +import { + hideProperties, + deleteSelected, + uxvar, + fullView, + exitFullView, +} from './ux' +import { + updateRestrictedElementsList, + updateRestrictedElementsInScope, + hideRestricted, + showRestricted, +} from './restrictedElementDiv' +import { removeMiniMap, updatelastMinimapShown } from './minimap' +import undo from './data/undo' +import redo from './data/redo' +import { copy, paste, selectAll } from './events' +import save from './data/save' +import { verilogModeGet } from './Verilog2CV' +import { setupTimingListeners } from './plotArea' + +var unit = 10 +var listenToSimulator = true + +export default function startListeners() { + // added the below functionalities in QuickButton.vue component local script tag part + + // $('#deleteSelected').on('click', () => { + // deleteSelected() + // }) + + // $('#zoomIn').on('click', () => { + // changeScale(0.2, 'zoomButton', 'zoomButton', 2) + // }) + + // $('#zoomOut').on('click', () => { + // changeScale(-0.2, 'zoomButton', 'zoomButton', 2) + // }) + + // $('#undoButton').on('click', () => { + // undo() + // }) + // $('#redoButton').on('click', () => { + // redo() + // }) + // $('#viewButton').on('click', () => { + // fullView() + // }) + + $(document).on('keyup', (e) => { + if (e.key === 'Escape') exitFullView() + }) + + $('#projectName').on('click', () => { + simulationArea.lastSelected = globalScope.root + setTimeout(() => { + document.getElementById('projname').select() + }, 100) + }) + /* Makes tabs reordering possible by making them sortable */ + // $("#tabsBar").sortable({ + // containment: 'parent', + // items: '> div', + // revert: false, + // opacity: 0.5, + // tolerance: 'pointer', + // placeholder: 'placeholder', + // forcePlaceholderSize: true, + // }); + + document + .getElementById('simulationArea') + .addEventListener('mousedown', (e) => { + simulationArea.mouseDown = true + + // Deselect Input + if (document.activeElement instanceof HTMLElement) + document.activeElement.blur() + + errorDetectedSet(false) + updateSimulationSet(true) + updatePositionSet(true) + updateCanvasSet(true) + + simulationArea.lastSelected = undefined + simulationArea.selected = false + simulationArea.hover = undefined + var rect = simulationArea.canvas.getBoundingClientRect() + simulationArea.mouseDownRawX = (e.clientX - rect.left) * DPR + simulationArea.mouseDownRawY = (e.clientY - rect.top) * DPR + simulationArea.mouseDownX = + Math.round( + (simulationArea.mouseDownRawX - globalScope.ox) / + globalScope.scale / + unit + ) * unit + simulationArea.mouseDownY = + Math.round( + (simulationArea.mouseDownRawY - globalScope.oy) / + globalScope.scale / + unit + ) * unit + simulationArea.oldx = globalScope.ox + simulationArea.oldy = globalScope.oy + + e.preventDefault() + scheduleBackup() + scheduleUpdate(1) + $('.dropdown.open').removeClass('open') + }) + document + .getElementById('simulationArea') + .addEventListener('mouseup', (e) => { + if (simulationArea.lastSelected) + simulationArea.lastSelected.newElement = false + /* + handling restricted circuit elements + */ + + if ( + simulationArea.lastSelected && + restrictedElements.includes( + simulationArea.lastSelected.objectType + ) && + !globalScope.restrictedCircuitElementsUsed.includes( + simulationArea.lastSelected.objectType + ) + ) { + globalScope.restrictedCircuitElementsUsed.push( + simulationArea.lastSelected.objectType + ) + updateRestrictedElementsList() + } + + // deselect multible elements with click + if ( + !simulationArea.shiftDown && + simulationArea.multipleObjectSelections.length > 0 + ) { + if ( + !simulationArea.multipleObjectSelections.includes( + simulationArea.lastSelected + ) + ) { + simulationArea.multipleObjectSelections = [] + } + } + }) + document + .getElementById('simulationArea') + .addEventListener('mousemove', onMouseMove) + + window.addEventListener('keyup', (e) => { + scheduleUpdate(1) + simulationArea.shiftDown = e.shiftKey + if (e.keyCode == 16) { + simulationArea.shiftDown = false + } + if (e.key == 'Meta' || e.key == 'Control') { + simulationArea.controlDown = false + } + }) + + window.addEventListener( + 'keydown', + (e) => { + if (document.activeElement.tagName == 'INPUT') return + if (document.activeElement != document.body) return + + simulationArea.shiftDown = e.shiftKey + if (e.key == 'Meta' || e.key == 'Control') { + simulationArea.controlDown = true + } + + if ( + simulationArea.controlDown && + e.key.charCodeAt(0) == 122 && + !simulationArea.shiftDown + ) { + // detect the special CTRL-Z code + undo() + } + if ( + simulationArea.controlDown && + e.key.charCodeAt(0) == 122 && + simulationArea.shiftDown + ) { + // detect the special Cmd + shift + z code (macOs) + redo() + } + if ( + simulationArea.controlDown && + e.key.charCodeAt(0) == 121 && + !simulationArea.shiftDown + ) { + // detect the special ctrl + Y code (windows) + redo() + } + + if (listenToSimulator) { + // If mouse is focusing on input element, then override any action + // if($(':focus').length){ + // return; + // } + + if ( + document.activeElement.tagName == 'INPUT' || + simulationArea.mouseRawX < 0 || + simulationArea.mouseRawY < 0 || + simulationArea.mouseRawX > width || + simulationArea.mouseRawY > height + ) { + return + } + // HACK TO REMOVE FOCUS ON PROPERTIES + if (document.activeElement.type == 'number') { + hideProperties() + showProperties(simulationArea.lastSelected) + } + + errorDetectedSet(false) + updateSimulationSet(true) + updatePositionSet(true) + simulationArea.shiftDown = e.shiftKey + + if (e.key == 'Meta' || e.key == 'Control') { + simulationArea.controlDown = true + } + + // zoom in (+) + if ( + (simulationArea.controlDown && + (e.keyCode == 187 || e.keyCode == 171)) || + e.keyCode == 107 + ) { + e.preventDefault() + ZoomIn() + } + // zoom out (-) + if ( + (simulationArea.controlDown && + (e.keyCode == 189 || e.keyCode == 173)) || + e.keyCode == 109 + ) { + e.preventDefault() + ZoomOut() + } + + if ( + simulationArea.mouseRawX < 0 || + simulationArea.mouseRawY < 0 || + simulationArea.mouseRawX > width || + simulationArea.mouseRawY > height + ) + return + + scheduleUpdate(1) + updateCanvasSet(true) + wireToBeCheckedSet(1) + + // Needs to be deprecated, moved to more recent listeners + if ( + simulationArea.controlDown && + (e.key == 'C' || e.key == 'c') + ) { + // simulationArea.copyList=simulationArea.multipleObjectSelections.slice(); + // if(simulationArea.lastSelected&&simulationArea.lastSelected!==simulationArea.root&&!simulationArea.copyList.contains(simulationArea.lastSelected)){ + // simulationArea.copyList.push(simulationArea.lastSelected); + // } + // copy(simulationArea.copyList); + } + + if ( + simulationArea.lastSelected && + simulationArea.lastSelected.keyDown + ) { + if ( + e.key.toString().length == 1 || + e.key.toString() == 'Backspace' || + e.key.toString() == 'Enter' + ) { + simulationArea.lastSelected.keyDown(e.key.toString()) + e.cancelBubble = true + e.returnValue = false + + //e.stopPropagation works in Firefox. + if (e.stopPropagation) { + e.stopPropagation() + e.preventDefault() + } + return + } + } + + if ( + simulationArea.lastSelected && + simulationArea.lastSelected.keyDown2 + ) { + if (e.key.toString().length == 1) { + simulationArea.lastSelected.keyDown2(e.key.toString()) + return + } + } + + if ( + simulationArea.lastSelected && + simulationArea.lastSelected.keyDown3 + ) { + if ( + e.key.toString() != 'Backspace' && + e.key.toString() != 'Delete' + ) { + simulationArea.lastSelected.keyDown3(e.key.toString()) + return + } + } + + if (e.keyCode == 16) { + simulationArea.shiftDown = true + if ( + simulationArea.lastSelected && + !simulationArea.lastSelected.keyDown && + simulationArea.lastSelected.objectType != 'Wire' && + simulationArea.lastSelected.objectType != + 'CircuitElement' && + !simulationArea.multipleObjectSelections.contains( + simulationArea.lastSelected + ) + ) { + simulationArea.multipleObjectSelections.push( + simulationArea.lastSelected + ) + } + } + + // Detect offline save shortcut (CTRL+SHIFT+S) + if ( + simulationArea.controlDown && + e.keyCode == 83 && + simulationArea.shiftDown + ) { + saveOffline() + e.preventDefault() + } + + // Detect Select all Shortcut + if ( + simulationArea.controlDown && + (e.keyCode == 65 || e.keyCode == 97) + ) { + selectAll() + e.preventDefault() + } + + // deselect all Shortcut + if (e.keyCode == 27) { + simulationArea.multipleObjectSelections = [] + simulationArea.lastSelected = undefined + e.preventDefault() + } + + if ( + (e.keyCode == 113 || e.keyCode == 81) && + simulationArea.lastSelected != undefined + ) { + if (simulationArea.lastSelected.bitWidth !== undefined) { + simulationArea.lastSelected.newBitWidth( + parseInt(prompt('Enter new bitWidth'), 10) + ) + } + } + + if ( + simulationArea.controlDown && + (e.key == 'T' || e.key == 't') + ) { + // e.preventDefault(); //browsers normally open a new tab + simulationArea.changeClockTime(prompt('Enter Time:')) + } + } + + if (e.keyCode == 8 || e.key == 'Delete') { + deleteSelected() + } + }, + true + ) + + document + .getElementById('simulationArea') + .addEventListener('dblclick', (e) => { + updateCanvasSet(true) + if ( + simulationArea.lastSelected && + simulationArea.lastSelected.dblclick !== undefined + ) { + simulationArea.lastSelected.dblclick() + } else if (!simulationArea.shiftDown) { + simulationArea.multipleObjectSelections = [] + } + scheduleUpdate(2) + }) + + document + .getElementById('simulationArea') + .addEventListener('mouseup', onMouseUp) + + document + .getElementById('simulationArea') + .addEventListener('mousewheel', MouseScroll) + document + .getElementById('simulationArea') + .addEventListener('DOMMouseScroll', MouseScroll) + + function MouseScroll(event) { + updateCanvasSet(true) + event.preventDefault() + var deltaY = event.wheelDelta ? event.wheelDelta : -event.detail + event.preventDefault() + var deltaY = event.wheelDelta ? event.wheelDelta : -event.detail + const direction = deltaY > 0 ? 1 : -1 + handleZoom(direction) + updateCanvasSet(true) + gridUpdateSet(true) + + if (layoutModeGet()) layoutUpdate() + else update() // Schedule update not working, this is INEFFICIENT + } + + document.addEventListener('cut', (e) => { + if (verilogModeGet()) return + if (document.activeElement.tagName == 'INPUT') return + if (document.activeElement.tagName != 'BODY') return + + if (listenToSimulator) { + simulationArea.copyList = + simulationArea.multipleObjectSelections.slice() + if ( + simulationArea.lastSelected && + simulationArea.lastSelected !== simulationArea.root && + !simulationArea.copyList.contains(simulationArea.lastSelected) + ) { + simulationArea.copyList.push(simulationArea.lastSelected) + } + + var textToPutOnClipboard = copy(simulationArea.copyList, true) + + // Updated restricted elements + updateRestrictedElementsInScope() + localStorage.setItem('clipboardData', textToPutOnClipboard) + e.preventDefault() + if (textToPutOnClipboard == undefined) return + if (isIe) { + window.clipboardData.setData('Text', textToPutOnClipboard) + } else { + e.clipboardData.setData('text/plain', textToPutOnClipboard) + } + } + }) + + document.addEventListener('copy', (e) => { + if (verilogModeGet()) return + if (document.activeElement.tagName == 'INPUT') return + if (document.activeElement.tagName != 'BODY') return + + if (listenToSimulator) { + simulationArea.copyList = + simulationArea.multipleObjectSelections.slice() + if ( + simulationArea.lastSelected && + simulationArea.lastSelected !== simulationArea.root && + !simulationArea.copyList.contains(simulationArea.lastSelected) + ) { + simulationArea.copyList.push(simulationArea.lastSelected) + } + + var textToPutOnClipboard = copy(simulationArea.copyList) + + // Updated restricted elements + updateRestrictedElementsInScope() + localStorage.setItem('clipboardData', textToPutOnClipboard) + e.preventDefault() + if (textToPutOnClipboard == undefined) return + if (isIe) { + window.clipboardData.setData('Text', textToPutOnClipboard) + } else { + e.clipboardData.setData('text/plain', textToPutOnClipboard) + } + } + }) + + document.addEventListener('paste', (e) => { + if (document.activeElement.tagName == 'INPUT') return + if (document.activeElement.tagName != 'BODY') return + + if (listenToSimulator) { + var data + if (isIe) { + data = window.clipboardData.getData('Text') + } else { + data = e.clipboardData.getData('text/plain') + } + + paste(data) + + // Updated restricted elements + updateRestrictedElementsInScope() + + e.preventDefault() + } + }) + + // 'drag and drop' event listener for subcircuit elements in layout mode + $('#subcircuitMenu').on( + 'dragstop', + '.draggableSubcircuitElement', + function (event, ui) { + const sideBarWidth = $('#guide_1')[0].clientWidth + let tempElement + + if (ui.position.top > 10 && ui.position.left > sideBarWidth) { + // make a shallow copy of the element with the new coordinates + tempElement = + globalScope[this.dataset.elementName][ + this.dataset.elementId + ] + + // Changing the coordinate doesn't work yet, nodes get far from element + tempElement.x = ui.position.left - sideBarWidth + tempElement.y = ui.position.top + for (let node of tempElement.nodeList) { + node.x = ui.position.left - sideBarWidth + node.y = ui.position.top + } + + tempBuffer.subElements.push(tempElement) + this.parentElement.removeChild(this) + } + } + ) + + restrictedElements.forEach((element) => { + $(`#${element}`).mouseover(() => { + showRestricted() + }) + + $(`#${element}`).mouseout(() => { + hideRestricted() + }) + }) + + zoomSliderListeners() + // setupLayoutModePanelListeners() + if (!embed) { + setupTimingListeners() + } +} + +var isIe = + navigator.userAgent.toLowerCase().indexOf('msie') != -1 || + navigator.userAgent.toLowerCase().indexOf('trident') != -1 + +function onMouseMove(e) { + var rect = simulationArea.canvas.getBoundingClientRect() + simulationArea.mouseRawX = (e.clientX - rect.left) * DPR + simulationArea.mouseRawY = (e.clientY - rect.top) * DPR + simulationArea.mouseXf = + (simulationArea.mouseRawX - globalScope.ox) / globalScope.scale + simulationArea.mouseYf = + (simulationArea.mouseRawY - globalScope.oy) / globalScope.scale + simulationArea.mouseX = Math.round(simulationArea.mouseXf / unit) * unit + simulationArea.mouseY = Math.round(simulationArea.mouseYf / unit) * unit + + updateCanvasSet(true) + + if ( + simulationArea.lastSelected && + (simulationArea.mouseDown || simulationArea.lastSelected.newElement) + ) { + updateCanvasSet(true) + var fn + + if (simulationArea.lastSelected == globalScope.root) { + fn = function () { + updateSelectionsAndPane() + } + } else { + fn = function () { + if (simulationArea.lastSelected) { + simulationArea.lastSelected.update() + } + } + } + scheduleUpdate(0, 20, fn) + } else { + scheduleUpdate(0, 200) + } +} + +function onMouseUp(e) { + simulationArea.mouseDown = false + if (!lightMode) { + updatelastMinimapShown() + setTimeout(removeMiniMap, 2000) + } + + errorDetectedSet(false) + updateSimulationSet(true) + updatePositionSet(true) + updateCanvasSet(true) + gridUpdateSet(true) + wireToBeCheckedSet(1) + + scheduleUpdate(1) + simulationArea.mouseDown = false + + for (var i = 0; i < 2; i++) { + updatePositionSet(true) + wireToBeCheckedSet(1) + update() + } + errorDetectedSet(false) + updateSimulationSet(true) + updatePositionSet(true) + updateCanvasSet(true) + gridUpdateSet(true) + wireToBeCheckedSet(1) + + scheduleUpdate(1) + var rect = simulationArea.canvas.getBoundingClientRect() + + if ( + !( + simulationArea.mouseRawX < 0 || + simulationArea.mouseRawY < 0 || + simulationArea.mouseRawX > width || + simulationArea.mouseRawY > height + ) + ) { + uxvar.smartDropXX = simulationArea.mouseX + 100 // Math.round(((simulationArea.mouseRawX - globalScope.ox+100) / globalScope.scale) / unit) * unit; + uxvar.smartDropYY = simulationArea.mouseY - 50 // Math.round(((simulationArea.mouseRawY - globalScope.oy+100) / globalScope.scale) / unit) * unit; + } +} + +function resizeTabs() { + var $windowsize = $('body').width() + var $sideBarsize = $('.side').width() + var $maxwidth = $windowsize - $sideBarsize + $('#tabsBar div').each(function (e) { + $(this).css({ 'max-width': $maxwidth - 30 }) + }) +} + +window.addEventListener('resize', resizeTabs) +resizeTabs() + +// $(() => { +// $('[data-toggle="tooltip"]').tooltip() +// }) + +// direction is only 1 or -1 +function handleZoom(direction) { + var zoomSlider = $('#customRange1') + var currentSliderValue = parseInt(zoomSlider.val(), 10) + currentSliderValue += direction + + if (globalScope.scale > 0.5 * DPR) { + zoomSlider.val(currentSliderValue).change() + } else if (globalScope.scale < 4 * DPR) { + zoomSlider.val(currentSliderValue).change() + } + + gridUpdateSet(true) + scheduleUpdate() +} + +export function ZoomIn() { + handleZoom(1) +} + +export function ZoomOut() { + handleZoom(-1) +} + +function zoomSliderListeners() { + document.getElementById('customRange1').value = 5 + document + .getElementById('simulationArea') + .addEventListener('DOMMouseScroll', zoomSliderScroll) + document + .getElementById('simulationArea') + .addEventListener('mousewheel', zoomSliderScroll) + let curLevel = document.getElementById('customRange1').value + $(document).on('input change', '#customRange1', function (e) { + let newValue = $(this).val() + let changeInScale = newValue - curLevel + updateCanvasSet(true) + changeScale(changeInScale * 0.1, 'zoomButton', 'zoomButton', 3) + gridUpdateSet(true) + curLevel = newValue + }) + function zoomSliderScroll(e) { + let zoomLevel = document.getElementById('customRange1').value + let deltaY = e.wheelDelta ? e.wheelDelta : -e.detail + const directionY = deltaY > 0 ? 1 : -1 + if (directionY > 0) zoomLevel++ + else zoomLevel-- + if (zoomLevel >= 45) { + zoomLevel = 45 + document.getElementById('customRange1').value = 45 + } else if (zoomLevel <= 0) { + zoomLevel = 0 + document.getElementById('customRange1').value = 0 + } else { + document.getElementById('customRange1').value = zoomLevel + curLevel = zoomLevel + } + } + + // previously used for the + and - zoom buttons in quickButtons + + // function sliderZoomButton(direction) { + // var zoomSlider = $('#customRange1') + // var currentSliderValue = parseInt(zoomSlider.val(), 10) + // if (direction === -1) { + // currentSliderValue-- + // } else { + // currentSliderValue++ + // } + // zoomSlider.val(currentSliderValue).change() + // } + + // $('#decrement').click(() => { + // sliderZoomButton(-1) + // }) + + // $('#increment').click(() => { + // sliderZoomButton(1) + // }) +} diff --git a/v0/src/simulator/src/metadata.json b/v0/src/simulator/src/metadata.json new file mode 100644 index 00000000..8ed4f225 --- /dev/null +++ b/v0/src/simulator/src/metadata.json @@ -0,0 +1,179 @@ +{ + "circuitElementList": [ + "Input", + "Output", + "NotGate", + "OrGate", + "AndGate", + "NorGate", + "NandGate", + "XorGate", + "XnorGate", + "SevenSegDisplay", + "SixteenSegDisplay", + "HexDisplay", + "Multiplexer", + "BitSelector", + "Splitter", + "Power", + "Ground", + "ConstantVal", + "ControlledInverter", + "TriState", + "Adder", + "verilogMultiplier", + "verilogDivider", + "verilogPower", + "verilogShiftLeft", + "TwoComplement", + "verilogShiftRight", + "Rom", + "RAM", + "verilogRAM", + "EEPROM", + "TflipFlop", + "JKflipFlop", + "SRflipFlop", + "DflipFlop", + "TTY", + "Keyboard", + "Clock", + "DigitalLed", + "Stepper", + "VariableLed", + "RGBLed", + "SquareRGBLed", + "RGBLedMatrix", + "Button", + "Demultiplexer", + "Buffer", + "SubCircuit", + "Flag", + "MSB", + "LSB", + "PriorityEncoder", + "Tunnel", + "ALU", + "Decoder", + "Random", + "Counter", + "Dlatch", + "TB_Input", + "TB_Output", + "ForceGate" + ], + "annotationList": ["Text", "Rectangle", "Arrow", "ImageAnnotation"], + "inputList": [ + "Random", + "Dlatch", + "JKflipFlop", + "TflipFlop", + "SRflipFlop", + "DflipFlop", + "Buffer", + "Stepper", + "Ground", + "Power", + "ConstantVal", + "Input", + "Clock", + "Button", + "Counter" + ], + "subCircuitInputList": [ + "Random", + "Dlatch", + "JKflipFlop", + "TflipFlop", + "SRflipFlop", + "DflipFlop", + "Buffer", + "Stepper", + "Ground", + "Power", + "ConstantVal", + "Clock", + "Button", + "Counter" + ], + "elementHierarchy": { + "Input": [ + { "name": "Input", "label": "Input" }, + { "name": "Button", "label": "Button" }, + { "name": "Power", "label": "Power" }, + { "name": "Ground", "label": "Ground" }, + { "name": "ConstantVal", "label": "Constant Value" }, + { "name": "Stepper", "label": "Stepper" }, + { "name": "Random", "label": "Random" }, + { "name": "Counter", "label": "Counter" } + ], + "Output": [ + { "name": "Output", "label": "Output" }, + { "name": "RGBLed", "label": "RGB Led" }, + { "name": "DigitalLed", "label": "Digital Led" }, + { "name": "VariableLed", "label": "Variable Led" }, + { "name": "HexDisplay", "label": "Hex Display" }, + { "name": "SevenSegDisplay", "label": "Seven Segment Display" }, + { "name": "SixteenSegDisplay", "label": "Sixteen Segment Display" }, + { "name": "SquareRGBLed", "label": "Square RGB Led" }, + { "name": "RGBLedMatrix", "label": "RGB Led Matrix" } + ], + "Gates": [ + { "name": "AndGate", "label": "And Gate" }, + { "name": "OrGate", "label": "Or Gate" }, + { "name": "NotGate", "label": "Not Gate" }, + { "name": "XorGate", "label": "Xor Gate" }, + { "name": "NandGate", "label": "Nand Gate" }, + { "name": "NorGate", "label": "Nor Gate" }, + { "name": "XnorGate", "label": "Xnor Gate" } + ], + "Decoders & Plexers": [ + { "name": "Multiplexer", "label": "Multiplexer" }, + { "name": "Demultiplexer", "label": "Demultiplexer" }, + { "name": "BitSelector", "label": "Bit Selector" }, + { "name": "MSB", "label": "MSB(Most Significant Bit)" }, + { "name": "LSB", "label": "LSB(Least Significant Bit)" }, + { "name": "PriorityEncoder", "label": "Priority Encoder" }, + { "name": "Decoder", "label": "Decoder" } + ], + "Sequential Elements": [ + { "name": "DflipFlop", "label": "D flip Flop" }, + { "name": "Dlatch", "label": "D latch" }, + { "name": "TflipFlop", "label": "T flip Flop" }, + { "name": "JKflipFlop", "label": "JK flip Flop" }, + { "name": "SRflipFlop", "label": "SR flip Flop" }, + { "name": "TTY", "label": "TTY" }, + { "name": "Keyboard", "label": "Keyboard" }, + { "name": "Clock", "label": "Clock" }, + { "name": "Rom", "label": "ROM" }, + { "name": "RAM", "label": "RAM" }, + { "name": "verilogRAM", "label": "Verilog RAM" }, + { "name": "EEPROM", "label": "EEPROM" } + ], + "Annotation": [ + { "name": "Rectangle", "label": "Rectangle" }, + { "name": "Arrow", "label": "Arrow" }, + { "name": "ImageAnnotation", "label": "Image Annotation" }, + { "name": "Text", "label": "Text" } + ], + "Misc": [ + { "name": "TwoComplement", "label": "Two Complement" }, + { "name": "Flag", "label": "Flag" }, + { "name": "Splitter", "label": "Splitter" }, + { "name": "Adder", "label": "Adder" }, + { "name": "ALU", "label": "ALU(Arithmetic and Logical Unit)" }, + { "name": "TriState", "label": "TriState Flip Flop" }, + { "name": "Tunnel", "label": "Tunnel" }, + { "name": "verilogMultiplier", "label": "Verilog Multiplier" }, + { "name": "verilogDivider", "label": "Verilog Divider" }, + { "name": "verilogPower", "label": "Verilog Power" }, + { "name": "verilogShiftLeft", "label": "Verilog Shift Left" }, + { "name": "verilogShiftRight", "label": "Verilog Shift Right" }, + { "name": "Buffer", "label": "Buffer" }, + { "name": "ControlledInverter", "label": "Controlled Inverter" }, + { "name": "TB_Input", "label": "TB Input" }, + { "name": "TB_Output", "label": "TB Output" }, + { "name": "ForceGate", "label": "Force Gate" } + ] + } +} diff --git a/v0/src/simulator/src/minimap.js b/v0/src/simulator/src/minimap.js new file mode 100644 index 00000000..6cfcf49d --- /dev/null +++ b/v0/src/simulator/src/minimap.js @@ -0,0 +1,193 @@ +import simulationArea from './simulationArea' +import { colors } from './themer/themer' +import { layoutModeGet } from './layoutMode' + +/** + * @type {Object} miniMapArea + * This object is used to draw the miniMap. + * @property {number} pageY + * @property {number} pageX + * @property {HTMLCanvasObject} canvas - the canvas object + * @property {function} setup - used to setup the parameters and dimensions + * @property {function} play - used to draw outline of minimap and call resolve + * @property {function} resolve - used to resolve all objects and draw them on minimap + * @property {function} clear - used to clear minimap + * @category minimap + */ +var miniMapArea +export default miniMapArea = { + canvas: document.getElementById('miniMapArea'), + setup() { + if (lightMode) return + this.canvas = document.getElementById('miniMapArea') + this.pageHeight = height // Math.round(((parseInt($("#simulationArea").height())))/ratio)*ratio-50; // -50 for tool bar? Check again + this.pageWidth = width // Math.round(((parseInt($("#simulationArea").width())))/ratio)*ratio; + this.pageY = this.pageHeight - globalScope.oy + this.pageX = this.pageWidth - globalScope.ox + + if (simulationArea.minHeight != undefined) { + this.minY = Math.min( + simulationArea.minHeight, + -globalScope.oy / globalScope.scale + ) + } else { + this.minY = -globalScope.oy / globalScope.scale + } + if (simulationArea.maxHeight != undefined) { + this.maxY = Math.max( + simulationArea.maxHeight, + this.pageY / globalScope.scale + ) + } else { + this.maxY = this.pageY / globalScope.scale + } + if (simulationArea.minWidth != undefined) { + this.minX = Math.min( + simulationArea.minWidth, + -globalScope.ox / globalScope.scale + ) + } else { + this.minX = -globalScope.ox / globalScope.scale + } + if (simulationArea.maxWidth != undefined) { + this.maxX = Math.max( + simulationArea.maxWidth, + this.pageX / globalScope.scale + ) + } else { + this.maxX = this.pageX / globalScope.scale + } + + var h = this.maxY - this.minY + var w = this.maxX - this.minX + + var ratio = Math.min(250 / h, 250 / w) + if (h > w) { + this.canvas.height = 250.0 + this.canvas.width = (250.0 * w) / h + } else { + this.canvas.width = 250.0 + this.canvas.height = (250.0 * h) / w + } + + this.canvas.height += 5 + this.canvas.width += 5 + + document.getElementById('miniMap').style.height = this.canvas.height + document.getElementById('miniMap').style.width = this.canvas.width + this.ctx = this.canvas.getContext('2d') + this.play(ratio) + }, + + play(ratio) { + if (lightMode || layoutModeGet()) return + + this.ctx.fillStyle = '#bbb' + this.ctx.rect(0, 0, this.canvas.width, this.canvas.height) + this.ctx.fill() + this.resolve(ratio) + }, + resolve(ratio) { + if (lightMode) return + + this.ctx.fillStyle = '#ddd' + this.ctx.beginPath() + this.ctx.rect( + 2.5 + + ((this.pageX - this.pageWidth) / globalScope.scale - + this.minX) * + ratio, + 2.5 + + ((this.pageY - this.pageHeight) / globalScope.scale - + this.minY) * + ratio, + (this.pageWidth * ratio) / globalScope.scale, + (this.pageHeight * ratio) / globalScope.scale + ) + this.ctx.fill() + + // to show the area of current canvas + var lst = updateOrder + const miniFill = colors['mini_fill'] + const miniStroke = colors['mini_stroke'] + + this.ctx.strokeStyle = miniStroke + this.ctx.fillStyle = miniFill + for (var i = 0; i < lst.length; i++) { + if (lst[i] === 'wires') { + for (var j = 0; j < globalScope[lst[i]].length; j++) { + this.ctx.beginPath() + this.ctx.moveTo( + 2.5 + + (globalScope[lst[i]][j].node1.absX() - this.minX) * + ratio, + 2.5 + + (globalScope[lst[i]][j].node1.absY() - this.minY) * + ratio + ) + this.ctx.lineTo( + 2.5 + + (globalScope[lst[i]][j].node2.absX() - this.minX) * + ratio, + 2.5 + + (globalScope[lst[i]][j].node2.absY() - this.minY) * + ratio + ) + this.ctx.stroke() + } + } else if (lst[i] != 'nodes') { + // Don't include SquareRGBLed here; it has correct size. + var ledY = 0 + if ( + lst[i] == 'DigitalLed' || + lst[i] == 'VariableLed' || + lst[i] == 'RGBLed' + ) { + ledY = 20 + } + + for (var j = 0; j < globalScope[lst[i]].length; j++) { + var xx = globalScope[lst[i]][j].x - simulationArea.minWidth + var yy = globalScope[lst[i]][j].y - simulationArea.minHeight + this.ctx.beginPath() + var obj = globalScope[lst[i]][j] + this.ctx.rect( + 2.5 + (obj.x - obj.leftDimensionX - this.minX) * ratio, + 2.5 + (obj.y - obj.upDimensionY - this.minY) * ratio, + (obj.rightDimensionX + obj.leftDimensionX) * ratio, + (obj.downDimensionY + obj.upDimensionY + ledY) * ratio + ) + + this.ctx.fill() + this.ctx.stroke() + } + } + } + }, + clear() { + if (lightMode) return + $('#miniMapArea').css('z-index', '-1') + this.context.clearRect(0, 0, this.canvas.width, this.canvas.height) + }, +} +var lastMiniMapShown +export function updatelastMinimapShown() { + lastMiniMapShown = new Date().getTime() +} +export function removeMiniMap() { + if (lightMode) return + + if ( + simulationArea.lastSelected == globalScope.root && + simulationArea.mouseDown + ) + return + if (lastMiniMapShown + 2000 >= new Date().getTime()) { + setTimeout( + removeMiniMap, + lastMiniMapShown + 2000 - new Date().getTime() + ) + return + } + $('#miniMap').fadeOut('fast') +} diff --git a/v0/src/simulator/src/moduleSetup.js b/v0/src/simulator/src/moduleSetup.js new file mode 100644 index 00000000..5002bc69 --- /dev/null +++ b/v0/src/simulator/src/moduleSetup.js @@ -0,0 +1,135 @@ +import modules from './modules' +import Adder from './modules/Adder' +import ALU from './modules/ALU' +import AndGate from './modules/AndGate' +import Arrow from './modules/Arrow' +import ImageAnnotation from './modules/ImageAnnotation' +import BitSelector from './modules/BitSelector' +import Buffer from './modules/Buffer' +import Button from './modules/Button' +import ConstantVal from './modules/ConstantVal' +import ControlledInverter from './modules/ControlledInverter' +import Counter from './modules/Counter' +import Decoder from './modules/Decoder' +import Demultiplexer from './modules/Demultiplexer' +import DigitalLed from './modules/DigitalLed' +import Flag from './modules/Flag' +import Ground from './modules/Ground' +import HexDisplay from './modules/HexDisplay' +import Input from './modules/Input' +import LSB from './modules/LSB' +import MSB from './modules/MSB' +import Multiplexer from './modules/Multiplexer' +import NandGate from './modules/NandGate' +import NorGate from './modules/NorGate' +import NotGate from './modules/NotGate' +import OrGate from './modules/OrGate' +import Output from './modules/Output' +import Power from './modules/Power' +import PriorityEncoder from './modules/PriorityEncoder' +import Random from './modules/Random' +import Rectangle from './modules/Rectangle' +import RGBLed from './modules/RGBLed' +import RGBLedMatrix from './modules/RGBLedMatrix' +import SevenSegDisplay from './modules/SevenSegDisplay' +import SixteenSegDisplay from './modules/SixteenSegDisplay' +import Splitter from './modules/Splitter' +import SquareRGBLed from './modules/SquareRGBLed' +import Stepper from './modules/Stepper' +import Text from './modules/Text' +import TriState from './modules/TriState' +import Tunnel from './modules/Tunnel' +import TwoComplement from './modules/TwoComplement' +import VariableLed from './modules/VariableLed' +import XnorGate from './modules/XnorGate' +import XorGate from './modules/XorGate' +import Clock from './sequential/Clock' +import DflipFlop from './sequential/DflipFlop' +import Dlatch from './sequential/Dlatch' +import EEPROM from './sequential/EEPROM' +import JKflipFlop from './sequential/JKflipFlop' +import Keyboard from './sequential/Keyboard' +import RAM from './sequential/RAM' +import Rom from './sequential/Rom' +import SRflipFlop from './sequential/SRflipFlop' +import TflipFlop from './sequential/TflipFlop' +import TTY from './sequential/TTY' +import ForceGate from './testbench/ForceGate' +import TB_Input from './testbench/testbenchInput' +import TB_Output from './testbench/testbenchOutput' +import verilogMultiplier from './modules/verilogMultiplier' +import verilogDivider from './modules/verilogDivider' +import verilogPower from './modules/verilogPower' +import verilogShiftLeft from './modules/verilogShiftLeft' +import verilogShiftRight from './modules/verilogShiftRight' +import verilogRAM from './sequential/verilogRAM' + +export default function setupModules() { + var moduleSet = { + AndGate, + Random, + NandGate, + Counter, + Multiplexer, + XorGate, + XnorGate, + SevenSegDisplay, + SixteenSegDisplay, + HexDisplay, + OrGate, + Stepper, + NotGate, + Text, + TriState, + Buffer, + ControlledInverter, + Adder, + verilogMultiplier, + verilogDivider, + verilogPower, + verilogShiftLeft, + verilogShiftRight, + TwoComplement, + Splitter, + Ground, + Power, + Input, + Output, + BitSelector, + ConstantVal, + NorGate, + DigitalLed, + VariableLed, + Button, + RGBLed, + SquareRGBLed, + Demultiplexer, + Decoder, + Flag, + MSB, + LSB, + PriorityEncoder, + Tunnel, + ALU, + Rectangle, + Arrow, + ImageAnnotation, + RGBLedMatrix, + TflipFlop, + DflipFlop, + Dlatch, + SRflipFlop, + JKflipFlop, + TTY, + Keyboard, + Clock, + Rom, + EEPROM, + RAM, + verilogRAM, + TB_Input, + TB_Output, + ForceGate, + } + Object.assign(modules, moduleSet) +} diff --git a/v0/src/simulator/src/modules.js b/v0/src/simulator/src/modules.js new file mode 100644 index 00000000..41bacb6f --- /dev/null +++ b/v0/src/simulator/src/modules.js @@ -0,0 +1,60 @@ +/* eslint-disable import/no-cycle */ +import simulationArea from './simulationArea' + +export function getNextPosition(x = 0, scope = globalScope) { + let possibleY = 20 + const done = {} + for (let i = 0; i < scope.Input.length - 1; i++) { + if (scope.Input[i].layoutProperties.x === x) { + done[scope.Input[i].layoutProperties.y] = 1 + } + } + for (let i = 0; i < scope.Output.length; i++) { + if (scope.Output[i].layoutProperties.x === x) { + done[scope.Output[i].layoutProperties.y] = 1 + } + } + while (done[possibleY] || done[possibleY + 10] || done[possibleY - 10]) { + possibleY += 10 + } + const height = possibleY + 20 + if (height > scope.layout.height) { + const oldHeight = scope.layout.height + scope.layout.height = height + for (let i = 0; i < scope.Input.length; i++) { + if (scope.Input[i].layoutProperties.y === oldHeight) { + scope.Input[i].layoutProperties.y = scope.layout.height + } + } + for (let i = 0; i < scope.Output.length; i++) { + if (scope.Output[i].layoutProperties.y === oldHeight) { + scope.Output[i].layoutProperties.y = scope.layout.height + } + } + } + return possibleY +} + +/** + * Global + */ +var modules = {} + +export default modules + +export function changeInputSize(size) { + if (size == undefined || size < 2 || size > 10) return + if (this.inputSize == size) return + size = parseInt(size, 10) + var obj = new modules[this.objectType]( + this.x, + this.y, + this.scope, + this.direction, + size, + this.bitWidth + ) + this.delete() + simulationArea.lastSelected = obj + return obj +} diff --git a/v0/src/simulator/src/modules/ALU.js b/v0/src/simulator/src/modules/ALU.js new file mode 100644 index 00000000..19cda667 --- /dev/null +++ b/v0/src/simulator/src/modules/ALU.js @@ -0,0 +1,200 @@ +/* eslint-disable no-bitwise */ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, fillText4 } from '../canvasApi' +import { colors } from '../themer/themer' + +/** + * @class + * ALU + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +export default class ALU extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT', bitWidth = 1) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['ALU'].push(this); + */ + this.message = 'ALU' + + this.setDimensions(30, 40) + this.rectangleObject = false + + this.inp1 = new Node(-30, -30, 0, this, this.bitwidth, 'A') + this.inp2 = new Node(-30, 30, 0, this, this.bitwidth, 'B') + + this.controlSignalInput = new Node(-10, -40, 0, this, 3, 'Ctrl') + this.carryOut = new Node(-10, 40, 1, this, 1, 'Cout') + this.output = new Node(30, 0, 1, this, this.bitwidth, 'Out') + } + + /** + * @memberof ALU + * function to change bitwidth of the element + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + this.bitWidth = bitWidth + this.inp1.bitWidth = bitWidth + this.inp2.bitWidth = bitWidth + this.output.bitWidth = bitWidth + } + + /** + * @memberof ALU + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.direction, this.bitWidth], + nodes: { + inp1: findNode(this.inp1), + inp2: findNode(this.inp2), + output: findNode(this.output), + carryOut: findNode(this.carryOut), + controlSignalInput: findNode(this.controlSignalInput), + }, + } + return data + } + + /** + * @memberof ALU + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + const xx = this.x + const yy = this.y + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.lineWidth = correctWidth(3) + ctx.beginPath() + moveTo(ctx, 30, 10, xx, yy, this.direction) + lineTo(ctx, 30, -10, xx, yy, this.direction) + lineTo(ctx, 10, -40, xx, yy, this.direction) + lineTo(ctx, -30, -40, xx, yy, this.direction) + lineTo(ctx, -30, -20, xx, yy, this.direction) + lineTo(ctx, -20, -10, xx, yy, this.direction) + lineTo(ctx, -20, 10, xx, yy, this.direction) + lineTo(ctx, -30, 20, xx, yy, this.direction) + lineTo(ctx, -30, 40, xx, yy, this.direction) + lineTo(ctx, 10, 40, xx, yy, this.direction) + lineTo(ctx, 30, 10, xx, yy, this.direction) + ctx.closePath() + ctx.stroke() + + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.fillStyle = colors['hover_select'] + } + ctx.fill() + ctx.stroke() + + ctx.beginPath() + ctx.fillStyle = 'Black' + ctx.textAlign = 'center' + + fillText4(ctx, 'B', -23, 30, xx, yy, this.direction, 6) + fillText4(ctx, 'A', -23, -30, xx, yy, this.direction, 6) + fillText4(ctx, 'CTR', -10, -30, xx, yy, this.direction, 6) + fillText4(ctx, 'Carry', -10, 30, xx, yy, this.direction, 6) + fillText4(ctx, 'Ans', 20, 0, xx, yy, this.direction, 6) + ctx.fill() + ctx.beginPath() + ctx.fillStyle = 'DarkGreen' + fillText4(ctx, this.message, 0, 0, xx, yy, this.direction, 12) + ctx.fill() + } + + /** + * @memberof ALU + * resolve output values based on inputData + */ + resolve() { + if (this.controlSignalInput.value === 0) { + this.output.value = this.inp1.value & this.inp2.value + simulationArea.simulationQueue.add(this.output) + this.carryOut.value = 0 + simulationArea.simulationQueue.add(this.carryOut) + this.message = 'A&B' + } else if (this.controlSignalInput.value === 1) { + this.output.value = this.inp1.value | this.inp2.value + + simulationArea.simulationQueue.add(this.output) + this.carryOut.value = 0 + simulationArea.simulationQueue.add(this.carryOut) + this.message = 'A|B' + } else if (this.controlSignalInput.value === 2) { + const sum = this.inp1.value + this.inp2.value + this.output.value = + (sum << (32 - this.bitWidth)) >>> (32 - this.bitWidth) + this.carryOut.value = +(sum >>> this.bitWidth !== 0) + simulationArea.simulationQueue.add(this.carryOut) + simulationArea.simulationQueue.add(this.output) + this.message = 'A+B' + } else if (this.controlSignalInput.value === 3) { + this.message = 'ALU' + } else if (this.controlSignalInput.value === 4) { + this.message = 'A&~B' + this.output.value = this.inp1.value & this.flipBits(this.inp2.value) + simulationArea.simulationQueue.add(this.output) + this.carryOut.value = 0 + simulationArea.simulationQueue.add(this.carryOut) + } else if (this.controlSignalInput.value === 5) { + this.message = 'A|~B' + this.output.value = this.inp1.value | this.flipBits(this.inp2.value) + simulationArea.simulationQueue.add(this.output) + this.carryOut.value = 0 + simulationArea.simulationQueue.add(this.carryOut) + } else if (this.controlSignalInput.value === 6) { + this.message = 'A-B' + this.output.value = + ((this.inp1.value - this.inp2.value) << + (32 - this.bitWidth)) >>> + (32 - this.bitWidth) + simulationArea.simulationQueue.add(this.output) + this.carryOut.value = 0 + simulationArea.simulationQueue.add(this.carryOut) + } else if (this.controlSignalInput.value === 7) { + this.message = 'A>> (32 - this.bitWidth) + this.carryOut.value = +(sum >>> this.bitWidth !== 0) + simulationArea.simulationQueue.add(this.carryOut) + simulationArea.simulationQueue.add(this.sum) + } + + generateVerilog() { + if (this.carryIn.verilogLabel) { + return `assign ${this.sum.verilogLabel} = ${this.inpA.verilogLabel} + ${this.inpB.verilogLabel} + ${this.carryIn.verilogLabel};` + } + return `assign ${this.sum.verilogLabel} = ${this.inpA.verilogLabel} + ${this.inpB.verilogLabel};` + } +} + +/** + * @memberof Adder + * Help Tip + * @type {string} + * @category modules + */ +Adder.prototype.tooltipText = 'Adder ToolTip : Performs addition of numbers.' +Adder.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/8misc?id=adder' +Adder.prototype.objectType = 'Adder' diff --git a/v0/src/simulator/src/modules/AndGate.js b/v0/src/simulator/src/modules/AndGate.js new file mode 100644 index 00000000..43c3fced --- /dev/null +++ b/v0/src/simulator/src/modules/AndGate.js @@ -0,0 +1,168 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, arc } from '../canvasApi' +import { changeInputSize } from '../modules' +import { colors } from '../themer/themer' +import { gateGenerateVerilog } from '../utils' + +/** + * @class + * AndGate + * @extends CircuitElement + * @param {number} x - x coordinate of And Gate. + * @param {number} y - y coordinate of And Gate. + * @param {Scope=} scope - Cirucit on which and gate is drawn + * @param {string=} dir - direction of And Gate + * @param {number=} inputLength - number of input nodes + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +export default class AndGate extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + inputLength = 2, + bitWidth = 1 + ) { + /** + * super call + */ + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['AndGate'].push(this); + */ + this.rectangleObject = false + this.setDimensions(15, 20) + this.inp = [] + this.inputSize = inputLength + + // variable inputLength , node creation + if (inputLength % 2 === 1) { + for (let i = 0; i < inputLength / 2 - 1; i++) { + const a = new Node(-10, -10 * (i + 1), 0, this) + this.inp.push(a) + } + let a = new Node(-10, 0, 0, this) + this.inp.push(a) + for (let i = inputLength / 2 + 1; i < inputLength; i++) { + a = new Node(-10, 10 * (i + 1 - inputLength / 2 - 1), 0, this) + this.inp.push(a) + } + } else { + for (let i = 0; i < inputLength / 2; i++) { + const a = new Node(-10, -10 * (i + 1), 0, this) + this.inp.push(a) + } + for (let i = inputLength / 2; i < inputLength; i++) { + const a = new Node(-10, 10 * (i + 1 - inputLength / 2), 0, this) + this.inp.push(a) + } + } + + this.output1 = new Node(20, 0, 1, this) + } + + /** + * @memberof AndGate + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [ + this.direction, + this.inputSize, + this.bitWidth, + ], + nodes: { + inp: this.inp.map(findNode), + output1: findNode(this.output1), + }, + } + return data + } + + /** + * @memberof AndGate + * resolve output values based on inputData + */ + resolve() { + let result = this.inp[0].value || 0 + if (this.isResolvable() === false) { + return + } + for (let i = 1; i < this.inputSize; i++) + result &= this.inp[i].value || 0 + this.output1.value = result >>> 0 + simulationArea.simulationQueue.add(this.output1) + } + + /** + * @memberof AndGate + * function to draw And Gate + */ + customDraw() { + var ctx = simulationArea.context + ctx.beginPath() + ctx.lineWidth = correctWidth(3) + ctx.strokeStyle = colors['stroke'] // ("rgba(0,0,0,1)"); + ctx.fillStyle = colors['fill'] + const xx = this.x + const yy = this.y + + moveTo(ctx, -10, -20, xx, yy, this.direction) + lineTo(ctx, 0, -20, xx, yy, this.direction) + arc(ctx, 0, 0, 20, -Math.PI / 2, Math.PI / 2, xx, yy, this.direction) + lineTo(ctx, -10, 20, xx, yy, this.direction) + lineTo(ctx, -10, -20, xx, yy, this.direction) + ctx.closePath() + + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + } + + generateVerilog() { + return gateGenerateVerilog.call(this, '&') + } +} + +/** + * @memberof AndGate + * Help Tip + * @type {string} + * @category modules + */ +AndGate.prototype.tooltipText = + 'And Gate Tooltip : Implements logical conjunction' + +/** + * @memberof AndGate + * @type {boolean} + * @category modules + */ +AndGate.prototype.alwaysResolve = true + +/** + * @memberof AndGate + * @type {string} + * @category modules + */ +AndGate.prototype.verilogType = 'and' + +/** + * @memberof AndGate + * function to change input nodes of the gate + * @category modules + */ +AndGate.prototype.changeInputSize = changeInputSize +AndGate.prototype.helplink = 'https://docs.circuitverse.org/#/chapter4/4gates?id=and-gate' +AndGate.prototype.objectType = 'AndGate' diff --git a/v0/src/simulator/src/modules/Arrow.js b/v0/src/simulator/src/modules/Arrow.js new file mode 100644 index 00000000..24eeddfd --- /dev/null +++ b/v0/src/simulator/src/modules/Arrow.js @@ -0,0 +1,83 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, arc } from '../canvasApi' +import { changeInputSize } from '../modules' +/** + * @class + * Arrow + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @category modules + */ +import { colors } from '../themer/themer' + +export default class Arrow extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT') { + super(x, y, scope, dir, 8) + /* this is done in this.baseSetup() now + this.scope['Arrow'].push(this); + */ + this.rectangleObject = false + this.fixedBitWidth = true + this.setDimensions(30, 20) + } + + /** + * @memberof Arrow + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.direction], + } + return data + } + + /** + * @memberof Arrow + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.lineWidth = correctWidth(3) + const xx = this.x + const yy = this.y + ctx.strokeStyle = colors['stroke_alt'] + ctx.fillStyle = colors['fill'] + + ctx.beginPath() + + moveTo(ctx, -30, -3, xx, yy, this.direction) + lineTo(ctx, 10, -3, xx, yy, this.direction) + lineTo(ctx, 10, -15, xx, yy, this.direction) + lineTo(ctx, 30, 0, xx, yy, this.direction) + lineTo(ctx, 10, 15, xx, yy, this.direction) + lineTo(ctx, 10, 3, xx, yy, this.direction) + lineTo(ctx, -30, 3, xx, yy, this.direction) + ctx.closePath() + ctx.stroke() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + } +} + +/** + * @memberof Arrow + * Help Tip + * @type {string} + * @category modules + */ +Arrow.prototype.tooltipText = 'Arrow ToolTip : Arrow Selected.' +Arrow.prototype.propagationDelayFixed = true +Arrow.prototype.helplink = 'https://docs.circuitverse.org/#/chapter4/7annotation?id=arrow' +Arrow.prototype.objectType = 'Arrow' diff --git a/v0/src/simulator/src/modules/BitSelector.js b/v0/src/simulator/src/modules/BitSelector.js new file mode 100644 index 00000000..8c91cee4 --- /dev/null +++ b/v0/src/simulator/src/modules/BitSelector.js @@ -0,0 +1,173 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode, extractBits } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, rect, fillText } from '../canvasApi' +/** + * @class + * BitSelector + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @param {number=} selectorBitWidth - 1 by default + * @category modules + */ +import { colors } from '../themer/themer' + +export default class BitSelector extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + bitWidth = 2, + selectorBitWidth = 1 + ) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['BitSelector'].push(this); + */ + this.setDimensions(20, 20) + this.selectorBitWidth = + selectorBitWidth || parseInt(prompt('Enter Selector bitWidth'), 10) + this.rectangleObject = false + this.inp1 = new Node(-20, 0, 0, this, this.bitWidth, 'Input') + this.output1 = new Node(20, 0, 1, this, 1, 'Output') + this.bitSelectorInp = new Node( + 0, + 20, + 0, + this, + this.selectorBitWidth, + 'Bit Selector' + ) + } + + /** + * @memberof BitSelector + * Function to change selector Bitwidth + * @param {size} + */ + changeSelectorBitWidth(size) { + if (size === undefined || size < 1 || size > 32) return + this.selectorBitWidth = size + this.bitSelectorInp.bitWidth = size + } + + /** + * @memberof BitSelector + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + nodes: { + inp1: findNode(this.inp1), + output1: findNode(this.output1), + bitSelectorInp: findNode(this.bitSelectorInp), + }, + constructorParamaters: [ + this.direction, + this.bitWidth, + this.selectorBitWidth, + ], + } + return data + } + + /** + * @memberof BitSelector + * function to change bitwidth of the element + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + this.inp1.bitWidth = bitWidth + this.bitWidth = bitWidth + } + + /** + * @memberof BitSelector + * resolve output values based on inputData + */ + resolve() { + this.output1.value = extractBits( + this.inp1.value, + this.bitSelectorInp.value + 1, + this.bitSelectorInp.value + 1 + ) // (this.inp1.value^(1<> ${this.bitSelectorInp.verilogLabel};` + } +} + +/** + * @memberof BitSelector + * Help Tip + * @type {string} + * @category modules + */ +BitSelector.prototype.tooltipText = + 'BitSelector ToolTip : Divides input bits into several equal-sized groups.' +BitSelector.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/5muxandplex?id=bitselector' + +/** + * @memberof BitSelector + * Mutable properties of the element + * @type {JSON} + * @category modules + */ +BitSelector.prototype.mutableProperties = { + selectorBitWidth: { + name: 'Selector Bit Width: ', + type: 'number', + max: '32', + min: '1', + func: 'changeSelectorBitWidth', + }, +} +BitSelector.prototype.objectType = 'BitSelector' diff --git a/v0/src/simulator/src/modules/Buffer.js b/v0/src/simulator/src/modules/Buffer.js new file mode 100644 index 00000000..f3e9cd69 --- /dev/null +++ b/v0/src/simulator/src/modules/Buffer.js @@ -0,0 +1,134 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, arc } from '../canvasApi' +import { changeInputSize } from '../modules' +/** + * @class + * Buffer + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +import { colors } from '../themer/themer' + +export default class Buffer extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT', bitWidth = 1) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['Buffer'].push(this); + */ + this.rectangleObject = false + this.setDimensions(15, 15) + this.state = 0 + this.preState = 0 + this.inp1 = new Node(-10, 0, 0, this) + this.reset = new Node(0, 0, 0, this, 1, 'reset') + this.output1 = new Node(20, 0, 1, this) + } + + /** + * @memberof Buffer + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.direction, this.bitWidth], + nodes: { + output1: findNode(this.output1), + inp1: findNode(this.inp1), + reset: findNode(this.reset), + }, + } + return data + } + + /** + * @memberof Buffer + * function to change bitwidth of the element + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + this.inp1.bitWidth = bitWidth + this.output1.bitWidth = bitWidth + this.bitWidth = bitWidth + } + + /** + * @memberof Buffer + * Checks if the element is resolvable + * @return {boolean} + */ + isResolvable() { + return true + } + + /** + * @memberof Buffer + * resolve output values based on inputData + */ + resolve() { + if (this.reset.value === 1) { + this.state = this.preState + } + if (this.inp1.value !== undefined) { + this.state = this.inp1.value + } + + this.output1.value = this.state + simulationArea.simulationQueue.add(this.output1) + } + + /** + * @memberof Buffer + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.strokeStyle = colors['stroke_alt'] + ctx.lineWidth = correctWidth(3) + const xx = this.x + const yy = this.y + ctx.beginPath() + ctx.fillStyle = colors['fill'] + moveTo(ctx, -10, -15, xx, yy, this.direction) + lineTo(ctx, 20, 0, xx, yy, this.direction) + lineTo(ctx, -10, 15, xx, yy, this.direction) + ctx.closePath() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + } + + generateVerilog() { + return ( + 'assign ' + + this.output1.verilogLabel + + ' = ' + + this.inp1.verilogLabel + + ';' + ) + } +} + +/** + * @memberof Buffer + * Help Tip + * @type {string} + * @category modules + */ +Buffer.prototype.tooltipText = + 'Buffer ToolTip : Isolate the input from the output.' +Buffer.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/8misc?id=buffer' +Buffer.prototype.objectType = 'Buffer' diff --git a/v0/src/simulator/src/modules/Button.js b/v0/src/simulator/src/modules/Button.js new file mode 100644 index 00000000..b108afaa --- /dev/null +++ b/v0/src/simulator/src/modules/Button.js @@ -0,0 +1,184 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, drawCircle2 } from '../canvasApi' + +/** + * @class + * Button + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @category modules + */ +export default class Button extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT') { + super(x, y, scope, dir, 1) + /* this is done in this.baseSetup() now + this.scope['Button'].push(this); + */ + this.state = 0 + this.output1 = new Node(30, 0, 1, this) + this.wasClicked = false + this.rectangleObject = false + this.setDimensions(10, 10) + } + + /** + * @memberof Button + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + nodes: { + output1: findNode(this.output1), + }, + values: { + state: this.state, + }, + constructorParamaters: [this.direction, this.bitWidth], + } + return data + } + + /** + * @memberof Button + * resolve output values based on inputData + */ + resolve() { + if (this.wasClicked) { + this.state = 1 + this.output1.value = this.state + } else { + this.state = 0 + this.output1.value = this.state + } + simulationArea.simulationQueue.add(this.output1) + } + + /** + * @memberof Button + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + const xx = this.x + const yy = this.y + ctx.fillStyle = '#ddd' + + ctx.strokeStyle = '#353535' + ctx.lineWidth = correctWidth(5) + + ctx.beginPath() + + moveTo(ctx, 10, 0, xx, yy, this.direction) + lineTo(ctx, 30, 0, xx, yy, this.direction) + ctx.stroke() + + ctx.beginPath() + + drawCircle2(ctx, 0, 0, 12, xx, yy, this.direction) + ctx.stroke() + + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.fillStyle = 'rgba(232, 13, 13,0.6)' + } + + if (this.wasClicked) { + ctx.fillStyle = 'rgba(232, 13, 13,0.8)' + } + ctx.fill() + } + + subcircuitDraw(xOffset = 0, yOffset = 0) { + var ctx = simulationArea.context + var xx = this.subcircuitMetadata.x + xOffset + var yy = this.subcircuitMetadata.y + yOffset + ctx.fillStyle = '#ddd' + + ctx.strokeStyle = '#353535' + ctx.lineWidth = correctWidth(3) + ctx.beginPath() + drawCircle2(ctx, 0, 0, 6, xx, yy, this.direction) + ctx.stroke() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected == this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = 'rgba(232, 13, 13,0.6)' + if (this.wasClicked) ctx.fillStyle = 'rgba(232, 13, 13,0.8)' + ctx.fill() + } + static verilogInstructions() { + return `Button - Buttons are not natively supported in verilog, consider using Inputs instead\n` + } + verilogBaseType() { + return this.verilogName() + (Button.selSizes.length - 1) + } + + //this code to generate Verilog + generateVerilog() { + Button.selSizes.push(this.data) + return CircuitElement.prototype.generateVerilog.call(this) + } + + static moduleVerilog() { + var output = '' + + for (var i = 0; i < Button.selSizes.length; i++) { + output += `// Skeleton for Button${i} + /* + module Button${i}(out); + output reg out; + + initial begin + //do something with the button here + end + endmodule + */ + ` + } + + return output + } + + //reset the sized before Verilog generation + static resetVerilog() { + Button.selSizes = [] + } +} + +/** + * @memberof Button + * Help Tip + * @type {string} + * @category modules + */ +Button.prototype.tooltipText = + 'Button ToolTip: High(1) when pressed and Low(0) when released.' + +/** + * @memberof Button + * Help URL + * @type {string} + * @category modules + */ +Button.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/2input?id=button' + +/** + * @memberof Button + * @type {number} + * @category modules + */ +Button.prototype.propagationDelay = 0 +Button.prototype.objectType = 'Button' +Button.prototype.canShowInSubcircuit = true diff --git a/v0/src/simulator/src/modules/ConstantVal.js b/v0/src/simulator/src/modules/ConstantVal.js new file mode 100644 index 00000000..d5f9028a --- /dev/null +++ b/v0/src/simulator/src/modules/ConstantVal.js @@ -0,0 +1,209 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, rect2, fillText, oppositeDirection } from '../canvasApi' +import { colors } from '../themer/themer' + +function bin2dec(binString) { + return parseInt(binString, 2) +} + +function dec2bin(dec, bitWidth = undefined) { + // only for positive nos + var bin = dec.toString(2) + if (bitWidth == undefined) return bin + return '0'.repeat(bitWidth - bin.length) + bin +} + +/** + * @class + * ConstantVal + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @param {string=} state - The state of element + * @category modules + */ +export default class ConstantVal extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + bitWidth = 1, + state = '0' + ) { + // state = state || prompt('Enter value'); + super(x, y, scope, dir, state.length) + /* this is done in this.baseSetup() now + this.scope['ConstantVal'].push(this); + */ + this.state = state + this.setDimensions(10 * this.state.length, 10) + this.bitWidth = bitWidth || this.state.length + this.directionFixed = true + this.orientationFixed = false + this.rectangleObject = false + + this.output1 = new Node(this.bitWidth * 10, 0, 1, this) + this.wasClicked = false + this.label = '' + } + + generateVerilog() { + return `localparam [${this.bitWidth - 1}:0] ${this.verilogLabel}=${ + this.bitWidth + }b'${this.state};` + } + + /** + * @memberof ConstantVal + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + nodes: { + output1: findNode(this.output1), + }, + constructorParamaters: [this.direction, this.bitWidth, this.state], + } + return data + } + + /** + * @memberof ConstantVal + * resolve output values based on inputData + */ + resolve() { + this.output1.value = bin2dec(this.state) + simulationArea.simulationQueue.add(this.output1) + } + + /** + * @memberof ConstantVal + * updates state using a prompt when dbl clicked + */ + dblclick() { + this.state = prompt('Re enter the value') || '0' + this.newBitWidth(this.state.toString().length) + } + + /** + * @memberof ConstantVal + * function to change bitwidth of the element + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + if (bitWidth > this.state.length) + this.state = '0'.repeat(bitWidth - this.state.length) + this.state + else if (bitWidth < this.state.length) + this.state = this.state.slice(this.bitWidth - bitWidth) + this.bitWidth = bitWidth // ||parseInt(prompt("Enter bitWidth"),10); + this.output1.bitWidth = bitWidth + this.setDimensions(10 * this.bitWidth, 10) + if (this.direction === 'RIGHT') { + this.output1.x = 10 * this.bitWidth + this.output1.leftx = 10 * this.bitWidth + } else if (this.direction === 'LEFT') { + this.output1.x = -10 * this.bitWidth + this.output1.leftx = 10 * this.bitWidth + } + } + + /** + * @memberof ConstantVal + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.lineWidth = correctWidth(1) + const xx = this.x + const yy = this.y + + rect2( + ctx, + -10 * this.bitWidth, + -10, + 20 * this.bitWidth, + 20, + xx, + yy, + 'RIGHT' + ) + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + + ctx.beginPath() + ctx.fillStyle = colors['input_text'] + ctx.textAlign = 'center' + const bin = this.state // dec2bin(this.state,this.bitWidth); + for (let k = 0; k < this.bitWidth; k++) { + fillText(ctx, bin[k], xx - 10 * this.bitWidth + 10 + k * 20, yy + 5) + } + ctx.fill() + } + + /** + * @memberof ConstantVal + * function to change direction of ConstantVal + * @param {string} dir - new direction + */ + newDirection(dir) { + if (dir === this.direction) return + this.direction = dir + this.output1.refresh() + if (dir === 'RIGHT' || dir === 'LEFT') { + this.output1.leftx = 10 * this.bitWidth + this.output1.lefty = 0 + } else { + this.output1.leftx = 10 // 10*this.bitWidth; + this.output1.lefty = 0 + } + + this.output1.refresh() + this.labelDirection = oppositeDirection[this.direction] + } + + generateVerilog() { + return `assign ${this.output1.verilogLabel} = ${this.bitWidth}'b${this.state};` + } +} + +/** + * @memberof ConstantVal + * Help Tip + * @type {string} + * @category modules + */ +ConstantVal.prototype.tooltipText = + 'Constant ToolTip: Bits are fixed. Double click element to change the bits.' + +/** + * @memberof ConstantVal + * Help URL + * @type {string} + * @category modules + */ +ConstantVal.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/2input?id=constantval' + +/** + * @memberof ConstantVal + * @type {number} + * @category modules + */ +ConstantVal.prototype.propagationDelay = 0 +ConstantVal.prototype.objectType = 'ConstantVal' diff --git a/v0/src/simulator/src/modules/ControlledInverter.js b/v0/src/simulator/src/modules/ControlledInverter.js new file mode 100644 index 00000000..c846abec --- /dev/null +++ b/v0/src/simulator/src/modules/ControlledInverter.js @@ -0,0 +1,122 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, drawCircle2 } from '../canvasApi' +import { changeInputSize } from '../modules' +/** + * @class + * ControlledInverter + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +import { colors } from '../themer/themer' + +export default class ControlledInverter extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT', bitWidth = 1) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['ControlledInverter'].push(this); + */ + this.rectangleObject = false + this.setDimensions(15, 15) + + this.inp1 = new Node(-10, 0, 0, this) + this.output1 = new Node(30, 0, 1, this) + this.state = new Node(0, 0, 0, this, 1, 'Enable') + } + + /** + * @memberof ControlledInverter + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.direction, this.bitWidth], + nodes: { + output1: findNode(this.output1), + inp1: findNode(this.inp1), + state: findNode(this.state), + }, + } + return data + } + + /** + * @memberof ControlledInverter + * function to change bitwidth of the element + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + this.inp1.bitWidth = bitWidth + this.output1.bitWidth = bitWidth + this.bitWidth = bitWidth + } + + /** + * @memberof ControlledInverter + * resolve output values based on inputData + */ + resolve() { + if (this.isResolvable() === false) { + return + } + if (this.state.value === 1) { + this.output1.value = + ((~this.inp1.value >>> 0) << (32 - this.bitWidth)) >>> + (32 - this.bitWidth) + simulationArea.simulationQueue.add(this.output1) + } + if (this.state.value === 0) { + this.output1.value = undefined + } + } + + /** + * @memberof ControlledInverter + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.strokeStyle = colors['stroke'] + ctx.lineWidth = correctWidth(3) + const xx = this.x + const yy = this.y + ctx.beginPath() + ctx.fillStyle = colors['fill'] + moveTo(ctx, -10, -15, xx, yy, this.direction) + lineTo(ctx, 20, 0, xx, yy, this.direction) + lineTo(ctx, -10, 15, xx, yy, this.direction) + ctx.closePath() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + ctx.beginPath() + drawCircle2(ctx, 25, 0, 5, xx, yy, this.direction) + ctx.stroke() + } + + generateVerilog() { + return `assign ${this.output1.verilogLabel} = (${this.state.verilogLabel}!=0) ? ~${this.inp1.verilogLabel} : ${this.inp1.verilogLabel};` + } +} + +/** + * @memberof ControlledInverter + * Help Tip + * @type {string} + * @category modules + */ +ControlledInverter.prototype.tooltipText = + 'Controlled Inverter ToolTip : Controlled buffer and NOT gate.' +ControlledInverter.prototype.objectType = 'ControlledInverter' diff --git a/v0/src/simulator/src/modules/Counter.js b/v0/src/simulator/src/modules/Counter.js new file mode 100644 index 00000000..7ffb23fc --- /dev/null +++ b/v0/src/simulator/src/modules/Counter.js @@ -0,0 +1,194 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { lineTo, moveTo, fillText, correctWidth, rect2 } from '../canvasApi' +import { colors } from '../themer/themer' + +/** + * @class + * Counter component. + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {number=} rows - number of rows + * @param {number=} cols - number of columns. + * Counts from zero to a particular maximum value, which is either + * specified by an input pin or determined by the Counter's bitWidth. + * The counter outputs its current value and a flag that indicates + * when the output value is zero and the clock is 1. + * The counter can be reset to zero at any point using the RESET pin. + * @category modules + */ +export default class Counter extends CircuitElement { + constructor(x, y, scope = globalScope, bitWidth = 8) { + super(x, y, scope, 'RIGHT', bitWidth) + /* this is done in this.baseSetup() now + this.scope['Counter'].push(this); + */ + this.directionFixed = true + this.rectangleObject = true + + this.setDimensions(20, 20) + + this.maxValue = new Node(-20, -10, 0, this, this.bitWidth, 'MaxValue') + this.clock = new Node(-20, +10, 0, this, 1, 'Clock') + this.reset = new Node(0, 20, 0, this, 1, 'Reset') + this.output = new Node(20, -10, 1, this, this.bitWidth, 'Value') + this.zero = new Node(20, 10, 1, this, 1, 'Zero') + + this.value = 0 + this.prevClockState = undefined + } + + customSave() { + return { + nodes: { + maxValue: findNode(this.maxValue), + clock: findNode(this.clock), + reset: findNode(this.reset), + output: findNode(this.output), + zero: findNode(this.zero), + }, + constructorParamaters: [this.bitWidth], + } + } + + newBitWidth(bitWidth) { + this.bitWidth = bitWidth + this.maxValue.bitWidth = bitWidth + this.output.bitWidth = bitWidth + } + + isResolvable() { + return true + } + + resolve() { + // Max value is either the value in the input pin or the max allowed by the bitWidth. + var maxValue = + this.maxValue.value != undefined + ? this.maxValue.value + : (1 << this.bitWidth) - 1 + var outputValue = this.value + + // Increase value when clock is raised + if (this.clock.value != this.prevClockState && this.clock.value == 1) { + outputValue++ + } + this.prevClockState = this.clock.value + + // Limit to the effective maximum value; this also accounts for bitWidth changes. + outputValue = outputValue % (maxValue + 1) + + // Reset to zero if RESET pin is on + if (this.reset.value == 1) { + outputValue = 0 + } + + // Output the new value + this.value = outputValue + if (this.output.value != outputValue) { + this.output.value = outputValue + simulationArea.simulationQueue.add(this.output) + } + + // Output the zero signal + var zeroValue = this.clock.value == 1 && outputValue == 0 ? 1 : 0 + if (this.zero.value != zeroValue) { + this.zero.value = zeroValue + simulationArea.simulationQueue.add(this.zero) + } + } + + customDraw() { + var ctx = simulationArea.context + var xx = this.x + var yy = this.y + + ctx.beginPath() + ctx.font = '20px Raleway' + ctx.fillStyle = colors['input_text'] + ctx.textAlign = 'center' + fillText(ctx, this.value.toString(16), this.x, this.y + 5) + ctx.fill() + + ctx.strokeStyle = colors['stroke'] + ctx.beginPath() + moveTo(ctx, -20, 5, xx, yy, this.direction) + lineTo(ctx, -15, 10, xx, yy, this.direction) + lineTo(ctx, -20, 15, xx, yy, this.direction) + ctx.stroke() + } + + // Draws the element in the subcuircuit. Used in layout mode + subcircuitDraw(xOffset = 0, yOffset = 0) { + var ctx = simulationArea.context + var xx = this.subcircuitMetadata.x + xOffset + var yy = this.subcircuitMetadata.y + yOffset + + ctx.beginPath() + ctx.font = '20px Raleway' + ctx.fillStyle = 'green' + ctx.textAlign = 'center' + fillText(ctx, this.value.toString(16), xx + 10, yy + 17) + ctx.fill() + + ctx.beginPath() + ctx.lineWidth = correctWidth(1) + rect2(ctx, 0, 0, 20, 20, xx, yy, this.direction) + ctx.stroke() + + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected == this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.fillStyle = 'rgba(255, 255, 32,0.6)' + ctx.fill() + } + } + static moduleVerilog() { + return ` + module Counter(val, zero, max, clk, rst); + parameter WIDTH = 1; + output reg [WIDTH-1:0] val; + output reg zero; + input [WIDTH-1:0] max; + input clk, rst; + + initial + val = 0; + + always @ (val) + if (val == 0) + zero = 1; + else + zero = 0; + + always @ (posedge clk or posedge rst) begin + if (rst) + val <= 0; + else + if (val == max) + val <= 0; + else + val <= val + 1; + end + endmodule` + } +} + +Counter.prototype.tooltipText = + 'Counter: a binary counter from zero to a given maximum value' +Counter.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/2input?id=counter' +Counter.prototype.objectType = 'Counter' +Counter.prototype.objectType = 'Counter' +Counter.prototype.canShowInSubcircuit = true +Counter.prototype.layoutProperties = { + rightDimensionX: 20, + leftDimensionX: 0, + upDimensionY: 0, + downDimensionY: 20, +} diff --git a/v0/src/simulator/src/modules/Decoder.js b/v0/src/simulator/src/modules/Decoder.js new file mode 100644 index 00000000..6b6d9278 --- /dev/null +++ b/v0/src/simulator/src/modules/Decoder.js @@ -0,0 +1,291 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, rect, fillText } from '../canvasApi' +/** + * @class + * Decoder + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +import { colors } from '../themer/themer' + +export default class Decoder extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'LEFT', bitWidth = 1) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['Decoder'].push(this); + */ + // this.controlSignalSize = controlSignalSize || parseInt(prompt("Enter control signal bitWidth"), 10); + this.outputsize = 1 << this.bitWidth + this.xOff = 0 + this.yOff = 1 + if (this.bitWidth === 1) { + this.xOff = 10 + } + if (this.bitWidth <= 3) { + this.yOff = 2 + } + + // this.changeControlSignalSize = function(size) { + // if (size === undefined || size < 1 || size > 32) return; + // if (this.controlSignalSize === size) return; + // let obj = new window[this.objectType](this.x, this.y, this.scope, this.direction, this.bitWidth, size); + // this.cleanDelete(); + // simulationArea.lastSelected = obj; + // return obj; + // } + // this.mutableProperties = { + // "controlSignalSize": { + // name: "Control Signal Size", + // type: "number", + // max: "32", + // min: "1", + // func: "changeControlSignalSize", + // }, + // } + // eslint-disable-next-line no-shadow + this.newBitWidth = function (bitWidth) { + // this.bitWidth = bitWidth; + // for (let i = 0; i < this.inputSize; i++) { + // this.outputs1[i].bitWidth = bitWidth + // } + // this.input.bitWidth = bitWidth; + if (bitWidth === undefined || bitWidth < 1 || bitWidth > 32) return + if (this.bitWidth === bitWidth) return + const obj = new Decoder( + this.x, + this.y, + this.scope, + this.direction, + bitWidth + ) + this.cleanDelete() + simulationArea.lastSelected = obj + return obj + } + + this.setDimensions(20 - this.xOff, this.yOff * 5 * this.outputsize) + this.rectangleObject = false + this.input = new Node(20 - this.xOff, 0, 0, this) + + this.output1 = [] + for (let i = 0; i < this.outputsize; i++) { + const a = new Node( + -20 + this.xOff, + +this.yOff * 10 * (i - this.outputsize / 2) + 10, + 1, + this, + 1 + ) + this.output1.push(a) + } + + // this.controlSignalInput = new Node(0,this.yOff * 10 * (this.outputsize / 2 - 1) +this.xOff + 10, 0, this, this.controlSignalSize,"Control Signal"); + } + + /** + * @memberof Decoder + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.direction, this.bitWidth], + nodes: { + output1: this.output1.map(findNode), + input: findNode(this.input), + }, + } + return data + } + + /** + * @memberof Decoder + * resolve output values based on inputData + */ + resolve() { + for (let i = 0; i < this.output1.length; i++) { + this.output1[i].value = 0 + } + if (this.input.value !== undefined) + this.output1[this.input.value].value = 1 // if input is undefined, don't change output + for (let i = 0; i < this.output1.length; i++) { + simulationArea.simulationQueue.add(this.output1[i]) + } + } + + /** + * @memberof Decoder + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + + const xx = this.x + const yy = this.y + + // ctx.beginPath(); + // moveTo(ctx, 0,this.yOff * 10 * (this.outputsize / 2 - 1) + 10 + 0.5 *this.xOff, xx, yy, this.direction); + // lineTo(ctx, 0,this.yOff * 5 * (this.outputsize - 1) +this.xOff, xx, yy, this.direction); + // ctx.stroke(); + + ctx.beginPath() + ctx.strokeStyle = colors['stroke'] + ctx.lineWidth = correctWidth(4) + ctx.fillStyle = colors['fill'] + moveTo( + ctx, + -20 + this.xOff, + -this.yOff * 10 * (this.outputsize / 2), + xx, + yy, + this.direction + ) + lineTo( + ctx, + -20 + this.xOff, + 20 + this.yOff * 10 * (this.outputsize / 2 - 1), + xx, + yy, + this.direction + ) + lineTo( + ctx, + 20 - this.xOff, + +this.yOff * 10 * (this.outputsize / 2 - 1) + this.xOff, + xx, + yy, + this.direction + ) + lineTo( + ctx, + 20 - this.xOff, + -this.yOff * 10 * (this.outputsize / 2) - this.xOff + 20, + xx, + yy, + this.direction + ) + + ctx.closePath() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.fillStyle = colors['hover_select'] + } + ctx.fill() + ctx.stroke() + + ctx.beginPath() + ctx.fillStyle = 'black' + ctx.textAlign = 'center' + // [xFill,yFill] = rotate(xx + this.output1[i].x - 7, yy + this.output1[i].y + 2); + for (let i = 0; i < this.outputsize; i++) { + if (this.direction === 'LEFT') + fillText( + ctx, + String(i), + xx + this.output1[i].x - 7, + yy + this.output1[i].y + 2, + 10 + ) + else if (this.direction === 'RIGHT') + fillText( + ctx, + String(i), + xx + this.output1[i].x + 7, + yy + this.output1[i].y + 2, + 10 + ) + else if (this.direction === 'UP') + fillText( + ctx, + String(i), + xx + this.output1[i].x, + yy + this.output1[i].y - 5, + 10 + ) + else + fillText( + ctx, + String(i), + xx + this.output1[i].x, + yy + this.output1[i].y + 10, + 10 + ) + } + ctx.fill() + } + + verilogBaseType() { + return this.verilogName() + this.output1.length + } + + //this code to generate Verilog + generateVerilog() { + Decoder.selSizes.add(this.bitWidth) + return CircuitElement.prototype.generateVerilog.call(this) + } + + static moduleVerilog() { + var output = '' + + for (var size of Decoder.selSizes) { + var numOutput = 1 << size + output += '\n' + output += 'module Decoder' + numOutput + output += '(' + for (var j = 0; j < numOutput; j++) { + output += 'out' + j + ', ' + } + output += 'sel);\n' + + output += ' output reg ' + for (var j = 0; j < numOutput - 1; j++) { + output += 'out' + j + ', ' + } + output += 'out' + (numOutput - 1) + ';\n' + + output += ' input [' + (size - 1) + ':0] sel;\n' + output += ' \n' + + output += ' always @ (*) begin\n' + for (var j = 0; j < numOutput; j++) { + output += ' out' + j + ' = 0;\n' + } + output += ' case (sel)\n' + for (var j = 0; j < numOutput; j++) { + output += ' ' + j + ' : out' + j + ' = 1;\n' + } + output += ' endcase\n' + output += ' end\n' + output += 'endmodule\n' + } + + return output + } + + //reset the sized before Verilog generation + static resetVerilog() { + Decoder.selSizes = new Set() + } +} + +/** + * @memberof Decoder + * Help Tip + * @type {string} + * @category modules + */ +Decoder.prototype.tooltipText = + 'Decoder ToolTip : Converts coded inputs into coded outputs.' +Decoder.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/5muxandplex?id=decoder' +Decoder.prototype.objectType = 'Decoder' diff --git a/v0/src/simulator/src/modules/Demultiplexer.js b/v0/src/simulator/src/modules/Demultiplexer.js new file mode 100644 index 00000000..feb21cfa --- /dev/null +++ b/v0/src/simulator/src/modules/Demultiplexer.js @@ -0,0 +1,324 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, fillText } from '../canvasApi' +/** + * @class + * Demultiplexer + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @param {number=} controlSignalSize - 1 by default + * @category modules + */ +import { colors } from '../themer/themer' + +export default class Demultiplexer extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'LEFT', + bitWidth = 1, + controlSignalSize = 1 + ) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['Demultiplexer'].push(this); + */ + this.controlSignalSize = + controlSignalSize || + parseInt(prompt('Enter control signal bitWidth'), 10) + this.outputsize = 1 << this.controlSignalSize + this.xOff = 0 + this.yOff = 1 + if (this.controlSignalSize === 1) { + this.xOff = 10 + } + if (this.controlSignalSize <= 3) { + this.yOff = 2 + } + + this.changeControlSignalSize = function (size) { + if (size === undefined || size < 1 || size > 32) return + if (this.controlSignalSize === size) return + const obj = new Demultiplexer( + this.x, + this.y, + this.scope, + this.direction, + this.bitWidth, + size + ) + this.cleanDelete() + simulationArea.lastSelected = obj + return obj + } + this.mutableProperties = { + controlSignalSize: { + name: 'Control Signal Size', + type: 'number', + max: '10', + min: '1', + func: 'changeControlSignalSize', + }, + } + // eslint-disable-next-line no-shadow + this.newBitWidth = function (bitWidth) { + this.bitWidth = bitWidth + for (let i = 0; i < this.outputsize; i++) { + this.output1[i].bitWidth = bitWidth + } + this.input.bitWidth = bitWidth + } + + this.setDimensions(20 - this.xOff, this.yOff * 5 * this.outputsize) + this.rectangleObject = false + this.input = new Node(20 - this.xOff, 0, 0, this) + + this.output1 = [] + for (let i = 0; i < this.outputsize; i++) { + const a = new Node( + -20 + this.xOff, + +this.yOff * 10 * (i - this.outputsize / 2) + 10, + 1, + this + ) + this.output1.push(a) + } + + this.controlSignalInput = new Node( + 0, + this.yOff * 10 * (this.outputsize / 2 - 1) + this.xOff + 10, + 0, + this, + this.controlSignalSize, + 'Control Signal' + ) + } + + /** + * @memberof Demultiplexer + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [ + this.direction, + this.bitWidth, + this.controlSignalSize, + ], + nodes: { + output1: this.output1.map(findNode), + input: findNode(this.input), + controlSignalInput: findNode(this.controlSignalInput), + }, + } + return data + } + + /** + * @memberof Demultiplexer + * resolve output values based on inputData + */ + resolve() { + for (let i = 0; i < this.output1.length; i++) { + this.output1[i].value = 0 + } + + this.output1[this.controlSignalInput.value].value = this.input.value + + for (let i = 0; i < this.output1.length; i++) { + simulationArea.simulationQueue.add(this.output1[i]) + } + } + + /** + * @memberof Demultiplexer + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + + const xx = this.x + const yy = this.y + + ctx.beginPath() + moveTo( + ctx, + 0, + this.yOff * 10 * (this.outputsize / 2 - 1) + 10 + 0.5 * this.xOff, + xx, + yy, + this.direction + ) + lineTo( + ctx, + 0, + this.yOff * 5 * (this.outputsize - 1) + this.xOff, + xx, + yy, + this.direction + ) + ctx.stroke() + + ctx.beginPath() + ctx.strokeStyle = colors['stroke'] + ctx.lineWidth = correctWidth(4) + ctx.fillStyle = colors['fill'] + moveTo( + ctx, + -20 + this.xOff, + -this.yOff * 10 * (this.outputsize / 2), + xx, + yy, + this.direction + ) + lineTo( + ctx, + -20 + this.xOff, + 20 + this.yOff * 10 * (this.outputsize / 2 - 1), + xx, + yy, + this.direction + ) + lineTo( + ctx, + 20 - this.xOff, + +this.yOff * 10 * (this.outputsize / 2 - 1) + this.xOff, + xx, + yy, + this.direction + ) + lineTo( + ctx, + 20 - this.xOff, + -this.yOff * 10 * (this.outputsize / 2) - this.xOff + 20, + xx, + yy, + this.direction + ) + ctx.closePath() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.fillStyle = colors['hover_select'] + } + ctx.fill() + ctx.stroke() + + ctx.beginPath() + ctx.fillStyle = 'black' + ctx.textAlign = 'center' + // [xFill,yFill] = rotate(xx + this.output1[i].x - 7, yy + this.output1[i].y + 2); + for (let i = 0; i < this.outputsize; i++) { + if (this.direction === 'LEFT') + fillText( + ctx, + String(i), + xx + this.output1[i].x - 7, + yy + this.output1[i].y + 2, + 10 + ) + else if (this.direction === 'RIGHT') + fillText( + ctx, + String(i), + xx + this.output1[i].x + 7, + yy + this.output1[i].y + 2, + 10 + ) + else if (this.direction === 'UP') + fillText( + ctx, + String(i), + xx + this.output1[i].x, + yy + this.output1[i].y - 5, + 10 + ) + else + fillText( + ctx, + String(i), + xx + this.output1[i].x, + yy + this.output1[i].y + 10, + 10 + ) + } + ctx.fill() + } + + verilogBaseType() { + return this.verilogName() + this.output1.length + } + + //this code to generate Verilog + generateVerilog() { + Demultiplexer.selSizes.add(this.controlSignalSize) + return CircuitElement.prototype.generateVerilog.call(this) + } + + //generate the needed modules + static moduleVerilog() { + var output = '' + + for (var size of Demultiplexer.selSizes) { + var numOutput = 1 << size + output += '\n' + output += 'module Demultiplexer' + numOutput + output += '(' + for (var j = 0; j < numOutput; j++) { + output += 'out' + j + ', ' + } + output += 'in, sel);\n' + + output += ' parameter WIDTH = 1;\n' + output += ' output reg [WIDTH-1:0] ' + for (var j = 0; j < numOutput - 1; j++) { + output += 'out' + j + ', ' + } + output += 'out' + (numOutput - 1) + ';\n' + + output += ' input [WIDTH-1:0] in;\n' + output += ' input [' + (size - 1) + ':0] sel;\n' + output += ' \n' + + output += ' always @ (*) begin\n' + for (var j = 0; j < numOutput; j++) { + output += ' out' + j + ' = 0;\n' + } + output += ' case (sel)\n' + for (var j = 0; j < numOutput; j++) { + output += ' ' + j + ' : out' + j + ' = in;\n' + } + output += ' endcase\n' + output += ' end\n' + output += 'endmodule\n' + } + + return output + } + + //reset the sized before Verilog generation + static resetVerilog() { + Demultiplexer.selSizes = new Set() + } +} + +/** + * @memberof Demultiplexer + * Help Tip + * @type {string} + * @category modules + */ +Demultiplexer.prototype.tooltipText = + 'DeMultiplexer ToolTip : Multiple outputs and a single line input.' +Demultiplexer.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/5muxandplex?id=demultiplexer' +Demultiplexer.prototype.objectType = 'Demultiplexer' diff --git a/v0/src/simulator/src/modules/DigitalLed.js b/v0/src/simulator/src/modules/DigitalLed.js new file mode 100644 index 00000000..df17fbfa --- /dev/null +++ b/v0/src/simulator/src/modules/DigitalLed.js @@ -0,0 +1,188 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { + correctWidth, + lineTo, + moveTo, + arc, + colorToRGBA, + drawCircle2, + validColor, +} from '../canvasApi' +import { changeInputSize } from '../modules' +/** + * @class + * DigitalLed + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} color - color of led + * @category modules + */ +import { colors } from '../themer/themer' + +export default class DigitalLed extends CircuitElement { + constructor(x, y, scope = globalScope, color = 'Red') { + // Calling base class constructor + + super(x, y, scope, 'UP', 1) + /* this is done in this.baseSetup() now + this.scope['DigitalLed'].push(this); + */ + this.rectangleObject = false + this.setDimensions(10, 20) + this.inp1 = new Node(-40, 0, 0, this, 1) + this.directionFixed = true + this.fixedBitWidth = true + this.color = color + const temp = colorToRGBA(this.color) + this.actualColor = `rgba(${temp[0]},${temp[1]},${temp[2]},${0.8})` + } + + /** + * @memberof DigitalLed + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.color], + nodes: { + inp1: findNode(this.inp1), + }, + } + return data + } + + /** + * @memberof DigitalLed + * function to change color of the led + */ + changeColor(value) { + if (validColor(value)) { + this.color = value + const temp = colorToRGBA(this.color) + this.actualColor = `rgba(${temp[0]},${temp[1]},${temp[2]},${0.8})` + } + } + + /** + * @memberof DigitalLed + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + + const xx = this.x + const yy = this.y + + ctx.strokeStyle = '#e3e4e5' + ctx.lineWidth = correctWidth(3) + ctx.beginPath() + moveTo(ctx, -20, 0, xx, yy, this.direction) + lineTo(ctx, -40, 0, xx, yy, this.direction) + ctx.stroke() + + ctx.strokeStyle = '#d3d4d5' + ctx.fillStyle = ['rgba(227,228,229,0.8)', this.actualColor][ + this.inp1.value || 0 + ] + ctx.lineWidth = correctWidth(1) + + ctx.beginPath() + + moveTo(ctx, -15, -9, xx, yy, this.direction) + lineTo(ctx, 0, -9, xx, yy, this.direction) + arc(ctx, 0, 0, 9, -Math.PI / 2, Math.PI / 2, xx, yy, this.direction) + lineTo(ctx, -15, 9, xx, yy, this.direction) + lineTo(ctx, -18, 12, xx, yy, this.direction) + arc( + ctx, + 0, + 0, + Math.sqrt(468), + Math.PI / 2 + Math.acos(12 / Math.sqrt(468)), + -Math.PI / 2 - Math.asin(18 / Math.sqrt(468)), + xx, + yy, + this.direction + ) + lineTo(ctx, -15, -9, xx, yy, this.direction) + ctx.stroke() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + } + + // Draws the element in the subcircuit. Used in layout mode + subcircuitDraw(xOffset = 0, yOffset = 0) { + var ctx = simulationArea.context + + var xx = this.subcircuitMetadata.x + xOffset + var yy = this.subcircuitMetadata.y + yOffset + + ctx.strokeStyle = '#090a0a' + ctx.fillStyle = ['rgba(227,228,229,0.8)', this.actualColor][ + this.inp1.value || 0 + ] + ctx.lineWidth = correctWidth(1) + + ctx.beginPath() + drawCircle2(ctx, 0, 0, 6, xx, yy, this.direction) + ctx.stroke() + + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected == this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = 'rgba(255, 255, 32,0.8)' + ctx.fill() + } + generateVerilog() { + var label = this.label ? this.verilogLabel : this.inp1.verilogLabel + return ` + always @ (*) + $display("DigitalLed:${label}=%d", ${this.inp1.verilogLabel});` + } +} + +/** + * @memberof DigitalLed + * Help Tip + * @type {string} + * @category modules + */ +DigitalLed.prototype.tooltipText = + 'Digital Led ToolTip: Digital LED glows high when input is High(1).' + +/** + * @memberof DigitalLed + * Help URL + * @type {string} + * @category modules + */ +DigitalLed.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/3output?id=digital-led' + +/** + * @memberof DigitalLed + * Mutable properties of the element + * @type {JSON} + * @category modules + */ +DigitalLed.prototype.mutableProperties = { + color: { + name: 'Color: ', + type: 'text', + func: 'changeColor', + }, +} +DigitalLed.prototype.objectType = 'DigitalLed' +DigitalLed.prototype.canShowInSubcircuit = true diff --git a/v0/src/simulator/src/modules/Flag.js b/v0/src/simulator/src/modules/Flag.js new file mode 100644 index 00000000..f332622b --- /dev/null +++ b/v0/src/simulator/src/modules/Flag.js @@ -0,0 +1,234 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, rect2, fillText } from '../canvasApi' +import plotArea from '../plotArea' +import EventQueue from '../eventQueue' +/** + * @class + * Flag + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @param {string} identifier - id + * @category modules + */ +import { colors } from '../themer/themer' + +export default class Flag extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + bitWidth = 1, + identifier + ) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['Flag'].push(this); + */ + this.setWidth(60) + this.setHeight(20) + this.rectangleObject = false + this.directionFixed = true + this.orientationFixed = false + this.identifier = identifier || `F${this.scope.Flag.length}` + this.plotValues = [] + + this.xSize = 10 + this.flagTimeUnit = 0 + + this.inp1 = new Node(40, 0, 0, this) + } + + resolve() { + this.flagTimeUnit = simulationArea.simulationQueue.time + const time = plotArea.getPlotTime(this.flagTimeUnit) + + if ( + this.plotValues.length && + this.plotValues[this.plotValues.length - 1][0] === time + ) { + this.plotValues.pop() + } + + if (this.plotValues.length === 0) { + this.plotValues.push([time, this.inp1.value]) + return + } + + if ( + this.plotValues[this.plotValues.length - 1][1] === this.inp1.value + ) { + return + } + this.plotValues.push([time, this.inp1.value]) + } + + /** + * @memberof Flag + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.direction, this.bitWidth], + nodes: { + inp1: findNode(this.inp1), + }, + values: { + identifier: this.identifier, + }, + } + return data + } + + /** + * @memberof Flag + * set the flag id + * @param {number} id - identifier for flag + */ + setIdentifier(id = '') { + if (id.length === 0) return + this.identifier = id + const len = this.identifier.length + if (len === 1) this.xSize = 20 + else if (len > 1 && len < 4) this.xSize = 10 + else this.xSize = 0 + } + + /** + * @memberof Flag + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.lineWidth = correctWidth(1) + const xx = this.x + const yy = this.y + + rect2( + ctx, + -50 + this.xSize, + -20, + 100 - 2 * this.xSize, + 40, + xx, + yy, + 'RIGHT' + ) + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + + ctx.font = '14px Raleway' + this.xOff = ctx.measureText(this.identifier).width + + ctx.beginPath() + rect2(ctx, -40 + this.xSize, -12, this.xOff + 10, 25, xx, yy, 'RIGHT') + ctx.fillStyle = '#eee' + ctx.strokeStyle = '#ccc' + ctx.fill() + ctx.stroke() + + ctx.beginPath() + ctx.textAlign = 'center' + ctx.fillStyle = 'black' + fillText( + ctx, + this.identifier, + xx - 35 + this.xOff / 2 + this.xSize, + yy + 5, + 14 + ) + ctx.fill() + + ctx.beginPath() + ctx.font = '30px Raleway' + ctx.textAlign = 'center' + ctx.fillStyle = ['blue', 'red'][+(this.inp1.value === undefined)] + if (this.inp1.value !== undefined) { + fillText( + ctx, + this.inp1.value.toString(16), + xx + 35 - this.xSize, + yy + 8, + 25 + ) + } else { + fillText(ctx, 'x', xx + 35 - this.xSize, yy + 8, 25) + } + ctx.fill() + } + + /** + * @memberof Flag + * function to change direction of Flag + * @param {string} dir - new direction + */ + newDirection(dir) { + if (dir === this.direction) return + this.direction = dir + this.inp1.refresh() + if (dir === 'RIGHT' || dir === 'LEFT') { + this.inp1.leftx = 50 - this.xSize + } else if (dir === 'UP') { + this.inp1.leftx = 20 + } else { + this.inp1.leftx = 20 + } + // if(this.direction=="LEFT" || this.direction=="RIGHT") this.inp1.leftx=50-this.xSize; + // this.inp1.refresh(); + + this.inp1.refresh() + } +} + +/** + * @memberof Flag + * Help Tip + * @type {string} + * @category modules + */ +Flag.prototype.tooltipText = + 'FLag ToolTip: Use this for debugging and plotting.' +Flag.prototype.helplink = + 'https://docs.circuitverse.org/#/timing_diagrams?id=using-flags' + +/** + * @memberof Flag + * Help URL + * @type {string} + * @category modules + */ +Flag.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/8misc?id=tunnel' + +/** + * @memberof Flag + * Mutable properties of the element + * @type {JSON} + * @category modules + */ +Flag.prototype.mutableProperties = { + identifier: { + name: 'Debug Flag identifier', + type: 'text', + maxlength: '5', + func: 'setIdentifier', + }, +} +Flag.prototype.objectType = 'Flag' +Flag.prototype.propagationDelay = 0 diff --git a/v0/src/simulator/src/modules/Ground.js b/v0/src/simulator/src/modules/Ground.js new file mode 100644 index 00000000..f1c88932 --- /dev/null +++ b/v0/src/simulator/src/modules/Ground.js @@ -0,0 +1,129 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, arc } from '../canvasApi' +import { changeInputSize } from '../modules' +/** + * @class + * Ground + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +import { colors } from '../themer/themer' + +export default class Ground extends CircuitElement { + constructor(x, y, scope = globalScope, bitWidth = 1) { + super(x, y, scope, 'RIGHT', bitWidth) + /* this is done in this.baseSetup() now + this.scope['Ground'].push(this); + */ + this.rectangleObject = false + this.setDimensions(10, 10) + this.directionFixed = true + this.output1 = new Node(0, -10, 1, this) + } + + /** + * @memberof Ground + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + nodes: { + output1: findNode(this.output1), + }, + values: { + state: this.state, + }, + constructorParamaters: [this.direction, this.bitWidth], + } + return data + } + + /** + * @memberof Ground + * resolve output values based on inputData + */ + resolve() { + this.output1.value = 0 + simulationArea.simulationQueue.add(this.output1) + } + + /** + * @memberof Ground + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + nodes: { + output1: findNode(this.output1), + }, + constructorParamaters: [this.bitWidth], + } + return data + } + + /** + * @memberof Ground + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + // + ctx.beginPath() + ctx.strokeStyle = [colors['stroke'], 'brown'][ + ((this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this)) + 0 + ] + ctx.lineWidth = correctWidth(3) + + const xx = this.x + const yy = this.y + + moveTo(ctx, 0, -10, xx, yy, this.direction) + lineTo(ctx, 0, 0, xx, yy, this.direction) + moveTo(ctx, -10, 0, xx, yy, this.direction) + lineTo(ctx, 10, 0, xx, yy, this.direction) + moveTo(ctx, -6, 5, xx, yy, this.direction) + lineTo(ctx, 6, 5, xx, yy, this.direction) + moveTo(ctx, -2.5, 10, xx, yy, this.direction) + lineTo(ctx, 2.5, 10, xx, yy, this.direction) + ctx.stroke() + } + + generateVerilog() { + return `assign ${this.output1.verilogLabel} = ${this.bitWidth}'b0;` + } +} + +/** + * @memberof Ground + * Help Tip + * @type {string} + * @category modules + */ +Ground.prototype.tooltipText = 'Ground: All bits are Low(0).' + +/** + * @memberof Ground + * Help URL + * @type {string} + * @category modules + */ +Ground.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/2input?id=ground' + +/** + * @memberof Ground + * @type {number} + * @category modules + */ +Ground.prototype.propagationDelay = 0 +Ground.prototype.objectType = 'Ground' diff --git a/v0/src/simulator/src/modules/HexDisplay.js b/v0/src/simulator/src/modules/HexDisplay.js new file mode 100644 index 00000000..b96cd096 --- /dev/null +++ b/v0/src/simulator/src/modules/HexDisplay.js @@ -0,0 +1,420 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { + correctWidth, + lineTo, + moveTo, + arc, + rect2, + validColor, + colorToRGBA, +} from '../canvasApi' +import { changeInputSize } from '../modules' +/** + * @class + * HexDisplay + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @category modules + */ +import { colors } from '../themer/themer' + +export default class HexDisplay extends CircuitElement { + constructor(x, y, scope = globalScope, color = 'Red') { + super(x, y, scope, 'RIGHT', 4) + /* this is done in this.baseSetup() now + this.scope['HexDisplay'].push(this); + */ + this.directionFixed = true + this.fixedBitWidth = true + this.setDimensions(30, 50) + this.inp = new Node(0, -50, 0, this, 4) + this.direction = 'RIGHT' + this.color = color + this.actualColor = color + } + + /** + * @memberof HexDisplay + * fn to change the color of HexDisplay + * @return {JSON} + */ + changeColor(value) { + if (validColor(value)) { + if (value.trim() === '') { + this.color = 'Red' + this.actualColor = 'rgba(255, 0, 0, 1)' + } else { + this.color = value + const temp = colorToRGBA(value) + this.actualColor = `rgba(${temp[0]},${temp[1]},${temp[2]}, ${temp[3]})` + } + } + } + + /** + * @memberof HexDisplay + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.color], + nodes: { + inp: findNode(this.inp), + }, + } + return data + } + + /** + * @memberof HexDisplay + * function to draw element + */ + customDrawSegment(x1, y1, x2, y2, color) { + if (color === undefined) color = 'lightgrey' + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = color + ctx.lineWidth = correctWidth(5) + const xx = this.x + const yy = this.y + + moveTo(ctx, x1, y1, xx, yy, this.direction) + lineTo(ctx, x2, y2, xx, yy, this.direction) + ctx.closePath() + ctx.stroke() + } + + /** + * @memberof HexDisplay + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + + const xx = this.x + const yy = this.y + + ctx.strokeStyle = colors['stroke'] + ctx.lineWidth = correctWidth(3) + + let a = 0, + b = 0, + c = 0, + d = 0, + e = 0, + f = 0, + g = 0 + switch (this.inp.value) { + case 0: + a = b = c = d = e = f = 1 + break + case 1: + b = c = 1 + break + case 2: + a = b = g = e = d = 1 + break + case 3: + a = b = g = c = d = 1 + break + case 4: + f = g = b = c = 1 + break + case 5: + a = f = g = c = d = 1 + break + case 6: + a = f = g = e = c = d = 1 + break + case 7: + a = b = c = 1 + break + case 8: + a = b = c = d = e = g = f = 1 + break + case 9: + a = f = g = b = c = 1 + break + case 0xa: + a = f = b = c = g = e = 1 + break + case 0xb: + f = e = g = c = d = 1 + break + case 0xc: + a = f = e = d = 1 + break + case 0xd: + b = c = g = e = d = 1 + break + case 0xe: + a = f = g = e = d = 1 + break + case 0xf: + a = f = g = e = 1 + break + default: + } + this.customDrawSegment( + 18, + -3, + 18, + -38, + ['lightgrey', this.actualColor][b] + ) + this.customDrawSegment( + 18, + 3, + 18, + 38, + ['lightgrey', this.actualColor][c] + ) + this.customDrawSegment( + -18, + -3, + -18, + -38, + ['lightgrey', this.actualColor][f] + ) + this.customDrawSegment( + -18, + 3, + -18, + 38, + ['lightgrey', this.actualColor][e] + ) + this.customDrawSegment( + -17, + -38, + 17, + -38, + ['lightgrey', this.actualColor][a] + ) + this.customDrawSegment( + -17, + 0, + 17, + 0, + ['lightgrey', this.actualColor][g] + ) + this.customDrawSegment( + -15, + 38, + 17, + 38, + ['lightgrey', this.actualColor][d] + ) + } + + subcircuitDrawSegment(x1, y1, x2, y2, color, xxSegment, yySegment) { + if (color == undefined) color = 'lightgrey' + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = color + ctx.lineWidth = correctWidth(3) + var xx = xxSegment + var yy = yySegment + + moveTo(ctx, x1, y1, xx, yy, this.direction) + lineTo(ctx, x2, y2, xx, yy, this.direction) + ctx.closePath() + ctx.stroke() + } + // Draws the element in the subcircuit. Used in layout mode + subcircuitDraw(xOffset = 0, yOffset = 0) { + var ctx = simulationArea.context + + var xx = this.subcircuitMetadata.x + xOffset + var yy = this.subcircuitMetadata.y + yOffset + + ctx.strokeStyle = 'black' + ctx.lineWidth = correctWidth(3) + let a = 0, + b = 0, + c = 0, + d = 0, + e = 0, + f = 0, + g = 0 + + switch (this.inp.value) { + case 0: + a = b = c = d = e = f = 1 + break + case 1: + b = c = 1 + break + case 2: + a = b = g = e = d = 1 + break + case 3: + a = b = g = c = d = 1 + break + case 4: + f = g = b = c = 1 + break + case 5: + a = f = g = c = d = 1 + break + case 6: + a = f = g = e = c = d = 1 + break + case 7: + a = b = c = 1 + break + case 8: + a = b = c = d = e = g = f = 1 + break + case 9: + a = f = g = b = c = 1 + break + case 0xa: + a = f = b = c = g = e = 1 + break + case 0xb: + f = e = g = c = d = 1 + break + case 0xc: + a = f = e = d = 1 + break + case 0xd: + b = c = g = e = d = 1 + break + case 0xe: + a = f = g = e = d = 1 + break + case 0xf: + a = f = g = e = 1 + break + default: + } + this.subcircuitDrawSegment( + 10, + -20, + 10, + -38, + ['lightgrey', this.actualColor][b], + xx, + yy + ) + this.subcircuitDrawSegment( + 10, + -17, + 10, + 1, + ['lightgrey', this.actualColor][c], + xx, + yy + ) + this.subcircuitDrawSegment( + -10, + -20, + -10, + -38, + ['lightgrey', this.actualColor][f], + xx, + yy + ) + this.subcircuitDrawSegment( + -10, + -17, + -10, + 1, + ['lightgrey', this.actualColor][e], + xx, + yy + ) + this.subcircuitDrawSegment( + -8, + -38, + 8, + -38, + ['lightgrey', this.actualColor][a], + xx, + yy + ) + this.subcircuitDrawSegment( + -8, + -18, + 8, + -18, + ['lightgrey', this.actualColor][g], + xx, + yy + ) + this.subcircuitDrawSegment( + -8, + 1, + 8, + 1, + ['lightgrey', this.actualColor][d], + xx, + yy + ) + + ctx.beginPath() + ctx.strokeStyle = 'black' + ctx.lineWidth = correctWidth(1) + rect2(ctx, -15, -42, 33, 51, xx, yy, this.direction) + ctx.stroke() + + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected == this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.fillStyle = 'rgba(255, 255, 32,0.6)' + ctx.fill() + } + } + generateVerilog() { + return ` + always @ (*) + $display("HexDisplay:${this.verilogLabel}=%d", ${this.inp.verilogLabel});` + } +} + +/** + * @memberof HexDisplay + * Help Tip + * @type {string} + * @category modules + */ +HexDisplay.prototype.tooltipText = + 'Hex Display ToolTip: Inputs a 4 Bit Hex number and displays it.' + +/** + * @memberof HexDisplay + * Help URL + * @type {string} + * @category modules + */ +HexDisplay.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/3output?id=hexdisplay' +HexDisplay.prototype.objectType = 'HexDisplay' +HexDisplay.prototype.canShowInSubcircuit = true +HexDisplay.prototype.layoutProperties = { + rightDimensionX: 20, + leftDimensionX: 15, + upDimensionY: 42, + downDimensionY: 10, +} + +/** + * @memberof HexDisplay + * Mutable properties of the element + * @type {JSON} + * @category modules + */ +HexDisplay.prototype.mutableProperties = { + color: { + name: 'Color: ', + type: 'text', + func: 'changeColor', + }, +} diff --git a/v0/src/simulator/src/modules/ImageAnnotation.js b/v0/src/simulator/src/modules/ImageAnnotation.js new file mode 100644 index 00000000..67ac4d8b --- /dev/null +++ b/v0/src/simulator/src/modules/ImageAnnotation.js @@ -0,0 +1,243 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, rect, fillText, drawImage } from '../canvasApi' +import { colors } from '../themer/themer' +import { promptFile, showMessage, getImageDimensions } from '../utils' +/** + * @class + * Image + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {number=} rows - number of rows + * @param {number=} cols - number of columns. + * @category modules + */ +export default class ImageAnnotation extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + rows = 15, + cols = 20, + imageUrl = '' + ) { + super(x, y, scope, 'RIGHT', 1) + this.directionFixed = true + this.fixedBitWidth = true + this.imageUrl = imageUrl + this.cols = cols || parseInt(prompt('Enter cols:'), 10) + this.rows = rows || parseInt(prompt('Enter rows:'), 10) + this.setSize() + this.loadImage() + } + + /** + * @memberof Image + * @param {number} size - new size of rows + */ + changeRowSize(size) { + if (size === undefined || size < 5 || size > 1000) return + if (this.rows === size) return + this.rows = parseInt(size, 10) + this.setSize() + return this + } + + /** + * @memberof Image + * @param {number} size - new size of columns + */ + changeColSize(size) { + if (size === undefined || size < 5 || size > 1000) return + if (this.cols === size) return + this.cols = parseInt(size, 10) + this.setSize() + return this + } + + /** + * @memberof Image + * listener function to change direction of Image + * @param {string} dir - new direction + */ + keyDown3(dir) { + if (dir === 'ArrowRight') { + this.changeColSize(this.cols + 2) + } + if (dir === 'ArrowLeft') { + this.changeColSize(this.cols - 2) + } + if (dir === 'ArrowDown') { + this.changeRowSize(this.rows + 2) + } + if (dir === 'ArrowUp') { + this.changeRowSize(this.rows - 2) + } + } + + /** + * @memberof Image + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.rows, this.cols, this.imageUrl], + } + return data + } + + /** + * @memberof Image + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + const xx = this.x + const yy = this.y + var w = this.elementWidth + var h = this.elementHeight + if (this.image && this.image.complete) { + drawImage(ctx, this.image, xx - w / 2, yy - h / 2, w, h) + } else { + ctx.beginPath() + ctx.strokeStyle = 'rgba(0,0,0,1)' + ctx.setLineDash([5 * globalScope.scale, 5 * globalScope.scale]) + ctx.lineWidth = correctWidth(1.5) + + rect(ctx, xx - w / 2, yy - h / 2, w, h) + ctx.stroke() + + if ( + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.fillStyle = 'rgba(255, 255, 32,0.1)' + ctx.fill() + } + + ctx.beginPath() + ctx.textAlign = 'center' + ctx.fillStyle = colors['text'] + fillText(ctx, 'Double Click to Insert Image', xx, yy, 10) + ctx.fill() + + ctx.setLineDash([]) + } + } + + /** + * Procedure if image is double clicked + **/ + dblclick() { + if (embed) return + this.uploadImage() + } + + async uploadImage() { + var file = await promptFile('image/*', false) + var apiUrl = 'https://api.imgur.com/3/image' + var apiKey = '9a33b3b370f1054' + var settings = { + crossDomain: true, + processData: false, + contentType: false, + type: 'POST', + url: apiUrl, + headers: { + Authorization: 'Client-ID ' + apiKey, + Accept: 'application/json', + }, + mimeType: 'multipart/form-data', + } + var formData = new FormData() + formData.append('image', file) + settings.data = formData + + // Response contains stringified JSON + // Image URL available at response.data.link + showMessage('Uploading Image') + var response = await $.ajax(settings) + showMessage('Image Uploaded') + this.imageUrl = JSON.parse(response).data.link + this.loadImage() + } + + async loadImage() { + if (!this.imageUrl) return + this.image = new Image() + this.image.crossOrigin = 'anonymous' + this.image.src = this.imageUrl + } + /** + * @memberof Image + * function to reset or (internally) set size + */ + setSize() { + this.elementWidth = this.cols * 10 + this.elementHeight = this.rows * 10 + this.upDimensionY = this.elementHeight / 2 + this.downDimensionY = this.elementHeight / 2 + this.leftDimensionX = this.elementWidth / 2 + this.rightDimensionX = this.elementWidth / 2 + } +} + +/** + * @memberof Image + * Help Tip + * @type {string} + * @category modules + */ +ImageAnnotation.prototype.tooltipText = + 'Image ToolTip: Embed an image in the circuit for annotation' +ImageAnnotation.prototype.propagationDelayFixed = true + +/** + * @memberof Image + * Mutable properties of the element + * @type {JSON} + * @category modules + */ +ImageAnnotation.prototype.mutableProperties = { + cols: { + name: 'Columns', + type: 'number', + max: '1000', + min: '5', + func: 'changeColSize', + }, + rows: { + name: 'Rows', + type: 'number', + max: '1000', + min: '5', + func: 'changeRowSize', + }, +} +ImageAnnotation.prototype.objectType = 'ImageAnnotation' +ImageAnnotation.prototype.rectangleObject = false +ImageAnnotation.prototype.mutableProperties = { + imageUrl: { + name: 'Upload Image', + type: 'button', + func: 'uploadImage', + }, + cols: { + name: 'Columns', + type: 'number', + max: '1000', + min: '5', + func: 'changeColSize', + }, + rows: { + name: 'Rows', + type: 'number', + max: '1000', + min: '5', + func: 'changeRowSize', + }, +} diff --git a/v0/src/simulator/src/modules/Input.js b/v0/src/simulator/src/modules/Input.js new file mode 100644 index 00000000..d33c8627 --- /dev/null +++ b/v0/src/simulator/src/modules/Input.js @@ -0,0 +1,210 @@ +/* eslint-disable no-unused-expressions */ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, oppositeDirection, fillText } from '../canvasApi' +import { getNextPosition } from '../modules' +import { generateId } from '../utils' +/** + * @class + * Input + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @param {Object=} layoutProperties - x,y and id + * @category modules + */ +import { colors } from '../themer/themer' + +function bin2dec(binString) { + return parseInt(binString, 2) +} + +function dec2bin(dec, bitWidth = undefined) { + // only for positive nos + var bin = dec.toString(2) + if (bitWidth == undefined) return bin + return '0'.repeat(bitWidth - bin.length) + bin +} + +export default class Input extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + bitWidth = 1, + layoutProperties + ) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['Input'].push(this); + */ + if (layoutProperties) { + this.layoutProperties = layoutProperties + } else { + this.layoutProperties = {} + this.layoutProperties.x = 0 + this.layoutProperties.y = getNextPosition(0, scope) + this.layoutProperties.id = generateId() + } + + // Call base class constructor + this.state = 0 + this.orientationFixed = false + this.state = bin2dec(this.state) // in integer format + this.output1 = new Node(this.bitWidth * 10, 0, 1, this) + this.wasClicked = false + this.directionFixed = true + this.setWidth(this.bitWidth * 10) + this.rectangleObject = true // Trying to make use of base class draw + } + + /** + * @memberof Input + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + nodes: { + output1: findNode(this.output1), + }, + values: { + state: this.state, + }, + constructorParamaters: [ + this.direction, + this.bitWidth, + this.layoutProperties, + ], + } + return data + } + + /** + * @memberof Input + * resolve output values based on inputData + */ + resolve() { + this.output1.value = this.state + simulationArea.simulationQueue.add(this.output1) + } + + // Check if override is necessary!! + + /** + * @memberof Input + * function to change bitwidth of the element + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + if (bitWidth < 1) return + const diffBitWidth = bitWidth - this.bitWidth + this.bitWidth = bitWidth // ||parseInt(prompt("Enter bitWidth"),10); + this.setWidth(this.bitWidth * 10) + this.state = 0 + this.output1.bitWidth = bitWidth + if (this.direction === 'RIGHT') { + this.x -= 10 * diffBitWidth + this.output1.x = 10 * this.bitWidth + this.output1.leftx = 10 * this.bitWidth + } else if (this.direction === 'LEFT') { + this.x += 10 * diffBitWidth + this.output1.x = -10 * this.bitWidth + this.output1.leftx = 10 * this.bitWidth + } + } + + /** + * @memberof Input + * listener function to set selected index + */ + click() { + // toggle + let pos = this.findPos() + if (pos === 0) pos = 1 // minor correction + if (pos < 1 || pos > this.bitWidth) return + this.state = ((this.state >>> 0) ^ (1 << (this.bitWidth - pos))) >>> 0 + } + + /** + * @memberof Input + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.beginPath() + ctx.lineWidth = correctWidth(3) + const xx = this.x + const yy = this.y + ctx.beginPath() + ctx.fillStyle = colors['input_text'] + ctx.textAlign = 'center' + const bin = dec2bin(this.state, this.bitWidth) + for (let k = 0; k < this.bitWidth; k++) { + fillText(ctx, bin[k], xx - 10 * this.bitWidth + 10 + k * 20, yy + 5) + } + ctx.fill() + } + + /** + * @memberof Input + * function to change direction of input + * @param {string} dir - new direction + */ + newDirection(dir) { + if (dir === this.direction) return + this.direction = dir + this.output1.refresh() + if (dir === 'RIGHT' || dir === 'LEFT') { + this.output1.leftx = 10 * this.bitWidth + this.output1.lefty = 0 + } else { + this.output1.leftx = 10 // 10*this.bitWidth; + this.output1.lefty = 0 + } + + this.output1.refresh() + this.labelDirection = oppositeDirection[this.direction] + } + + /** + * @memberof Input + * function to find position of mouse click + */ + findPos() { + return Math.round( + (simulationArea.mouseX - this.x + 10 * this.bitWidth) / 20.0 + ) + } +} + +/** + * @memberof Input + * Help Tip + * @type {string} + * @category modules + */ +Input.prototype.tooltipText = + 'Input ToolTip: Toggle the individual bits by clicking on them.' + +/** + * @memberof Input + * Help URL + * @type {string} + * @category modules + */ +Input.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/2input?id=input' + +/** + * @memberof Input + * @type {number} + * @category modules + */ +Input.prototype.propagationDelay = 0 +Input.prototype.objectType = 'Input' diff --git a/v0/src/simulator/src/modules/LSB.js b/v0/src/simulator/src/modules/LSB.js new file mode 100644 index 00000000..0f7e4e61 --- /dev/null +++ b/v0/src/simulator/src/modules/LSB.js @@ -0,0 +1,144 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode, dec2bin } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, rect, fillText } from '../canvasApi' +/** + * @class + * LSB + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +import { colors } from '../themer/themer' + +export default class LSB extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT', bitWidth = 1) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['LSB'].push(this); + */ + this.leftDimensionX = 10 + this.rightDimensionX = 20 + this.setHeight(30) + this.directionFixed = true + this.bitWidth = bitWidth || parseInt(prompt('Enter bitWidth'), 10) + this.rectangleObject = false + // this.inputSize = 1 << this.bitWidth; + this.intputSize = this.bitWidth + + this.inp1 = new Node(-10, 0, 0, this, this.inputSize) + this.output1 = new Node(20, 0, 1, this, this.bitWidth) + this.enable = new Node(20, 20, 1, this, 1) + } + + /** + * @memberof LSB + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + nodes: { + inp1: findNode(this.inp1), + output1: findNode(this.output1), + enable: findNode(this.enable), + }, + constructorParamaters: [this.direction, this.bitWidth], + } + return data + } + + /** + * @memberof LSB + * function to change bitwidth of the element + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + // this.inputSize = 1 << bitWidth + this.inputSize = bitWidth + this.inp1.bitWidth = this.inputSize + this.bitWidth = bitWidth + this.output1.bitWidth = bitWidth + } + + /** + * @memberof LSB + * resolve output values based on inputData + */ + resolve() { + const inp = dec2bin(this.inp1.value) + let out = 0 + for (let i = inp.length - 1; i >= 0; i--) { + if (inp[i] === '1') { + out = inp.length - 1 - i + break + } + } + this.output1.value = out + simulationArea.simulationQueue.add(this.output1) + if (inp != 0) { + this.enable.value = 1 + } else { + this.enable.value = 0 + } + simulationArea.simulationQueue.add(this.enable) + } + + /** + * @memberof LSB + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.lineWidth = correctWidth(3) + const xx = this.x + const yy = this.y + rect(ctx, xx - 10, yy - 30, 30, 60) + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + + ctx.beginPath() + ctx.fillStyle = 'black' + ctx.textAlign = 'center' + fillText(ctx, 'LSB', xx + 6, yy - 12, 10) + fillText(ctx, 'EN', xx + this.enable.x - 12, yy + this.enable.y + 3, 8) + ctx.fill() + + ctx.beginPath() + ctx.fillStyle = 'green' + ctx.textAlign = 'center' + if (this.output1.value !== undefined) { + fillText(ctx, this.output1.value, xx + 5, yy + 14, 13) + } + ctx.stroke() + ctx.fill() + } + generateVerilog() { + return `assign ${this.output1.verilogLabel} = (${this.enable.verilogLabel}!=0) ? ${this.inp1.verilogLabel}[0] : 0;` + } +} + +/** + * @memberof LSB + * Help Tip + * @type {string} + * @category modules + */ +LSB.prototype.tooltipText = + 'LSB ToolTip : The least significant bit or the low-order bit.' +LSB.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/5muxandplex?id=least-significant-bit-lsb-detector' +LSB.prototype.objectType = 'LSB' diff --git a/v0/src/simulator/src/modules/MSB.js b/v0/src/simulator/src/modules/MSB.js new file mode 100644 index 00000000..fbe14b2c --- /dev/null +++ b/v0/src/simulator/src/modules/MSB.js @@ -0,0 +1,141 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode, dec2bin } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, rect, fillText } from '../canvasApi' +/** + * @class + * MSB + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +import { colors } from '../themer/themer' + +export default class MSB extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT', bitWidth = 1) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['MSB'].push(this); + */ + // this.setDimensions(20, 20); + this.leftDimensionX = 10 + this.rightDimensionX = 20 + this.setHeight(30) + this.directionFixed = true + this.bitWidth = bitWidth || parseInt(prompt('Enter bitWidth'), 10) + this.rectangleObject = false + // this.inputSize = 1 << this.bitWidth; + this.intputSize = this.bitWidth + + this.inp1 = new Node(-10, 0, 0, this, this.inputSize) + this.output1 = new Node(20, 0, 1, this, this.bitWidth) + this.enable = new Node(20, 20, 1, this, 1) + } + + /** + * @memberof MSB + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + nodes: { + inp1: findNode(this.inp1), + output1: findNode(this.output1), + enable: findNode(this.enable), + }, + constructorParamaters: [this.direction, this.bitWidth], + } + return data + } + + /** + * @memberof MSB + * function to change bitwidth of the element + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + // this.inputSize = 1 << bitWidth + this.inputSize = bitWidth + this.inp1.bitWidth = this.inputSize + this.bitWidth = bitWidth + this.output1.bitWidth = bitWidth + } + + /** + * @memberof MSB + * resolve output values based on inputData + */ + resolve() { + const inp = this.inp1.value + this.output1.value = dec2bin(inp).length - 1 + simulationArea.simulationQueue.add(this.output1) + if (inp != 0) { + this.enable.value = 1 + } else { + this.enable.value = 0 + } + simulationArea.simulationQueue.add(this.enable) + } + + /** + * @memberof MSB + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.lineWidth = correctWidth(3) + const xx = this.x + const yy = this.y + rect(ctx, xx - 10, yy - 30, 30, 60) + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + + ctx.beginPath() + ctx.fillStyle = 'black' + ctx.textAlign = 'center' + fillText(ctx, 'MSB', xx + 6, yy - 12, 10) + fillText(ctx, 'EN', xx + this.enable.x - 12, yy + this.enable.y + 3, 8) + ctx.fill() + + ctx.beginPath() + ctx.fillStyle = 'green' + ctx.textAlign = 'center' + if (this.output1.value !== undefined) { + fillText(ctx, this.output1.value, xx + 5, yy + 14, 13) + } + ctx.stroke() + ctx.fill() + } + + generateVerilog() { + return `assign ${this.output1.verilogLabel} = (${ + this.enable.verilogLabel + }!=0) ? ${this.inp1.verilogLabel}[${this.inp1.bitWidth - 1}] : 0;` + } +} + +/** + * @memberof MSB + * Help Tip + * @type {string} + * @category modules + */ +MSB.prototype.tooltipText = + 'MSB ToolTip : The most significant bit or the high-order bit.' +MSB.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/5muxandplex?id=most-significant-bit-msb-detector' +MSB.prototype.objectType = 'MSB' diff --git a/v0/src/simulator/src/modules/Multiplexer.js b/v0/src/simulator/src/modules/Multiplexer.js new file mode 100644 index 00000000..638b9d01 --- /dev/null +++ b/v0/src/simulator/src/modules/Multiplexer.js @@ -0,0 +1,344 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, fillText } from '../canvasApi' +import { changeInputSize } from '../modules' +/** + * @class + * Multiplexer + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @param {number=} controlSignalSize - 1 by default + * @category modules + */ +import { colors } from '../themer/themer' + +export default class Multiplexer extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + bitWidth = 1, + controlSignalSize = 1 + ) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['Multiplexer'].push(this); + */ + this.controlSignalSize = + controlSignalSize || + parseInt(prompt('Enter control signal bitWidth'), 10) + this.inputSize = 1 << this.controlSignalSize + this.xOff = 0 + this.yOff = 1 + if (this.controlSignalSize === 1) { + this.xOff = 10 + } + if (this.controlSignalSize <= 3) { + this.yOff = 2 + } + this.setDimensions(20 - this.xOff, this.yOff * 5 * this.inputSize) + this.rectangleObject = false + this.inp = [] + for (let i = 0; i < this.inputSize; i++) { + const a = new Node( + -20 + this.xOff, + +this.yOff * 10 * (i - this.inputSize / 2) + 10, + 0, + this + ) + this.inp.push(a) + } + this.output1 = new Node(20 - this.xOff, 0, 1, this) + this.controlSignalInput = new Node( + 0, + this.yOff * 10 * (this.inputSize / 2 - 1) + this.xOff + 10, + 0, + this, + this.controlSignalSize, + 'Control Signal' + ) + } + + /** + * @memberof Multiplexer + * function to change control signal of the element + */ + changeControlSignalSize(size) { + if (size === undefined || size < 1 || size > 32) return + if (this.controlSignalSize === size) return + const obj = new Multiplexer( + this.x, + this.y, + this.scope, + this.direction, + this.bitWidth, + size + ) + this.cleanDelete() + simulationArea.lastSelected = obj + return obj + } + + /** + * @memberof Multiplexer + * function to change bitwidth of the element + * @param {number} bitWidth - bitwidth + */ + newBitWidth(bitWidth) { + this.bitWidth = bitWidth + for (let i = 0; i < this.inputSize; i++) { + this.inp[i].bitWidth = bitWidth + } + this.output1.bitWidth = bitWidth + } + + /** + * @memberof Multiplexer + * @type {boolean} + */ + isResolvable() { + if ( + this.controlSignalInput.value !== undefined && + this.inp[this.controlSignalInput.value].value !== undefined + ) + return true + return false + } + + /** + * @memberof Multiplexer + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [ + this.direction, + this.bitWidth, + this.controlSignalSize, + ], + nodes: { + inp: this.inp.map(findNode), + output1: findNode(this.output1), + controlSignalInput: findNode(this.controlSignalInput), + }, + } + return data + } + + /** + * @memberof Multiplexer + * resolve output values based on inputData + */ + resolve() { + if (this.isResolvable() === false) { + return + } + this.output1.value = this.inp[this.controlSignalInput.value].value + simulationArea.simulationQueue.add(this.output1) + } + + /** + * @memberof Multiplexer + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + + const xx = this.x + const yy = this.y + + ctx.beginPath() + moveTo( + ctx, + 0, + this.yOff * 10 * (this.inputSize / 2 - 1) + 10 + 0.5 * this.xOff, + xx, + yy, + this.direction + ) + lineTo( + ctx, + 0, + this.yOff * 5 * (this.inputSize - 1) + this.xOff, + xx, + yy, + this.direction + ) + ctx.stroke() + + ctx.lineWidth = correctWidth(3) + ctx.beginPath() + ctx.strokeStyle = colors['stroke'] + + ctx.fillStyle = colors['fill'] + moveTo( + ctx, + -20 + this.xOff, + -this.yOff * 10 * (this.inputSize / 2), + xx, + yy, + this.direction + ) + lineTo( + ctx, + -20 + this.xOff, + 20 + this.yOff * 10 * (this.inputSize / 2 - 1), + xx, + yy, + this.direction + ) + lineTo( + ctx, + 20 - this.xOff, + +this.yOff * 10 * (this.inputSize / 2 - 1) + this.xOff, + xx, + yy, + this.direction + ) + lineTo( + ctx, + 20 - this.xOff, + -this.yOff * 10 * (this.inputSize / 2) - this.xOff + 20, + xx, + yy, + this.direction + ) + + ctx.closePath() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.fillStyle = colors['hover_select'] + } + ctx.fill() + ctx.stroke() + + ctx.beginPath() + // ctx.lineWidth = correctWidth(2); + ctx.fillStyle = 'black' + ctx.textAlign = 'center' + for (let i = 0; i < this.inputSize; i++) { + if (this.direction === 'RIGHT') + fillText( + ctx, + String(i), + xx + this.inp[i].x + 7, + yy + this.inp[i].y + 2, + 10 + ) + else if (this.direction === 'LEFT') + fillText( + ctx, + String(i), + xx + this.inp[i].x - 7, + yy + this.inp[i].y + 2, + 10 + ) + else if (this.direction === 'UP') + fillText( + ctx, + String(i), + xx + this.inp[i].x, + yy + this.inp[i].y - 4, + 10 + ) + else + fillText( + ctx, + String(i), + xx + this.inp[i].x, + yy + this.inp[i].y + 10, + 10 + ) + } + ctx.fill() + } + + verilogBaseType() { + return this.verilogName() + this.inp.length + } + + //this code to generate Verilog + generateVerilog() { + Multiplexer.selSizes.add(this.controlSignalSize) + return CircuitElement.prototype.generateVerilog.call(this) + } + + //generate the needed modules + static moduleVerilog() { + var output = '' + + for (var size of Multiplexer.selSizes) { + var numInput = 1 << size + var inpString = '' + for (var j = 0; j < numInput; j++) { + inpString += `in${j}, ` + } + output += `\nmodule Multiplexer${numInput}(out, ${inpString}sel);\n` + + output += ' parameter WIDTH = 1;\n' + output += ' output reg [WIDTH-1:0] out;\n' + + output += ' input [WIDTH-1:0] ' + for (var j = 0; j < numInput - 1; j++) { + output += `in${j}, ` + } + output += 'in' + (numInput - 1) + ';\n' + + output += ` input [${size - 1}:0] sel;\n` + output += ' \n' + + output += ' always @ (*)\n' + output += ' case (sel)\n' + for (var j = 0; j < numInput; j++) { + output += ` ${j} : out = in${j};\n` + } + output += ' endcase\n' + output += 'endmodule\n' + output += '\n' + } + + return output + } + //reset the sized before Verilog generation + static resetVerilog() { + Multiplexer.selSizes = new Set() + } +} + +/** + * @memberof Multiplexer + * Help Tip + * @type {string} + * @category modules + */ +Multiplexer.prototype.tooltipText = + 'Multiplexer ToolTip : Multiple inputs and a single line output.' +Multiplexer.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/5muxandplex?id=multiplexer' + +/** + * @memberof Multiplexer + * multable properties of element + * @type {JSON} + * @category modules + */ +Multiplexer.prototype.mutableProperties = { + controlSignalSize: { + name: 'Control Signal Size', + type: 'number', + max: '10', + min: '1', + func: 'changeControlSignalSize', + }, +} +Multiplexer.prototype.objectType = 'Multiplexer' diff --git a/v0/src/simulator/src/modules/NandGate.js b/v0/src/simulator/src/modules/NandGate.js new file mode 100644 index 00000000..1cf25e82 --- /dev/null +++ b/v0/src/simulator/src/modules/NandGate.js @@ -0,0 +1,169 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, drawCircle2, arc } from '../canvasApi' +import { changeInputSize } from '../modules' +import { gateGenerateVerilog } from '../utils' + +/** + * @class + * NandGate + * @extends CircuitElement + * @param {number} x - x coordinate of nand Gate. + * @param {number} y - y coordinate of nand Gate. + * @param {Scope=} scope - Cirucit on which nand gate is drawn + * @param {string=} dir - direction of nand Gate + * @param {number=} inputLength - number of input nodes + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +import { colors } from '../themer/themer' + +export default class NandGate extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + inputLength = 2, + bitWidth = 1 + ) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['NandGate'].push(this); + */ + this.rectangleObject = false + this.setDimensions(15, 20) + this.inp = [] + this.inputSize = inputLength + // variable inputLength , node creation + if (inputLength % 2 === 1) { + for (let i = 0; i < inputLength / 2 - 1; i++) { + const a = new Node(-10, -10 * (i + 1), 0, this) + this.inp.push(a) + } + let a = new Node(-10, 0, 0, this) + this.inp.push(a) + for (let i = inputLength / 2 + 1; i < inputLength; i++) { + a = new Node(-10, 10 * (i + 1 - inputLength / 2 - 1), 0, this) + this.inp.push(a) + } + } else { + for (let i = 0; i < inputLength / 2; i++) { + const a = new Node(-10, -10 * (i + 1), 0, this) + this.inp.push(a) + } + for (let i = inputLength / 2; i < inputLength; i++) { + const a = new Node(-10, 10 * (i + 1 - inputLength / 2), 0, this) + this.inp.push(a) + } + } + this.output1 = new Node(30, 0, 1, this) + } + + /** + * @memberof NandGate + * fn to create save Json Data of object + * @return {JSON} + */ + // fn to create save Json Data of object + customSave() { + const data = { + constructorParamaters: [ + this.direction, + this.inputSize, + this.bitWidth, + ], + nodes: { + inp: this.inp.map(findNode), + output1: findNode(this.output1), + }, + } + return data + } + + /** + * @memberof NandGate + * resolve output values based on inputData + */ + resolve() { + let result = this.inp[0].value || 0 + if (this.isResolvable() === false) { + return + } + for (let i = 1; i < this.inputSize; i++) + result &= this.inp[i].value || 0 + result = + ((~result >>> 0) << (32 - this.bitWidth)) >>> (32 - this.bitWidth) + this.output1.value = result + simulationArea.simulationQueue.add(this.output1) + } + + /** + * @memberof NandGate + * function to draw nand Gate + */ + customDraw() { + var ctx = simulationArea.context + ctx.beginPath() + ctx.lineWidth = correctWidth(3) + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + const xx = this.x + const yy = this.y + moveTo(ctx, -10, -20, xx, yy, this.direction) + lineTo(ctx, 0, -20, xx, yy, this.direction) + arc(ctx, 0, 0, 20, -Math.PI / 2, Math.PI / 2, xx, yy, this.direction) + lineTo(ctx, -10, 20, xx, yy, this.direction) + lineTo(ctx, -10, -20, xx, yy, this.direction) + ctx.closePath() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + ctx.beginPath() + drawCircle2(ctx, 25, 0, 5, xx, yy, this.direction) + ctx.stroke() + } + + generateVerilog() { + return gateGenerateVerilog.call(this, '&', true) + } +} + +/** + * @memberof NandGate + * Help Tip + * @type {string} + * @category modules + */ +NandGate.prototype.tooltipText = + 'Nand Gate ToolTip : Combination of AND and NOT gates' + +/** + * @memberof NandGate + * @type {boolean} + * @category modules + */ +NandGate.prototype.alwaysResolve = true + +/** + * @memberof NandGate + * function to change input nodes of the gate + * @category modules + */ +NandGate.prototype.changeInputSize = changeInputSize + +/** + * @memberof NandGate + * @type {string} + * @category modules + */ +NandGate.prototype.verilogType = 'nand' +NandGate.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/4gates?id=nand-gate' +NandGate.prototype.objectType = 'NandGate' diff --git a/v0/src/simulator/src/modules/NorGate.js b/v0/src/simulator/src/modules/NorGate.js new file mode 100644 index 00000000..38572dd5 --- /dev/null +++ b/v0/src/simulator/src/modules/NorGate.js @@ -0,0 +1,183 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { gateGenerateVerilog } from '../utils' + +import { + correctWidth, + bezierCurveTo, + moveTo, + arc2, + drawCircle2, +} from '../canvasApi' +import { changeInputSize } from '../modules' +/** + * @class + * NorGate + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} inputs - number of input nodes + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +import { colors } from '../themer/themer' + +export default class NorGate extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + inputs = 2, + bitWidth = 1 + ) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['NorGate'].push(this); + */ + this.rectangleObject = false + this.setDimensions(15, 20) + + this.inp = [] + this.inputSize = inputs + + if (inputs % 2 === 1) { + for (let i = 0; i < inputs / 2 - 1; i++) { + const a = new Node(-10, -10 * (i + 1), 0, this) + this.inp.push(a) + } + let a = new Node(-10, 0, 0, this) + this.inp.push(a) + for (let i = inputs / 2 + 1; i < inputs; i++) { + a = new Node(-10, 10 * (i + 1 - inputs / 2 - 1), 0, this) + this.inp.push(a) + } + } else { + for (let i = 0; i < inputs / 2; i++) { + const a = new Node(-10, -10 * (i + 1), 0, this) + this.inp.push(a) + } + for (let i = inputs / 2; i < inputs; i++) { + const a = new Node(-10, 10 * (i + 1 - inputs / 2), 0, this) + this.inp.push(a) + } + } + this.output1 = new Node(30, 0, 1, this) + } + + /** + * @memberof NorGate + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [ + this.direction, + this.inputSize, + this.bitWidth, + ], + nodes: { + inp: this.inp.map(findNode), + output1: findNode(this.output1), + }, + } + return data + } + + /** + * @memberof NorGate + * resolve output values based on inputData + */ + resolve() { + let result = this.inp[0].value || 0 + for (let i = 1; i < this.inputSize; i++) + result |= this.inp[i].value || 0 + result = + ((~result >>> 0) << (32 - this.bitWidth)) >>> (32 - this.bitWidth) + this.output1.value = result + simulationArea.simulationQueue.add(this.output1) + } + + /** + * @memberof NorGate + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.strokeStyle = colors['stroke'] + ctx.lineWidth = correctWidth(3) + + const xx = this.x + const yy = this.y + ctx.beginPath() + ctx.fillStyle = colors['fill'] + + moveTo(ctx, -10, -20, xx, yy, this.direction, true) + bezierCurveTo(0, -20, +15, -10, 20, 0, xx, yy, this.direction) + bezierCurveTo( + 0 + 15, + 0 + 10, + 0, + 0 + 20, + -10, + +20, + xx, + yy, + this.direction + ) + bezierCurveTo(0, 0, 0, 0, -10, -20, xx, yy, this.direction) + ctx.closePath() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + ctx.beginPath() + drawCircle2(ctx, 25, 0, 5, xx, yy, this.direction) + ctx.stroke() + // for debugging + } + + generateVerilog() { + return gateGenerateVerilog.call(this, '|', true) + } +} + +/** + * @memberof NorGate + * Help Tip + * @type {string} + * @category modules + */ +NorGate.prototype.tooltipText = + 'Nor Gate ToolTip : Combination of OR gate and NOT gate.' + +/** + * @memberof NorGate + * @type {boolean} + * @category modules + */ +NorGate.prototype.alwaysResolve = true + +/** + * @memberof SevenSegDisplay + * function to change input nodes of the element + * @category modules + */ +NorGate.prototype.changeInputSize = changeInputSize + +/** + * @memberof SevenSegDisplay + * @type {string} + * @category modules + */ +NorGate.prototype.verilogType = 'nor' +NorGate.prototype.helplink = 'https://docs.circuitverse.org/#/chapter4/4gates?id=nor-gate' +NorGate.prototype.objectType = 'NorGate' diff --git a/v0/src/simulator/src/modules/NotGate.js b/v0/src/simulator/src/modules/NotGate.js new file mode 100644 index 00000000..38ec3a05 --- /dev/null +++ b/v0/src/simulator/src/modules/NotGate.js @@ -0,0 +1,113 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, drawCircle2 } from '../canvasApi' +import { changeInputSize } from '../modules' +/** + * @class + * NotGate + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +import { colors } from '../themer/themer' + +export default class NotGate extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT', bitWidth = 1) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['NotGate'].push(this); + */ + this.rectangleObject = false + this.setDimensions(15, 15) + + this.inp1 = new Node(-10, 0, 0, this) + this.output1 = new Node(20, 0, 1, this) + } + + /** + * @memberof NotGate + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.direction, this.bitWidth], + nodes: { + output1: findNode(this.output1), + inp1: findNode(this.inp1), + }, + } + return data + } + + /** + * @memberof NotGate + * resolve output values based on inputData + */ + resolve() { + if (this.isResolvable() === false) { + return + } + this.output1.value = + ((~this.inp1.value >>> 0) << (32 - this.bitWidth)) >>> + (32 - this.bitWidth) + simulationArea.simulationQueue.add(this.output1) + } + + /** + * @memberof NotGate + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.strokeStyle = colors['stroke'] + ctx.lineWidth = correctWidth(3) + + const xx = this.x + const yy = this.y + ctx.beginPath() + ctx.fillStyle = colors['fill'] + moveTo(ctx, -10, -10, xx, yy, this.direction) + lineTo(ctx, 10, 0, xx, yy, this.direction) + lineTo(ctx, -10, 10, xx, yy, this.direction) + ctx.closePath() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + ctx.beginPath() + drawCircle2(ctx, 15, 0, 5, xx, yy, this.direction) + ctx.stroke() + } + + generateVerilog() { + return ( + 'assign ' + + this.output1.verilogLabel + + ' = ~' + + this.inp1.verilogLabel + + ';' + ) + } +} + +/** + * @memberof NotGate + * Help Tip + * @type {string} + * @category modules + */ +NotGate.prototype.tooltipText = + 'Not Gate Tooltip : Inverts the input digital signal.' +NotGate.prototype.helplink = 'https://docs.circuitverse.org/#/chapter4/4gates?id=not-gate' +NotGate.prototype.objectType = 'NotGate' +NotGate.prototype.verilogType = 'not' diff --git a/v0/src/simulator/src/modules/OrGate.js b/v0/src/simulator/src/modules/OrGate.js new file mode 100644 index 00000000..31b557cd --- /dev/null +++ b/v0/src/simulator/src/modules/OrGate.js @@ -0,0 +1,175 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, bezierCurveTo, moveTo, arc2 } from '../canvasApi' +import { changeInputSize } from '../modules' +import { gateGenerateVerilog } from '../utils' + +/** + * @class + * OrGate + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} inputs - number of input nodes + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +import { colors } from '../themer/themer' + +export default class OrGate extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + inputs = 2, + bitWidth = 1 + ) { + // Calling base class constructor + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['OrGate'].push(this); + */ + this.rectangleObject = false + this.setDimensions(15, 20) + // Inherit base class prototype + this.inp = [] + this.inputSize = inputs + if (inputs % 2 === 1) { + for (let i = Math.floor(inputs / 2) - 1; i >= 0; i--) { + const a = new Node(-10, -10 * (i + 1), 0, this) + this.inp.push(a) + } + let a = new Node(-10, 0, 0, this) + this.inp.push(a) + for (let i = 0; i < Math.floor(inputs / 2); i++) { + a = new Node(-10, 10 * (i + 1), 0, this) + this.inp.push(a) + } + } else { + for (let i = inputs / 2 - 1; i >= 0; i--) { + const a = new Node(-10, -10 * (i + 1), 0, this) + this.inp.push(a) + } + for (let i = 0; i < inputs / 2; i++) { + const a = new Node(-10, 10 * (i + 1), 0, this) + this.inp.push(a) + } + } + this.output1 = new Node(20, 0, 1, this) + } + + /** + * @memberof OrGate + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [ + this.direction, + this.inputSize, + this.bitWidth, + ], + + nodes: { + inp: this.inp.map(findNode), + output1: findNode(this.output1), + }, + } + return data + } + + /** + * @memberof OrGate + * resolve output values based on inputData + */ + resolve() { + let result = this.inp[0].value || 0 + if (this.isResolvable() === false) { + return + } + for (let i = 1; i < this.inputSize; i++) + result |= this.inp[i].value || 0 + this.output1.value = result >>> 0 + simulationArea.simulationQueue.add(this.output1) + } + + /** + * @memberof OrGate + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.strokeStyle = colors['stroke'] + ctx.lineWidth = correctWidth(3) + + const xx = this.x + const yy = this.y + ctx.beginPath() + ctx.fillStyle = colors['fill'] + + moveTo(ctx, -10, -20, xx, yy, this.direction, true) + bezierCurveTo(0, -20, +15, -10, 20, 0, xx, yy, this.direction) + bezierCurveTo( + 0 + 15, + 0 + 10, + 0, + 0 + 20, + -10, + +20, + xx, + yy, + this.direction + ) + bezierCurveTo(0, 0, 0, 0, -10, -20, xx, yy, this.direction) + ctx.closePath() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + } + + generateVerilog() { + return gateGenerateVerilog.call(this, '|') + } +} + +/** + * @memberof OrGate + * Help Tip + * @type {string} + * @category modules + */ +OrGate.prototype.tooltipText = + 'Or Gate Tooltip : Implements logical disjunction' + +/** + * @memberof OrGate + * function to change input nodes of the element + * @category modules + */ +OrGate.prototype.changeInputSize = changeInputSize + +/** + * @memberof SevenSegDisplay + * @type {boolean} + * @category modules + */ +OrGate.prototype.alwaysResolve = true + +/** + * @memberof SevenSegDisplay + * @type {string} + * @category modules + */ +OrGate.prototype.verilogType = 'or' +OrGate.prototype.helplink = 'https://docs.circuitverse.org/#/chapter4/4gates?id=or-gate' +OrGate.prototype.objectType = 'OrGate' diff --git a/v0/src/simulator/src/modules/Output.js b/v0/src/simulator/src/modules/Output.js new file mode 100644 index 00000000..23ef3975 --- /dev/null +++ b/v0/src/simulator/src/modules/Output.js @@ -0,0 +1,219 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, fillText, rect2, oppositeDirection } from '../canvasApi' +import { getNextPosition } from '../modules' +import { generateId } from '../utils' +import { colors } from '../themer/themer' + +function bin2dec(binString) { + return parseInt(binString, 2) +} + +function dec2bin(dec, bitWidth = undefined) { + // only for positive nos + var bin = dec.toString(2) + if (bitWidth == undefined) return bin + return '0'.repeat(bitWidth - bin.length) + bin +} + +/** + * @class + * Output + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} inputLength - number of input nodes + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +export default class Output extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'LEFT', + bitWidth = 1, + layoutProperties + ) { + // Calling base class constructor + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['Output'].push(this); + */ + if (layoutProperties) { + this.layoutProperties = layoutProperties + } else { + this.layoutProperties = {} + this.layoutProperties.x = scope.layout.width + this.layoutProperties.y = getNextPosition(scope.layout.width, scope) + this.layoutProperties.id = generateId() + } + + this.rectangleObject = false + this.directionFixed = true + this.orientationFixed = false + this.setDimensions(this.bitWidth * 10, 10) + this.inp1 = new Node(this.bitWidth * 10, 0, 0, this) + } + + /** + * @memberof Output + * function to generate verilog for output + * @return {string} + */ + generateVerilog() { + return `assign ${this.label} = ${this.inp1.verilogLabel};` + } + + /** + * @memberof Output + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + nodes: { + inp1: findNode(this.inp1), + }, + constructorParamaters: [ + this.direction, + this.bitWidth, + this.layoutProperties, + ], + } + return data + } + + /** + * @memberof Output + * function to change bitwidth of the element + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + if (bitWidth < 1) return + const diffBitWidth = bitWidth - this.bitWidth + this.state = undefined + this.inp1.bitWidth = bitWidth + this.bitWidth = bitWidth + this.setWidth(10 * this.bitWidth) + + if (this.direction === 'RIGHT') { + this.x -= 10 * diffBitWidth + this.inp1.x = 10 * this.bitWidth + this.inp1.leftx = 10 * this.bitWidth + } else if (this.direction === 'LEFT') { + this.x += 10 * diffBitWidth + this.inp1.x = -10 * this.bitWidth + this.inp1.leftx = 10 * this.bitWidth + } + } + + /** + * @memberof Output + * function to draw element + */ + customDraw() { + this.state = this.inp1.value + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = [colors['out_rect'], colors['stroke_alt']][ + +(this.inp1.value === undefined) + ] + ctx.fillStyle = colors['fill'] + ctx.lineWidth = correctWidth(3) + const xx = this.x + const yy = this.y + + rect2( + ctx, + -10 * this.bitWidth, + -10, + 20 * this.bitWidth, + 20, + xx, + yy, + 'RIGHT' + ) + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.fillStyle = colors['hover_select'] + } + + ctx.fill() + ctx.stroke() + + ctx.beginPath() + ctx.font = '20px Raleway' + ctx.fillStyle = colors['input_text'] + ctx.textAlign = 'center' + let bin + if (this.state === undefined) { + bin = 'x'.repeat(this.bitWidth) + } else { + bin = dec2bin(this.state, this.bitWidth) + } + + for (let k = 0; k < this.bitWidth; k++) { + fillText(ctx, bin[k], xx - 10 * this.bitWidth + 10 + k * 20, yy + 5) + } + ctx.fill() + } + + /** + * @memberof Output + * function to change direction of Output + * @param {string} dir - new direction + */ + newDirection(dir) { + if (dir === this.direction) return + this.direction = dir + this.inp1.refresh() + if (dir === 'RIGHT' || dir === 'LEFT') { + this.inp1.leftx = 10 * this.bitWidth + this.inp1.lefty = 0 + } else { + this.inp1.leftx = 10 // 10*this.bitWidth; + this.inp1.lefty = 0 + } + + this.inp1.refresh() + this.labelDirection = oppositeDirection[this.direction] + } + + generateVerilog() { + return ( + 'assign ' + this.verilogLabel + ' = ' + this.inp1.verilogLabel + ';' + ) + } +} + +/** + * @memberof Output + * Help Tip + * @type {string} + * @category modules + */ +Output.prototype.tooltipText = + 'Output ToolTip: Simple output element showing output in binary.' + +/** + * @memberof Output + * Help URL + * @type {string} + * @category modules + */ +Output.prototype.helplink = 'https://docs.circuitverse.org/#/chapter4/3output?id=output' + +/** + * @memberof Output + * @type {number} + * @category modules + */ +Output.prototype.propagationDelay = 0 +Output.prototype.objectType = 'Output' diff --git a/v0/src/simulator/src/modules/Power.js b/v0/src/simulator/src/modules/Power.js new file mode 100644 index 00000000..87c419a5 --- /dev/null +++ b/v0/src/simulator/src/modules/Power.js @@ -0,0 +1,146 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, arc } from '../canvasApi' +import { changeInputSize } from '../modules' +/** + * @class + * Power + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +import { colors } from '../themer/themer' + +export default class Power extends CircuitElement { + constructor(x, y, scope = globalScope, bitWidth = 1) { + super(x, y, scope, 'RIGHT', bitWidth) + /* this is done in this.baseSetup() now + this.scope['Power'].push(this); + */ + this.directionFixed = true + this.rectangleObject = false + this.setDimensions(10, 10) + this.output1 = new Node(0, 10, 1, this) + } + + /** + * @memberof Power + * resolve output values based on inputData + */ + resolve() { + this.output1.value = ~0 >>> (32 - this.bitWidth) + simulationArea.simulationQueue.add(this.output1) + } + + /** + * @memberof Power + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + nodes: { + output1: findNode(this.output1), + }, + constructorParamaters: [this.bitWidth], + } + return data + } + + /** + * @memberof Power + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + const xx = this.x + const yy = this.y + ctx.beginPath() + ctx.strokeStyle = 'rgba(0,0,0,1)' + ctx.lineWidth = correctWidth(3) + ctx.fillStyle = colors['fill'] + moveTo(ctx, 0, -10, xx, yy, this.direction) + lineTo(ctx, -10, 0, xx, yy, this.direction) + lineTo(ctx, 10, 0, xx, yy, this.direction) + lineTo(ctx, 0, -10, xx, yy, this.direction) + ctx.closePath() + ctx.stroke() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + moveTo(ctx, 0, 0, xx, yy, this.direction) + lineTo(ctx, 0, 10, xx, yy, this.direction) + ctx.stroke() + } + + generateVerilog() { + return `assign ${this.output1.verilogLabel} = ~${this.bitWidth}'b0;` + } +} + +/** + * @memberof Power + * Help Tip + * @type {string} + * @category modules + */ +Power.prototype.tooltipText = 'Power: All bits are High(1).' + +/** + * @memberof Power + * Help URL + * @type {string} + * @category modules + */ +Power.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/2input?id=power' + +/** + * @memberof Power + * @type {number} + * @category modules + */ +Power.prototype.propagationDelay = 0 + +function getNextPosition(x = 0, scope = globalScope) { + let possibleY = 20 + const done = {} + for (let i = 0; i < scope.Input.length; i++) { + if (scope.Input[i].layoutProperties.x === x) { + done[scope.Input[i].layoutProperties.y] = 1 + } + } + for (let i = 0; i < scope.Output.length; i++) { + if (scope.Output[i].layoutProperties.x === x) { + done[scope.Output[i].layoutProperties.y] = 1 + } + } + while (done[possibleY] || done[possibleY + 10] || done[possibleY - 10]) { + possibleY += 10 + } + const height = possibleY + 20 + if (height > scope.layout.height) { + const oldHeight = scope.layout.height + scope.layout.height = height + for (let i = 0; i < scope.Input.length; i++) { + if (scope.Input[i].layoutProperties.y === oldHeight) { + scope.Input[i].layoutProperties.y = scope.layout.height + } + } + for (let i = 0; i < scope.Output.length; i++) { + if (scope.Output[i].layoutProperties.y === oldHeight) { + scope.Output[i].layoutProperties.y = scope.layout.height + } + } + } + return possibleY +} +Power.prototype.objectType = 'Power' diff --git a/v0/src/simulator/src/modules/PriorityEncoder.js b/v0/src/simulator/src/modules/PriorityEncoder.js new file mode 100644 index 00000000..b74d7f90 --- /dev/null +++ b/v0/src/simulator/src/modules/PriorityEncoder.js @@ -0,0 +1,276 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode, dec2bin } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, rect, fillText } from '../canvasApi' +/** + * @class + * PriorityEncoder + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +import { colors } from '../themer/themer' + +export default class PriorityEncoder extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT', bitWidth = 1) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['PriorityEncoder'].push(this); + */ + this.bitWidth = bitWidth || parseInt(prompt('Enter bitWidth'), 10) + this.inputSize = 1 << this.bitWidth + + this.yOff = 1 + if (this.bitWidth <= 3) { + this.yOff = 2 + } + + this.setDimensions(20, this.yOff * 5 * this.inputSize) + this.directionFixed = true + this.rectangleObject = false + + this.inp1 = [] + for (let i = 0; i < this.inputSize; i++) { + const a = new Node( + -10, + +this.yOff * 10 * (i - this.inputSize / 2) + 10, + 0, + this, + 1 + ) + this.inp1.push(a) + } + + this.output1 = [] + for (let i = 0; i < this.bitWidth; i++) { + const a = new Node( + 30, + +2 * 10 * (i - this.bitWidth / 2) + 10, + 1, + this, + 1 + ) + this.output1.push(a) + } + + this.enable = new Node( + 10, + 20 + this.inp1[this.inputSize - 1].y, + 1, + this, + 1 + ) + } + + /** + * @memberof PriorityEncoder + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + nodes: { + inp1: this.inp1.map(findNode), + output1: this.output1.map(findNode), + enable: findNode(this.enable), + }, + constructorParamaters: [this.direction, this.bitWidth], + } + return data + } + + /** + * @memberof PriorityEncoder + * function to change bitwidth of the element + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + if (bitWidth === undefined || bitWidth < 1 || bitWidth > 32) return + if (this.bitWidth === bitWidth) return + + this.bitWidth = bitWidth + const obj = new PriorityEncoder( + this.x, + this.y, + this.scope, + this.direction, + this.bitWidth + ) + this.inputSize = 1 << bitWidth + + this.cleanDelete() + simulationArea.lastSelected = obj + return obj + } + + /** + * @memberof PriorityEncoder + * resolve output values based on inputData + */ + resolve() { + let out = 0 + let temp = 0 + for (let i = this.inputSize - 1; i >= 0; i--) { + if (this.inp1[i].value === 1) { + out = dec2bin(i) + break + } + } + temp = out + + if (out.length !== undefined) { + this.enable.value = 1 + } else { + this.enable.value = 0 + } + simulationArea.simulationQueue.add(this.enable) + + if (temp.length === undefined) { + temp = '0' + for (let i = 0; i < this.bitWidth - 1; i++) { + temp = `0${temp}` + } + } + + if (temp.length !== this.bitWidth) { + for (let i = temp.length; i < this.bitWidth; i++) { + temp = `0${temp}` + } + } + + for (let i = this.bitWidth - 1; i >= 0; i--) { + this.output1[this.bitWidth - 1 - i].value = Number(temp[i]) + simulationArea.simulationQueue.add( + this.output1[this.bitWidth - 1 - i] + ) + } + } + + /** + * @memberof PriorityEncoder + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.lineWidth = correctWidth(3) + const xx = this.x + const yy = this.y + if (this.bitWidth <= 3) { + rect( + ctx, + xx - 10, + yy - 10 - this.yOff * 5 * this.inputSize, + 40, + 20 * (this.inputSize + 1) + ) + } else { + rect( + ctx, + xx - 10, + yy - 10 - this.yOff * 5 * this.inputSize, + 40, + 10 * (this.inputSize + 3) + ) + } + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + + ctx.beginPath() + ctx.fillStyle = 'black' + ctx.textAlign = 'center' + for (let i = 0; i < this.inputSize; i++) { + fillText(ctx, String(i), xx, yy + this.inp1[i].y + 2, 10) + } + for (let i = 0; i < this.bitWidth; i++) { + fillText( + ctx, + String(i), + xx + this.output1[0].x - 10, + yy + this.output1[i].y + 2, + 10 + ) + } + fillText(ctx, 'EN', xx + this.enable.x, yy + this.enable.y - 5, 10) + ctx.fill() + } + + verilogBaseType() { + return this.verilogName() + this.inp1.length + } + + generateVerilog() { + PriorityEncoder.selSizes.add(this.bitWidth) + return CircuitElement.prototype.generateVerilog.call(this) + } + + static moduleVerilog() { + var output = '' + + for (var size of PriorityEncoder.selSizes) { + var numInput = 1 << size + output += '\n' + output += 'module PriorityEncoder' + numInput + output += '(sel, ze, ' + for (var j = 0; j < numInput - 1; j++) { + output += 'in' + j + ', ' + } + output += 'in' + (numInput - 1) + ');\n' + + output += ' output reg [' + (size - 1) + ':0] sel;\n' + output += ' output reg ze;\n' + + output += ' input ' + for (var j = 0; j < numInput - 1; j++) { + output += 'in' + j + ', ' + } + output += 'in' + (numInput - 1) + ';\n' + output += '\n' + + output += ' always @ (*) begin\n' + output += ' sel = 0;\n' + output += ' ze = 0;\n' + output += ' if (in' + (numInput - 1) + ')\n' + output += ' sel = ' + (numInput - 1) + ';\n' + for (var j = numInput - 2; j <= 0; j++) { + output += ' else if (in' + j + ')\n' + output += ' sel = ' + j + ';\n' + } + output += ' else\n' + output += ' ze = 1;\n' + output += ' end\n' + output += 'endmodule\n' + } + + return output + } + + //reset the sized before Verilog generation + static resetVerilog() { + PriorityEncoder.selSizes = new Set() + } +} + +/** + * @memberof PriorityEncoder + * Help Tip + * @type {string} + * @category modules + */ +PriorityEncoder.prototype.tooltipText = + 'Priority Encoder ToolTip : Compresses binary inputs into a smaller number of outputs.' +PriorityEncoder.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/5muxandplex?id=priority-encoder' +PriorityEncoder.prototype.objectType = 'PriorityEncoder' diff --git a/v0/src/simulator/src/modules/RGBLed.js b/v0/src/simulator/src/modules/RGBLed.js new file mode 100644 index 00000000..a1bcc659 --- /dev/null +++ b/v0/src/simulator/src/modules/RGBLed.js @@ -0,0 +1,177 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, arc, drawCircle2 } from '../canvasApi' +import { changeInputSize } from '../modules' +/** + * @class + * RGBLed + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @category modules + */ +import { colors } from '../themer/themer' + +export default class RGBLed extends CircuitElement { + constructor(x, y, scope = globalScope) { + // Calling base class constructor + super(x, y, scope, 'UP', 8) + /* this is done in this.baseSetup() now + this.scope['RGBLed'].push(this); + */ + this.rectangleObject = false + this.inp = [] + this.setDimensions(10, 10) + this.inp1 = new Node(-40, -10, 0, this, 8) + this.inp2 = new Node(-40, 0, 0, this, 8) + this.inp3 = new Node(-40, 10, 0, this, 8) + this.inp.push(this.inp1) + this.inp.push(this.inp2) + this.inp.push(this.inp3) + this.directionFixed = true + this.fixedBitWidth = true + } + + /** + * @memberof RGBLed + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + nodes: { + inp1: findNode(this.inp1), + inp2: findNode(this.inp2), + inp3: findNode(this.inp3), + }, + } + return data + } + + /** + * @memberof RGBLed + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + + const xx = this.x + const yy = this.y + + ctx.strokeStyle = 'green' + ctx.lineWidth = correctWidth(3) + ctx.beginPath() + moveTo(ctx, -20, 0, xx, yy, this.direction) + lineTo(ctx, -40, 0, xx, yy, this.direction) + ctx.stroke() + + ctx.strokeStyle = 'red' + ctx.lineWidth = correctWidth(3) + ctx.beginPath() + moveTo(ctx, -20, -10, xx, yy, this.direction) + lineTo(ctx, -40, -10, xx, yy, this.direction) + ctx.stroke() + + ctx.strokeStyle = 'blue' + ctx.lineWidth = correctWidth(3) + ctx.beginPath() + moveTo(ctx, -20, 10, xx, yy, this.direction) + lineTo(ctx, -40, 10, xx, yy, this.direction) + ctx.stroke() + + const a = this.inp1.value + const b = this.inp2.value + const c = this.inp3.value + ctx.strokeStyle = '#d3d4d5' + ctx.fillStyle = [ + `rgba(${a}, ${b}, ${c}, 0.8)`, + 'rgba(227, 228, 229, 0.8)', + ][(a === undefined || b === undefined || c === undefined) + 0] + // ctx.fillStyle = ["rgba(200, 200, 200, 0.3)","rgba(227, 228, 229, 0.8)"][((a === undefined || b === undefined || c === undefined) || (a === 0 && b === 0 && c === 0)) + 0]; + ctx.lineWidth = correctWidth(1) + + ctx.beginPath() + + moveTo(ctx, -18, -11, xx, yy, this.direction) + lineTo(ctx, 0, -11, xx, yy, this.direction) + arc(ctx, 0, 0, 11, -Math.PI / 2, Math.PI / 2, xx, yy, this.direction) + lineTo(ctx, -18, 11, xx, yy, this.direction) + lineTo(ctx, -21, 15, xx, yy, this.direction) + arc( + ctx, + 0, + 0, + Math.sqrt(666), + Math.PI / 2 + Math.acos(15 / Math.sqrt(666)), + -Math.PI / 2 - Math.asin(21 / Math.sqrt(666)), + xx, + yy, + this.direction + ) + lineTo(ctx, -18, -11, xx, yy, this.direction) + ctx.stroke() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + } + + // Draws the element in the subcuircuit. Used in layout mode + subcircuitDraw(xOffset = 0, yOffset = 0) { + var ctx = simulationArea.context + + var xx = this.subcircuitMetadata.x + xOffset + var yy = this.subcircuitMetadata.y + yOffset + var dimensionSize = 6 + + var a = this.inp1.value + var b = this.inp2.value + var c = this.inp3.value + ctx.strokeStyle = '#090a0a' + ctx.fillStyle = [ + 'rgba(' + a + ', ' + b + ', ' + c + ', 0.8)', + 'rgba(227, 228, 229, 0.8)', + ][(a === undefined || b === undefined || c === undefined) + 0] + ctx.lineWidth = correctWidth(1) + + ctx.beginPath() + drawCircle2(ctx, 0, 0, dimensionSize, xx, yy, this.direction) + ctx.stroke() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected == this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = 'rgba(255, 255, 32,0.8)' + ctx.fill() + } + generateVerilog() { + return ` + always @ (*) + $display("RGBLed:{${this.inp1.verilogLabel},${this.inp2.verilogLabel},${this.inp3.verilogLabel}} = {%d,%d,%d}", ${this.inp1.verilogLabel}, ${this.inp2.verilogLabel}, ${this.inp3.verilogLabel});` + } +} + +/** + * @memberof RGBLed + * Help Tip + * @type {string} + * @category modules + */ +RGBLed.prototype.tooltipText = + 'RGB Led ToolTip: RGB Led inputs 8 bit values for the colors RED, GREEN and BLUE.' + +/** + * @memberof RGBLed + * Help URL + * @type {string} + * @category modules + */ +RGBLed.prototype.helplink = 'https://docs.circuitverse.org/#/chapter4/3output?id=rgbled' +RGBLed.prototype.objectType = 'RGBLed' +RGBLed.prototype.canShowInSubcircuit = true diff --git a/v0/src/simulator/src/modules/RGBLedMatrix.js b/v0/src/simulator/src/modules/RGBLedMatrix.js new file mode 100644 index 00000000..00fe1757 --- /dev/null +++ b/v0/src/simulator/src/modules/RGBLedMatrix.js @@ -0,0 +1,383 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, rect2, rotate, lineTo, moveTo } from '../canvasApi' + +/** + * @class + * RGBLedMatrix + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {number=} rows - number of rows + * @param {number=} cols - number of columns. + * @category modules + */ +export default class RGBLedMatrix extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + { + rows = 8, + columns = 8, + ledSize = 2, + showGrid = true, + colors = [], + } = {} + ) { + super(x, y, scope, 'RIGHT', 8) + /* this is done in this.baseSetup() now + this.scope['RGBLedMatrix'].push(this); + */ + this.fixedBitWidth = true + this.directionFixed = true + this.rectangleObject = true + this.alwaysResolve = true + this.labelDirection = 'UP' + this.leftDimensionX = 0 + this.upDimensionY = 0 + + // These pins provide bulk-editing of the colors + this.rowEnableNodes = [] // 1-bit pin for each row, on the left side. + this.columnEnableNodes = [] // 1-bit pin for each column, on the bottom. + this.columnColorNodes = [] // 24-bit pin for each column, on the top. + + // These pins provide single-pixel editing; these are on the right side. + this.colorNode = new Node(0, -10, NODE_INPUT, this, 24, 'COLOR') + this.rowNode = new Node(0, 0, NODE_INPUT, this, 1, 'ROW') + this.columnNode = new Node(0, 10, NODE_INPUT, this, 1, 'COLUMN') + + this.colors = colors + this.showGrid = showGrid + this.changeSize(rows, columns, ledSize, false) + } + + toggleGrid() { + this.showGrid = !this.showGrid + } + + changeRows(rows) { + this.changeSize(rows, this.columns, this.ledSize, true) + } + + changeColumns(columns) { + this.changeSize(this.rows, columns, this.ledSize, true) + } + + changeLedSize(ledSize) { + this.changeSize(this.rows, this.columns, ledSize, true) + } + + changeSize(rows, columns, ledSize, move) { + rows = parseInt(rows, 10) + if (isNaN(rows) || rows < 0 || rows > this.maxRows) return + + columns = parseInt(columns, 10) + if (isNaN(columns) || columns < 0 || columns > this.maxColumns) return + + ledSize = parseInt(ledSize, 10) + if (isNaN(ledSize) || ledSize < 0 || ledSize > this.maxLedSize) return + + // The size of an individual LED, in canvas units. + var ledWidth = 10 * ledSize + var ledHeight = 10 * ledSize + + // The size of the LED matrix, in canvas units. + var gridWidth = ledWidth * columns + var gridHeight = ledHeight * rows + + // We need to position the element in the 10x10 grid. + // Depending on the size of the leds we need to add different paddings so position correctly. + var padding = ledSize % 2 ? 5 : 10 + + // The dimensions of the element, in canvas units. + var halfWidth = gridWidth / 2 + padding + var halfHeight = gridHeight / 2 + padding + + // Move the element in order to keep the position of the nodes stable so wires don't break. + if (move) { + this.x -= this.leftDimensionX - halfWidth + this.y -= this.upDimensionY - halfHeight + } + + // Update the dimensions of the element. + this.setDimensions(halfWidth, halfHeight) + + // Offset of the nodes in relation to the element's center. + var nodePadding = [10, 20, 20][ledSize - 1] + var nodeOffsetX = nodePadding - halfWidth + var nodeOffsetY = nodePadding - halfHeight + + // When the led size changes it is better to delete all nodes to break connected the wires. + // Otherwise, wires can end up connected in unexpected ways. + var resetAllNodes = ledSize != this.ledSize + + // Delete unused row-enable nodes, reposition remaining nodes and add new nodes. + this.rowEnableNodes + .splice(resetAllNodes ? 0 : rows) + .forEach((node) => node.delete()) + this.rowEnableNodes.forEach((node, i) => { + node.x = node.leftx = -halfWidth + node.y = node.lefty = i * ledHeight + nodeOffsetY + }) + while (this.rowEnableNodes.length < rows) { + this.rowEnableNodes.push( + new Node( + -halfWidth, + this.rowEnableNodes.length * ledHeight + nodeOffsetY, + NODE_INPUT, + this, + 1, + 'R' + this.rowEnableNodes.length + ) + ) + } + + // Delete unused column-enable nodes, reposition remaining nodes and add new nodes. + this.columnEnableNodes + .splice(resetAllNodes ? 0 : columns) + .forEach((node) => node.delete()) + this.columnEnableNodes.forEach((node, i) => { + node.x = node.leftx = i * ledWidth + nodeOffsetX + node.y = node.lefty = halfHeight + }) + while (this.columnEnableNodes.length < columns) { + this.columnEnableNodes.push( + new Node( + this.columnEnableNodes.length * ledWidth + nodeOffsetX, + halfHeight, + NODE_INPUT, + this, + 1, + 'C' + this.columnEnableNodes.length + ) + ) + } + + // Delete unused column color nodes, reposition remaining nodes and add new nodes. + this.columnColorNodes + .splice(resetAllNodes ? 0 : columns) + .forEach((node) => node.delete()) + this.columnColorNodes.forEach((node, i) => { + node.x = node.leftx = i * ledWidth + nodeOffsetX + node.y = node.lefty = -halfHeight + }) + while (this.columnColorNodes.length < columns) { + this.columnColorNodes.push( + new Node( + this.columnColorNodes.length * ledWidth + nodeOffsetX, + -halfHeight, + NODE_INPUT, + this, + 24, + 'CLR' + this.columnColorNodes.length + ) + ) + } + + // Delete unused color storage and add storage for new rows. + this.colors.splice(rows) + this.colors.forEach((c) => c.splice(columns)) + while (this.colors.length < rows) { + this.colors.push([]) + } + + // Reposition the single-pixel nodes + this.rowNode.bitWidth = Math.ceil(Math.log2(rows)) + this.rowNode.label = 'ROW (' + this.rowNode.bitWidth + ' bits)' + this.columnNode.bitWidth = Math.ceil(Math.log2(columns)) + this.columnNode.label = 'COLUMN (' + this.columnNode.bitWidth + ' bits)' + var singlePixelNodePadding = rows > 1 ? nodeOffsetY : nodeOffsetY - 10 + var singlePixelNodeDistance = rows <= 2 ? 10 : ledHeight + ;[this.colorNode, this.rowNode, this.columnNode].forEach((node, i) => { + node.x = node.leftx = halfWidth + node.y = node.lefty = + i * singlePixelNodeDistance + singlePixelNodePadding + }) + + // Store the new values + this.rows = rows + this.columns = columns + this.ledSize = ledSize + + return this + } + + customSave() { + // Save the size of the LED matrix. + // Unlike a read LED matrix, we also persist the color of each pixel. + // This allows circuit preview to show the colors at the time the simulation was saved. + return { + constructorParamaters: [ + { + rows: this.rows, + columns: this.columns, + ledSize: this.ledSize, + showGrid: this.showGrid, + colors: this.colors, + }, + ], + nodes: { + rowEnableNodes: this.rowEnableNodes.map(findNode), + columnEnableNodes: this.columnEnableNodes.map(findNode), + columnColorNodes: this.columnColorNodes.map(findNode), + colorNode: findNode(this.colorNode), + rowNode: findNode(this.rowNode), + columnNode: findNode(this.columnNode), + }, + } + } + + resolve() { + var colorValue = this.colorNode.value + var hasColorValue = colorValue != undefined + + var rows = this.rows + var columns = this.columns + var rowEnableNodes = this.rowEnableNodes + var columnEnableNodes = this.columnEnableNodes + var columnColorNodes = this.columnColorNodes + var colors = this.colors + + for (var row = 0; row < rows; row++) { + if (rowEnableNodes[row].value === 1) { + for (var column = 0; column < columns; column++) { + // Method 1: set pixel by rowEnable + columnColor pins + var columnColor = columnColorNodes[column].value + if (columnColor !== undefined) { + colors[row][column] = columnColor + } + + // Method 2: set pixel by rowEnable + columnEnable + color pins + if ( + hasColorValue && + columnEnableNodes[column].value === 1 + ) { + colors[row][column] = colorValue + } + } + } + } + + // Method 3: set pixel by write + pixel index + color pins. + var hasRowNodeValue = this.rowNode.value != undefined || rows == 1 + var hasColumnNodeValue = + this.columnNode.value != undefined || columns == 1 + if (hasColorValue && hasRowNodeValue && hasColumnNodeValue) { + var rowNodeValue = this.rowNode.value || 0 + var columnNodeValue = this.columnNode.value || 0 + if (rowNodeValue < rows && columnNodeValue < columns) { + colors[rowNodeValue][columnNodeValue] = colorValue + } + } + } + + customDraw() { + var ctx = simulationArea.context + var rows = this.rows + var columns = this.columns + var colors = this.colors + var xx = this.x + var yy = this.y + var dir = this.direction + var ledWidth = 10 * this.ledSize + var ledHeight = 10 * this.ledSize + var top = this.rowEnableNodes[0].y - ledHeight / 2 + var left = this.columnColorNodes[0].x - ledWidth / 2 + var width = this.columns * ledWidth + var height = this.rows * ledHeight + var bottom = top + height + var right = left + width + + var [w, h] = rotate( + ledWidth * globalScope.scale, + ledHeight * globalScope.scale, + dir + ) + var xoffset = Math.round(globalScope.ox + xx * globalScope.scale) + var yoffset = Math.round(globalScope.oy + yy * globalScope.scale) + for (var row = 0; row < rows; row++) { + for (var column = 0; column < columns; column++) { + var color = colors[row][column] || 0 + ctx.beginPath() + ctx.fillStyle = + 'rgb(' + + ((color & 0xff0000) >> 16) + + ',' + + ((color & 0xff00) >> 8) + + ',' + + (color & 0xff) + + ')' + let x1, y1 + ;[x1, y1] = rotate( + left + column * ledWidth, + top + row * ledHeight, + dir + ) + x1 = x1 * globalScope.scale + y1 = y1 * globalScope.scale + ctx.rect(xoffset + x1, yoffset + y1, w, h) + ctx.fill() + } + } + + if (this.showGrid) { + ctx.beginPath() + ctx.strokeStyle = '#323232' + ctx.lineWidth = correctWidth(1) + rect2(ctx, left, top, width, height, xx, yy, dir) + for (var x = left + ledWidth; x < right; x += ledWidth) { + moveTo(ctx, x, top, xx, yy, dir) + lineTo(ctx, x, bottom, xx, yy, dir) + } + for (var y = top + ledHeight; y < bottom; y += ledHeight) { + moveTo(ctx, left, y, xx, yy, dir) + lineTo(ctx, right, y, xx, yy, dir) + } + ctx.stroke() + } + } +} + +RGBLedMatrix.prototype.tooltipText = 'RGB Led Matrix' + +// Limit the size of the matrix otherwise the simulation starts to lag. +RGBLedMatrix.prototype.maxRows = 128 +RGBLedMatrix.prototype.maxColumns = 128 + +// Let the user choose between 3 sizes of LEDs: small, medium and large. +RGBLedMatrix.prototype.maxLedSize = 3 + +RGBLedMatrix.prototype.mutableProperties = { + rows: { + name: 'Rows', + type: 'number', + max: RGBLedMatrix.prototype.maxRows, + min: 1, + func: 'changeRows', + }, + columns: { + name: 'Columns', + type: 'number', + max: RGBLedMatrix.prototype.maxColumns, + min: 1, + func: 'changeColumns', + }, + ledSize: { + name: 'LED Size', + type: 'number', + max: RGBLedMatrix.prototype.maxLedSize, + min: 1, + func: 'changeLedSize', + }, + showGrid: { + name: 'Toggle Grid', + type: 'button', + max: RGBLedMatrix.prototype.maxLedSize, + min: 1, + func: 'toggleGrid', + }, +} +RGBLedMatrix.prototype.objectType = 'RGBLedMatrix' diff --git a/v0/src/simulator/src/modules/Random.js b/v0/src/simulator/src/modules/Random.js new file mode 100644 index 00000000..01c9427b --- /dev/null +++ b/v0/src/simulator/src/modules/Random.js @@ -0,0 +1,172 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { fillText, lineTo, moveTo, correctWidth, rect2 } from '../canvasApi' +/** + * @class + * Random + * Random is used to generate random value. + * It has 2 input node: + * clock and max random output value + * @extends CircuitElement + * @param {number} x - x coord of element + * @param {number} y - y coord of element + * @param {Scope=} scope - the ciruit in which we want the Element + * @param {string=} dir - direcion in which element has to drawn + * @category modules + */ +import { colors } from '../themer/themer' + +export default class Random extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT', bitWidth = 1) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['Random'].push(this); + */ + this.directionFixed = true + this.setDimensions(20, 20) + this.rectangleObject = true + this.currentRandomNo = 0 + this.clockInp = new Node(-20, +10, 0, this, 1, 'Clock') + this.maxValue = new Node(-20, -10, 0, this, this.bitWidth, 'MaxValue') + this.output = new Node(20, -10, 1, this, this.bitWidth, 'RandomValue') + this.prevClockState = 0 + this.wasClicked = false + } + + /** + * @memberof Random + * return true if clock is connected and if maxValue is set or unconnected. + */ + isResolvable() { + if ( + this.clockInp.value != undefined && + (this.maxValue.value != undefined || + this.maxValue.connections.length == 0) + ) { + return true + } + return false + } + + newBitWidth(bitWidth) { + this.bitWidth = bitWidth + this.maxValue.bitWidth = bitWidth + this.output.bitWidth = bitWidth + } + + /** + * @memberof Random + * Edge triggered when the clock state changes a + * Random number is generated less then the maxValue. + */ + resolve() { + var maxValue = this.maxValue.connections.length + ? this.maxValue.value + 1 + : 2 << (this.bitWidth - 1) + if (this.clockInp.value != undefined) { + if (this.clockInp.value != this.prevClockState) { + if (this.clockInp.value == 1) { + this.currentRandomNo = Math.floor(Math.random() * maxValue) + } + this.prevClockState = this.clockInp.value + } + } + if (this.output.value != this.currentRandomNo) { + this.output.value = this.currentRandomNo + simulationArea.simulationQueue.add(this.output) + } + } + + customSave() { + var data = { + nodes: { + clockInp: findNode(this.clockInp), + maxValue: findNode(this.maxValue), + output: findNode(this.output), + }, + constructorParamaters: [this.direction, this.bitWidth], + } + return data + } + + customDraw() { + var ctx = simulationArea.context + // + ctx.fillStyle = colors['fill'] + ctx.strokeStyle = colors['stroke'] + ctx.beginPath() + var xx = this.x + var yy = this.y + ctx.font = '20px Raleway' + ctx.fillStyle = colors['input_text'] + ctx.textAlign = 'center' + fillText(ctx, this.currentRandomNo.toString(10), this.x, this.y + 5) + ctx.fill() + ctx.beginPath() + moveTo(ctx, -20, 5, xx, yy, this.direction) + lineTo(ctx, -15, 10, xx, yy, this.direction) + lineTo(ctx, -20, 15, xx, yy, this.direction) + ctx.stroke() + } + + // Draws the element in the subcircuit. Used in layout mode + subcircuitDraw(xOffset = 0, yOffset = 0) { + var ctx = simulationArea.context + var xx = this.subcircuitMetadata.x + xOffset + var yy = this.subcircuitMetadata.y + yOffset + + ctx.beginPath() + ctx.font = '20px Raleway' + ctx.fillStyle = 'green' + ctx.textAlign = 'center' + fillText(ctx, this.currentRandomNo.toString(16), xx + 10, yy + 17) + ctx.fill() + + ctx.beginPath() + ctx.lineWidth = correctWidth(1) + rect2(ctx, 0, 0, 20, 20, xx, yy, this.direction) + ctx.stroke() + + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected == this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.fillStyle = 'rgba(255, 255, 32,0.6)' + ctx.fill() + } + } + + static moduleVerilog() { + return ` + module Random(val, clk, max); + parameter WIDTH = 1; + output reg [WIDTH-1:0] val; + input clk; + input [WIDTH-1:0] max; + + always @ (posedge clk) + if (^max === 1'bX) + val = $urandom_range(0, {WIDTH{1'b1}}); + else + val = $urandom_range(0, max); + endmodule + ` + } +} + +Random.prototype.tooltipText = 'Random ToolTip : Random Selected.' + +Random.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/2input?id=random' + +Random.prototype.objectType = 'Random' + +Random.prototype.canShowInSubcircuit = true +Random.prototype.layoutProperties = { + rightDimensionX: 20, + leftDimensionX: 0, + upDimensionY: 0, + downDimensionY: 20, +} diff --git a/v0/src/simulator/src/modules/Rectangle.js b/v0/src/simulator/src/modules/Rectangle.js new file mode 100644 index 00000000..09bf240f --- /dev/null +++ b/v0/src/simulator/src/modules/Rectangle.js @@ -0,0 +1,159 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, rect } from '../canvasApi' +/** + * @class + * Rectangle + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {number=} rows - number of rows + * @param {number=} cols - number of columns. + * @category modules + */ +export default class Rectangle extends CircuitElement { + constructor(x, y, scope = globalScope, rows = 15, cols = 20) { + super(x, y, scope, 'RIGHT', 1) + /* this is done in this.baseSetup() now + this.scope['Rectangle'].push(this); + */ + this.directionFixed = true + this.fixedBitWidth = true + this.rectangleObject = false + this.cols = cols || parseInt(prompt('Enter cols:'), 10) + this.rows = rows || parseInt(prompt('Enter rows:'), 10) + this.setSize() + } + + /** + * @memberof Rectangle + * @param {number} size - new size of rows + */ + changeRowSize(size) { + if (size === undefined || size < 5 || size > 1000) return + if (this.rows === size) return + this.rows = parseInt(size, 10) + this.setSize() + return this + } + + /** + * @memberof Rectangle + * @param {number} size - new size of columns + */ + changeColSize(size) { + if (size === undefined || size < 5 || size > 1000) return + if (this.cols === size) return + this.cols = parseInt(size, 10) + this.setSize() + return this + } + + /** + * @memberof Rectangle + * listener function to change direction of rectangle + * @param {string} dir - new direction + */ + keyDown3(dir) { + if (dir === 'ArrowRight') { + this.changeColSize(this.cols + 2) + } + if (dir === 'ArrowLeft') { + this.changeColSize(this.cols - 2) + } + if (dir === 'ArrowDown') { + this.changeRowSize(this.rows + 2) + } + if (dir === 'ArrowUp') { + this.changeRowSize(this.rows - 2) + } + } + + /** + * @memberof Rectangle + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.rows, this.cols], + } + return data + } + + /** + * @memberof Rectangle + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = 'rgba(0,0,0,1)' + ctx.setLineDash([5 * globalScope.scale, 5 * globalScope.scale]) + ctx.lineWidth = correctWidth(1.5) + const xx = this.x + const yy = this.y + rect(ctx, xx, yy, this.elementWidth, this.elementHeight) + ctx.stroke() + + if ( + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.fillStyle = 'rgba(255, 255, 32,0.1)' + ctx.fill() + } + ctx.setLineDash([]) + } + + /** + * @memberof Rectangle + * function to reset or (internally) set size + */ + setSize() { + this.elementWidth = this.cols * 10 + this.elementHeight = this.rows * 10 + this.upDimensionY = 0 + this.leftDimensionX = 0 + this.rightDimensionX = this.elementWidth + this.downDimensionY = this.elementHeight + } +} + +/** + * @memberof Rectangle + * Help Tip + * @type {string} + * @category modules + */ +Rectangle.prototype.tooltipText = + 'Rectangle ToolTip : Used to Box the Circuit or area you want to highlight.' +Rectangle.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/7annotation?id=rectangle' +Rectangle.prototype.propagationDelayFixed = true + +/** + * @memberof Rectangle + * Mutable properties of the element + * @type {JSON} + * @category modules + */ +Rectangle.prototype.mutableProperties = { + cols: { + name: 'Columns', + type: 'number', + max: '1000', + min: '5', + func: 'changeColSize', + }, + rows: { + name: 'Rows', + type: 'number', + max: '1000', + min: '5', + func: 'changeRowSize', + }, +} +Rectangle.prototype.objectType = 'Rectangle' diff --git a/v0/src/simulator/src/modules/SevenSegDisplay.js b/v0/src/simulator/src/modules/SevenSegDisplay.js new file mode 100644 index 00000000..eedd26f0 --- /dev/null +++ b/v0/src/simulator/src/modules/SevenSegDisplay.js @@ -0,0 +1,321 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { + colorToRGBA, + correctWidth, + lineTo, + moveTo, + rect, + rect2, + validColor, +} from '../canvasApi' + +/** + * @class + * SevenSegDisplay + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @category modules + */ +export default class SevenSegDisplay extends CircuitElement { + constructor(x, y, scope = globalScope, color = 'Red') { + super(x, y, scope, 'RIGHT', 1) + /* this is done in this.baseSetup() now + this.scope['SevenSegDisplay'].push(this); + */ + this.fixedBitWidth = true + this.directionFixed = true + this.setDimensions(30, 50) + + this.g = new Node(-20, -50, 0, this) + this.f = new Node(-10, -50, 0, this) + this.a = new Node(+10, -50, 0, this) + this.b = new Node(+20, -50, 0, this) + this.e = new Node(-20, +50, 0, this) + this.d = new Node(-10, +50, 0, this) + this.c = new Node(+10, +50, 0, this) + this.dot = new Node(+20, +50, 0, this) + this.direction = 'RIGHT' + this.color = color + this.actualColor = color + } + + /** + * @memberof SevenSegDisplay + * fn to change the color of SevenSegmentDisplay + * @return {JSON} + */ + changeColor(value) { + if (validColor(value)) { + if (value.trim() === '') { + this.color = 'Red' + this.actualColor = 'rgba(255, 0, 0, 1)' + } else { + this.color = value + const temp = colorToRGBA(value) + this.actualColor = `rgba(${temp[0]},${temp[1]},${temp[2]}, ${temp[3]})` + } + } + } + /** + * @memberof SevenSegDisplay + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.color], + nodes: { + g: findNode(this.g), + f: findNode(this.f), + a: findNode(this.a), + b: findNode(this.b), + d: findNode(this.d), + e: findNode(this.e), + c: findNode(this.c), + dot: findNode(this.dot), + }, + } + return data + } + + /** + * @memberof SevenSegDisplay + * helper function to create save Json Data of object + */ + customDrawSegment(x1, y1, x2, y2, color) { + if (color === undefined) color = 'lightgrey' + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = color + ctx.lineWidth = correctWidth(5) + const xx = this.x + const yy = this.y + moveTo(ctx, x1, y1, xx, yy, this.direction) + lineTo(ctx, x2, y2, xx, yy, this.direction) + ctx.closePath() + ctx.stroke() + } + + /** + * @memberof SevenSegDisplay + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + const xx = this.x + const yy = this.y + this.customDrawSegment( + 18, + -3, + 18, + -38, + ['lightgrey', this.actualColor][this.b.value] + ) + this.customDrawSegment( + 18, + 3, + 18, + 38, + ['lightgrey', this.actualColor][this.c.value] + ) + this.customDrawSegment( + -18, + -3, + -18, + -38, + ['lightgrey', this.actualColor][this.f.value] + ) + this.customDrawSegment( + -18, + 3, + -18, + 38, + ['lightgrey', this.actualColor][this.e.value] + ) + this.customDrawSegment( + -17, + -38, + 17, + -38, + ['lightgrey', this.actualColor][this.a.value] + ) + this.customDrawSegment( + -17, + 0, + 17, + 0, + ['lightgrey', this.actualColor][this.g.value] + ) + this.customDrawSegment( + -15, + 38, + 17, + 38, + ['lightgrey', this.actualColor][this.d.value] + ) + ctx.beginPath() + const dotColor = + ['lightgrey', this.actualColor][this.dot.value] || 'lightgrey' + ctx.strokeStyle = dotColor + rect(ctx, xx + 22, yy + 42, 2, 2) + ctx.stroke() + } + + subcircuitDrawSegment(x1, y1, x2, y2, color, xxSegment, yySegment) { + if (color == undefined) color = 'lightgrey' + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = color + ctx.lineWidth = correctWidth(3) + var xx = xxSegment + var yy = yySegment + moveTo(ctx, x1, y1, xx, yy, this.direction) + lineTo(ctx, x2, y2, xx, yy, this.direction) + ctx.closePath() + ctx.stroke() + } + + // Draws the element in the subcuircuit. Used in layout mode + subcircuitDraw(xOffset = 0, yOffset = 0) { + var ctx = simulationArea.context + + var xx = this.subcircuitMetadata.x + xOffset + var yy = this.subcircuitMetadata.y + yOffset + + this.subcircuitDrawSegment( + 10, + -20, + 10, + -38, + ['lightgrey', this.actualColor][this.b.value], + xx, + yy + ) + this.subcircuitDrawSegment( + 10, + -17, + 10, + 1, + ['lightgrey', this.actualColor][this.c.value], + xx, + yy + ) + this.subcircuitDrawSegment( + -10, + -20, + -10, + -38, + ['lightgrey', this.actualColor][this.f.value], + xx, + yy + ) + this.subcircuitDrawSegment( + -10, + -17, + -10, + 1, + ['lightgrey', this.actualColor][this.e.value], + xx, + yy + ) + this.subcircuitDrawSegment( + -8, + -38, + 8, + -38, + ['lightgrey', this.actualColor][this.a.value], + xx, + yy + ) + this.subcircuitDrawSegment( + -8, + -18, + 8, + -18, + ['lightgrey', this.actualColor][this.g.value], + xx, + yy + ) + this.subcircuitDrawSegment( + -8, + 1, + 8, + 1, + ['lightgrey', this.actualColor][this.d.value], + xx, + yy + ) + + ctx.beginPath() + var dotColor = + ['lightgrey', this.actualColor][this.dot.value] || 'lightgrey' + ctx.strokeStyle = dotColor + rect(ctx, xx + 13, yy + 5, 1, 1) + ctx.stroke() + + ctx.beginPath() + ctx.strokeStyle = 'black' + ctx.lineWidth = correctWidth(1) + rect2(ctx, -15, -42, 33, 51, xx, yy, this.direction) + ctx.stroke() + + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected == this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.fillStyle = 'rgba(255, 255, 32,0.6)' + ctx.fill() + } + } + generateVerilog() { + return ` + always @ (*) + $display("SevenSegDisplay:${this.verilogLabel}.abcdefg. = %b%b%b%b%b%b%b%b}", + ${this.a.verilogLabel}, ${this.b.verilogLabel}, ${this.c.verilogLabel}, ${this.d.verilogLabel}, ${this.e.verilogLabel}, ${this.f.verilogLabel}, ${this.g.verilogLabel}, ${this.dot.verilogLabel});` + } +} + +/** + * @memberof SevenSegDisplay + * Help Tip + * @type {string} + * @category modules + */ +SevenSegDisplay.prototype.tooltipText = + 'Seven Display ToolTip: Consists of 7+1 single bit inputs.' + +/** + * @memberof SevenSegDisplay + * Help URL + * @type {string} + * @category modules + */ +SevenSegDisplay.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/3output?id=sevensegdisplay' +SevenSegDisplay.prototype.objectType = 'SevenSegDisplay' +SevenSegDisplay.prototype.canShowInSubcircuit = true +SevenSegDisplay.prototype.layoutProperties = { + rightDimensionX: 20, + leftDimensionX: 15, + upDimensionY: 42, + downDimensionY: 10, +} + +/** + * @memberof SevenSegDisplay + * Mutable properties of the element + * @type {JSON} + * @category modules + */ +SevenSegDisplay.prototype.mutableProperties = { + color: { + name: 'Color: ', + type: 'text', + func: 'changeColor', + }, +} diff --git a/v0/src/simulator/src/modules/SixteenSegDisplay.js b/v0/src/simulator/src/modules/SixteenSegDisplay.js new file mode 100644 index 00000000..944ba443 --- /dev/null +++ b/v0/src/simulator/src/modules/SixteenSegDisplay.js @@ -0,0 +1,489 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { + colorToRGBA, + correctWidth, + lineTo, + moveTo, + rect, + rect2, + validColor, +} from '../canvasApi' +import { changeInputSize } from '../modules' +/** + * @class + * SixteenSegDisplay + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @category modules + */ +export default class SixteenSegDisplay extends CircuitElement { + constructor(x, y, scope = globalScope, color = 'Red') { + super(x, y, scope, 'RIGHT', 16) + /* this is done in this.baseSetup() now + this.scope['SixteenSegDisplay'].push(this); + */ + this.fixedBitWidth = true + this.directionFixed = true + this.setDimensions(30, 50) + this.input1 = new Node(0, -50, 0, this, 16) + this.dot = new Node(0, 50, 0, this, 1) + this.direction = 'RIGHT' + this.color = color + this.actualColor = color + } + + /** + * @memberof SixteenSegDisplay + * fn to change the color of SixteenSegmentDisplay + * @return {JSON} + */ + changeColor(value) { + if (validColor(value)) { + if (value.trim() === '') { + this.color = 'Red' + this.actualColor = 'rgba(255, 0, 0, 1)' + } else { + this.color = value + const temp = colorToRGBA(value) + this.actualColor = `rgba(${temp[0]},${temp[1]},${temp[2]}, ${temp[3]})` + } + } + } + + /** + * @memberof SixteenSegDisplay + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.color], + nodes: { + input1: findNode(this.input1), + dot: findNode(this.dot), + }, + } + return data + } + + /** + * @memberof SixteenSegDisplay + * function to draw element + */ + customDrawSegment(x1, y1, x2, y2, color) { + if (color === undefined) color = 'lightgrey' + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = color + ctx.lineWidth = correctWidth(4) + const xx = this.x + const yy = this.y + moveTo(ctx, x1, y1, xx, yy, this.direction) + lineTo(ctx, x2, y2, xx, yy, this.direction) + ctx.closePath() + ctx.stroke() + } + + /** + * @memberof SixteenSegDisplay + * function to draw element + */ + customDrawSegmentSlant(x1, y1, x2, y2, color) { + if (color === undefined) color = 'lightgrey' + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = color + ctx.lineWidth = correctWidth(3) + const xx = this.x + const yy = this.y + moveTo(ctx, x1, y1, xx, yy, this.direction) + lineTo(ctx, x2, y2, xx, yy, this.direction) + ctx.closePath() + ctx.stroke() + } + + /** + * @memberof SixteenSegDisplay + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + const xx = this.x + const yy = this.y + const color = ['lightgrey', this.actualColor] + const { value } = this.input1 + this.customDrawSegment( + -20, + -38, + 0, + -38, + ['lightgrey', this.actualColor][(value >> 15) & 1] + ) // a1 + this.customDrawSegment( + 20, + -38, + 0, + -38, + ['lightgrey', this.actualColor][(value >> 14) & 1] + ) // a2 + this.customDrawSegment( + 21.5, + -2, + 21.5, + -36, + ['lightgrey', this.actualColor][(value >> 13) & 1] + ) // b + this.customDrawSegment( + 21.5, + 2, + 21.5, + 36, + ['lightgrey', this.actualColor][(value >> 12) & 1] + ) // c + this.customDrawSegment( + -20, + 38, + 0, + 38, + ['lightgrey', this.actualColor][(value >> 11) & 1] + ) // d1 + this.customDrawSegment( + 20, + 38, + 0, + 38, + ['lightgrey', this.actualColor][(value >> 10) & 1] + ) // d2 + this.customDrawSegment( + -21.5, + 2, + -21.5, + 36, + ['lightgrey', this.actualColor][(value >> 9) & 1] + ) // e + this.customDrawSegment( + -21.5, + -36, + -21.5, + -2, + ['lightgrey', this.actualColor][(value >> 8) & 1] + ) // f + this.customDrawSegment( + -20, + 0, + 0, + 0, + ['lightgrey', this.actualColor][(value >> 7) & 1] + ) // g1 + this.customDrawSegment( + 20, + 0, + 0, + 0, + ['lightgrey', this.actualColor][(value >> 6) & 1] + ) // g2 + this.customDrawSegmentSlant( + 0, + 0, + -21, + -37, + ['lightgrey', this.actualColor][(value >> 5) & 1] + ) // h + this.customDrawSegment( + 0, + -2, + 0, + -36, + ['lightgrey', this.actualColor][(value >> 4) & 1] + ) // i + this.customDrawSegmentSlant( + 0, + 0, + 21, + -37, + ['lightgrey', this.actualColor][(value >> 3) & 1] + ) // j + this.customDrawSegmentSlant( + 0, + 0, + 21, + 37, + ['lightgrey', this.actualColor][(value >> 2) & 1] + ) // k + this.customDrawSegment( + 0, + 2, + 0, + 36, + ['lightgrey', this.actualColor][(value >> 1) & 1] + ) // l + this.customDrawSegmentSlant( + 0, + 0, + -21, + 37, + ['lightgrey', this.actualColor][(value >> 0) & 1] + ) // m + ctx.beginPath() + const dotColor = + ['lightgrey', this.actualColor][this.dot.value] || 'lightgrey' + ctx.strokeStyle = dotColor + rect(ctx, xx + 22, yy + 42, 2, 2) + ctx.stroke() + } + + subcircuitDrawSegment(x1, y1, x2, y2, color, xxSegment, yySegment) { + if (color == undefined) color = 'lightgrey' + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = color + ctx.lineWidth = correctWidth(3) + var xx = xxSegment + var yy = yySegment + moveTo(ctx, x1, y1, xx, yy, this.direction) + lineTo(ctx, x2, y2, xx, yy, this.direction) + ctx.closePath() + ctx.stroke() + } + + subcircuitDrawSegmentSlant(x1, y1, x2, y2, color, xxSegment, yySegment) { + if (color == undefined) color = 'lightgrey' + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = color + ctx.lineWidth = correctWidth(2) + var xx = xxSegment + var yy = yySegment + moveTo(ctx, x1, y1, xx, yy, this.direction) + lineTo(ctx, x2, y2, xx, yy, this.direction) + ctx.closePath() + ctx.stroke() + } + + // Draws the element in the subcircuit. Used in layout mode + subcircuitDraw(xOffset = 0, yOffset = 0) { + var ctx = simulationArea.context + + var xx = this.subcircuitMetadata.x + xOffset + var yy = this.subcircuitMetadata.y + yOffset + + var color = ['lightgrey', this.actualColor] + var value = this.input1.value + + this.subcircuitDrawSegment( + -10, + -38, + 0, + -38, + ['lightgrey', this.actualColor][(value >> 15) & 1], + xx, + yy + ) //a1 + this.subcircuitDrawSegment( + 10, + -38, + 0, + -38, + ['lightgrey', this.actualColor][(value >> 14) & 1], + xx, + yy + ) //a2 + this.subcircuitDrawSegment( + 11.5, + -19, + 11.5, + -36, + ['lightgrey', this.actualColor][(value >> 13) & 1], + xx, + yy + ) //b + this.subcircuitDrawSegment( + 11.5, + 2, + 11.5, + -15, + ['lightgrey', this.actualColor][(value >> 12) & 1], + xx, + yy + ) //c + this.subcircuitDrawSegment( + -10, + 4, + 0, + 4, + ['lightgrey', this.actualColor][(value >> 11) & 1], + xx, + yy + ) //d1 + this.subcircuitDrawSegment( + 10, + 4, + 0, + 4, + ['lightgrey', this.actualColor][(value >> 10) & 1], + xx, + yy + ) //d2 + this.subcircuitDrawSegment( + -11.5, + 2, + -11.5, + -15, + ['lightgrey', this.actualColor][(value >> 9) & 1], + xx, + yy + ) //e + this.subcircuitDrawSegment( + -11.5, + -36, + -11.5, + -19, + ['lightgrey', this.actualColor][(value >> 8) & 1], + xx, + yy + ) //f + this.subcircuitDrawSegment( + -10, + -17, + 0, + -17, + ['lightgrey', this.actualColor][(value >> 7) & 1], + xx, + yy + ) //g1 + this.subcircuitDrawSegment( + 10, + -17, + 0, + -17, + ['lightgrey', this.actualColor][(value >> 6) & 1], + xx, + yy + ) //g2 + this.subcircuitDrawSegmentSlant( + 0, + -17, + -9, + -36, + ['lightgrey', this.actualColor][(value >> 5) & 1], + xx, + yy + ) //h + this.subcircuitDrawSegment( + 0, + -36, + 0, + -19, + ['lightgrey', this.actualColor][(value >> 4) & 1], + xx, + yy + ) //i + this.subcircuitDrawSegmentSlant( + 0, + -17, + 9, + -36, + ['lightgrey', this.actualColor][(value >> 3) & 1], + xx, + yy + ) //j + this.subcircuitDrawSegmentSlant( + 0, + -17, + 9, + 0, + ['lightgrey', this.actualColor][(value >> 2) & 1], + xx, + yy + ) //k + this.subcircuitDrawSegment( + 0, + -17, + 0, + 2, + ['lightgrey', this.actualColor][(value >> 1) & 1], + xx, + yy + ) //l + this.subcircuitDrawSegmentSlant( + 0, + -17, + -9, + 0, + ['lightgrey', this.actualColor][(value >> 0) & 1], + xx, + yy + ) //m + + ctx.beginPath() + var dotColor = + ['lightgrey', this.actualColor][this.dot.value] || 'lightgrey' + ctx.strokeStyle = dotColor + rect(ctx, xx + 13, yy + 5, 1, 1) + ctx.stroke() + + ctx.beginPath() + ctx.strokeStyle = 'black' + ctx.lineWidth = correctWidth(1) + rect2(ctx, -15, -42, 33, 51, xx, yy, this.direction) + ctx.stroke() + + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected == this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.fillStyle = 'rgba(255, 255, 32,0.6)' + ctx.fill() + } + } + generateVerilog() { + return ` + always @ (*) + $display("SixteenSegDisplay:{${this.input1.verilogLabel},${this.dot.verilogLabel}} = {%16b,%1b}", ${this.input1.verilogLabel}, ${this.dot.verilogLabel});` + } +} + +/** + * @memberof SixteenSegDisplay + * Help Tip + * @type {string} + * @category modules + */ +SixteenSegDisplay.prototype.tooltipText = + 'Sixteen Display ToolTip: Consists of 16+1 bit inputs.' + +/** + * @memberof SixteenSegDisplay + * Help URL + * @type {string} + * @category modules + */ +SixteenSegDisplay.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/3output?id=sixteensegdisplay' +SixteenSegDisplay.prototype.objectType = 'SixteenSegDisplay' +SixteenSegDisplay.prototype.canShowInSubcircuit = true +SixteenSegDisplay.prototype.layoutProperties = { + rightDimensionX: 20, + leftDimensionX: 15, + upDimensionY: 42, + downDimensionY: 10, +} + +/** + * @memberof SixteenSegDisplay + * Mutable properties of the element + * @type {JSON} + * @category modules + */ +SixteenSegDisplay.prototype.mutableProperties = { + color: { + name: 'Color: ', + type: 'text', + func: 'changeColor', + }, +} diff --git a/v0/src/simulator/src/modules/Splitter.js b/v0/src/simulator/src/modules/Splitter.js new file mode 100644 index 00000000..ffcb296c --- /dev/null +++ b/v0/src/simulator/src/modules/Splitter.js @@ -0,0 +1,359 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, fillText2 } from '../canvasApi' +import { colors } from '../themer/themer' + +function extractBits(num, start, end) { + return (num << (32 - end)) >>> (32 - (end - start + 1)) +} + +/** + * @class + * Splitter + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @param {number=} bitWidthSplit - number of input nodes + * @category modules + */ +export default class Splitter extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + bitWidth = undefined, + bitWidthSplit = undefined + ) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['Splitter'].push(this); + */ + this.rectangleObject = false + + this.bitWidthSplit = + bitWidthSplit || + ( + prompt('Enter bitWidth Split') || + `${'1 '.repeat((this.bitWidth || 1) - 1)}1` + ) + .split(' ') + .filter((lambda) => lambda !== '') + .map((lambda) => parseInt(lambda, 10) || 1) + this.splitCount = this.bitWidthSplit.length + + this.setDimensions(10, (this.splitCount - 1) * 10 + 10) + this.yOffset = (this.splitCount / 2 - 1) * 20 + + this.inp1 = new Node(-10, 10 + this.yOffset, 0, this, this.bitWidth) + + this.outputs = [] + // this.prevOutValues=new Array(this.splitCount) + for (let i = 0; i < this.splitCount; i++) { + this.outputs.push( + new Node( + 20, + i * 20 - this.yOffset - 20, + 0, + this, + this.bitWidthSplit[i] + ) + ) + } + + this.prevInpValue = undefined + } + + /** + * @memberof Splitter + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [ + this.direction, + this.bitWidth, + this.bitWidthSplit, + ], + nodes: { + outputs: this.outputs.map(findNode), + inp1: findNode(this.inp1), + }, + } + return data + } + + /** + * @memberof Splitter + * fn to remove proporgation delay. + * @return {JSON} + */ + removePropagation() { + if (this.inp1.value === undefined) { + let i = 0 + for (i = 0; i < this.outputs.length; i++) { + // False Hit + if (this.outputs[i].value === undefined) return + } + for (i = 0; i < this.outputs.length; i++) { + if (this.outputs[i].value !== undefined) { + this.outputs[i].value = undefined + simulationArea.simulationQueue.add(this.outputs[i]) + } + } + } else if (this.inp1.value !== undefined) { + this.inp1.value = undefined + simulationArea.simulationQueue.add(this.inp1) + } + this.prevInpValue = undefined + } + + /** + * @memberof Splitter + * Checks if the element is resolvable + * @return {boolean} + */ + isResolvable() { + let resolvable = false + if (this.inp1.value !== this.prevInpValue) { + if (this.inp1.value !== undefined) return true + return false + } + let i + for (i = 0; i < this.splitCount; i++) { + if (this.outputs[i].value === undefined) break + } + if (i === this.splitCount) resolvable = true + return resolvable + } + + /** + * @memberof Splitter + * resolve output values based on inputData + */ + resolve() { + if (this.isResolvable() === false) { + return + } + if ( + this.inp1.value !== undefined && + this.inp1.value !== this.prevInpValue + ) { + let bitCount = 1 + for (let i = 0; i < this.splitCount; i++) { + const bitSplitValue = extractBits( + this.inp1.value, + bitCount, + bitCount + this.bitWidthSplit[i] - 1 + ) + if (this.outputs[i].value !== bitSplitValue) { + if (this.outputs[i].value !== bitSplitValue) { + this.outputs[i].value = bitSplitValue + simulationArea.simulationQueue.add(this.outputs[i]) + } + } + bitCount += this.bitWidthSplit[i] + } + } else { + let n = 0 + for (let i = this.splitCount - 1; i >= 0; i--) { + n <<= this.bitWidthSplit[i] + n += this.outputs[i].value + } + if (this.inp1.value !== n >>> 0) { + this.inp1.value = n >>> 0 + simulationArea.simulationQueue.add(this.inp1) + } + // else if (this.inp1.value !== n) { + // } + } + this.prevInpValue = this.inp1.value + } + + /** + * @memberof Splitter + * fn to reset values of splitter + */ + reset() { + this.prevInpValue = undefined + } + + /** + * @memberof Splitter + * fn to process verilog of the element + * @return {JSON} + */ + processVerilog() { + if ( + this.inp1.verilogLabel !== '' && + this.outputs[0].verilogLabel === '' + ) { + let bitCount = 0 + for (let i = 0; i < this.splitCount; i++) { + // let bitSplitValue = extractBits(this.inp1.value, bitCount, bitCount + this.bitWidthSplit[i] - 1); + if (this.bitWidthSplit[i] > 1) { + const label = `${this.inp1.verilogLabel}[ ${ + bitCount + this.bitWidthSplit[i] - 1 + }:${bitCount}]` + } else { + const label = `${this.inp1.verilogLabel}[${bitCount}]` + } + if (this.outputs[i].verilogLabel !== label) { + this.outputs[i].verilogLabel = label + this.scope.stack.push(this.outputs[i]) + } + bitCount += this.bitWidthSplit[i] + } + } else if ( + this.inp1.verilogLabel === '' && + this.outputs[0].verilogLabel !== '' + ) { + const label = `{${this.outputs + .map((x) => x.verilogLabel) + .join(',')}}` + if (this.inp1.verilogLabel !== label) { + this.inp1.verilogLabel = label + this.scope.stack.push(this.inp1) + } + } + } + + /** + * @memberof Splitter + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + // + ctx.strokeStyle = [colors['splitter'], 'brown'][ + ((this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this)) + 0 + ] + ctx.lineWidth = correctWidth(3) + const xx = this.x + const yy = this.y + ctx.beginPath() + moveTo(ctx, -10, 10 + this.yOffset, xx, yy, this.direction) + lineTo(ctx, 0, 0 + this.yOffset, xx, yy, this.direction) + lineTo( + ctx, + 0, + -20 * (this.splitCount - 1) + this.yOffset, + xx, + yy, + this.direction + ) + let bitCount = 0 + for (let i = this.splitCount - 1; i >= 0; i--) { + moveTo(ctx, 0, -20 * i + this.yOffset, xx, yy, this.direction) + lineTo(ctx, 20, -20 * i + this.yOffset, xx, yy, this.direction) + } + ctx.stroke() + ctx.beginPath() + ctx.fillStyle = colors['text'] + for (let i = this.splitCount - 1; i >= 0; i--) { + var splitLabel + if (this.bitWidthSplit[this.splitCount - i - 1] == 1) + splitLabel = `${bitCount}` + else + splitLabel = `${bitCount}:${ + bitCount + this.bitWidthSplit[this.splitCount - i - 1] - 1 + }` + + fillText2( + ctx, + splitLabel, + 16, + -20 * i + this.yOffset + 10, + xx, + yy, + this.direction + ) + bitCount += this.bitWidthSplit[this.splitCount - i - 1] + } + ctx.fill() + } + + processVerilog() { + // Combiner + if (this.inp1.verilogLabel == '') { + this.isSplitter = false + this.inp1.verilogLabel = this.verilogLabel + '_cmb' + if ( + !this.scope.verilogWireList[this.bitWidth].contains( + this.inp1.verilogLabel + ) + ) + this.scope.verilogWireList[this.bitWidth].push( + this.inp1.verilogLabel + ) + this.scope.stack.push(this.inp1) + return + } + + // Splitter + this.isSplitter = true + for (var j = 0; j < this.outputs.length; j++) { + var bitCount = 0 + var inpLabel = this.inp1.verilogLabel + // Already Split Regex + var re = /^(.*)\[(\d*):(\d*)\]$/ + if (re.test(inpLabel)) { + var matches = inpLabel.match(re) + inpLabel = matches[1] + bitCount = parseInt(matches[3]) + } + for (var i = 0; i < this.splitCount; i++) { + if (this.bitWidthSplit[i] > 1) + var label = + inpLabel + + '[' + + (bitCount + this.bitWidthSplit[i] - 1) + + ':' + + bitCount + + ']' + else var label = inpLabel + '[' + bitCount + ']' + if (this.outputs[i].verilogLabel != label) { + this.outputs[i].verilogLabel = label + this.scope.stack.push(this.outputs[i]) + } + bitCount += this.bitWidthSplit[i] + } + } + } + //added to generate Splitter INPUTS + generateVerilog() { + var res = '' + if (!this.isSplitter) { + res += 'assign ' + this.inp1.verilogLabel + ' = {' + for (var i = this.outputs.length - 1; i > 0; i--) + res += this.outputs[i].verilogLabel + ',' + res += this.outputs[0].verilogLabel + '};' + } + return res + } +} + +/** + * @memberof Splitter + * Help Tip + * @type {string} + * @category modules + */ +Splitter.prototype.tooltipText = + 'Splitter ToolTip: Split multiBit Input into smaller bitwidths or vice versa.' + +/** + * @memberof Splitter + * Help URL + * @type {string} + * @category modules + */ +Splitter.prototype.helplink = 'https://docs.circuitverse.org/#/chapter4/8misc?id=splitter' +Splitter.prototype.objectType = 'Splitter' diff --git a/v0/src/simulator/src/modules/SquareRGBLed.js b/v0/src/simulator/src/modules/SquareRGBLed.js new file mode 100644 index 00000000..b93e0bb6 --- /dev/null +++ b/v0/src/simulator/src/modules/SquareRGBLed.js @@ -0,0 +1,215 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, rect2 } from '../canvasApi' + +/** + * @class + * SquareRGBLed + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} pinLength - pins per node. + * @category modules + */ +export default class SquareRGBLed extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'UP', pinLength = 1) { + super(x, y, scope, dir, 8) + /* this is done in this.baseSetup() now + this.scope['SquareRGBLed'].push(this); + */ + this.rectangleObject = false + this.setDimensions(15, 15) + this.pinLength = pinLength === undefined ? 1 : pinLength + const nodeX = -10 - 10 * pinLength + this.inp1 = new Node(nodeX, -10, 0, this, 8, 'R') + this.inp2 = new Node(nodeX, 0, 0, this, 8, 'G') + this.inp3 = new Node(nodeX, 10, 0, this, 8, 'B') + this.inp = [this.inp1, this.inp2, this.inp3] + this.labelDirection = 'UP' + this.fixedBitWidth = true + + // eslint-disable-next-line no-shadow + this.changePinLength = function (pinLength) { + if (pinLength === undefined) return + pinLength = parseInt(pinLength, 10) + if (pinLength < 0 || pinLength > 1000) return + + // Calculate the new position of the LED, so the nodes will stay in the same place. + const diff = 10 * (pinLength - this.pinLength) + // eslint-disable-next-line no-nested-ternary + const diffX = + this.direction === 'LEFT' + ? -diff + : this.direction === 'RIGHT' + ? diff + : 0 + // eslint-disable-next-line no-nested-ternary + const diffY = + this.direction === 'UP' + ? -diff + : this.direction === 'DOWN' + ? diff + : 0 + + // Build a new LED with the new values; preserve label properties too. + const obj = new SquareRGBLed( + this.x + diffX, + this.y + diffY, + this.scope, + this.direction, + pinLength + ) + obj.label = this.label + obj.labelDirection = this.labelDirection + + this.cleanDelete() + simulationArea.lastSelected = obj + return obj + } + + this.mutableProperties = { + pinLength: { + name: 'Pin Length', + type: 'number', + max: '1000', + min: '0', + func: 'changePinLength', + }, + } + } + + /** + * @memberof SquareRGBLed + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.direction, this.pinLength], + nodes: { + inp1: findNode(this.inp1), + inp2: findNode(this.inp2), + inp3: findNode(this.inp3), + }, + } + return data + } + + /** + * @memberof SquareRGBLed + * function to draw element + */ + customDraw() { + const ctx = simulationArea.context + const xx = this.x + const yy = this.y + const r = this.inp1.value + const g = this.inp2.value + const b = this.inp3.value + + const colors = ['rgb(174,20,20)', 'rgb(40,174,40)', 'rgb(0,100,255)'] + for (let i = 0; i < 3; i++) { + const x = -10 - 10 * this.pinLength + const y = i * 10 - 10 + ctx.lineWidth = correctWidth(3) + + // A gray line, which makes it easy on the eyes when the pin length is large + ctx.beginPath() + ctx.lineCap = 'butt' + ctx.strokeStyle = 'rgb(227, 228, 229)' + moveTo(ctx, -15, y, xx, yy, this.direction) + lineTo(ctx, x + 10, y, xx, yy, this.direction) + ctx.stroke() + + // A colored line, so people know which pin does what. + ctx.lineCap = 'round' + ctx.beginPath() + ctx.strokeStyle = colors[i] + moveTo(ctx, x + 10, y, xx, yy, this.direction) + lineTo(ctx, x, y, xx, yy, this.direction) + ctx.stroke() + } + + ctx.strokeStyle = '#d3d4d5' + ctx.fillStyle = + r === undefined && g === undefined && b === undefined + ? 'rgb(227, 228, 229)' + : `rgb(${r || 0}, ${g || 0}, ${b || 0})` + ctx.lineWidth = correctWidth(1) + ctx.beginPath() + rect2(ctx, -15, -15, 30, 30, xx, yy, this.direction) + ctx.stroke() + + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.fillStyle = 'rgba(255, 255, 32)' + } + + ctx.fill() + } + // Draws the element in the subcuircuit. Used in layout mode + subcircuitDraw(xOffset = 0, yOffset = 0) { + var ctx = simulationArea.context + var xx = this.subcircuitMetadata.x + xOffset + var yy = this.subcircuitMetadata.y + yOffset + var r = this.inp1.value + var g = this.inp2.value + var b = this.inp3.value + + ctx.strokeStyle = '#d3d4d5' + ctx.fillStyle = + r === undefined && g === undefined && b === undefined + ? 'rgb(227, 228, 229)' + : 'rgb(' + (r || 0) + ', ' + (g || 0) + ', ' + (b || 0) + ')' + ctx.lineWidth = correctWidth(1) + ctx.beginPath() + rect2(ctx, 0, 0, 15, 15, xx, yy, this.direction) + ctx.stroke() + + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected == this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.fillStyle = 'rgba(255, 255, 32)' + } + + ctx.fill() + } + + generateVerilog() { + return this.generateVerilog.call(this) + } +} + +/** + * @memberof SquareRGBLed + * Help Tip + * @type {string} + * @category modules + */ +SquareRGBLed.prototype.tooltipText = + 'Square RGB Led ToolTip: RGB Led inputs 8 bit values for the colors RED, GREEN and BLUE.' + +/** + * @memberof SquareRGBLed + * Help URL + * @type {string} + * @category modules + */ +SquareRGBLed.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/3output?id=squarergbled' +SquareRGBLed.prototype.objectType = 'SquareRGBLed' +SquareRGBLed.prototype.canShowInSubcircuit = true +SquareRGBLed.prototype.layoutProperties = { + rightDimensionX: 15, + leftDimensionX: 0, + upDimensionY: 15, + downDimensionY: 0, +} diff --git a/v0/src/simulator/src/modules/Stepper.js b/v0/src/simulator/src/modules/Stepper.js new file mode 100644 index 00000000..c6cfbd3f --- /dev/null +++ b/v0/src/simulator/src/modules/Stepper.js @@ -0,0 +1,102 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { fillText } from '../canvasApi' +import { changeInputSize } from '../modules' +/** + * @class + * Stepper + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bitwidth of element + * @category modules + */ +import { colors } from '../themer/themer' + +export default class Stepper extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT', bitWidth = 8) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['Stepper'].push(this); + */ + this.setDimensions(20, 20) + + this.output1 = new Node(20, 0, 1, this, bitWidth) + this.state = 0 + } + + /** + * @memberof Stepper + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + var data = { + constructorParamaters: [this.direction, this.bitWidth], + nodes: { + output1: findNode(this.output1), + }, + values: { + state: this.state, + }, + } + return data + } + + /** + * @memberof Stepper + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.beginPath() + ctx.font = '20px Raleway' + ctx.fillStyle = colors['input_text'] + ctx.textAlign = 'center' + fillText(ctx, this.state.toString(16), this.x, this.y + 5) + ctx.fill() + } + + /** + * @memberof Stepper + * resolve output values based on inputData + */ + resolve() { + this.state = Math.min(this.state, (1 << this.bitWidth) - 1) + this.output1.value = this.state + simulationArea.simulationQueue.add(this.output1) + } + + /** + * Listener function for increasing value of state + * @memberof Stepper + * @param {string} key - the key pressed + */ + keyDown2(key) { + if (this.state < 1 << this.bitWidth && (key === '+' || key === '=')) + this.state++ + if (this.state > 0 && (key === '_' || key === '-')) this.state-- + } +} + +/** + * @memberof Stepper + * Help Tip + * @type {string} + * @category modules + */ +Stepper.prototype.tooltipText = + 'Stepper ToolTip: Increase/Decrease value by selecting the stepper and using +/- keys.' + +/** + * @memberof Stepper + * Help URL + * @type {string} + * @category modules + */ +Stepper.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/2input?id=stepper' +Stepper.prototype.objectType = 'Stepper' diff --git a/v0/src/simulator/src/modules/Text.js b/v0/src/simulator/src/modules/Text.js new file mode 100644 index 00000000..7248c575 --- /dev/null +++ b/v0/src/simulator/src/modules/Text.js @@ -0,0 +1,208 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { rect2, fillText } from '../canvasApi' +/** + * @class + * Text + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} label - label of element + * @param {number=} fontSize - font size + * @category modules + */ +import { colors } from '../themer/themer' +import { copy, paste } from '../events' + +export default class Text extends CircuitElement { + constructor(x, y, scope = globalScope, label = '', fontSize = 14) { + super(x, y, scope, 'RIGHT', 1) + /* this is done in this.baseSetup() now + this.scope['Text'].push(this); + */ + // this.setDimensions(15, 15); + this.fixedBitWidth = true + this.directionFixed = true + this.labelDirectionFixed = true + this.setLabel(label) + this.setFontSize(fontSize) + } + + /** + * @memberof Text + * function for setting text inside the element + * @param {string=} str - the label + */ + setLabel(str = '') { + this.label = str + var ctx = simulationArea.context + ctx.font = `${this.fontSize}px Raleway` + this.leftDimensionX = 10 + this.rightDimensionX = ctx.measureText(this.label).width + 10 + this.setTextboxSize() + } + + /** + * @memberof Text + * function for setting font size inside the element + * @param {number=} str - the font size + */ + setFontSize(fontSize = 14) { + this.fontSize = fontSize + var ctx = simulationArea.context + ctx.font = `${this.fontSize}px Raleway` + this.setTextboxSize() + } + + setTextboxSize() { + this.leftDimensionX = 10 + var maxWidth = 0 + var labels = this.label.split('\n') + var ctx = simulationArea.context + labels.forEach( + (l) => (maxWidth = Math.max(maxWidth, ctx.measureText(l).width)) + ) + this.rightDimensionX = maxWidth + 10 + this.downDimensionY = labels.length * this.fontSize + } + + /** + * @memberof Text + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.label, this.fontSize], + } + return data + } + + /** + * @memberof Text + * Listener function for Text Box + * @param {string} key - the label + */ + keyDown(key) { + if (simulationArea.controlDown && (key === 'c' || key === 'C')) { + const textToPutOnClipboard = copy([this]) + navigator.clipboard.writeText(textToPutOnClipboard) + localStorage.setItem('clipboardData', textToPutOnClipboard) + } else if (simulationArea.controlDown && (key === 'v' || key === 'V')) { + paste(localStorage.getItem('clipboardData')) + } else if (key.length === 1) { + if (this.label === 'Enter Text Here') { + this.setLabel(key) + } else { + this.setLabel(this.label + key) + } + } else if (key === 'Backspace') { + if (this.label === 'Enter Text Here') { + this.setLabel('') + } else { + this.setLabel(this.label.slice(0, -1)) + } + } else if (key === 'Enter') { + if (this.label === 'Enter Text Here') { + this.setLabel('') + } else { + this.setLabel(this.label + '\n') + } + } + $('[name=setLabel]').val(this.label) + } + + /** + * @memberof Text + * Function for drawing text box + */ + draw() { + // + if (this.label.length === 0 && simulationArea.lastSelected !== this) + this.delete() + var ctx = simulationArea.context + ctx.strokeStyle = colors['stroke'] + ctx.lineWidth = 1 + const xx = this.x + const yy = this.y + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.beginPath() + ctx.fillStyle = colors['fill'] + const magicDimension = this.fontSize - 14 + rect2( + ctx, + -this.leftDimensionX, + -this.upDimensionY - magicDimension, + this.leftDimensionX + this.rightDimensionX, + this.upDimensionY + this.downDimensionY + magicDimension, + this.x, + this.y, + 'RIGHT' + ) + ctx.fillStyle = 'rgba(255, 255, 32,0.1)' + ctx.fill() + ctx.stroke() + } + ctx.beginPath() + ctx.textAlign = 'left' + ctx.fillStyle = colors['text'] + var labels = this.label.split('\n') + for (var i = 0; i < labels.length; i++) { + fillText( + ctx, + labels[i], + xx, + yy + 5 + i * this.fontSize, + this.fontSize + ) + } + ctx.fill() + } +} + +/** + * @memberof Text + * Help Tip + * @type {string} + * @category modules + */ +Text.prototype.tooltipText = 'Text ToolTip: Use this to document your circuit.' + +/** + * @memberof Text + * Help URL + * @type {string} + * @category modules + */ +Text.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/7annotation?id=text' + +/** + * @memberof Text + * Mutable properties of the element + * @type {JSON} + * @category modules + */ +Text.prototype.mutableProperties = { + fontSize: { + name: 'Font size: ', + type: 'number', + max: '84', + min: '14', + func: 'setFontSize', + }, + label: { + name: 'Text: ', + type: 'textarea', + func: 'setLabel', + }, +} +Text.prototype.disableLabel = true +Text.prototype.objectType = 'Text' +Text.prototype.propagationDelayFixed = true diff --git a/v0/src/simulator/src/modules/TriState.js b/v0/src/simulator/src/modules/TriState.js new file mode 100644 index 00000000..e4048fc9 --- /dev/null +++ b/v0/src/simulator/src/modules/TriState.js @@ -0,0 +1,129 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, arc } from '../canvasApi' +import { changeInputSize } from '../modules' +/** + * @class + * TriState + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +import { colors } from '../themer/themer' + +export default class TriState extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT', bitWidth = 1) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['TriState'].push(this); + */ + this.rectangleObject = false + this.setDimensions(15, 15) + + this.inp1 = new Node(-10, 0, 0, this) + this.output1 = new Node(20, 0, 1, this) + this.state = new Node(0, 0, 0, this, 1, 'Enable') + } + + // TriState.prototype.propagationDelay=10000; + + /** + * @memberof TriState + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.direction, this.bitWidth], + nodes: { + output1: findNode(this.output1), + inp1: findNode(this.inp1), + state: findNode(this.state), + }, + } + return data + } + + /** + * @memberof TriState + * function to change bitwidth of the element + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + this.inp1.bitWidth = bitWidth + this.output1.bitWidth = bitWidth + this.bitWidth = bitWidth + } + + /** + * @memberof TriState + * resolve output values based on inputData + */ + resolve() { + if (this.isResolvable() === false) { + return + } + + if (this.state.value === 1) { + if (this.output1.value !== this.inp1.value) { + this.output1.value = this.inp1.value // >>>0)<<(32-this.bitWidth))>>>(32-this.bitWidth); + simulationArea.simulationQueue.add(this.output1) + } + simulationArea.contentionPending.clean(this) + } else if ( + this.output1.value !== undefined && + !simulationArea.contentionPending.contains(this) + ) { + this.output1.value = undefined + simulationArea.simulationQueue.add(this.output1) + } + simulationArea.contentionPending.clean(this) + } + + /** + * @memberof TriState + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.strokeStyle = colors['stroke'] + ctx.lineWidth = correctWidth(3) + const xx = this.x + const yy = this.y + ctx.beginPath() + ctx.fillStyle = colors['fill'] + moveTo(ctx, -10, -15, xx, yy, this.direction) + lineTo(ctx, 20, 0, xx, yy, this.direction) + lineTo(ctx, -10, 15, xx, yy, this.direction) + ctx.closePath() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + } + + generateVerilog() { + return `assign ${this.output1.verilogLabel} = (${this.state.verilogLabel}!=0) ? ${this.inp1.verilogLabel} : ${this.inp1.bitWidth}'b?;` + } +} + +/** + * @memberof TriState + * Help Tip + * @type {string} + * @category modules + */ +TriState.prototype.tooltipText = + 'TriState ToolTip : Effectively removes the output from the circuit.' +TriState.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/8misc?id=tristate-buffer' +TriState.prototype.objectType = 'TriState' diff --git a/v0/src/simulator/src/modules/Tunnel.js b/v0/src/simulator/src/modules/Tunnel.js new file mode 100644 index 00000000..7e3f96c5 --- /dev/null +++ b/v0/src/simulator/src/modules/Tunnel.js @@ -0,0 +1,351 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, rect2, fillText } from '../canvasApi' +import plotArea from '../plotArea' +import { showError } from '../utils' +/** + * @class + * Tunnel + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @param {string=} identifier - number of input nodes + * @category modules + */ +import { colors } from '../themer/themer' + +export default class Tunnel extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'LEFT', + bitWidth = 1, + identifier + ) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['Tunnel'].push(this); + */ + this.rectangleObject = false + this.centerElement = true + this.xSize = 10 + this.plotValues = [] + this.inp1 = new Node(0, 0, 0, this) + this.checked = false // has this tunnel been checked by another paired tunnel + this.setIdentifier(identifier || 'T') + this.setBounds() + // if tunnels with this's identifier exist, then set the bitwidth to that of those tunnels + if (this.scope.tunnelList[this.identifier].length > 0) { + this.newBitWidth(this.scope.tunnelList[this.identifier][0].bitWidth) + } + } + + /** + * @memberof Tunnel + * function to change direction of Tunnel + * @param {string} dir - new direction + */ + newDirection(dir) { + if (this.direction === dir) return + this.direction = dir + this.setBounds() + } + + setBounds() { + let xRotate = 0 + let yRotate = 0 + if (this.direction === 'LEFT') { + xRotate = 0 + yRotate = 0 + } else if (this.direction === 'RIGHT') { + xRotate = 120 - this.xSize + yRotate = 0 + } else if (this.direction === 'UP') { + xRotate = 60 - this.xSize / 2 + yRotate = -20 + } else { + xRotate = 60 - this.xSize / 2 + yRotate = 20 + } + + this.leftDimensionX = Math.abs(-120 + xRotate + this.xSize) + this.upDimensionY = Math.abs(-20 + yRotate) + this.rightDimensionX = Math.abs(xRotate) + this.downDimensionY = Math.abs(20 + yRotate) + + // rect2(ctx, -120 + xRotate + this.xSize, -20 + yRotate, 120 - this.xSize, 40, xx, yy, "RIGHT"); + } + + /** + * @memberof Tunnel + * resolve output values based on inputData + */ + resolve() { + // Don't check for paired tunnels' value if already checked by another paired tunnel (O(n)) + if (this.checked) { + this.checked = false + return + } + // Check for bitwidth error since it bypasses node's resolve() function which usually checks bitwidths + for (const tunnel of this.scope.tunnelList[this.identifier]) { + if (tunnel.inp1.bitWidth !== this.inp1.bitWidth) { + this.inp1.highlighted = true + tunnel.inp1.highlighted = true + showError( + `BitWidth Error: ${this.inp1.bitWidth} and ${tunnel.inp1.bitWidth}` + ) + } + if (tunnel.inp1.value !== this.inp1.value) { + tunnel.inp1.value = this.inp1.value + simulationArea.simulationQueue.add(tunnel.inp1) + } + if (tunnel !== this) tunnel.checked = true + } + } + + /** + * @memberof Tunnel + * function to set tunnel value + * @param {Scope} scope - tunnel value + */ + updateScope(scope) { + this.scope = scope + this.inp1.updateScope(scope) + this.setIdentifier(this.identifier) + } + + /** + * @memberof Tunnel + * function to set plot value + */ + setPlotValue() { + return + const time = plotArea.stopWatch.ElapsedMilliseconds + if ( + this.plotValues.length && + this.plotValues[this.plotValues.length - 1][0] === time + ) { + this.plotValues.pop() + } + + if (this.plotValues.length === 0) { + this.plotValues.push([time, this.inp1.value]) + return + } + + if ( + this.plotValues[this.plotValues.length - 1][1] === this.inp1.value + ) { + return + } + this.plotValues.push([time, this.inp1.value]) + } + + /** + * @memberof Tunnel + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [ + this.direction, + this.bitWidth, + this.identifier, + ], + nodes: { + inp1: findNode(this.inp1), + }, + values: { + identifier: this.identifier, + }, + } + return data + } + + /** + * @memberof Tunnel + * function to set tunnel identifier value + * @param {string=} id - id so that every link is unique + */ + setIdentifier(id = '') { + if (id.length === 0) return + if (this.scope.tunnelList[this.identifier]) + this.scope.tunnelList[this.identifier].clean(this) + this.identifier = id + if (this.scope.tunnelList[this.identifier]) + this.scope.tunnelList[this.identifier].push(this) + else this.scope.tunnelList[this.identifier] = [this] + + // Change the bitwidth to be same as the other elements with this.identifier + if ( + this.scope.tunnelList[this.identifier] && + this.scope.tunnelList[this.identifier].length > 1 + ) { + this.bitWidth = this.inp1.bitWidth = + this.scope.tunnelList[this.identifier][0].bitWidth + } + + const len = this.identifier.length + if (len === 1) this.xSize = 40 + else if (len > 1 && len < 4) this.xSize = 20 + else this.xSize = 0 + this.setBounds() + } + + /** + * @memberof Tunnel + * delete the tunnel element + */ + delete() { + this.scope.Tunnel.clean(this) + this.scope.tunnelList[this.identifier].clean(this) + super.delete() + } + + /** + * @memberof Tunnel + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.lineWidth = correctWidth(1) + const xx = this.x + const yy = this.y + + let xRotate = 0 + let yRotate = 0 + if (this.direction === 'LEFT') { + xRotate = 0 + yRotate = 0 + } else if (this.direction === 'RIGHT') { + xRotate = 120 - this.xSize + yRotate = 0 + } else if (this.direction === 'UP') { + xRotate = 60 - this.xSize / 2 + yRotate = -20 + } else { + xRotate = 60 - this.xSize / 2 + yRotate = 20 + } + + rect2( + ctx, + -120 + xRotate + this.xSize, + -20 + yRotate, + 120 - this.xSize, + 40, + xx, + yy, + 'RIGHT' + ) + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.fillStyle = colors['hover_select'] + } + ctx.fill() + ctx.stroke() + + ctx.font = '14px Raleway' + this.xOff = ctx.measureText(this.identifier).width + ctx.beginPath() + rect2( + ctx, + -105 + xRotate + this.xSize, + -11 + yRotate, + this.xOff + 10, + 23, + xx, + yy, + 'RIGHT' + ) + ctx.fillStyle = '#eee' + ctx.strokeStyle = '#ccc' + ctx.fill() + ctx.stroke() + + ctx.beginPath() + ctx.textAlign = 'center' + ctx.fillStyle = 'black' + fillText( + ctx, + this.identifier, + xx - 100 + this.xOff / 2 + xRotate + this.xSize, + yy + 6 + yRotate, + 14 + ) + ctx.fill() + + ctx.beginPath() + ctx.font = '30px Raleway' + ctx.textAlign = 'center' + ctx.fillStyle = ['blue', 'red'][+(this.inp1.value === undefined)] + if (this.inp1.value !== undefined) { + fillText( + ctx, + this.inp1.value.toString(16), + xx - 23 + xRotate, + yy + 8 + yRotate, + 25 + ) + } else { + fillText(ctx, 'x', xx - 23 + xRotate, yy + 8 + yRotate, 25) + } + ctx.fill() + } + + /** + * Overridden from CircuitElement. Sets all paired tunnels' bitwidths for syncronization + * @param {number} bitWidth - bitwidth to set to + */ + newBitWidth(bitWidth) { + for (let tunnel of this.scope.tunnelList[this.identifier]) { + if (tunnel.fixedBitWidth) continue + if (tunnel.bitWidth === undefined) continue + if (tunnel.bitWidth < 1) continue + tunnel.bitWidth = bitWidth + for (let i = 0; i < tunnel.nodeList.length; i++) { + tunnel.nodeList[i].bitWidth = bitWidth + } + } + } +} + +/** + * @memberof Tunnel + * Help Tip + * @type {string} + * @category modules + */ +Tunnel.prototype.tooltipText = 'Tunnel ToolTip : Tunnel Selected.' +Tunnel.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/8misc?id=tunnel' + +Tunnel.prototype.overrideDirectionRotation = true + +/** + * @memberof Tunnel + * Mutable properties of the element + * @type {JSON} + * @category modules + */ +Tunnel.prototype.mutableProperties = { + identifier: { + name: 'Debug Flag identifier', + type: 'text', + maxlength: '5', + func: 'setIdentifier', + }, +} +Tunnel.prototype.objectType = 'Tunnel' diff --git a/v0/src/simulator/src/modules/TwoComplement.js b/v0/src/simulator/src/modules/TwoComplement.js new file mode 100644 index 00000000..ace62ec7 --- /dev/null +++ b/v0/src/simulator/src/modules/TwoComplement.js @@ -0,0 +1,102 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, fillText, drawCircle2 } from '../canvasApi' +import { changeInputSize } from '../modules' +/** + * @class + * TwoComplement + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +import { colors } from '../themer/themer' + +export default class TwoComplement extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT', bitWidth = 1) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['TwoComplement'].push(this); + */ + this.rectangleObject = false + this.setDimensions(15, 15) + this.inp1 = new Node(-10, 0, 0, this, this.bitWidth, 'input stream') + this.output1 = new Node(20, 0, 1, this, this.bitWidth, "2's complement") + } + + /** + * @memberof TwoComplement + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.direction, this.bitWidth], + nodes: { + output1: findNode(this.output1), + inp1: findNode(this.inp1), + }, + } + return data + } + + /** + * @memberof TwoComplement + * resolve output values based on inputData + */ + resolve() { + if (this.isResolvable() === false) { + return + } + let output = + ((~this.inp1.value >>> 0) << (32 - this.bitWidth)) >>> + (32 - this.bitWidth) + output += 1 + this.output1.value = + (output << (32 - this.bitWidth)) >>> (32 - this.bitWidth) + simulationArea.simulationQueue.add(this.output1) + } + + /** + * @memberof TwoComplement + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.strokeStyle = colors['stroke'] + ctx.lineWidth = correctWidth(3) + const xx = this.x + const yy = this.y + ctx.beginPath() + ctx.fillStyle = 'black' + fillText(ctx, "2'", xx, yy, 10) + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.beginPath() + drawCircle2(ctx, 5, 0, 15, xx, yy, this.direction) + ctx.stroke() + } + + generateVerilog() { + return `assign ${this.output1.verilogLabel} = ~${this.inp1.verilogLabel} + 1;` + } +} + +/** + * @memberof TwoComplement + * Help Tip + * @type {string} + * @category modules + */ +TwoComplement.prototype.tooltipText = + "Two's Complement Tooltip : Calculates the two's complement" +TwoComplement.prototype.objectType = 'TwoComplement' diff --git a/v0/src/simulator/src/modules/VariableLed.js b/v0/src/simulator/src/modules/VariableLed.js new file mode 100644 index 00000000..a32cb5aa --- /dev/null +++ b/v0/src/simulator/src/modules/VariableLed.js @@ -0,0 +1,205 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { + correctWidth, + lineTo, + moveTo, + arc, + drawCircle2, + colorToRGBA, + validColor, +} from '../canvasApi' +import { changeInputSize } from '../modules' +/** + * @class + * VariableLed + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @category modules + */ +import { colors } from '../themer/themer' + +export default class VariableLed extends CircuitElement { + constructor(x, y, scope = globalScope, color = 'Red') { + // Calling base class constructor + + super(x, y, scope, 'UP', 8) + /* this is done in this.baseSetup() now + this.scope['VariableLed'].push(this); + */ + this.rectangleObject = false + this.setDimensions(10, 20) + this.inp1 = new Node(-40, 0, 0, this, 8) + this.directionFixed = true + this.fixedBitWidth = true + this.color = color + const temp = colorToRGBA(this.color) + this.actualColor = `rgba(${temp[0]},${temp[1]},${temp[2]},${0.8})` + } + + /** + * @memberof VariableLed + * fn to change the color of VariableLed + * @return {JSON} + */ + changeColor(value) { + if (validColor(value)) { + if (value.trim() === '') { + this.color = 'Red' + this.actualColor = 'rgba(255, 0, 0, 1)' + } else { + this.color = value + const temp = colorToRGBA(value) + this.actualColor = `rgba(${temp[0]},${temp[1]},${temp[2]}, ${temp[3]})` + } + } + } + + /** + * @memberof VariableLed + * fn to set the rgba value of the color + * @return {JSON} + */ + createRGBA(alpha = 1) { + const len = this.actualColor.length + const temp = this.actualColor + .split('') + .slice(5, len - 1) + .join('') + .split(',') + if (alpha.toString() === 'NaN') + return `rgba(${temp[0]}, ${temp[1]}, ${temp[2]}, 1)` + return `rgba(${temp[0]},${temp[1]},${temp[2]},${alpha})` + } + + /** + * @memberof VariableLed + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.color], + nodes: { + inp1: findNode(this.inp1), + }, + } + return data + } + + /** + * @memberof VariableLed + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + + const xx = this.x + const yy = this.y + + ctx.strokeStyle = '#353535' + ctx.lineWidth = correctWidth(3) + ctx.beginPath() + moveTo(ctx, -20, 0, xx, yy, this.direction) + lineTo(ctx, -40, 0, xx, yy, this.direction) + ctx.stroke() + const c = this.inp1.value + const alpha = c / 255 + ctx.strokeStyle = '#090a0a' + ctx.fillStyle = [this.createRGBA(alpha), 'rgba(227, 228, 229, 0.8)'][ + (c === undefined || c === 0) + 0 + ] + ctx.lineWidth = correctWidth(1) + + ctx.beginPath() + + moveTo(ctx, -20, -9, xx, yy, this.direction) + lineTo(ctx, 0, -9, xx, yy, this.direction) + arc(ctx, 0, 0, 9, -Math.PI / 2, Math.PI / 2, xx, yy, this.direction) + lineTo(ctx, -20, 9, xx, yy, this.direction) + /* lineTo(ctx,-18,12,xx,yy,this.direction); + arc(ctx,0,0,Math.sqrt(468),((Math.PI/2) + Math.acos(12/Math.sqrt(468))),((-Math.PI/2) - Math.asin(18/Math.sqrt(468))),xx,yy,this.direction); + + */ + lineTo(ctx, -20, -9, xx, yy, this.direction) + ctx.stroke() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + } + + // Draws the element in the subcuircuit. Used in layout mode + subcircuitDraw(xOffset = 0, yOffset = 0) { + var ctx = simulationArea.context + + var xx = this.subcircuitMetadata.x + xOffset + var yy = this.subcircuitMetadata.y + yOffset + + var c = this.inp1.value + var alpha = c / 255 + ctx.strokeStyle = '#090a0a' + ctx.fillStyle = [this.createRGBA(alpha), 'rgba(227, 228, 229, 0.8)'][ + (c === undefined || c == 0) + 0 + ] + ctx.lineWidth = correctWidth(1) + + ctx.beginPath() + drawCircle2(ctx, 0, 0, 6, xx, yy, this.direction) + ctx.stroke() + + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected == this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = 'rgba(255, 255, 32,0.8)' + ctx.fill() + } + + generateVerilog() { + return ` + always @ (*) + $display("VeriableLed:${this.inp1.verilogLabel}=%d", ${this.inp1.verilogLabel});` + } +} + +/** + * @memberof VariableLed + * Help Tip + * @type {string} + * @category modules + */ +VariableLed.prototype.tooltipText = + 'Variable Led ToolTip: Variable LED inputs an 8 bit value and glows with a proportional intensity.' + +/** + * @memberof VariableLed + * Mutable properties of the element + * @type {JSON} + * @category modules + */ +VariableLed.prototype.mutableProperties = { + color: { + name: 'Color: ', + type: 'text', + func: 'changeColor', + }, +} + +/** + * @memberof VariableLed + * Help URL + * @type {string} + * @category modules + */ +VariableLed.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/3output?id=variableled' +VariableLed.prototype.objectType = 'VariableLed' +VariableLed.prototype.canShowInSubcircuit = true diff --git a/v0/src/simulator/src/modules/XnorGate.js b/v0/src/simulator/src/modules/XnorGate.js new file mode 100644 index 00000000..dd93e734 --- /dev/null +++ b/v0/src/simulator/src/modules/XnorGate.js @@ -0,0 +1,199 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { + correctWidth, + bezierCurveTo, + moveTo, + arc2, + drawCircle2, +} from '../canvasApi' +import { gateGenerateVerilog } from '../utils' + +import { changeInputSize } from '../modules' +/** + * @class + * XnorGate + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} inputLength - number of input nodes + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +import { colors } from '../themer/themer' + +export default class XnorGate extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + inputs = 2, + bitWidth = 1 + ) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['XnorGate'].push(this); + */ + this.rectangleObject = false + this.setDimensions(15, 20) + + this.inp = [] + this.inputSize = inputs + + if (inputs % 2 === 1) { + for (let i = 0; i < inputs / 2 - 1; i++) { + const a = new Node(-20, -10 * (i + 1), 0, this) + this.inp.push(a) + } + let a = new Node(-20, 0, 0, this) + this.inp.push(a) + for (let i = inputs / 2 + 1; i < inputs; i++) { + a = new Node(-20, 10 * (i + 1 - inputs / 2 - 1), 0, this) + this.inp.push(a) + } + } else { + for (let i = 0; i < inputs / 2; i++) { + const a = new Node(-20, -10 * (i + 1), 0, this) + this.inp.push(a) + } + for (let i = inputs / 2; i < inputs; i++) { + const a = new Node(-20, 10 * (i + 1 - inputs / 2), 0, this) + this.inp.push(a) + } + } + this.output1 = new Node(30, 0, 1, this) + } + + /** + * @memberof XnorGate + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [ + this.direction, + this.inputSize, + this.bitWidth, + ], + nodes: { + inp: this.inp.map(findNode), + output1: findNode(this.output1), + }, + } + return data + } + + /** + * @memberof XnorGate + * resolve output values based on inputData + */ + resolve() { + let result = this.inp[0].value || 0 + if (this.isResolvable() === false) { + return + } + for (let i = 1; i < this.inputSize; i++) + result ^= this.inp[i].value || 0 + result = + ((~result >>> 0) << (32 - this.bitWidth)) >>> (32 - this.bitWidth) + this.output1.value = result + simulationArea.simulationQueue.add(this.output1) + } + + /** + * @memberof XnorGate + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.strokeStyle = colors['stroke'] + ctx.lineWidth = correctWidth(3) + + const xx = this.x + const yy = this.y + ctx.beginPath() + ctx.fillStyle = colors['fill'] + moveTo(ctx, -10, -20, xx, yy, this.direction, true) + bezierCurveTo(0, -20, +15, -10, 20, 0, xx, yy, this.direction) + bezierCurveTo( + 0 + 15, + 0 + 10, + 0, + 0 + 20, + -10, + +20, + xx, + yy, + this.direction + ) + bezierCurveTo(0, 0, 0, 0, -10, -20, xx, yy, this.direction) + // arc(ctx, 0, 0, -20, (-Math.PI / 2), (Math.PI / 2), xx, yy, this.direction); + ctx.closePath() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + ctx.beginPath() + arc2( + ctx, + -35, + 0, + 25, + 1.7 * Math.PI, + 0.3 * Math.PI, + xx, + yy, + this.direction + ) + ctx.stroke() + ctx.beginPath() + drawCircle2(ctx, 25, 0, 5, xx, yy, this.direction) + ctx.stroke() + } + + generateVerilog() { + return gateGenerateVerilog.call(this, '^', true) + } +} + +/** + * @memberof XnorGate + * @type {boolean} + * @category modules + */ +XnorGate.prototype.alwaysResolve = true + +/** + * @memberof XnorGate + * Help Tip + * @type {string} + * @category modules + */ +XnorGate.prototype.tooltipText = + 'Xnor Gate ToolTip : Logical complement of the XOR gate' + +/** + * @memberof XnorGate + * function to change input nodes of the element + * @category modules + */ +XnorGate.prototype.changeInputSize = changeInputSize + +/** + * @memberof XnorGate + * @type {string} + * @category modules + */ +XnorGate.prototype.verilogType = 'xnor' +XnorGate.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/4gates?id=xnor-gate' +XnorGate.prototype.objectType = 'XnorGate' diff --git a/v0/src/simulator/src/modules/XorGate.js b/v0/src/simulator/src/modules/XorGate.js new file mode 100644 index 00000000..74c5b0d8 --- /dev/null +++ b/v0/src/simulator/src/modules/XorGate.js @@ -0,0 +1,187 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, bezierCurveTo, moveTo, arc2 } from '../canvasApi' +import { changeInputSize } from '../modules' +import { gateGenerateVerilog } from '../utils' + +/** + * @class + * XorGate + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} inputs - number of input nodes + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +import { colors } from '../themer/themer' + +export default class XorGate extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + inputs = 2, + bitWidth = 1 + ) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['XorGate'].push(this); + */ + this.rectangleObject = false + this.setDimensions(15, 20) + + this.inp = [] + this.inputSize = inputs + + if (inputs % 2 === 1) { + for (let i = 0; i < inputs / 2 - 1; i++) { + const a = new Node(-20, -10 * (i + 1), 0, this) + this.inp.push(a) + } + let a = new Node(-20, 0, 0, this) + this.inp.push(a) + for (let i = inputs / 2 + 1; i < inputs; i++) { + a = new Node(-20, 10 * (i + 1 - inputs / 2 - 1), 0, this) + this.inp.push(a) + } + } else { + for (let i = 0; i < inputs / 2; i++) { + const a = new Node(-20, -10 * (i + 1), 0, this) + this.inp.push(a) + } + for (let i = inputs / 2; i < inputs; i++) { + const a = new Node(-20, 10 * (i + 1 - inputs / 2), 0, this) + this.inp.push(a) + } + } + this.output1 = new Node(20, 0, 1, this) + } + + /** + * @memberof XorGate + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [ + this.direction, + this.inputSize, + this.bitWidth, + ], + nodes: { + inp: this.inp.map(findNode), + output1: findNode(this.output1), + }, + } + return data + } + + /** + * @memberof XorGate + * resolve output values based on inputData + */ + resolve() { + let result = this.inp[0].value || 0 + if (this.isResolvable() === false) { + return + } + for (let i = 1; i < this.inputSize; i++) + result ^= this.inp[i].value || 0 + + this.output1.value = result + simulationArea.simulationQueue.add(this.output1) + } + + /** + * @memberof XorGate + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.strokeStyle = colors['stroke'] + ctx.lineWidth = correctWidth(3) + + const xx = this.x + const yy = this.y + ctx.beginPath() + ctx.fillStyle = colors['fill'] + moveTo(ctx, -10, -20, xx, yy, this.direction, true) + bezierCurveTo(0, -20, +15, -10, 20, 0, xx, yy, this.direction) + bezierCurveTo( + 0 + 15, + 0 + 10, + 0, + 0 + 20, + -10, + +20, + xx, + yy, + this.direction + ) + bezierCurveTo(0, 0, 0, 0, -10, -20, xx, yy, this.direction) + // arc(ctx, 0, 0, -20, (-Math.PI / 2), (Math.PI / 2), xx, yy, this.direction); + ctx.closePath() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + ctx.beginPath() + arc2( + ctx, + -35, + 0, + 25, + 1.7 * Math.PI, + 0.3 * Math.PI, + xx, + yy, + this.direction + ) + ctx.stroke() + } + + generateVerilog() { + return gateGenerateVerilog.call(this, '^') + } +} + +/** + * @memberof XorGate + * Help Tip + * @type {string} + * @category modules + */ +XorGate.prototype.tooltipText = 'Xor Gate Tooltip : Implements an exclusive OR.' + +/** + * @memberof XorGate + * @type {boolean} + * @category modules + */ +XorGate.prototype.alwaysResolve = true + +/** + * @memberof XorGate + * function to change input nodes of the element + * @category modules + */ +XorGate.prototype.changeInputSize = changeInputSize + +/** + * @memberof XorGate + * @type {string} + * @category modules + */ +XorGate.prototype.verilogType = 'xor' +XorGate.prototype.helplink = 'https://docs.circuitverse.org/#/chapter4/4gates?id=xor-gate' +XorGate.prototype.objectType = 'XorGate' diff --git a/v0/src/simulator/src/modules/verilogDivider.js b/v0/src/simulator/src/modules/verilogDivider.js new file mode 100644 index 00000000..cd6acaf5 --- /dev/null +++ b/v0/src/simulator/src/modules/verilogDivider.js @@ -0,0 +1,127 @@ +/* eslint-disable no-bitwise */ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' + +/** + * @class + * verilogDivider + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. modules + * @category modules + */ +export default class verilogDivider extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + bitWidth = 1, + outputBitWidth = 1 + ) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['verilogDivider'].push(this); + */ + this.setDimensions(20, 20) + this.outputBitWidth = outputBitWidth + this.inpA = new Node(-20, -10, 0, this, this.bitWidth, 'A') + this.inpB = new Node(-20, 0, 0, this, this.bitWidth, 'B') + this.quotient = new Node( + 20, + 0, + 1, + this, + this.outputBitWidth, + 'Quotient' + ) + this.remainder = new Node( + 20, + 0, + 1, + this, + this.outputBitWidth, + 'Remainder' + ) + } + + /** + * @memberof verilogDivider + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [ + this.direction, + this.bitWidth, + this.outputBitWidth, + ], + nodes: { + inpA: findNode(this.inpA), + inpB: findNode(this.inpB), + quotient: findNode(this.quotient), + remainder: findNode(this.remainder), + }, + } + return data + } + + /** + * @memberof verilogDivider + * Checks if the element is resolvable + * @return {boolean} + */ + isResolvable() { + return this.inpA.value !== undefined && this.inpB.value !== undefined + } + + /** + * @memberof verilogDivider + * function to change bitwidth of the element + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + this.bitWidth = bitWidth + this.inpA.bitWidth = bitWidth + this.inpB.bitWidth = bitWidth + this.quotient.bitWidth = bitWidth + this.remainder.bitWidth = bitWidth + } + + /** + * @memberof verilogDivider + * resolve output values based on inputData + */ + resolve() { + if (this.isResolvable() === false) { + return + } + const quotient = this.inpA.value / this.inpB.value + const remainder = this.inpA.value % this.inpB.value + this.remainder.value = + (remainder << (32 - this.outputBitWidth)) >>> + (32 - this.outputBitWidth) + this.quotient.value = + (quotient << (32 - this.outputBitWidth)) >>> + (32 - this.outputBitWidth) + simulationArea.simulationQueue.add(this.quotient) + simulationArea.simulationQueue.add(this.remainder) + } +} + +/** + * @memberof verilogDivider + * Help Tip + * @type {string} + * @category modules + */ +verilogDivider.prototype.tooltipText = + 'verilogDivider ToolTip : Performs addition of numbers.' +verilogDivider.prototype.helplink = + 'https://docs.circuitverse.org/#/miscellaneous?id=verilogDivider' +verilogDivider.prototype.objectType = 'verilogDivider' diff --git a/v0/src/simulator/src/modules/verilogMultiplier.js b/v0/src/simulator/src/modules/verilogMultiplier.js new file mode 100644 index 00000000..d428c036 --- /dev/null +++ b/v0/src/simulator/src/modules/verilogMultiplier.js @@ -0,0 +1,106 @@ +/* eslint-disable no-bitwise */ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' + +/** + * @class + * verilogMultiplier + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. modules + * @category modules + */ +export default class verilogMultiplier extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + bitWidth = 1, + outputBitWidth = 1 + ) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['verilogMultiplier'].push(this); + */ + this.setDimensions(20, 20) + this.outputBitWidth = outputBitWidth + this.inpA = new Node(-20, -10, 0, this, this.bitWidth, 'A') + this.inpB = new Node(-20, 0, 0, this, this.bitWidth, 'B') + this.product = new Node(20, 0, 1, this, this.outputBitWidth, 'Product') + } + + /** + * @memberof verilogMultiplier + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [ + this.direction, + this.bitWidth, + this.outputBitWidth, + ], + nodes: { + inpA: findNode(this.inpA), + inpB: findNode(this.inpB), + product: findNode(this.product), + }, + } + return data + } + + /** + * @memberof verilogMultiplier + * Checks if the element is resolvable + * @return {boolean} + */ + isResolvable() { + return this.inpA.value !== undefined && this.inpB.value !== undefined + } + + /** + * @memberof verilogMultiplier + * function to change bitwidth of the element + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + this.bitWidth = bitWidth + this.inpA.bitWidth = bitWidth + this.inpB.bitWidth = bitWidth + this.product.bitWidth = bitWidth + } + + /** + * @memberof verilogMultiplier + * resolve output values based on inputData + */ + resolve() { + if (this.isResolvable() === false) { + return + } + const product = this.inpA.value * this.inpB.value + + this.product.value = + (product << (32 - this.outputBitWidth)) >>> + (32 - this.outputBitWidth) + simulationArea.simulationQueue.add(this.product) + } +} + +/** + * @memberof verilogMultiplier + * Help Tip + * @type {string} + * @category modules + */ +verilogMultiplier.prototype.tooltipText = + 'verilogMultiplier ToolTip : Performs addition of numbers.' +verilogMultiplier.prototype.helplink = + 'https://docs.circuitverse.org/#/miscellaneous?id=verilogMultiplier' +verilogMultiplier.prototype.objectType = 'verilogMultiplier' diff --git a/v0/src/simulator/src/modules/verilogPower.js b/v0/src/simulator/src/modules/verilogPower.js new file mode 100644 index 00000000..ded6fdc1 --- /dev/null +++ b/v0/src/simulator/src/modules/verilogPower.js @@ -0,0 +1,106 @@ +/* eslint-disable no-bitwise */ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' + +/** + * @class + * verilogPower + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. modules + * @category modules + */ +export default class verilogPower extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + bitWidth = 1, + outputBitWidth = 1 + ) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['verilogPower'].push(this); + */ + this.setDimensions(20, 20) + this.outputBitWidth = outputBitWidth + this.inpA = new Node(-20, -10, 0, this, this.bitWidth, 'A') + this.inpB = new Node(-20, 0, 0, this, this.bitWidth, 'B') + this.answer = new Node(20, 0, 1, this, this.outputBitWidth, 'Answer') + } + + /** + * @memberof verilogPower + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [ + this.direction, + this.bitWidth, + this.outputBitWidth, + ], + nodes: { + inpA: findNode(this.inpA), + inpB: findNode(this.inpB), + answer: findNode(this.answer), + }, + } + return data + } + + /** + * @memberof verilogPower + * Checks if the element is resolvable + * @return {boolean} + */ + isResolvable() { + return this.inpA.value !== undefined && this.inpB.value !== undefined + } + + /** + * @memberof verilogPower + * function to change bitwidth of the element + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + this.bitWidth = bitWidth + this.inpA.bitWidth = bitWidth + this.inpB.bitWidth = bitWidth + this.answer.bitWidth = bitWidth + } + + /** + * @memberof verilogPower + * resolve output values based on inputData + */ + resolve() { + if (this.isResolvable() === false) { + return + } + const answer = Math.pow(this.inpA.value, this.inpB.value) + + this.answer.value = + (answer << (32 - this.outputBitWidth)) >>> + (32 - this.outputBitWidth) + simulationArea.simulationQueue.add(this.answer) + } +} + +/** + * @memberof verilogPower + * Help Tip + * @type {string} + * @category modules + */ +verilogPower.prototype.tooltipText = + 'verilogPower ToolTip : Performs addition of numbers.' +verilogPower.prototype.helplink = + 'https://docs.circuitverse.org/#/miscellaneous?id=verilogPower' +verilogPower.prototype.objectType = 'verilogPower' diff --git a/v0/src/simulator/src/modules/verilogShiftLeft.js b/v0/src/simulator/src/modules/verilogShiftLeft.js new file mode 100644 index 00000000..65a61a4c --- /dev/null +++ b/v0/src/simulator/src/modules/verilogShiftLeft.js @@ -0,0 +1,108 @@ +/* eslint-disable no-bitwise */ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' + +/** + * @class + * verilogShiftLeft + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. modules + * @category modules + */ +export default class verilogShiftLeft extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + bitWidth = 1, + outputBitWidth = 1 + ) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['verilogShiftLeft'].push(this); + */ + this.setDimensions(20, 20) + this.outputBitWidth = outputBitWidth + this.inp1 = new Node(-20, -10, 0, this, this.bitWidth, 'Input') + this.shiftInp = new Node(-20, 0, 0, this, this.bitWidth, 'ShiftInput') + this.output1 = new Node(20, 0, 1, this, this.outputBitWidth, 'Output') + } + + /** + * @memberof verilogShiftLeft + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [ + this.direction, + this.bitWidth, + this.outputBitWidth, + ], + nodes: { + inp1: findNode(this.inp1), + shiftInp: findNode(this.shiftInp), + output1: findNode(this.output1), + }, + } + return data + } + + /** + * @memberof verilogShiftLeft + * Checks if the element is resolvable + * @return {boolean} + */ + isResolvable() { + return ( + this.inp1.value !== undefined && this.shiftInp.value !== undefined + ) + } + + /** + * @memberof verilogShiftLeft + * function to change bitwidth of the element + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + this.bitWidth = bitWidth + this.inp1.bitWidth = bitWidth + this.shiftInp.bitWidth = bitWidth + this.output1.bitWidth = bitWidth + } + + /** + * @memberof verilogShiftLeft + * resolve output values based on inputData + */ + resolve() { + if (this.isResolvable() === false) { + return + } + const output1 = this.inp1.value << this.shiftInp.value + + this.output1.value = + (output1 << (32 - this.outputBitWidth)) >>> + (32 - this.outputBitWidth) + simulationArea.simulationQueue.add(this.output1) + } +} + +/** + * @memberof verilogShiftLeft + * Help Tip + * @type {string} + * @category modules + */ +verilogShiftLeft.prototype.tooltipText = + 'verilogShiftLeft ToolTip : Performs addition of numbers.' +verilogShiftLeft.prototype.helplink = + 'https://docs.circuitverse.org/#/miscellaneous?id=verilogShiftLeft' +verilogShiftLeft.prototype.objectType = 'verilogShiftLeft' diff --git a/v0/src/simulator/src/modules/verilogShiftRight.js b/v0/src/simulator/src/modules/verilogShiftRight.js new file mode 100644 index 00000000..a37332fb --- /dev/null +++ b/v0/src/simulator/src/modules/verilogShiftRight.js @@ -0,0 +1,108 @@ +/* eslint-disable no-bitwise */ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' + +/** + * @class + * verilogShiftRight + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. modules + * @category modules + */ +export default class verilogShiftRight extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + bitWidth = 1, + outputBitWidth = 1 + ) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['verilogShiftRight'].push(this); + */ + this.setDimensions(20, 20) + this.outputBitWidth = outputBitWidth + this.inp1 = new Node(-20, -10, 0, this, this.bitWidth, 'Input') + this.shiftInp = new Node(-20, 0, 0, this, this.bitWidth, 'ShiftInput') + this.output1 = new Node(20, 0, 1, this, this.outputBitWidth, 'Output') + } + + /** + * @memberof verilogShiftRight + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [ + this.direction, + this.bitWidth, + this.outputBitWidth, + ], + nodes: { + inp1: findNode(this.inp1), + shiftInp: findNode(this.shiftInp), + output1: findNode(this.output1), + }, + } + return data + } + + /** + * @memberof verilogShiftRight + * Checks if the element is resolvable + * @return {boolean} + */ + isResolvable() { + return ( + this.inp1.value !== undefined && this.shiftInp.value !== undefined + ) + } + + /** + * @memberof verilogShiftRight + * function to change bitwidth of the element + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + this.bitWidth = bitWidth + this.inp1.bitWidth = bitWidth + this.shiftInp.bitWidth = bitWidth + this.output1.bitWidth = bitWidth + } + + /** + * @memberof verilogShiftRight + * resolve output values based on inputData + */ + resolve() { + if (this.isResolvable() === false) { + return + } + const output1 = this.inp1.value >> this.shiftInp.value + + this.output1.value = + (output1 << (32 - this.outputBitWidth)) >>> + (32 - this.outputBitWidth) + simulationArea.simulationQueue.add(this.output1) + } +} + +/** + * @memberof verilogShiftRight + * Help Tip + * @type {string} + * @category modules + */ +verilogShiftRight.prototype.tooltipText = + 'verilogShiftRight ToolTip : Performs addition of numbers.' +verilogShiftRight.prototype.helplink = + 'https://docs.circuitverse.org/#/miscellaneous?id=verilogShiftRight' +verilogShiftRight.prototype.objectType = 'verilogShiftRight' diff --git a/v0/src/simulator/src/node.js b/v0/src/simulator/src/node.js new file mode 100644 index 00000000..696a664b --- /dev/null +++ b/v0/src/simulator/src/node.js @@ -0,0 +1,1021 @@ +/* eslint-disable import/no-cycle */ +import { drawCircle, drawLine, arc } from './canvasApi' +import simulationArea from './simulationArea' +import { distance, showError } from './utils' +import { + renderCanvas, + scheduleUpdate, + wireToBeCheckedSet, + updateSimulationSet, + updateCanvasSet, + forceResetNodesSet, + canvasMessageData, +} from './engine' +import Wire from './wire' +// import { colors } from './themer/themer'; +import { colors } from './themer/themer' + +/** + * Constructs all the connections of Node node + * @param {Node} node - node to be constructed + * @param {JSON} data - the saved data which is used to load + * @category node + */ +export function constructNodeConnections(node, data) { + for (var i = 0; i < data.connections.length; i++) { + if ( + !node.connections.contains(node.scope.allNodes[data.connections[i]]) + ) + node.connect(node.scope.allNodes[data.connections[i]]) + } +} + +/** + * Fn to replace node by node @ index in global Node List - used when loading + * @param {Node} node - node to be replaced + * @param {number} index - index of node to be replaced + * @category node + */ +export function replace(node, index) { + if (index == -1) { + return node + } + var { scope } = node + var { parent } = node + parent.nodeList.clean(node) + node.delete() + node = scope.allNodes[index] + node.parent = parent + parent.nodeList.push(node) + node.updateRotation() + return node +} +function rotate(x1, y1, dir) { + if (dir == 'LEFT') { + return [-x1, y1] + } + if (dir == 'DOWN') { + return [y1, x1] + } + if (dir == 'UP') { + return [y1, -x1] + } + return [x1, y1] +} + +export function extractBits(num, start, end) { + return (num << (32 - end)) >>> (32 - (end - start + 1)) +} + +export function bin2dec(binString) { + return parseInt(binString, 2) +} + +export function dec2bin(dec, bitWidth = undefined) { + // only for positive nos + var bin = dec.toString(2) + if (bitWidth == undefined) return bin + return '0'.repeat(bitWidth - bin.length) + bin +} + +/** + * find Index of a node + * @param {Node} x - Node to be dound + * @category node + */ +export function findNode(x) { + return x.scope.allNodes.indexOf(x) +} + +/** + * function makes a node according to data providede + * @param {JSON} data - the data used to load a Project + * @param {Scope} scope - scope to which node has to be loaded + * @category node + */ +export function loadNode(data, scope) { + var n = new Node( + data.x, + data.y, + data.type, + scope.root, + data.bitWidth, + data.label + ) +} + +/** + * get Node in index x in scope and set parent + * @param {Node} x - the desired node + * @param {Scope} scope - the scope + * @param {CircuitElement} parent - The parent of node + * @category node + */ +function extractNode(x, scope, parent) { + var n = scope.allNodes[x] + n.parent = parent + return n +} + +// output node=1 +// input node=0 +// intermediate node =2 + +window.NODE_INPUT = 0 +window.NODE_OUTPUT = 1 +window.NODE_INTERMEDIATE = 2 +/** + * used to give id to a node. + * @type {number} + * @category node + */ +var uniqueIdCounter = 10 + +/** + * This class is responsible for all the Nodes.Nodes are connected using Wires + * Nodes are of 3 types; + * NODE_INPUT = 0; + * NODE_OUTPUT = 1; + * NODE_INTERMEDIATE = 2; + * Input and output nodes belong to some CircuitElement(it's parent) + * @param {number} x - x coord of Node + * @param {number} y - y coord of Node + * @param {number} type - type of node + * @param {CircuitElement} parent - parent element + * @param {?number} bitWidth - the bits of node in input and output nodes + * @param {string=} label - label for a node + * @category node + */ +export default class Node { + constructor(x, y, type, parent, bitWidth = undefined, label = '') { + // Should never raise, but just in case + if (isNaN(x) || isNaN(y)) { + this.delete() + showError('Fatal error occurred') + return + } + + forceResetNodesSet(true) + + this.objectType = 'Node' + this.subcircuitOverride = false + this.id = `node${uniqueIdCounter}` + uniqueIdCounter++ + this.parent = parent + if (type != 2 && this.parent.nodeList !== undefined) { + this.parent.nodeList.push(this) + } + + if (bitWidth == undefined) { + this.bitWidth = parent.bitWidth + } else { + this.bitWidth = bitWidth + } + this.label = label + this.prevx = undefined + this.prevy = undefined + this.leftx = x + this.lefty = y + this.x = x + this.y = y + + this.type = type + this.connections = new Array() + this.value = undefined + this.radius = 5 + this.clicked = false + this.hover = false + this.wasClicked = false + this.scope = this.parent.scope + /** + * @type {string} + * value of this.prev is + * 'a' : whenever a node is not being dragged this.prev is 'a' + * 'x' : when node is being dragged horizontally + * 'y' : when node is being dragged vertically + */ + this.prev = 'a' + this.count = 0 + this.highlighted = false + + // This fn is called during rotations and setup + this.refresh() + + if (this.type == 2) { + this.parent.scope.nodes.push(this) + } + + this.parent.scope.allNodes.push(this) + + this.queueProperties = { + inQueue: false, + time: undefined, + index: undefined, + } + } + + /** + * @param {string} - new label + * Function to set label + */ + setLabel(label) { + this.label = label // || ""; + } + + /** + * function to convert a node to intermediate node + */ + converToIntermediate() { + this.type = 2 + this.x = this.absX() + this.y = this.absY() + this.parent = this.scope.root + this.scope.nodes.push(this) + } + + /** + * Helper fuction to move a node. Sets up some variable which help in changing node. + */ + startDragging() { + this.oldx = this.x + this.oldy = this.y + } + + /** + * Helper fuction to move a node. + */ + drag() { + this.x = this.oldx + simulationArea.mouseX - simulationArea.mouseDownX + this.y = this.oldy + simulationArea.mouseY - simulationArea.mouseDownY + } + + /** + * Funciton for saving a node + */ + saveObject() { + if (this.type == 2) { + this.leftx = this.x + this.lefty = this.y + } + var data = { + x: this.leftx, + y: this.lefty, + type: this.type, + bitWidth: this.bitWidth, + label: this.label, + connections: [], + } + for (var i = 0; i < this.connections.length; i++) { + data.connections.push(findNode(this.connections[i])) + } + return data + } + + /** + * helper function to help rotating parent + */ + updateRotation() { + var x + var y + ;[x, y] = rotate(this.leftx, this.lefty, this.parent.direction) + this.x = x + this.y = y + } + + /** + * Refreshes a node after roation of parent + */ + refresh() { + this.updateRotation() + for (var i = 0; i < this.connections.length; i++) { + this.connections[i].connections.clean(this) + } + this.connections = [] + } + + /** + * gives absolute x position of the node + */ + absX() { + return this.x + this.parent.x + } + + /** + * gives absolute y position of the node + */ + absY() { + return this.y + this.parent.y + } + + /** + * update the scope of a node + */ + updateScope(scope) { + this.scope = scope + if (this.type == 2) this.parent = scope.root + } + + /** + * return true if node is connected or not connected but false if undefined. + */ + isResolvable() { + return this.value != undefined + } + + /** + * function used to reset the nodes + */ + reset() { + this.value = undefined + this.highlighted = false + } + + /** + * function to connect two nodes. + */ + connect(n) { + if (n == this) return + if (n.connections.contains(this)) return + var w = new Wire(this, n, this.parent.scope) + this.connections.push(n) + n.connections.push(this) + + updateCanvasSet(true) + updateSimulationSet(true) + scheduleUpdate() + } + + /** + * connects but doesnt draw the wire between nodes + */ + connectWireLess(n) { + if (n == this) return + if (n.connections.contains(this)) return + this.connections.push(n) + n.connections.push(this) + + updateCanvasSet(true) + updateSimulationSet(true) + scheduleUpdate() + } + + /** + * disconnecting two nodes connected wirelessly + */ + disconnectWireLess(n) { + this.connections.clean(n) + n.connections.clean(this) + } + + /** + * function to resolve a node + */ + resolve() { + // Remove Propogation of values (TriState) + if (this.value == undefined) { + for (var i = 0; i < this.connections.length; i++) { + if (this.connections[i].value !== undefined) { + this.connections[i].value = undefined + simulationArea.simulationQueue.add(this.connections[i]) + } + } + + if (this.type == NODE_INPUT) { + if (this.parent.objectType == 'Splitter') { + this.parent.removePropagation() + } else if (this.parent.isResolvable()) { + simulationArea.simulationQueue.add(this.parent) + } else { + this.parent.removePropagation() + } + } + + if (this.type == NODE_OUTPUT && !this.subcircuitOverride) { + if ( + this.parent.isResolvable() && + !this.parent.queueProperties.inQueue + ) { + if (this.parent.objectType == 'TriState') { + if (this.parent.state.value) { + simulationArea.simulationQueue.add(this.parent) + } + } else { + simulationArea.simulationQueue.add(this.parent) + } + } + } + + return + } + + if (this.type == 0) { + if (this.parent.isResolvable()) { + simulationArea.simulationQueue.add(this.parent) + } + } + + for (var i = 0; i < this.connections.length; i++) { + const node = this.connections[i] + + if (node.value != this.value || node.bitWidth != this.bitWidth) { + if ( + node.type == 1 && + node.value != undefined && + node.parent.objectType != 'TriState' && + !(node.subcircuitOverride && node.scope != this.scope) && // Subcircuit Input Node Output Override + node.parent.objectType != 'SubCircuit' + ) { + // Subcircuit Output Node Override + this.highlighted = true + node.highlighted = true + var circuitName = node.scope.name + var circuitElementName = node.parent.objectType + showError( + `Contention Error: ${this.value} and ${node.value} at ${circuitElementName} in ${circuitName}` + ) + } else if (node.bitWidth == this.bitWidth || node.type == 2) { + if ( + node.parent.objectType == 'TriState' && + node.value != undefined && + node.type == 1 + ) { + if (node.parent.state.value) { + simulationArea.contentionPending.push(node.parent) + } + } + + node.bitWidth = this.bitWidth + node.value = this.value + simulationArea.simulationQueue.add(node) + } else { + this.highlighted = true + node.highlighted = true + showError( + `BitWidth Error: ${this.bitWidth} and ${node.bitWidth}` + ) + } + } + } + } + + /** + * this function checks if hover over the node + */ + checkHover() { + if (!simulationArea.mouseDown) { + if (simulationArea.hover == this) { + this.hover = this.isHover() + if (!this.hover) { + simulationArea.hover = undefined + this.showHover = false + } + } else if (!simulationArea.hover) { + this.hover = this.isHover() + if (this.hover) { + simulationArea.hover = this + } else { + this.showHover = false + } + } else { + this.hover = false + this.showHover = false + } + } + } + + /** + * this function draw a node + */ + draw() { + const ctx = simulationArea.context + // + const color = colors['color_wire_draw'] + if (this.clicked) { + if (this.prev == 'x') { + drawLine( + ctx, + this.absX(), + this.absY(), + simulationArea.mouseX, + this.absY(), + color, + 3 + ) + drawLine( + ctx, + simulationArea.mouseX, + this.absY(), + simulationArea.mouseX, + simulationArea.mouseY, + color, + 3 + ) + } else if (this.prev == 'y') { + drawLine( + ctx, + this.absX(), + this.absY(), + this.absX(), + simulationArea.mouseY, + color, + 3 + ) + drawLine( + ctx, + this.absX(), + simulationArea.mouseY, + simulationArea.mouseX, + simulationArea.mouseY, + color, + 3 + ) + } else if ( + Math.abs(this.x + this.parent.x - simulationArea.mouseX) > + Math.abs(this.y + this.parent.y - simulationArea.mouseY) + ) { + drawLine( + ctx, + this.absX(), + this.absY(), + simulationArea.mouseX, + this.absY(), + color, + 3 + ) + } else { + drawLine( + ctx, + this.absX(), + this.absY(), + this.absX(), + simulationArea.mouseY, + color, + 3 + ) + } + } + var colorNode = colors['stroke'] + const colorNodeConnect = colors['color_wire_con'] + const colorNodePow = colors['color_wire_pow'] + const colorNodeLose = colors['color_wire_lose'] + const colorNodeSelected = colors['node'] + + if (this.bitWidth == 1) + colorNode = [colorNodeConnect, colorNodePow][this.value] + if (this.value == undefined) colorNode = colorNodeLose + if (this.type == 2) this.checkHover() + if (this.type == 2) { + drawCircle(ctx, this.absX(), this.absY(), 3, colorNode) + } else { + drawCircle(ctx, this.absX(), this.absY(), 3, colorNodeSelected) + } + + if ( + this.highlighted || + simulationArea.lastSelected == this || + (this.isHover() && + !simulationArea.selected && + !simulationArea.shiftDown) || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.strokeStyle = colorNodeSelected + ctx.beginPath() + ctx.lineWidth = 3 + arc( + ctx, + this.x, + this.y, + 8, + 0, + Math.PI * 2, + this.parent.x, + this.parent.y, + 'RIGHT' + ) + ctx.closePath() + ctx.stroke() + } + + if (this.hover || simulationArea.lastSelected == this) { + if (this.showHover || simulationArea.lastSelected == this) { + canvasMessageData.x = this.absX() + canvasMessageData.y = this.absY() - 15 + if (this.type == 2) { + var v = 'X' + if (this.value !== undefined) { + v = this.value.toString(16) + } + if (this.label.length) { + canvasMessageData.string = `${this.label} : ${v}` + } else { + canvasMessageData.string = v + } + } else if (this.label.length) { + canvasMessageData.string = this.label + } + } else { + setTimeout(() => { + if (simulationArea.hover) + simulationArea.hover.showHover = true + updateCanvasSet(true) + renderCanvas(globalScope) + }, 400) + } + } + } + + /** + * checks if a node has been deleted + */ + checkDeleted() { + if (this.deleted) this.delete() + if (this.connections.length == 0 && this.type == 2) this.delete() + } + + /** + * used to update nodes if there is a event like click or hover on the node. + * many booleans are used to check if certain properties are to be updated. + */ + update() { + if (embed) return + + if (this == simulationArea.hover) simulationArea.hover = undefined + this.hover = this.isHover() + + if (!simulationArea.mouseDown) { + if (this.absX() != this.prevx || this.absY() != this.prevy) { + // Connect to any node + this.prevx = this.absX() + this.prevy = this.absY() + this.nodeConnect() + } + } + + if (this.hover) { + simulationArea.hover = this + } + + if ( + simulationArea.mouseDown && + ((this.hover && !simulationArea.selected) || + simulationArea.lastSelected == this) + ) { + simulationArea.selected = true + simulationArea.lastSelected = this + this.clicked = true + } else { + this.clicked = false + } + + if (!this.wasClicked && this.clicked) { + this.wasClicked = true + this.prev = 'a' + if (this.type == 2) { + if ( + !simulationArea.shiftDown && + simulationArea.multipleObjectSelections.contains(this) + ) { + for ( + var i = 0; + i < simulationArea.multipleObjectSelections.length; + i++ + ) { + simulationArea.multipleObjectSelections[ + i + ].startDragging() + } + } + + if (simulationArea.shiftDown) { + simulationArea.lastSelected = undefined + if ( + simulationArea.multipleObjectSelections.contains(this) + ) { + simulationArea.multipleObjectSelections.clean(this) + } else { + simulationArea.multipleObjectSelections.push(this) + } + } else { + simulationArea.lastSelected = this + } + } + } else if (this.wasClicked && this.clicked) { + if ( + !simulationArea.shiftDown && + simulationArea.multipleObjectSelections.contains(this) + ) { + for ( + var i = 0; + i < simulationArea.multipleObjectSelections.length; + i++ + ) { + simulationArea.multipleObjectSelections[i].drag() + } + } + if (this.type == 2) { + if ( + this.connections.length == 1 && + this.connections[0].absX() == simulationArea.mouseX && + this.absX() == simulationArea.mouseX + ) { + this.y = simulationArea.mouseY - this.parent.y + this.prev = 'a' + return + } + if ( + this.connections.length == 1 && + this.connections[0].absY() == simulationArea.mouseY && + this.absY() == simulationArea.mouseY + ) { + this.x = simulationArea.mouseX - this.parent.x + this.prev = 'a' + return + } + if ( + this.connections.length == 1 && + this.connections[0].absX() == this.absX() && + this.connections[0].absY() == this.absY() + ) { + this.connections[0].clicked = true + this.connections[0].wasClicked = true + simulationArea.lastSelected = this.connections[0] + this.delete() + return + } + } + + if ( + this.prev == 'a' && + distance( + simulationArea.mouseX, + simulationArea.mouseY, + this.absX(), + this.absY() + ) >= 10 + ) { + if ( + Math.abs(this.x + this.parent.x - simulationArea.mouseX) > + Math.abs(this.y + this.parent.y - simulationArea.mouseY) + ) { + this.prev = 'x' + } else { + this.prev = 'y' + } + } else if ( + this.prev == 'x' && + this.absY() == simulationArea.mouseY + ) { + this.prev = 'a' + } else if ( + this.prev == 'y' && + this.absX() == simulationArea.mouseX + ) { + this.prev = 'a' + } + } else if (this.wasClicked && !this.clicked) { + this.wasClicked = false + + if ( + simulationArea.mouseX == this.absX() && + simulationArea.mouseY == this.absY() + ) { + return // no new node situation + } + + var x1 + var y1 + var x2 + var y2 + var flag = 0 + var n1 + var n2 + + // (x,y) present node, (x1,y1) node 1 , (x2,y2) node 2 + // n1 - node 1, n2 - node 2 + // node 1 may or may not be there + // flag = 0 - node 2 only + // flag = 1 - node 1 and node 2 + x2 = simulationArea.mouseX + y2 = simulationArea.mouseY + const x = this.absX() + const y = this.absY() + + if (x != x2 && y != y2) { + // Rare Exception Cases + if ( + this.prev == 'a' && + distance( + simulationArea.mouseX, + simulationArea.mouseY, + this.absX(), + this.absY() + ) >= 10 + ) { + if ( + Math.abs( + this.x + this.parent.x - simulationArea.mouseX + ) > + Math.abs(this.y + this.parent.y - simulationArea.mouseY) + ) { + this.prev = 'x' + } else { + this.prev = 'y' + } + } + + flag = 1 + if (this.prev == 'x') { + x1 = x2 + y1 = y + } else if (this.prev == 'y') { + y1 = y2 + x1 = x + } + } + + if (flag == 1) { + for (var i = 0; i < this.parent.scope.allNodes.length; i++) { + if ( + x1 == this.parent.scope.allNodes[i].absX() && + y1 == this.parent.scope.allNodes[i].absY() + ) { + n1 = this.parent.scope.allNodes[i] + break + } + } + + if (n1 == undefined) { + n1 = new Node(x1, y1, 2, this.scope.root) + for (var i = 0; i < this.parent.scope.wires.length; i++) { + if (this.parent.scope.wires[i].checkConvergence(n1)) { + this.parent.scope.wires[i].converge(n1) + break + } + } + } + this.connect(n1) + } + + for (var i = 0; i < this.parent.scope.allNodes.length; i++) { + if ( + x2 == this.parent.scope.allNodes[i].absX() && + y2 == this.parent.scope.allNodes[i].absY() + ) { + n2 = this.parent.scope.allNodes[i] + break + } + } + + if (n2 == undefined) { + n2 = new Node(x2, y2, 2, this.scope.root) + for (var i = 0; i < this.parent.scope.wires.length; i++) { + if (this.parent.scope.wires[i].checkConvergence(n2)) { + this.parent.scope.wires[i].converge(n2) + break + } + } + } + if (flag == 0) this.connect(n2) + else n1.connect(n2) + if (simulationArea.lastSelected == this) + simulationArea.lastSelected = n2 + } + + if (this.type == 2 && simulationArea.mouseDown == false) { + if (this.connections.length == 2) { + if ( + this.connections[0].absX() == this.connections[1].absX() || + this.connections[0].absY() == this.connections[1].absY() + ) { + this.connections[0].connect(this.connections[1]) + this.delete() + } + } else if (this.connections.length == 0) this.delete() + } + } + + /** + * function delete a node + */ + delete() { + updateSimulationSet(true) + this.deleted = true + this.parent.scope.allNodes.clean(this) + this.parent.scope.nodes.clean(this) + + this.parent.scope.root.nodeList.clean(this) // Hope this works! - Can cause bugs + + if (simulationArea.lastSelected == this) + simulationArea.lastSelected = undefined + for (var i = 0; i < this.connections.length; i++) { + this.connections[i].connections.clean(this) + this.connections[i].checkDeleted() + } + wireToBeCheckedSet(1) + forceResetNodesSet(true) + scheduleUpdate() + } + + isClicked() { + return ( + this.absX() == simulationArea.mouseX && + this.absY() == simulationArea.mouseY + ) + } + + isHover() { + return ( + this.absX() == simulationArea.mouseX && + this.absY() == simulationArea.mouseY + ) + } + + /** + * if input nodde: it resolves the parent + * else: it adds all the nodes onto the stack + * and they are processed to generate verilog + */ + nodeConnect() { + var x = this.absX() + var y = this.absY() + var n + + for (var i = 0; i < this.parent.scope.allNodes.length; i++) { + if ( + this != this.parent.scope.allNodes[i] && + x == this.parent.scope.allNodes[i].absX() && + y == this.parent.scope.allNodes[i].absY() + ) { + n = this.parent.scope.allNodes[i] + if (this.type == 2) { + for (var j = 0; j < this.connections.length; j++) { + n.connect(this.connections[j]) + } + this.delete() + } else { + this.connect(n) + } + + break + } + } + + if (n == undefined) { + for (var i = 0; i < this.parent.scope.wires.length; i++) { + if (this.parent.scope.wires[i].checkConvergence(this)) { + var n = this + if (this.type != 2) { + n = new Node( + this.absX(), + this.absY(), + 2, + this.scope.root + ) + this.connect(n) + } + this.parent.scope.wires[i].converge(n) + break + } + } + } + } + + processVerilog() { + if (this.type == NODE_INPUT) { + if (this.parent.isVerilogResolvable()) { + this.scope.stack.push(this.parent) + } + } + + for (var i = 0; i < this.connections.length; i++) { + if (this.connections[i].verilogLabel != this.verilogLabel) { + this.connections[i].verilogLabel = this.verilogLabel + this.scope.stack.push(this.connections[i]) + } + } + } +} + +/** + * delay in simulation of the node. + * @category node + */ +Node.prototype.propagationDelay = 0 + +/** + * backward comaptibilty? + * @category node + */ +Node.prototype.cleanDelete = Node.prototype.delete + +Node.prototype.processVerilog = function () { + if (this.type == NODE_INPUT) { + this.scope.stack.push(this.parent) + } + for (var i = 0; i < this.connections.length; i++) { + if (this.connections[i].verilogLabel != this.verilogLabel) { + this.connections[i].verilogLabel = this.verilogLabel + this.scope.stack.push(this.connections[i]) + } + } +} diff --git a/v0/src/simulator/src/plotArea.js b/v0/src/simulator/src/plotArea.js new file mode 100644 index 00000000..79bf2582 --- /dev/null +++ b/v0/src/simulator/src/plotArea.js @@ -0,0 +1,521 @@ +import simulationArea from './simulationArea' +import { convertors } from './utils' + +var DPR = window.devicePixelRatio || 1 + +// Helper function to scale to display +function sh(x) { + return x * DPR +} + +/** + * Spec Constants + * Size Spec Diagram - https://app.diagrams.net/#G1HFoesRvNyDap95sNJswTy3nH09emDriC + * NOTE: Since DPR is set on page load, changing of screen in runtime will not work well + * @TODO + * - Support for color themes + * - Replace constants with functions? - Can support Zoom in and Zoom out of canvas then + */ +var frameInterval = 100 // Refresh rate +var timeLineHeight = sh(20) +var padding = sh(2) +var plotHeight = sh(20) +var waveFormPadding = sh(5) +var waveFormHeight = plotHeight - 2 * waveFormPadding +var flagLabelWidth = sh(75) +var cycleWidth = sh(30) +var backgroundColor = 'black' +var foregroundColor = '#eee' +var textColor = 'black' +var waveFormColor = 'cyan' +var timeLineStartX = flagLabelWidth + padding + +// Helper functions for canvas + +function getFullHeight(flagCount) { + return timeLineHeight + (plotHeight + padding) * flagCount +} + +function getFlagStartY(flagIndex) { + return getFullHeight(flagIndex) + padding +} + +function getCycleStartX(cycleNumber) { + return timeLineStartX + (cycleNumber - plotArea.cycleOffset) * cycleWidth +} + +/** + * @type {Object} plotArea + * @category plotArea + */ +const plotArea = { + cycleOffset: 0, // Determines timeline offset + DPR: window.devicePixelRatio || 1, + canvas: document.getElementById('plotArea'), + cycleCount: 0, // Number of clock cycles passed + cycleTime: 0, // Time of last clock tick (in ms) + executionStartTime: 0, // Last time play() function ran in engine.js (in ms) + autoScroll: true, // if true, timeline will scroll to keep current time in display + width: 0, // canvas width + height: 0, // canvas height + unitUsed: 0, // Number of simulation units used by the engine + cycleUnit: 1000, // Number of simulation units per cycle + mouseDown: false, + mouseX: 0, // Current mouse position + mouseDownX: 0, // position of mouse when clicked + mouseDownTime: 0, // time when mouse clicked (in ms) + // Reset timeline to 0 and resume autoscroll + reset() { + this.cycleCount = 0 + this.cycleTime = new Date().getTime() + for (var i = 0; i < globalScope.Flag.length; i++) { + globalScope.Flag[i].plotValues = [ + [0, globalScope.Flag[i].inp1.value], + ] + globalScope.Flag[i].cachedIndex = 0 + } + this.unitUsed = 0 + this.resume() + this.resize() + }, + // Resume autoscroll + resume() { + this.autoScroll = true + }, + // pause autoscroll + pause() { + this.autoScroll = false + plotArea.scrollAcc = 0 + }, + // Called every time clock is ticked + nextCycle() { + this.cycleCount++ + this.cycleTime = new Date().getTime() + }, + // Called everytime play() function is execute in engine.js + setExecutionTime() { + this.executionStartTime = new Date().getTime() + }, + // Scale timeline up + zoomIn() { + cycleWidth += sh(2) + }, + // Scale timeline down + zoomOut() { + cycleWidth -= sh(2) + }, + // download as image + download() { + var img = this.canvas.toDataURL(`image/png`) + const anchor = document.createElement('a') + anchor.href = img + anchor.download = `waveform.png` + anchor.click() + }, + // update canvas size to use full screen + resize() { + var oldHeight = this.height + var oldWidth = this.width + this.width = document.getElementById('plot').clientWidth * this.DPR + this.height = getFullHeight(globalScope.Flag.length) + if (oldHeight == this.height && oldWidth == this.width) return + this.canvas.width = this.width + this.canvas.height = this.height + this.plot() + }, + // Setup function, called on page load + setup() { + this.canvas = document.getElementById('plotArea') + if (!embed) { + this.ctx = this.canvas.getContext('2d') + } + this.timeOutPlot = setInterval(() => { + plotArea.plot() + }, frameInterval) + this.reset() + }, + // Used to resolve analytical time in clock cycles + getPlotTime(timeUnit) { + var time = this.cycleCount // Current cycle count + time += timeUnit / this.cycleUnit // Add propagation delay + // For user interactions like buttons - calculate time since clock tick + var timePeriod = simulationArea.timePeriod + var executionDelay = this.executionStartTime - this.cycleTime + var delayFraction = executionDelay / timePeriod + // Add time since clock tick + time += delayFraction + return time + }, + // Auto calibrate clock simulation units based on usage + calibrate() { + var recommendedUnit = Math.max(20, Math.round(this.unitUsed * 3)) + this.cycleUnit = recommendedUnit + $('#timing-diagram-units').val(recommendedUnit) + this.reset() + }, + // Get current time in clock cycles + getCurrentTime() { + var time = this.cycleCount + var timePeriod = simulationArea.timePeriod + var delay = new Date().getTime() - this.cycleTime + var delayFraction = delay / timePeriod + time += delayFraction + return time + }, + update() { + this.resize() + var dangerColor = '#dc5656' + var normalColor = '#42b983' + this.unitUsed = Math.max( + this.unitUsed, + simulationArea.simulationQueue.time + ) + var unitUsed = this.unitUsed + var units = this.cycleUnit + var utilization = Math.round((unitUsed * 10000) / units) / 100 + $('#timing-diagram-log').html( + `Utilization: ${Math.round(unitUsed)} Units (${utilization}%)` + ) + if (utilization >= 90 || utilization <= 10) { + var recommendedUnit = Math.max(20, Math.round(unitUsed * 3)) + $('#timing-diagram-log').append( + ` Recommended Units: ${recommendedUnit}` + ) + $('#timing-diagram-log').css('background-color', dangerColor) + if (utilization >= 100) { + this.clear() + return + } + } else { + $('#timing-diagram-log').css('background-color', normalColor) + } + + var width = this.width + var endTime = this.getCurrentTime() + + if (this.autoScroll) { + // Formula used: + // (endTime - x) * cycleWidth = width - timeLineStartX; + // x = endTime - (width - timeLineStartX) / cycleWidth + this.cycleOffset = Math.max( + 0, + endTime - (width - timeLineStartX) / cycleWidth + ) + } else if (!plotArea.mouseDown) { + // Scroll + this.cycleOffset -= plotArea.scrollAcc + // Friction + plotArea.scrollAcc *= 0.95 + // No negative numbers allowed, so negative scroll to 0 + if (this.cycleOffset < 0) plotArea.scrollAcc = this.cycleOffset / 5 + // Set position to 0, to avoid infinite scrolling + if (Math.abs(this.cycleOffset) < 0.01) this.cycleOffset = 0 + } + }, + render() { + var { width, height } = this + this.canvas.height = height + this.canvas.width = width + var endTime = this.getCurrentTime() + // Reset canvas + this.clear() + var ctx = this.ctx + + // Background Color + ctx.fillStyle = backgroundColor + ctx.fillRect(0, 0, width, height) + + ctx.lineWidth = sh(1) + ctx.font = `${sh(15)}px Raleway` + ctx.textAlign = 'left' + + // Timeline + ctx.fillStyle = foregroundColor + ctx.fillRect(timeLineStartX, 0, this.canvas.width, timeLineHeight) + ctx.fillRect(0, 0, flagLabelWidth, timeLineHeight) + ctx.fillStyle = textColor + ctx.fillText('Time', sh(5), timeLineHeight * 0.7) + + // Timeline numbers + ctx.font = `${sh(9)}px Times New Roman` + ctx.strokeStyle = textColor + ctx.textAlign = 'center' + for ( + var i = Math.floor(plotArea.cycleOffset); + getCycleStartX(i) <= width; + i++ + ) { + var x = getCycleStartX(i) + // Large ticks + number + // @TODO - collapse number if it doesn't fit + if (x >= timeLineStartX) { + ctx.fillText(`${i}`, x, timeLineHeight - sh(15) / 2) + ctx.beginPath() + ctx.moveTo(x, timeLineHeight - sh(5)) + ctx.lineTo(x, timeLineHeight) + ctx.stroke() + } + // Small ticks + for (var j = 1; j < 5; j++) { + var x1 = x + Math.round((j * cycleWidth) / 5) + if (x1 >= timeLineStartX) { + ctx.beginPath() + ctx.moveTo(x1, timeLineHeight - sh(2)) + ctx.lineTo(x1, timeLineHeight) + ctx.stroke() + } + } + } + + // Flag Labels + ctx.textAlign = 'left' + for (var i = 0; i < globalScope.Flag.length; i++) { + var startHeight = getFlagStartY(i) + ctx.fillStyle = foregroundColor + ctx.fillRect(0, startHeight, flagLabelWidth, plotHeight) + ctx.fillStyle = textColor + ctx.fillText( + globalScope.Flag[i].identifier, + sh(5), + startHeight + plotHeight * 0.7 + ) + } + + // Waveform Status Flags + const WAVEFORM_NOT_STARTED = 0 + const WAVEFORM_STARTED = 1 + const WAVEFORM_OVER = 3 + + // Waveform + ctx.strokeStyle = waveFormColor + ctx.textAlign = 'center' + var endX = Math.min(getCycleStartX(endTime), width) + + for (var i = 0; i < globalScope.Flag.length; i++) { + var plotValues = globalScope.Flag[i].plotValues + var startHeight = getFlagStartY(i) + waveFormPadding + var yTop = startHeight + var yMid = startHeight + waveFormHeight / 2 + var yBottom = startHeight + waveFormHeight + var state = WAVEFORM_NOT_STARTED + var prevY + + // Find correct index to start plotting from + var j = 0 + // Using caching for optimal performance + if (globalScope.Flag[i].cachedIndex) { + j = globalScope.Flag[i].cachedIndex + } + // Move to beyond timeLineStartX + while ( + j + 1 < plotValues.length && + getCycleStartX(plotValues[j][0]) < timeLineStartX + ) { + j++ + } + // Move to just before timeLineStartX + while (j > 0 && getCycleStartX(plotValues[j][0]) > timeLineStartX) { + j-- + } + // Cache index + globalScope.Flag[i].cachedIndex = j + + // Plot + for (; j < plotValues.length; j++) { + var x = getCycleStartX(plotValues[j][0]) + + // Handle out of bound + if (x < timeLineStartX) { + if (j + 1 != plotValues.length) { + // Next one also is out of bound, so skip this one completely + var x1 = getCycleStartX(plotValues[j + 1][0]) + if (x1 < timeLineStartX) continue + } + x = timeLineStartX + } + + var value = plotValues[j][1] + if (value === undefined) { + if (state == WAVEFORM_STARTED) { + ctx.stroke() + } + state = WAVEFORM_NOT_STARTED + continue + } + if (globalScope.Flag[i].bitWidth == 1) { + if (x > endX) break + var y = value == 1 ? yTop : yBottom + if (state == WAVEFORM_NOT_STARTED) { + // Start new plot + state = WAVEFORM_STARTED + ctx.beginPath() + ctx.moveTo(x, y) + } else { + ctx.lineTo(x, prevY) + ctx.lineTo(x, y) + } + prevY = y + } else { + var endX + if (j + 1 == plotValues.length) { + endX = getCycleStartX(endTime) + } else { + endX = getCycleStartX(plotValues[j + 1][0]) + } + var smallOffset = waveFormHeight / 2 + ctx.beginPath() + ctx.moveTo(endX, yMid) + ctx.lineTo(endX - smallOffset, yTop) + ctx.lineTo(x + smallOffset, yTop) + ctx.lineTo(x, yMid) + ctx.lineTo(x + smallOffset, yBottom) + ctx.lineTo(endX - smallOffset, yBottom) + ctx.closePath() + ctx.stroke() + + // Text position + // Clamp start and end are within the screen + var x1 = Math.max(x, timeLineStartX) + var x2 = Math.min(endX, width) + var textPositionX = (x1 + x2) / 2 + + ctx.font = `${sh(9)}px Times New Roman` + ctx.fillStyle = 'white' + ctx.fillText( + convertors.dec2hex(value), + textPositionX, + yMid + sh(3) + ) + } + if (x > width) { + state = WAVEFORM_OVER + ctx.stroke() + break + } + } + if (state == WAVEFORM_STARTED) { + if (globalScope.Flag[i].bitWidth == 1) { + ctx.lineTo(endX, prevY) + } + ctx.stroke() + } + } + }, + // Driver function to render and update + plot() { + if (embed) return + if (globalScope.Flag.length === 0) { + this.canvas.width = 0 + this.canvas.height = 0 + return + } + + this.update() + this.render() + }, + clear() { + this.ctx.clearRect(0, 0, plotArea.canvas.width, plotArea.canvas.height) + }, +} +export default plotArea + +/** + * type {Object} timingDiagramButtonActions + * @category plotArea + * @description Actions for buttons in timing diagram + * @property {function} smallHeight - Decrease waveform height + * @property {function} largeHeight - Increase waveform height + */ + +const timingDiagramButtonActions = { + smallHeight() { + if (plotHeight >= sh(20)) { + plotHeight -= sh(5) + waveFormHeight = plotHeight - 2 * waveFormPadding + } + }, + largeHeight() { + if (plotHeight < sh(50)) { + plotHeight += sh(5) + waveFormHeight = plotHeight - 2 * waveFormPadding + } + }, +} + +export { timingDiagramButtonActions } + +export function setupTimingListeners() { + // $('.timing-diagram-smaller').on('click', () => { + // $('#plot').width(Math.max($('#plot').width() - 20, 560)) + // plotArea.resize() + // }) + // $('.timing-diagram-larger').on('click', () => { + // $('#plot').width($('#plot').width() + 20) + // plotArea.resize() + // }) + // $('.timing-diagram-small-height').on('click', () => { + // if (plotHeight >= sh(20)) { + // plotHeight -= sh(5) + // waveFormHeight = plotHeight - 2 * waveFormPadding + // } + // }) + // $('.timing-diagram-large-height').on('click', () => { + // if (plotHeight < sh(50)) { + // plotHeight += sh(5) + // waveFormHeight = plotHeight - 2 * waveFormPadding + // } + // }) + // $('.timing-diagram-reset').on('click', () => { + // plotArea.reset() + // }) + // $('.timing-diagram-calibrate').on('click', () => { + // plotArea.calibrate() + // }) + // $('.timing-diagram-resume').on('click', () => { + // plotArea.resume() + // }) + // $('.timing-diagram-pause').on('click', () => { + // plotArea.pause() + // }) + // $('.timing-diagram-download').on('click', () => { + // plotArea.download() + // }) + // $('.timing-diagram-zoom-in').on('click', () => { + // plotArea.zoomIn() + // }) + // $('.timing-diagram-zoom-out').on('click', () => { + // plotArea.zoomOut() + // }) + // $('#timing-diagram-units').on('change paste keyup', function () { + // var timeUnits = parseInt($(this).val(), 10) + // if (isNaN(timeUnits) || timeUnits < 1) return + // plotArea.cycleUnit = timeUnits + // }) + document.getElementById('plotArea').addEventListener('mousedown', (e) => { + var rect = plotArea.canvas.getBoundingClientRect() + var x = sh(e.clientX - rect.left) + plotArea.scrollAcc = 0 + plotArea.autoScroll = false + plotArea.mouseDown = true + plotArea.mouseX = x + plotArea.mouseDownX = x + plotArea.mouseDownTime = new Date().getTime() + }) + document.getElementById('plotArea').addEventListener('mouseup', (e) => { + plotArea.mouseDown = false + var time = new Date().getTime() - plotArea.mouseDownTime + var offset = (plotArea.mouseX - plotArea.mouseDownX) / cycleWidth + plotArea.scrollAcc = (offset * frameInterval) / time + }) + + document.getElementById('plotArea').addEventListener('mousemove', (e) => { + var rect = plotArea.canvas.getBoundingClientRect() + var x = sh(e.clientX - rect.left) + if (plotArea.mouseDown) { + plotArea.cycleOffset -= (x - plotArea.mouseX) / cycleWidth + plotArea.mouseX = x + } else { + plotArea.mouseDown = false + } + }) +} diff --git a/v0/src/simulator/src/quinMcCluskey.js b/v0/src/simulator/src/quinMcCluskey.js new file mode 100644 index 00000000..f15041db --- /dev/null +++ b/v0/src/simulator/src/quinMcCluskey.js @@ -0,0 +1,227 @@ +// Algorithm used for Combinational Analysis + +export default function BooleanMinimize( + numVarsArg, + minTermsArg, + dontCaresArg = [] +) { + var __result + + Object.defineProperties(this, { + minTerms: { + value: minTermsArg, + enumerable: false, + writable: false, + configurable: true, + }, + + dontCares: { + value: dontCaresArg, + enumerable: false, + writable: false, + configurable: true, + }, + + numVars: { + value: numVarsArg, + enumerable: false, + writable: false, + configurable: true, + }, + + result: { + enumerable: true, + configurable: true, + get: function () { + if (__result === undefined) { + __result = BooleanMinimize.prototype.solve.call(this) + } + + return __result + }, + set: function () { + throw new Error('result cannot be assigned a value') + }, + }, + }) +} + +BooleanMinimize.prototype.solve = function () { + function dec_to_binary_string(n) { + var str = n.toString(2) + + while (str.length != this.numVars) { + str = '0' + str + } + + return str + } + + function num_set_bits(s) { + var ans = 0 + for (let i = 0; i < s.length; ++i) if (s[i] === '1') ans++ + return ans + } + + function get_prime_implicants(allTerms) { + var table = [] + var primeImplicants = new Set() + var reduced + + while (1) { + for (let i = 0; i <= this.numVars; ++i) table[i] = new Set() + for (let i = 0; i < allTerms.length; ++i) + table[num_set_bits(allTerms[i])].add(allTerms[i]) + + allTerms = [] + reduced = new Set() + + for (let i = 0; i < table.length - 1; ++i) { + for (let str1 of table[i]) { + for (let str2 of table[i + 1]) { + let diff = -1 + + for (let j = 0; j < this.numVars; ++j) { + if (str1[j] != str2[j]) { + if (diff === -1) { + diff = j + } else { + diff = -1 + break + } + } + } + + if (diff !== -1) { + allTerms.push( + str1.slice(0, diff) + '-' + str1.slice(diff + 1) + ) + reduced.add(str1) + reduced.add(str2) + } + } + } + } + + for (let t of table) { + for (let str of t) { + if (!reduced.has(str)) primeImplicants.add(str) + } + } + + if (!reduced.size) break + } + + return primeImplicants + } + + function get_essential_prime_implicants(primeImplicants, minTerms) { + var table = [], + column + + function check_if_similar(minTerm, primeImplicant) { + for (let i = 0; i < primeImplicant.length; ++i) { + if ( + primeImplicant[i] !== '-' && + minTerm[i] !== primeImplicant[i] + ) + return false + } + + return true + } + + function get_complexity(terms) { + var complexity = terms.length + + for (let t of terms) { + for (let i = 0; i < t.length; ++i) { + if (t[i] !== '-') { + complexity++ + if (t[i] === '0') complexity++ + } + } + } + + return complexity + } + + function isSubset(sub, sup) { + for (let i of sub) { + if (!sup.has(i)) return false + } + + return true + } + + for (let m of minTerms) { + column = [] + + for (let i = 0; i < primeImplicants.length; ++i) { + if (check_if_similar(m, primeImplicants[i])) { + column.push(i) + } + } + + table.push(column) + } + + var possibleSets = [], + tempSets + + for (let i of table[0]) { + possibleSets.push(new Set([i])) + } + + for (let i = 1; i < table.length; ++i) { + tempSets = [] + for (let s of possibleSets) { + for (let p of table[i]) { + let x = new Set(s) + x.add(p) + let append = true + + for (let j = tempSets.length - 1; j >= 0; --j) { + if (isSubset(x, tempSets[j])) { + tempSets.splice(j, 1) + } else { + append = false + } + } + + if (append) { + tempSets.push(x) + } + } + + possibleSets = tempSets + } + } + + var essentialImplicants, + minComplexity = 1e9 + + for (let s of possibleSets) { + let p = [] + for (let i of s) { + p.push(primeImplicants[i]) + } + let comp = get_complexity(p) + if (comp < minComplexity) { + essentialImplicants = p + minComplexity = comp + } + } + + return essentialImplicants + } + + var minTerms = this.minTerms.map(dec_to_binary_string.bind(this)) + var dontCares = this.dontCares.map(dec_to_binary_string.bind(this)) + + return get_essential_prime_implicants.call( + this, + Array.from(get_prime_implicants.call(this, minTerms.concat(dontCares))), + minTerms + ) +} diff --git a/v0/src/simulator/src/restrictedElementDiv.js b/v0/src/simulator/src/restrictedElementDiv.js new file mode 100644 index 00000000..74e1aad5 --- /dev/null +++ b/v0/src/simulator/src/restrictedElementDiv.js @@ -0,0 +1,44 @@ +export function updateRestrictedElementsList() { + if (restrictedElements.length === 0) return + + const { restrictedCircuitElementsUsed } = globalScope + let restrictedStr = '' + + restrictedCircuitElementsUsed.forEach((element) => { + restrictedStr += `${element}, ` + }) + + if (restrictedStr === '') { + restrictedStr = 'None' + } else { + restrictedStr = restrictedStr.slice(0, -2) + } + + document.getElementById('restrictedElementsDiv--list').innerHTML = restrictedStr +} + +export function updateRestrictedElementsInScope(scope = globalScope) { + // Do nothing if no restricted elements + if (restrictedElements.length === 0) return + + const restrictedElementsUsed = [] + restrictedElements.forEach((element) => { + if (scope[element].length > 0) { + restrictedElementsUsed.push(element) + } + }) + + scope.restrictedCircuitElementsUsed = restrictedElementsUsed + updateRestrictedElementsList() +} + +export function showRestricted() { + document.getElementById('restrictedDiv').classList.remove('display--none') + // Show no help text for restricted elements + document.getElementById('Help').classList.remove('show') + document.getElementById('restrictedDiv').innerHTML = 'The element has been restricted by mentor. Usage might lead to deduction in marks' +} + +export function hideRestricted() { + document.getElementById('restrictedDiv').classList.add('display--none') +} diff --git a/v0/src/simulator/src/sequential.js b/v0/src/simulator/src/sequential.js new file mode 100644 index 00000000..bd1740de --- /dev/null +++ b/v0/src/simulator/src/sequential.js @@ -0,0 +1,25 @@ +import { scheduleUpdate, play, updateCanvasSet } from './engine' +import simulationArea from './simulationArea' + +/** + * a global function as a helper for simulationArea.changeClockEnable + * @category sequential + */ +export function changeClockEnable(val) { + simulationArea.clockEnabled = val +} + +/** + * WIP function defined and used + * @param {number} n + * @category sequential + */ +export function runTest(n = 10) { + var t = new Date().getTime() + for (var i = 0; i < n; i++) { + clockTick() + } + updateCanvasSet(true) + play() + scheduleUpdate() +} diff --git a/v0/src/simulator/src/sequential/Clock.js b/v0/src/simulator/src/sequential/Clock.js new file mode 100644 index 00000000..86e24955 --- /dev/null +++ b/v0/src/simulator/src/sequential/Clock.js @@ -0,0 +1,95 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo } from '../canvasApi' +import { colors } from '../themer/themer' +/** + * @class + * Clock + * Clock + * @extends CircuitElement + * @param {number} x - x coord of element + * @param {number} y - y coord of element + * @param {Scope=} scope - the ciruit in which we want the Element + * @param {string=} dir - direcion in which element has to drawn + * @category sequential + */ +export default class Clock extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT') { + super(x, y, scope, dir, 1) + /* + this.scope['Clock'].push(this); + */ + this.fixedBitWidth = true + this.output1 = new Node(10, 0, 1, this, 1) + this.state = 0 + this.output1.value = this.state + this.wasClicked = false + this.interval = null + } + + customSave() { + var data = { + nodes: { + output1: findNode(this.output1), + }, + constructorParamaters: [this.direction], + } + return data + } + + resolve() { + this.output1.value = this.state + simulationArea.simulationQueue.add(this.output1) + } + + toggleState() { + // toggleState + this.state = (this.state + 1) % 2 + this.output1.value = this.state + } + + customDraw() { + var ctx = simulationArea.context + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.lineWidth = correctWidth(3) + var xx = this.x + var yy = this.y + + ctx.beginPath() + ctx.strokeStyle = [colors['color_wire_con'], colors['color_wire_pow']][ + this.state + ] + ctx.lineWidth = correctWidth(2) + if (this.state == 0) { + moveTo(ctx, -6, 0, xx, yy, 'RIGHT') + lineTo(ctx, -6, 5, xx, yy, 'RIGHT') + lineTo(ctx, 0, 5, xx, yy, 'RIGHT') + lineTo(ctx, 0, -5, xx, yy, 'RIGHT') + lineTo(ctx, 6, -5, xx, yy, 'RIGHT') + lineTo(ctx, 6, 0, xx, yy, 'RIGHT') + } else { + moveTo(ctx, -6, 0, xx, yy, 'RIGHT') + lineTo(ctx, -6, -5, xx, yy, 'RIGHT') + lineTo(ctx, 0, -5, xx, yy, 'RIGHT') + lineTo(ctx, 0, 5, xx, yy, 'RIGHT') + lineTo(ctx, 6, 5, xx, yy, 'RIGHT') + lineTo(ctx, 6, 0, xx, yy, 'RIGHT') + } + ctx.stroke() + } + + static verilogInstructions() { + return 'Clock - Use a single global clock\n' + } +} + +Clock.prototype.tooltipText = 'Clock' + +Clock.prototype.click = Clock.prototype.toggleState +Clock.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/6sequentialelements?id=clock' +Clock.prototype.objectType = 'Clock' +Clock.prototype.propagationDelay = 0 +Clock.prototype.propagationDelayFixed = true diff --git a/v0/src/simulator/src/sequential/DflipFlop.js b/v0/src/simulator/src/sequential/DflipFlop.js new file mode 100644 index 00000000..4543ba61 --- /dev/null +++ b/v0/src/simulator/src/sequential/DflipFlop.js @@ -0,0 +1,168 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, fillText } from '../canvasApi' +import { colors } from '../themer/themer' +/** + * @class + * DflipFlop + * D flip flop has 5 input nodes: + * clock, data input, preset, reset ,enable. + * @extends CircuitElement + * @param {number} x - x coord of element + * @param {number} y - y coord of element + * @param {Scope=} scope - the ciruit in which we want the Element + * @param {string=} dir - direcion in which element has to drawn + * @category sequential + */ +export default class DflipFlop extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT', bitWidth = 1) { + super(x, y, scope, dir, bitWidth) + /* + this.scope['DflipFlop'].push(this); + */ + this.directionFixed = true + this.setDimensions(20, 20) + this.rectangleObject = true + this.clockInp = new Node(-20, +10, 0, this, 1, 'Clock') + this.dInp = new Node(-20, -10, 0, this, this.bitWidth, 'D') + this.qOutput = new Node(20, -10, 1, this, this.bitWidth, 'Q') + this.qInvOutput = new Node(20, 10, 1, this, this.bitWidth, 'Q Inverse') + this.reset = new Node(10, 20, 0, this, 1, 'Asynchronous Reset') + this.preset = new Node(0, 20, 0, this, this.bitWidth, 'Preset') + this.en = new Node(-10, 20, 0, this, 1, 'Enable') + this.masterState = 0 + this.slaveState = 0 + this.prevClockState = 0 + + this.wasClicked = false + } + + /** + * WIP always resolvable? + */ + isResolvable() { + return true + // if (this.reset.value == 1) return true; + // if (this.clockInp.value != undefined && this.dInp.value != undefined) return true; + // return false; + } + + newBitWidth(bitWidth) { + this.bitWidth = bitWidth + this.dInp.bitWidth = bitWidth + this.qOutput.bitWidth = bitWidth + this.qInvOutput.bitWidth = bitWidth + this.preset.bitWidth = bitWidth + } + + /** + * @memberof DflipFlop + * On the leading edge of the clock signal (LOW-to-HIGH) the first stage, + * the “master” latches the input condition at D, while the output stage is deactivated. + * On the trailing edge of the clock signal (HIGH-to-LOW) the second “slave” stage is + * now activated, latching on to the output from the first master circuit. + * Then the output stage appears to be triggered on the negative edge of the clock pulse. + * This fuction sets the value for the node qOutput based on the previous state + * and input of the clock. We flip the bits to find qInvOutput + */ + resolve() { + if (this.reset.value == 1) { + this.masterState = this.slaveState = this.preset.value || 0 + } else if (this.en.value == 0) { + this.prevClockState = this.clockInp.value + } else if (this.en.value == 1 || this.en.connections.length == 0) { + // if(this.en.value==1) // Creating Infinite Loop, WHY ?? + if (this.clockInp.value == this.prevClockState) { + if (this.clockInp.value == 0 && this.dInp.value != undefined) { + this.masterState = this.dInp.value + } + } else if (this.clockInp.value != undefined) { + if (this.clockInp.value == 1) { + this.slaveState = this.masterState + } else if ( + this.clockInp.value == 0 && + this.dInp.value != undefined + ) { + this.masterState = this.dInp.value + } + this.prevClockState = this.clockInp.value + } + } + + if (this.qOutput.value != this.slaveState) { + this.qOutput.value = this.slaveState + this.qInvOutput.value = this.flipBits(this.slaveState) + simulationArea.simulationQueue.add(this.qOutput) + simulationArea.simulationQueue.add(this.qInvOutput) + } + } + + customSave() { + var data = { + nodes: { + clockInp: findNode(this.clockInp), + dInp: findNode(this.dInp), + qOutput: findNode(this.qOutput), + qInvOutput: findNode(this.qInvOutput), + reset: findNode(this.reset), + preset: findNode(this.preset), + en: findNode(this.en), + }, + constructorParamaters: [this.direction, this.bitWidth], + } + return data + } + + customDraw() { + var ctx = simulationArea.context + // + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.beginPath() + ctx.lineWidth = correctWidth(3) + var xx = this.x + var yy = this.y + // rect(ctx, xx - 20, yy - 20, 40, 40); + moveTo(ctx, -20, 5, xx, yy, this.direction) + lineTo(ctx, -15, 10, xx, yy, this.direction) + lineTo(ctx, -20, 15, xx, yy, this.direction) + // if ((this.b.hover&&!simulationArea.shiftDown)|| simulationArea.lastSelected == this || simulationArea.multipleObjectSelections.contains(this)) ctx.fillStyle = "rgba(255, 255, 32,0.8)";ctx.fill(); + ctx.stroke() + + ctx.beginPath() + ctx.font = '20px Raleway' + ctx.fillStyle = colors['input_text'] + ctx.textAlign = 'center' + fillText(ctx, this.slaveState.toString(16), xx, yy + 5) + ctx.fill() + } + + static moduleVerilog() { + return ` +module DflipFlop(q, q_inv, clk, d, a_rst, pre, en); + parameter WIDTH = 1; + output reg [WIDTH-1:0] q, q_inv; + input clk, a_rst, pre, en; + input [WIDTH-1:0] d; + + always @ (posedge clk or posedge a_rst) + if (a_rst) begin + q <= 'b0; + q_inv <= 'b1; + end else if (en == 0) ; + else begin + q <= d; + q_inv <= ~d; + end +endmodule + ` + } +} + +DflipFlop.prototype.tooltipText = + 'D FlipFlop ToolTip : Introduces delay in timing circuit.' +DflipFlop.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/6sequentialelements?id=d-flip-flop' + +DflipFlop.prototype.objectType = 'DflipFlop' diff --git a/v0/src/simulator/src/sequential/Dlatch.js b/v0/src/simulator/src/sequential/Dlatch.js new file mode 100644 index 00000000..7532d3db --- /dev/null +++ b/v0/src/simulator/src/sequential/Dlatch.js @@ -0,0 +1,119 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, fillText } from '../canvasApi' +/** + * @class + * Dlatch + * D latch has 2 input nodes: + * clock, data input. + * Difference between this and D - FlipFlop is + * that Flip flop must have a clock. + * @extends CircuitElement + * @param {number} x - x coord of element + * @param {number} y - y coord of element + * @param {Scope=} scope - the ciruit in which we want the Element + * @param {string=} dir - direcion in which element has to drawn + * @category sequential + */ +import { colors } from '../themer/themer' +export default class Dlatch extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT', bitWidth = 1) { + super(x, y, scope, dir, bitWidth) + /* + this.scope['Dlatch'].push(this); + */ + this.directionFixed = true + this.setDimensions(20, 20) + this.rectangleObject = true + this.clockInp = new Node(-20, +10, 0, this, 1, 'Clock') + this.dInp = new Node(-20, -10, 0, this, this.bitWidth, 'D') + this.qOutput = new Node(20, -10, 1, this, this.bitWidth, 'Q') + this.qInvOutput = new Node(20, 10, 1, this, this.bitWidth, 'Q Inverse') + // this.reset = new Node(10, 20, 0, this, 1, "Asynchronous Reset"); + // this.preset = new Node(0, 20, 0, this, this.bitWidth, "Preset"); + // this.en = new Node(-10, 20, 0, this, 1, "Enable"); + this.state = 0 + this.prevClockState = 0 + this.wasClicked = false + } + + /** + * Idea: shoould be D FF? + */ + isResolvable() { + if (this.clockInp.value != undefined && this.dInp.value != undefined) + return true + return false + } + + newBitWidth(bitWidth) { + this.bitWidth = bitWidth + this.dInp.bitWidth = bitWidth + this.qOutput.bitWidth = bitWidth + this.qInvOutput.bitWidth = bitWidth + // this.preset.bitWidth = bitWidth; + } + + /** + * @memberof Dlatch + * when the clock input is high we update the state + * qOutput is set to the state + */ + resolve() { + if (this.clockInp.value == 1 && this.dInp.value != undefined) { + this.state = this.dInp.value + } + + if (this.qOutput.value != this.state) { + this.qOutput.value = this.state + this.qInvOutput.value = this.flipBits(this.state) + simulationArea.simulationQueue.add(this.qOutput) + simulationArea.simulationQueue.add(this.qInvOutput) + } + } + + customSave() { + var data = { + nodes: { + clockInp: findNode(this.clockInp), + dInp: findNode(this.dInp), + qOutput: findNode(this.qOutput), + qInvOutput: findNode(this.qInvOutput), + // reset: findNode(this.reset), + // preset: findNode(this.preset), + // en: findNode(this.en), + }, + constructorParamaters: [this.direction, this.bitWidth], + } + return data + } + + customDraw() { + var ctx = simulationArea.context + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.beginPath() + ctx.lineWidth = correctWidth(3) + var xx = this.x + var yy = this.y + // rect(ctx, xx - 20, yy - 20, 40, 40); + moveTo(ctx, -20, 5, xx, yy, this.direction) + lineTo(ctx, -15, 10, xx, yy, this.direction) + lineTo(ctx, -20, 15, xx, yy, this.direction) + // if ((this.b.hover&&!simulationArea.shiftDown)|| simulationArea.lastSelected == this || simulationArea.multipleObjectSelections.contains(this)) ctx.fillStyle = "rgba(255, 255, 32,0.8)";ctx.fill(); + ctx.stroke() + ctx.beginPath() + ctx.font = '20px Raleway' + ctx.fillStyle = colors['input_text'] + ctx.textAlign = 'center' + fillText(ctx, this.state.toString(16), xx, yy + 5) + ctx.fill() + } +} + +Dlatch.prototype.tooltipText = 'D Latch : Single input Flip flop or D FlipFlop' +Dlatch.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/6sequentialelements?id=d-latch' + +Dlatch.prototype.objectType = 'Dlatch' diff --git a/v0/src/simulator/src/sequential/EEPROM.js b/v0/src/simulator/src/sequential/EEPROM.js new file mode 100644 index 00000000..3a3427b8 --- /dev/null +++ b/v0/src/simulator/src/sequential/EEPROM.js @@ -0,0 +1,104 @@ +import RAM from './RAM' +/** + * @class + * EEPROM Component. + * @extends CircuitElement + * @param {number} x - x coord of element + * @param {number} y - y coord of element + * @param {Scope=} scope - the ciruit in which we want the Element + * @param {string=} dir - direcion in which element has to drawn + + * + * This is basically a RAM component that persists its contents. + * + * We consider EEPROMs more 'expensive' than RAMs, so we arbitrarily limit + * the addressWith to a maximum of 10 bits (1024 addresses) with a default of 8-bit (256). + * + * In the EEPROM all addresses are initialized to zero. + * This way we serialize unused values as "0" instead of "null". + * + * These two techniques help keep reduce the size of saved projects. + * @category sequential + */ +export default class EEPROM extends RAM { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + bitWidth = 8, + addressWidth = 8, + data = null + ) { + super(x, y, scope, dir, bitWidth, addressWidth) + /* + this.scope['EEPROM'].push(this); + */ + this.data = data || this.data + } + + clearData() { + super.clearData() + for (var i = 0; i < this.data.length; i++) + this.data[i] = this.data[i] || 0 + } + + customSave() { + var saveInfo = super.customSave(this) + + // Normalize this.data to use zeroes instead of null when serialized. + var { data } = this + + saveInfo.constructorParamaters.push(data) + return saveInfo + } + + //This is a EERAM without a clock - not normal + //reset is supported + static moduleVerilog() { + return ` + module EEPROM(dout, addr, din, we, dmp, rst); + parameter WIDTH = 8; + parameter ADDR = 10; + output [WIDTH-1:0] dout; + input [ADDR-1:0] addr; + input [WIDTH-1:0] din; + input we; + input dmp; + input rst; + reg [WIDTH-1:0] mem[2**ADDR-1:0]; + integer j; + + assign dout = mem[addr]; + + always @ (*) begin + if (!rst) + for (j=0; j < 2**ADDR-1; j=j+1) begin + mem[j] = 0; + end + if (!we) + mem[addr] = din; + dout = mem[addr]; + end + endmodule + ` + } +} + +EEPROM.prototype.tooltipText = + 'Electrically Erasable Programmable Read-Only Memory' +EEPROM.prototype.shortName = 'EEPROM' +EEPROM.prototype.maxAddressWidth = 10 +EEPROM.prototype.mutableProperties = { + addressWidth: { + name: 'Address Width', + type: 'number', + max: '10', + min: '1', + func: 'changeAddressWidth', + }, + dump: RAM.prototype.mutableProperties.dump, + load: RAM.prototype.mutableProperties.load, + reset: RAM.prototype.mutableProperties.reset, +} +EEPROM.prototype.objectType = 'EEPROM' diff --git a/v0/src/simulator/src/sequential/JKflipFlop.js b/v0/src/simulator/src/sequential/JKflipFlop.js new file mode 100644 index 00000000..c02eae3f --- /dev/null +++ b/v0/src/simulator/src/sequential/JKflipFlop.js @@ -0,0 +1,166 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, fillText } from '../canvasApi' +/** + * @class + * JKflipFlop + * JK flip flop has 6 input nodes: + * clock, J input, K input, preset, reset ,enable. + * @extends CircuitElement + * @param {number} x - x coord of element + * @param {number} y - y coord of element + * @param {Scope=} scope - the ciruit in which we want the Element + * @param {string=} dir - direcion in which element has to drawn + * @category sequential + */ +import { colors } from '../themer/themer' +export default class JKflipFlop extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT') { + super(x, y, scope, dir, 1) + /* + this.scope['JKflipFlop'].push(this); + */ + this.directionFixed = true + this.fixedBitWidth = true + this.setDimensions(20, 20) + this.rectangleObject = true + this.J = new Node(-20, -10, 0, this, 1, 'J') + this.K = new Node(-20, 0, 0, this, 1, 'K') + this.clockInp = new Node(-20, 10, 0, this, 1, 'Clock') + this.qOutput = new Node(20, -10, 1, this, 1, 'Q') + this.qInvOutput = new Node(20, 10, 1, this, 1, 'Q Inverse') + this.reset = new Node(10, 20, 0, this, 1, 'Asynchronous Reset') + this.preset = new Node(0, 20, 0, this, 1, 'Preset') + this.en = new Node(-10, 20, 0, this, 1, 'Enable') + this.state = 0 + this.slaveState = 0 + this.masterState = 0 + this.prevClockState = 0 + + // this.wasClicked = false; + } + + /** + * @memberof JKflipFlop + * if none of the predefined nodes have been deleted it isresolvable + */ + isResolvable() { + if (this.reset.value == 1) return true + if ( + this.clockInp.value != undefined && + this.J.value != undefined && + this.K.value != undefined + ) + return true + return false + } + + newBitWidth(bitWidth) { + this.bitWidth = bitWidth + this.dInp.bitWidth = bitWidth + this.qOutput.bitWidth = bitWidth + this.qInvOutput.bitWidth = bitWidth + this.preset.bitWidth = bitWidth + } + + /** + * @memberof JKflipFlop + * Edge triggered master slave JK flip flop is resolved by + * setting the slaveState = masterState when there is an edge + * in the clock. masterState = this.J when no change in clock. + */ + resolve() { + if (this.reset.value == 1) { + this.masterState = this.slaveState = this.preset.value || 0 + } else if (this.en.value == 0) { + this.prevClockState = this.clockInp.value + } else if (this.en.value == 1 || this.en.connections.length == 0) { + if (this.clockInp.value == this.prevClockState) { + if ( + this.clockInp.value == 0 && + this.J.value != undefined && + this.K.value != undefined + ) { + if (this.J.value && this.K.value) { + this.masterState = 1 ^ this.slaveState + } else if (this.J.value ^ this.K.value) { + this.masterState = this.J.value + } + } + } else if (this.clockInp.value != undefined) { + if (this.clockInp.value == 1) { + this.slaveState = this.masterState + } else if ( + this.clockInp.value == 0 && + this.J.value != undefined && + this.K.value != undefined + ) { + if (this.J.value && this.K.value) { + this.masterState = 1 ^ this.slaveState + } else if (this.J.value ^ this.K.value) { + this.masterState = this.J.value + } + } + this.prevClockState = this.clockInp.value + } + } + + if (this.qOutput.value != this.slaveState) { + this.qOutput.value = this.slaveState + this.qInvOutput.value = this.flipBits(this.slaveState) + simulationArea.simulationQueue.add(this.qOutput) + simulationArea.simulationQueue.add(this.qInvOutput) + } + } + + customSave() { + var data = { + nodes: { + J: findNode(this.J), + K: findNode(this.K), + clockInp: findNode(this.clockInp), + qOutput: findNode(this.qOutput), + qInvOutput: findNode(this.qInvOutput), + reset: findNode(this.reset), + preset: findNode(this.preset), + en: findNode(this.en), + }, + constructorParamaters: [this.direction], + } + return data + } + + customDraw() { + var ctx = simulationArea.context + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.beginPath() + ctx.lineWidth = correctWidth(3) + var xx = this.x + var yy = this.y + + // rect(ctx, xx - 20, yy - 20, 40, 40); + moveTo(ctx, -20, 5, xx, yy, this.direction) + lineTo(ctx, -15, 10, xx, yy, this.direction) + lineTo(ctx, -20, 15, xx, yy, this.direction) + + // if ((this.b.hover&&!simulationArea.shiftDown)|| simulationArea.lastSelected == this || simulationArea.multipleObjectSelections.contains(this)) ctx.fillStyle = "rgba(255, 255, 32,0.8)";ctx.fill(); + ctx.stroke() + + ctx.beginPath() + ctx.font = '20px Raleway' + ctx.fillStyle = colors['input_text'] + ctx.textAlign = 'center' + fillText(ctx, this.slaveState.toString(16), xx, yy + 5) + ctx.fill() + } +} + +JKflipFlop.prototype.tooltipText = + 'JK FlipFlop ToolTip : gated SR flip-flop with the addition of a clock input.' + +JKflipFlop.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/6sequentialelements?id=jk-flip-flop' + +JKflipFlop.prototype.objectType = 'JKflipFlop' diff --git a/v0/src/simulator/src/sequential/Keyboard.js b/v0/src/simulator/src/sequential/Keyboard.js new file mode 100644 index 00000000..ffeb7a9c --- /dev/null +++ b/v0/src/simulator/src/sequential/Keyboard.js @@ -0,0 +1,232 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, fillText3 } from '../canvasApi' +/** + * @class + * Keyboard + * KeyBoard - We can give 3 inputs: clock, enable and available. + * An output of 7 bits is given out when clockInp = 1. + * @extends CircuitElement + * @param {number} x - x coord of element + * @param {number} y - y coord of element + * @param {Scope=} scope - the ciruit in which we want the Element + * @param {string=} dir - direcion in which element has to drawn + * @category sequential + */ +import { colors } from '../themer/themer' +export default class Keyboard extends CircuitElement { + constructor(x, y, scope = globalScope, bufferSize = 32) { + super(x, y, scope, 'RIGHT', 1) + /* + this.scope['Keyboard'].push(this); + */ + this.directionFixed = true + this.fixedBitWidth = true + + this.bufferSize = bufferSize || parseInt(prompt('Enter buffer size:')) + this.elementWidth = Math.max(80, Math.ceil(this.bufferSize / 2) * 20) + this.elementHeight = 40 // Math.max(40,Math.ceil(this.rows*15/20)*20); + this.setWidth(this.elementWidth / 2) + this.setHeight(this.elementHeight / 2) + + this.clockInp = new Node( + -this.elementWidth / 2, + this.elementHeight / 2 - 10, + 0, + this, + 1, + 'Clock' + ) + this.asciiOutput = new Node( + 30, + this.elementHeight / 2, + 1, + this, + 7, + 'Ascii Output' + ) + this.available = new Node( + 10, + this.elementHeight / 2, + 1, + this, + 1, + 'Available' + ) + this.reset = new Node(-10, this.elementHeight / 2, 0, this, 1, 'Reset') + this.en = new Node(-30, this.elementHeight / 2, 0, this, 1, 'Enable') + this.prevClockState = 0 + this.buffer = '' + this.bufferOutValue = undefined + } + + /** + * @memberof Keyboard + * this funcion sets the size of maximum input that can + * be given to the keyboard at once before it starts sending data. + */ + changeBufferSize(size) { + if (size == undefined || size < 20 || size > 100) return + if (this.bufferSize == size) return + var obj = new Keyboard(this.x, this.y, this.scope, size) + this.delete() + simulationArea.lastSelected = obj + return obj + } + + /** + * @memberof Keyboard + * Adds the keyy pressed to the buffer + */ + keyDown(key) { + if (key.length != 1) return + this.buffer += key + if (this.buffer.length > this.bufferSize) { + this.buffer = this.buffer.slice(1) + } + } + + /** + * @memberof Keyboard + * not resolvable if enable = 0 or clock is undefined + */ + isResolvable() { + if (this.reset.value == 1) return true + if ( + this.en.value == 0 || + (this.en.connections.length && this.en.value == undefined) + ) + return false + else if (this.clockInp.value == undefined) return false + return true + } + + /** + * @memberof Keyboard + * Whenever clock is enabled (1) then one charecter + * from the buffer is converted to ascii and transmitted + * through the output nodes. + */ + resolve() { + if (this.reset.value == 1) { + this.buffer = '' + return + } + if (this.en.value == 0) { + return + } + + if (this.available.value != 0) { + this.available.value = 0 // this.bufferOutValue; + simulationArea.simulationQueue.add(this.available) + } + + if (this.clockInp.value == this.prevClockState) { + if (this.clockInp.value == 0) { + if (this.buffer.length) { + this.bufferOutValue = this.buffer[0].charCodeAt(0) + } else { + this.bufferOutValue = undefined + } + } + } else if (this.clockInp.value != undefined) { + if (this.clockInp.value == 1 && this.buffer.length) { + if (this.bufferOutValue == this.buffer[0].charCodeAt(0)) { + // WHY IS THIS REQUIRED ?? + this.buffer = this.buffer.slice(1) + } + } else if (this.buffer.length) { + this.bufferOutValue = this.buffer[0].charCodeAt(0) + } else { + this.bufferOutValue = undefined + } + this.prevClockState = this.clockInp.value + } + + if (this.asciiOutput.value != this.bufferOutValue) { + this.asciiOutput.value = this.bufferOutValue + simulationArea.simulationQueue.add(this.asciiOutput) + } + + if (this.bufferOutValue !== undefined && this.available.value != 1) { + this.available.value = 1 // this.bufferOutValue; + simulationArea.simulationQueue.add(this.available) + } + } + + customSave() { + var data = { + nodes: { + clockInp: findNode(this.clockInp), + asciiOutput: findNode(this.asciiOutput), + available: findNode(this.available), + reset: findNode(this.reset), + en: findNode(this.en), + }, + constructorParamaters: [this.bufferSize], + } + return data + } + + customDraw() { + var ctx = simulationArea.context + // + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.beginPath() + ctx.lineWidth = correctWidth(3) + var xx = this.x + var yy = this.y + moveTo( + ctx, + -this.elementWidth / 2, + this.elementHeight / 2 - 15, + xx, + yy, + this.direction + ) + lineTo( + ctx, + 5 - this.elementWidth / 2, + this.elementHeight / 2 - 10, + xx, + yy, + this.direction + ) + lineTo( + ctx, + -this.elementWidth / 2, + this.elementHeight / 2 - 5, + xx, + yy, + this.direction + ) + + ctx.stroke() + + ctx.beginPath() + ctx.fillStyle = colors['input_text'] + ctx.textAlign = 'center' + var lineData = + this.buffer + ' '.repeat(this.bufferSize - this.buffer.length) + fillText3(ctx, lineData, 0, +5, xx, yy, 15, 'Courier New', 'center') + ctx.fill() + } +} + +Keyboard.prototype.tooltipText = 'Keyboard' +Keyboard.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/6sequentialelements?id=keyboard' + +Keyboard.prototype.mutableProperties = { + bufferSize: { + name: 'Buffer Size', + type: 'number', + max: '100', + min: '20', + func: 'changeBufferSize', + }, +} + +Keyboard.prototype.objectType = 'Keyboard' diff --git a/v0/src/simulator/src/sequential/RAM.js b/v0/src/simulator/src/sequential/RAM.js new file mode 100644 index 00000000..b406f921 --- /dev/null +++ b/v0/src/simulator/src/sequential/RAM.js @@ -0,0 +1,374 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, fillText2, fillText4, drawCircle2 } from '../canvasApi' +import { parseNumber, showMessage } from '../utils' +/** + * @class + * RAM Component. + * @extends CircuitElement + * @param {number} x - x coord of element + * @param {number} y - y coord of element + * @param {Scope=} scope - the ciruit in which we want the Element + * @param {string=} dir - direcion in which element has to drawn + * + * Two settings are available: + * - addressWidth: 1 to 20, default=10. Controls the width of the address input. + * - bitWidth: 1 to 32, default=8. Controls the width of data pins. + * + * Amount of memory in the element is 2^addressWidth x bitWidth bits. + * Minimum RAM size is: 2^1 x 1 = 2 bits. + * Maximum RAM size is: 2^20 x 32 = 1M x 32 bits => 32 Mbits => 4MB. + * Maximum 8-bits size: 2^20 x 8 = 1M x 8 bits => 1MB. + * Default RAM size is: 2^10 x 8 = 1024 bytes => 1KB. + * + * RAMs are volatile therefore this component does not persist the memory contents. + * + * Changes to addressWidth and bitWidth also cause data to be lost. + * Think of these operations as being equivalent to taking a piece of RAM out of a + * circuit board and replacing it with another RAM of different size. + * + * The contents of the RAM can be reset to zero by setting the RESET pin 1 or + * or by selecting the component and pressing the "Reset" button in the properties window. + * + * The contents of the RAM can be dumped to the console by transitioning CORE DUMP pin to 1 + * or by selecting the component and pressing the "Core Dump" button in the properties window. + * Address spaces that have not been written will show up as `undefined` in the core dump. + * + * NOTE: The maximum address width of 20 is arbitrary. + * Larger values are possible, but in practice circuits won't need this much + * memory and keeping the value small helps avoid allocating too much memory on the browser. + * Internally we use a sparse array, so only the addresses that are written are actually + * allocated. Nevertheless, it is better to prevent large allocations from happening + * by keeping the max addressWidth small. If needed, we can increase the max. + * @category sequential + */ +import { colors } from '../themer/themer' +import { showError } from '../utils' +export default class RAM extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + bitWidth = 8, + addressWidth = 10 + ) { + super(x, y, scope, dir, Math.min(Math.max(1, bitWidth), 32)) + /* + this.scope['RAM'].push(this); + */ + this.setDimensions(60, 40) + + this.directionFixed = true + this.labelDirection = 'UP' + + this.addressWidth = Math.min( + Math.max(1, addressWidth), + this.maxAddressWidth + ) + this.address = new Node( + -this.leftDimensionX, + -20, + 0, + this, + this.addressWidth, + 'ADDRESS' + ) + this.dataIn = new Node( + -this.leftDimensionX, + 0, + 0, + this, + this.bitWidth, + 'DATA IN' + ) + this.write = new Node(-this.leftDimensionX, 20, 0, this, 1, 'WRITE') + this.reset = new Node(0, this.downDimensionY, 0, this, 1, 'RESET') + this.coreDump = new Node( + -20, + this.downDimensionY, + 0, + this, + 1, + 'CORE DUMP' + ) + this.dataOut = new Node( + this.rightDimensionX, + 0, + 1, + this, + this.bitWidth, + 'DATA OUT' + ) + this.prevCoreDumpValue = undefined + + this.clearData() + } + + customSave() { + return { + // NOTE: data is not persisted since RAMs are volatile. + constructorParamaters: [ + this.direction, + this.bitWidth, + this.addressWidth, + ], + nodes: { + address: findNode(this.address), + dataIn: findNode(this.dataIn), + write: findNode(this.write), + reset: findNode(this.reset), + coreDump: findNode(this.coreDump), + dataOut: findNode(this.dataOut), + }, + } + } + + newBitWidth(value) { + value = parseInt(value) + if ( + !isNaN(value) && + this.bitWidth != value && + value >= 1 && + value <= 32 + ) { + this.bitWidth = value + this.dataIn.bitWidth = value + this.dataOut.bitWidth = value + this.clearData() + } + } + + changeAddressWidth(value) { + value = parseInt(value) + if ( + !isNaN(value) && + this.addressWidth != value && + value >= 1 && + value <= this.maxAddressWidth + ) { + this.addressWidth = value + this.address.bitWidth = value + this.clearData() + } + } + + clearData() { + this.data = new Array(Math.pow(2, this.addressWidth)) + this.tooltipText = `${this.memSizeString()} ${this.shortName}` + } + + isResolvable() { + return ( + this.address.value !== undefined || + this.reset.value !== undefined || + this.coreDump.value !== undefined + ) + } + + resolve() { + if (this.write.value == 1) { + this.data[this.address.value] = this.dataIn.value + } + + if (this.reset.value == 1) { + this.clearData() + } + + if ( + this.coreDump.value && + this.prevCoreDumpValue != this.coreDump.value + ) { + this.dump() + } + this.prevCoreDumpValue = this.coreDump.value + + this.dataOut.value = this.data[this.address.value] || 0 + simulationArea.simulationQueue.add(this.dataOut) + } + + customDraw() { + var ctx = simulationArea.context + // + var xx = this.x + var yy = this.y + + ctx.beginPath() + ctx.strokeStyle = 'gray' + ctx.fillStyle = this.write.value ? 'red' : 'lightgreen' + ctx.lineWidth = correctWidth(1) + drawCircle2(ctx, 50, -30, 3, xx, yy, this.direction) + ctx.fill() + ctx.stroke() + + ctx.beginPath() + ctx.textAlign = 'center' + ctx.fillStyle = 'black' + fillText4(ctx, this.memSizeString(), 0, -10, xx, yy, this.direction, 12) + fillText4(ctx, this.shortName, 0, 10, xx, yy, this.direction, 12) + fillText2( + ctx, + 'A', + this.address.x + 12, + this.address.y, + xx, + yy, + this.direction + ) + fillText2( + ctx, + 'DI', + this.dataIn.x + 12, + this.dataIn.y, + xx, + yy, + this.direction + ) + fillText2( + ctx, + 'W', + this.write.x + 12, + this.write.y, + xx, + yy, + this.direction + ) + fillText2( + ctx, + 'DO', + this.dataOut.x - 15, + this.dataOut.y, + xx, + yy, + this.direction + ) + ctx.fill() + } + + memSizeString() { + var mag = ['', 'K', 'M'] + var unit = + this.bitWidth == 8 + ? 'B' + : this.bitWidth == 1 + ? 'b' + : ` x ${this.bitWidth}b` + var v = Math.pow(2, this.addressWidth) + var m = 0 + while (v >= 1024 && m < mag.length - 1) { + v /= 1024 + m++ + } + return v + mag[m] + unit + } + + dump() { + var logLabel = console.group && this.label + if (logLabel) { + console.group(this.label) + } + + showMessage('Data dumped to developer Console') + + console.log(JSON.stringify(this.data)) + + if (logLabel) { + console.groupEnd() + } + } + + dblclick() { + this.promptData() + } + + promptData() { + var data = prompt( + 'Enter Data (separated by space, comma, tab or newline) (data can be in hex, binary, octal or decimal)' + ) + if (!data) { + showError('No data entered.') + return + } + var oldData = this.data + try { + var ramSize = 1 << this.addressWidth + var maxNumber = 1 << this.bitWidth + this.clearData() + + data = data.split(/[, \n\t]/) + data = data.filter((x) => x.length) + if (data.length > ramSize) { + throw `Capacity: ${ramSize}. But ${data.length} data cells found` + } + + for (var i = 0; i < data.length; i++) { + var dataCell = parseNumber(data[i]) + if (isNaN(dataCell)) + throw `Address ${i}: ${data[i]} is not a number` + if (dataCell < 0) throw `Address ${i}: ${data[i]} is negative` + if (dataCell >= maxNumber) + throw `Address ${i}: ${data[i]} is too large` + this.data[i] = dataCell + } + showMessage(`${data.length} data cells loaded`) + } catch (e) { + this.data = oldData + showError(e) + } + } + + //This is a RAM without a clock - not normal + //reset is not supported + static moduleVerilog() { + return ` + module RAM(dout, addr, din, we, dmp, rst); + parameter WIDTH = 8; + parameter ADDR = 10; + output [WIDTH-1:0] dout; + input [ADDR-1:0] addr; + input [WIDTH-1:0] din; + input we; + input dmp; + input rst; + reg [WIDTH-1:0] mem [2**ADDR-1:0]; + + assign dout = mem[addr]; + + always @ (*) begin + if (!we) + mem[addr] = din; + end + endmodule + ` + } +} + +RAM.prototype.tooltipText = 'Random Access Memory' +RAM.prototype.shortName = 'RAM' +RAM.prototype.maxAddressWidth = 20 +RAM.prototype.mutableProperties = { + addressWidth: { + name: 'Address Width', + type: 'number', + max: '20', + min: '1', + func: 'changeAddressWidth', + }, + dump: { + name: 'Core Dump', + type: 'button', + func: 'dump', + }, + load: { + name: 'Load Data', + type: 'button', + func: 'promptData', + }, + reset: { + name: 'Reset', + type: 'button', + func: 'clearData', + }, +} +RAM.prototype.objectType = 'RAM' diff --git a/v0/src/simulator/src/sequential/Rom.js b/v0/src/simulator/src/sequential/Rom.js new file mode 100644 index 00000000..69bb544b --- /dev/null +++ b/v0/src/simulator/src/sequential/Rom.js @@ -0,0 +1,314 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, rect2, fillText3 } from '../canvasApi' +/** + * @class + * Rom + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {Array=} data - bit width per node. + * @category sequential + */ +import { colors } from '../themer/themer' +export default class Rom extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + data = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + ) { + super(x, y, scope, 'RIGHT', 1) + /* + this.scope['Rom'].push(this); + */ + this.fixedBitWidth = true + this.directionFixed = true + this.rectangleObject = false + this.setDimensions(80, 50) + this.memAddr = new Node(-80, 0, 0, this, 4, 'Address') + this.en = new Node(0, 50, 0, this, 1, 'Enable') + this.dataOut = new Node(80, 0, 1, this, 8, 'DataOut') + this.data = + data || + prompt('Enter data') + .split(' ') + .map((lambda) => parseInt(lambda, 16)) + } + + /** + * @memberof Rom + * Checks if the element is resolvable + * @return {boolean} + */ + isResolvable() { + if ( + (this.en.value === 1 || this.en.connections.length === 0) && + this.memAddr.value !== undefined + ) + return true + return false + } + + /** + * @memberof Rom + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.data], + nodes: { + memAddr: findNode(this.memAddr), + dataOut: findNode(this.dataOut), + en: findNode(this.en), + }, + } + return data + } + + /** + * @memberof Rom + * function to find position of the index of part of rom selected. + * @return {number} + */ + findPos() { + const i = Math.floor((simulationArea.mouseX - this.x + 35) / 20) + const j = Math.floor((simulationArea.mouseY - this.y + 35) / 16) + if (i < 0 || j < 0 || i > 3 || j > 3) return undefined + return j * 4 + i + } + + /** + * @memberof Rom + * listener function to set selected index + * @return {number} + */ + click() { + // toggle + this.selectedIndex = this.findPos() + } + + /** + * @memberof Rom + * to take input in rom + * @return {number} + */ + keyDown(key) { + if (key === 'Backspace') this.delete() + if (this.selectedIndex === undefined) return + key = key.toLowerCase() + if (!~'1234567890abcdef'.indexOf(key)) return + + this.data[this.selectedIndex] = + (this.data[this.selectedIndex] * 16 + parseInt(key, 16)) % 256 + } + + /** + * @memberof Rom + * function to draw element + */ + customDraw() { + const ctx = simulationArea.context + const xx = this.x + const yy = this.y + const hoverIndex = this.findPos() + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.lineWidth = correctWidth(3) + ctx.beginPath() + rect2( + ctx, + -this.leftDimensionX, + -this.upDimensionY, + this.leftDimensionX + this.rightDimensionX, + this.upDimensionY + this.downDimensionY, + this.x, + this.y, + [this.direction, 'RIGHT'][+this.directionFixed] + ) + if ( + hoverIndex === undefined && + ((!simulationArea.shiftDown && this.hover) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this)) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + ctx.strokeStyle = 'black' + ctx.fillStyle = '#fafafa' + ctx.lineWidth = correctWidth(1) + ctx.beginPath() + for (let i = 0; i < 16; i += 4) { + for (let j = i; j < i + 4; j++) { + rect2(ctx, (j % 4) * 20, i * 4, 20, 16, xx - 35, yy - 35) + } + } + ctx.fill() + ctx.stroke() + if (hoverIndex !== undefined) { + ctx.beginPath() + ctx.fillStyle = 'yellow' + rect2( + ctx, + (hoverIndex % 4) * 20, + Math.floor(hoverIndex / 4) * 16, + 20, + 16, + xx - 35, + yy - 35 + ) + ctx.fill() + ctx.stroke() + } + if (this.selectedIndex !== undefined) { + ctx.beginPath() + ctx.fillStyle = 'lightgreen' + rect2( + ctx, + (this.selectedIndex % 4) * 20, + Math.floor(this.selectedIndex / 4) * 16, + 20, + 16, + xx - 35, + yy - 35 + ) + ctx.fill() + ctx.stroke() + } + if (this.memAddr.value !== undefined) { + ctx.beginPath() + ctx.fillStyle = colors['input_text'] + rect2( + ctx, + (this.memAddr.value % 4) * 20, + Math.floor(this.memAddr.value / 4) * 16, + 20, + 16, + xx - 35, + yy - 35 + ) + ctx.fill() + ctx.stroke() + } + + ctx.beginPath() + ctx.fillStyle = 'Black' + fillText3(ctx, 'A', -65, 5, xx, yy, 16, 'Raleway', 'right') + fillText3(ctx, 'D', 75, 5, xx, yy, 16, 'Raleway', 'right') + fillText3(ctx, 'En', 5, 47, xx, yy, 16, 'Raleway', 'right') + ctx.fill() + + ctx.beginPath() + ctx.fillStyle = 'Black' + for (let i = 0; i < 16; i += 4) { + for (let j = i; j < i + 4; j++) { + let s = this.data[j].toString(16) + if (s.length < 2) s = `0${s}` + fillText3( + ctx, + s, + (j % 4) * 20, + i * 4, + xx - 35 + 10, + yy - 35 + 12, + 14, + 'Raleway', + 'center' + ) + } + } + ctx.fill() + + ctx.beginPath() + ctx.fillStyle = 'Black' + for (let i = 0; i < 16; i += 4) { + let s = i.toString(16) + if (s.length < 2) s = `0${s}` + fillText3( + ctx, + s, + 0, + i * 4, + xx - 40, + yy - 35 + 12, + 14, + 'Raleway', + 'right' + ) + } + ctx.fill() + } + + /** + * @memberof Rom + * resolve output values based on inputData + */ + resolve() { + if (this.isResolvable() === false) { + return + } + this.dataOut.value = this.data[this.memAddr.value] + simulationArea.simulationQueue.add(this.dataOut) + } + + verilogBaseType() { + return this.verilogName() + (Rom.selSizes.length - 1) + } + //this code to generate Verilog + generateVerilog() { + Rom.selSizes.push(this.data) + return CircuitElement.prototype.generateVerilog.call(this) + } + + //This code to determine what sizes are used to generate the needed modules + //generate the needed modules + static moduleVerilog() { + var output = '' + + for (var i = 0; i < Rom.selSizes.length; i++) { + output += ` + module Rom${i}(dout, addr, en); + parameter WIDTH = 8; + parameter ADDR = 4; + output reg [WIDTH-1:0] dout; + input [ADDR-1:0] addr; + input en; + + always @ (*) begin + if (en == 0) + dout = {WIDTH{1'bz}}; + else + case (addr) + ` + for (var j = 0; j < 1 << 4; j++) { + output += + ' ' + j + ' : dout = ' + Rom.selSizes[i][j] + ';\n' + } + + output += ` endcase + end + endmodule + ` + } + + return output + } + //reset the sized before Verilog generation + static resetVerilog() { + Rom.selSizes = [] + } +} + +/** + * @memberof Rom + * Help Tip + * @type {string} + * @category sequential + */ +Rom.prototype.tooltipText = 'Read-only memory' +Rom.prototype.helplink = 'https://docs.circuitverse.org/#/chapter4/6sequentialelements?id=rom' +Rom.prototype.objectType = 'Rom' diff --git a/v0/src/simulator/src/sequential/SRflipFlop.js b/v0/src/simulator/src/sequential/SRflipFlop.js new file mode 100644 index 00000000..af1a16ce --- /dev/null +++ b/v0/src/simulator/src/sequential/SRflipFlop.js @@ -0,0 +1,131 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, fillText } from '../canvasApi' +/** + * @class + * SRflipFlop + * SR flip flop has 6 input nodes: + * clock, S input, R input, preset, reset ,enable. + * @extends CircuitElement + * @param {number} x - x coord of element + * @param {number} y - y coord of element + * @param {Scope=} scope - the ciruit in which we want the Element + * @param {string=} dir - direcion in which element has to drawn + * @category sequential + */ +import { colors } from '../themer/themer' +export default class SRflipFlop extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT') { + super(x, y, scope, dir, 1) + /* + this.scope['SRflipFlop'].push(this); + */ + this.directionFixed = true + this.fixedBitWidth = true + this.setDimensions(20, 20) + this.rectangleObject = true + this.R = new Node(-20, +10, 0, this, 1, 'R') + this.S = new Node(-20, -10, 0, this, 1, 'S') + this.qOutput = new Node(20, -10, 1, this, 1, 'Q') + this.qInvOutput = new Node(20, 10, 1, this, 1, 'Q Inverse') + this.reset = new Node(10, 20, 0, this, 1, 'Asynchronous Reset') + this.preset = new Node(0, 20, 0, this, 1, 'Preset') + this.en = new Node(-10, 20, 0, this, 1, 'Enable') + this.state = 0 + // this.slaveState = 0; + // this.prevClockState = 0; + // this.wasClicked = false; + } + + newBitWidth(bitWidth) { + this.bitWidth = bitWidth + this.dInp.bitWidth = bitWidth + this.qOutput.bitWidth = bitWidth + this.qInvOutput.bitWidth = bitWidth + this.preset.bitWidth = bitWidth + } + + /** + * @memberof SRflipFlop + * always resolvable + */ + isResolvable() { + return true + if (this.reset.value == 1) return true + if (this.S.value != undefined && this.R.value != undefined) return true + return false + } + + /** + * @memberof SRflipFlop + * function to resolve SR flip flop if S != R we can + * set this.state to value S. + */ + resolve() { + if (this.reset.value == 1) { + this.state = this.preset.value || 0 + } else if ( + (this.en.value == 1 || this.en.connections == 0) && + this.S.value ^ this.R.value + ) { + this.state = this.S.value + } + + if (this.qOutput.value != this.state) { + this.qOutput.value = this.state + this.qInvOutput.value = this.flipBits(this.state) + simulationArea.simulationQueue.add(this.qOutput) + simulationArea.simulationQueue.add(this.qInvOutput) + } + } + + customSave() { + var data = { + nodes: { + S: findNode(this.S), + R: findNode(this.R), + qOutput: findNode(this.qOutput), + qInvOutput: findNode(this.qInvOutput), + reset: findNode(this.reset), + preset: findNode(this.preset), + en: findNode(this.en), + }, + constructorParamaters: [this.direction], + } + return data + } + + customDraw() { + var ctx = simulationArea.context + // + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.beginPath() + ctx.lineWidth = correctWidth(3) + var xx = this.x + var yy = this.y + + // rect(ctx, xx - 20, yy - 20, 40, 40); + // moveTo(ctx, -20, 5, xx, yy, this.direction); + // lineTo(ctx, -15, 10, xx, yy, this.direction); + // lineTo(ctx, -20, 15, xx, yy, this.direction); + + // if ((this.b.hover&&!simulationArea.shiftDown)|| simulationArea.lastSelected == this || simulationArea.multipleObjectSelections.contains(this)) ctx.fillStyle = "rgba(255, 255, 32,0.8)";ctx.fill(); + ctx.stroke() + + ctx.beginPath() + ctx.font = '20px Raleway' + ctx.fillStyle = colors['input_text'] + ctx.textAlign = 'center' + fillText(ctx, this.state.toString(16), xx, yy + 5) + ctx.fill() + } +} + +SRflipFlop.prototype.tooltipText = 'SR FlipFlop ToolTip : SR FlipFlop Selected.' + +SRflipFlop.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/6sequentialelements?id=sr-flip-flop' + +SRflipFlop.prototype.objectType = 'SRflipFlop' diff --git a/v0/src/simulator/src/sequential/TTY.js b/v0/src/simulator/src/sequential/TTY.js new file mode 100644 index 00000000..d0c89461 --- /dev/null +++ b/v0/src/simulator/src/sequential/TTY.js @@ -0,0 +1,250 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, fillText3 } from '../canvasApi' +import { colors } from '../themer/themer' + +/** + * @class + * TTY + * TypeWriter - We can give 4 inputs: + * clock and input of 7 bits are main input required + * on the edge change the data is added onto the display + * screen of the typewriter + * @extends CircuitElement + * @param {number} x - x coord of element + * @param {number} y - y coord of element + * @param {Scope=} scope - the ciruit in which we want the Element + * @param {string=} dir - direcion in which element has to drawn + * @category sequential + */ +export default class TTY extends CircuitElement { + constructor(x, y, scope = globalScope, rows = 3, cols = 32) { + super(x, y, scope, 'RIGHT', 1) + /* + this.scope['TTY'].push(this); + */ + this.directionFixed = true + this.fixedBitWidth = true + this.cols = cols || parseInt(prompt('Enter cols:')) + this.rows = rows || parseInt(prompt('Enter rows:')) + + this.elementWidth = Math.max(40, Math.ceil(this.cols / 2) * 20) + this.elementHeight = Math.max(40, Math.ceil((this.rows * 15) / 20) * 20) + this.setWidth(this.elementWidth / 2) + this.setHeight(this.elementHeight / 2) + // this.element = new Element(x, y, "TTY",this.elementWidth/2, this,this.elementHeight/2); + + this.clockInp = new Node( + -this.elementWidth / 2, + this.elementHeight / 2 - 10, + 0, + this, + 1, + 'Clock' + ) + this.asciiInp = new Node( + -this.elementWidth / 2, + this.elementHeight / 2 - 30, + 0, + this, + 7, + 'Ascii Input' + ) + // this.qOutput = new Node(20, -10, 1, this); + this.reset = new Node( + 30 - this.elementWidth / 2, + this.elementHeight / 2, + 0, + this, + 1, + 'Reset' + ) + this.en = new Node( + 10 - this.elementWidth / 2, + this.elementHeight / 2, + 0, + this, + 1, + 'Enable' + ) + // this.masterState = 0; + // this.slaveState = 0; + this.prevClockState = 0 + + this.data = '' + this.buffer = '' + } + + /** + * @memberof TTY + * this funciton is used to change the size of the screen + */ + changeRowSize(size) { + if (size == undefined || size < 1 || size > 10) return + if (this.rows == size) return + var obj = new TTY(this.x, this.y, this.scope, size, this.cols) + this.delete() + simulationArea.lastSelected = obj + return obj + } + + /** + * @memberof TTY + * this funciton is used to change the size of the screen + */ + changeColSize(size) { + if (size == undefined || size < 20 || size > 100) return + if (this.cols == size) return + var obj = new TTY(this.x, this.y, this.scope, this.rows, size) + this.delete() + simulationArea.lastSelected = obj + return obj + } + + /** + * @memberof TTY + * if no input or enable key is set to 0 returns false + */ + isResolvable() { + if (this.reset.value == 1) return true + if ( + this.en.value == 0 || + (this.en.connections.length && this.en.value == undefined) + ) + return false + else if (this.clockInp.value == undefined) return false + else if (this.asciiInp.value == undefined) return false + return true + } + + /** + * @memberof TTY + * To resolve the Typewriter clock and input of 7 bits are + * used to get the ascii and then on the edge change the + * data is added onto the display screen of the typewriter. + */ + resolve() { + if (this.reset.value == 1) { + this.data = '' + return + } + if (this.en.value == 0) { + this.buffer = '' + return + } + + if (this.clockInp.value == this.prevClockState) { + if (this.clockInp.value == 0) { + this.buffer = String.fromCharCode(this.asciiInp.value) + } + } else if (this.clockInp.value != undefined) { + if (this.clockInp.value == 1) { + this.data += this.buffer + if (this.data.length > this.cols * this.rows) { + this.data = this.data.slice(1) + } + } else if (this.clockInp.value == 0) { + this.buffer = String.fromCharCode(this.asciiInp.value) + } + this.prevClockState = this.clockInp.value + } + } + + customSave() { + var data = { + nodes: { + clockInp: findNode(this.clockInp), + asciiInp: findNode(this.asciiInp), + reset: findNode(this.reset), + en: findNode(this.en), + }, + constructorParamaters: [this.rows, this.cols], + } + return data + } + + customDraw() { + var ctx = simulationArea.context + // + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.beginPath() + ctx.lineWidth = correctWidth(3) + var xx = this.x + var yy = this.y + // rect(ctx, xx - this.elementWidth/2, yy - this.elementHeight/2, this.elementWidth, this.elementHeight); + + moveTo( + ctx, + -this.elementWidth / 2, + this.elementHeight / 2 - 15, + xx, + yy, + this.direction + ) + lineTo( + ctx, + 5 - this.elementWidth / 2, + this.elementHeight / 2 - 10, + xx, + yy, + this.direction + ) + lineTo( + ctx, + -this.elementWidth / 2, + this.elementHeight / 2 - 5, + xx, + yy, + this.direction + ) + + // if ((this.b.hover&&!simulationArea.shiftDown)|| simulationArea.lastSelected == this || simulationArea.multipleObjectSelections.contains(this)) + // ctx.fillStyle = "rgba(255, 255, 32,0.8)"; + ctx.stroke() + + ctx.beginPath() + ctx.fillStyle = colors['input_text'] + ctx.textAlign = 'center' + var startY = -7.5 * this.rows + 3 + for (var i = 0; i < this.data.length; i += this.cols) { + var lineData = this.data.slice(i, i + this.cols) + lineData += ' '.repeat(this.cols - lineData.length) + fillText3( + ctx, + lineData, + 0, + startY + (i / this.cols) * 15 + 9, + xx, + yy, + 15, + 'Courier New', + 'center' + ) + } + ctx.fill() + } +} + +TTY.prototype.tooltipText = 'TTY ToolTip : Tele typewriter selected.' +TTY.prototype.helplink = 'https://docs.circuitverse.org/#/chapter4/6sequentialelements?id=tty' + +TTY.prototype.mutableProperties = { + cols: { + name: 'Columns', + type: 'number', + max: '100', + min: '20', + func: 'changeColSize', + }, + rows: { + name: 'Rows', + type: 'number', + max: '10', + min: '1', + func: 'changeRowSize', + }, +} + +TTY.prototype.objectType = 'TTY' diff --git a/v0/src/simulator/src/sequential/TflipFlop.js b/v0/src/simulator/src/sequential/TflipFlop.js new file mode 100644 index 00000000..cedb9f6b --- /dev/null +++ b/v0/src/simulator/src/sequential/TflipFlop.js @@ -0,0 +1,179 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, fillText } from '../canvasApi' +import { colors } from '../themer/themer' + +/** + * @class + * TflipFlop + * T flip flop has 5 input nodes: + * clock, data input, preset, reset ,enable. + * @extends CircuitElement + * @param {number} x - x coord of element + * @param {number} y - y coord of element + * @param {Scope=} scope - the ciruit in which we want the Element + * @param {string=} dir - direcion in which element has to drawn + * @category sequential + */ +export default class TflipFlop extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT') { + super(x, y, scope, dir, 1) + /* + this.scope['TflipFlop'].push(this); + */ + this.directionFixed = true + this.fixedBitWidth = true + this.setDimensions(20, 20) + this.rectangleObject = true + this.clockInp = new Node(-20, +10, 0, this, 1, 'Clock') + this.dInp = new Node(-20, -10, 0, this, this.bitWidth, 'T') + this.qOutput = new Node(20, -10, 1, this, this.bitWidth, 'Q') + this.qInvOutput = new Node(20, 10, 1, this, this.bitWidth, 'Q Inverse') + this.reset = new Node(10, 20, 0, this, 1, 'Asynchronous Reset') + this.preset = new Node(0, 20, 0, this, this.bitWidth, 'Preset') + this.en = new Node(-10, 20, 0, this, 1, 'Enable') + this.masterState = 0 + this.slaveState = 0 + this.prevClockState = 0 + + // this.wasClicked = false; + } + + /** + * @memberof TflipFlop + * returns true if clock is defined + */ + isResolvable() { + if (this.reset.value == 1) return true + if (this.clockInp.value != undefined && this.dInp.value != undefined) + return true + return false + } + + /** + * @memberof TflipFlop + * @param {number} bitWidth - the new bitwidth + */ + newBitWidth(bitWidth) { + this.bitWidth = bitWidth + this.dInp.bitWidth = bitWidth + this.qOutput.bitWidth = bitWidth + this.qInvOutput.bitWidth = bitWidth + this.preset.bitWidth = bitWidth + } + + /** + * @memberof TflipFlop + * On the leading edge of the clock signal (LOW-to-HIGH) the first stage, + * the “master” latches the input condition at D, while the output stage is deactivated. + * On the trailing edge of the clock signal (HIGH-to-LOW) the second “slave” stage is + * now activated, latching on to the output from the first master circuit. + * This fuction sets the value for the node qOutput based on + * the previous state and input of the clock by taking xor. + * We flip the bits to find qInvOutput + */ + resolve() { + if (this.reset.value == 1) { + // if reset bit is set + this.masterState = this.slaveState = this.preset.value || 0 + } else if (this.en.value == 0) { + // if enabled bit is 0 + this.prevClockState = this.clockInp.value + } else if (this.en.value == 1 || this.en.connections.length == 0) { + // if enabled bit is 1 or not connected to anything. + if (this.clockInp.value == this.prevClockState) { + if (this.clockInp.value == 0 && this.dInp.value != undefined) { + // value is xor of + this.masterState = this.dInp.value ^ this.slaveState + } + } else if (this.clockInp.value != undefined) { + if (this.clockInp.value == 1) { + this.slaveState = this.masterState + } else if ( + this.clockInp.value == 0 && + this.dInp.value != undefined + ) { + this.masterState = this.dInp.value ^ this.slaveState + } + this.prevClockState = this.clockInp.value + } + } + + if (this.qOutput.value != this.slaveState) { + this.qOutput.value = this.slaveState + this.qInvOutput.value = this.flipBits(this.slaveState) + simulationArea.simulationQueue.add(this.qOutput) + simulationArea.simulationQueue.add(this.qInvOutput) + } + } + + customSave() { + var data = { + nodes: { + clockInp: findNode(this.clockInp), + dInp: findNode(this.dInp), + qOutput: findNode(this.qOutput), + qInvOutput: findNode(this.qInvOutput), + reset: findNode(this.reset), + preset: findNode(this.preset), + en: findNode(this.en), + }, + constructorParamaters: [this.direction, this.bitWidth], + } + return data + } + + customDraw() { + var ctx = simulationArea.context + // + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.beginPath() + ctx.lineWidth = correctWidth(3) + var xx = this.x + var yy = this.y + // rect(ctx, xx - 20, yy - 20, 40, 40); + moveTo(ctx, -20, 5, xx, yy, this.direction) + lineTo(ctx, -15, 10, xx, yy, this.direction) + lineTo(ctx, -20, 15, xx, yy, this.direction) + + // if ((this.b.hover&&!simulationArea.shiftDown)|| simulationArea.lastSelected == this || simulationArea.multipleObjectSelections.contains(this)) ctx.fillStyle = "rgba(255, 255, 32,0.8)";ctx.fill(); + ctx.stroke() + ctx.beginPath() + ctx.font = '20px Raleway' + ctx.fillStyle = colors['input_text'] + ctx.textAlign = 'center' + fillText(ctx, this.slaveState.toString(16), xx, yy + 5) + ctx.fill() + } + + static moduleVerilog() { + return ` + module TflipFlop(q, q_inv, clk, t, a_rst, pre, en); + parameter WIDTH = 1; + output reg [WIDTH-1:0] q, q_inv; + input clk, a_rst, pre, en; + input [WIDTH-1:0] t; + + always @ (posedge clk or posedge a_rst) + if (a_rst) begin + q <= 'b0; + q_inv <= 'b1; + end else if (en == 0) ; + else if (t) begin + q <= q ^ t; + q_inv <= ~q ^ t; + end + endmodule + ` + } +} + +TflipFlop.prototype.tooltipText = + 'T FlipFlop ToolTip : Changes state / Toggles whenever the clock input is strobed.' + +TflipFlop.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/6sequentialelements?id=t-flip-flop' + +TflipFlop.prototype.objectType = 'TflipFlop' diff --git a/v0/src/simulator/src/sequential/verilogRAM.js b/v0/src/simulator/src/sequential/verilogRAM.js new file mode 100644 index 00000000..d1e9c71d --- /dev/null +++ b/v0/src/simulator/src/sequential/verilogRAM.js @@ -0,0 +1,577 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, fillText2, fillText4, drawCircle2 } from '../canvasApi' +/** + * @class + * verilogRAM Component. + * @extends CircuitElement + * @param {number} x - x coord of element + * @param {number} y - y coord of element + * @param {Scope=} scope - the ciruit in which we want the Element + * @param {string=} dir - direcion in which element has to drawn + * + * Two settings are available: + * - addressWidth: 1 to 20, default=10. Controls the width of the address input. + * - bitWidth: 1 to 32, default=8. Controls the width of data pins. + * + * Amount of memory in the element is 2^addressWidth x bitWidth bits. + * Minimum verilogRAM size is: 2^1 x 1 = 2 bits. + * Maximum verilogRAM size is: 2^20 x 32 = 1M x 32 bits => 32 Mbits => 4MB. + * Maximum 8-bits size: 2^20 x 8 = 1M x 8 bits => 1MB. + * Default verilogRAM size is: 2^10 x 8 = 1024 bytes => 1KB. + * + * verilogRAMs are volatile therefore this component does not persist the memory contents. + * + * Changes to addressWidth and bitWidth also cause data to be lost. + * Think of these operations as being equivalent to taking a piece of verilogRAM out of a + * circuit board and replacing it with another verilogRAM of different size. + * + * The contents of the verilogRAM can be reset to zero by setting the RESET pin 1 or + * or by selecting the component and pressing the "Reset" button in the properties window. + * + * The contents of the verilogRAM can be dumped to the console by transitioning CORE DUMP pin to 1 + * or by selecting the component and pressing the "Core Dump" button in the properties window. + * Address spaces that have not been written will show up as `undefined` in the core dump. + * + * NOTE: The maximum address width of 20 is arbitrary. + * Larger values are possible, but in practice circuits won't need this much + * memory and keeping the value small helps avoid allocating too much memory on the browser. + * Internally we use a sparse array, so only the addresses that are written are actually + * allocated. Nevertheless, it is better to prevent large allocations from happening + * by keeping the max addressWidth small. If needed, we can increase the max. + * @category sequential + */ +import { colors } from '../themer/themer' + +function customResolve( + clockInp, + dInp, + qOutput, + en, + masterState, + slaveState, + prevClockState, + clock_polarity, + enable_polarity, + numIterations +) { + for (var i = 0; i < numIterations; i++) { + if (clock_polarity[i] != undefined) { + clock_polarity[i] == true ? 1 : 0 + } + + if (enable_polarity[i] != undefined) { + enable_polarity[i] == true ? 1 : 0 + } + + if (clock_polarity[i] == undefined && enable_polarity[i] == undefined) { + if (dInp[i].value != undefined) { + qOutput[i].value = dInp[i].value + simulationArea.simulationQueue.add(qOutput[i]) + } + } else if ( + clock_polarity[i] == undefined && + enable_polarity[i] != undefined + ) { + if ( + (en_value[i] == undefined || + en[i].value == enable_polarity[i]) && + dInp[i].value != undefined + ) { + qOutput[i].value = dInp[i].value + simulationArea.simulationQueue.add(qOutput[i]) + } + } else if ( + clock_polarity[i] != undefined && + enable_polarity[i] == undefined + ) { + if (clockInp[i].value == prevClockState[i]) { + if (clockInp[i].value == 0 && dInp[i].value != undefined) { + masterState[i] = dInp[i].value + } + } else if (clockInp[i].value != undefined) { + if (clockInp[i].value == 1) { + slaveState[i] = masterState[i] + } else if ( + clockInp[i].value == 0 && + dInp[i].value != undefined + ) { + masterState[i] = dInp[i].value + } + prevClockState[i] = clockInp[i].value + } + + if (qOutput[i].value != slaveState[i]) { + qOutput[i].value = slaveState[i] + simulationArea.simulationQueue.add(qOutput[i]) + } + } else { + if (en[i].value == 0) { + prevClockState[i] = clockInp[i].value + } else if (en[i].value == 1 || en[i].connections.length == 0) { + // if(en.value==1) // Creating Infinite Loop, WHY ?? + if (clockInp[i].value == prevClockState[i]) { + if (clockInp[i].value == 0 && dInp[i].value != undefined) { + masterState[i] = dInp[i].value + } + } else if (clockInp[i].value != undefined) { + if (clockInp[i].value == 1) { + slaveState[i] = masterState[i] + } else if ( + clockInp[i].value == 0 && + dInp[i].value != undefined + ) { + masterState[i] = dInp[i].value + } + prevClockState[i] = clockInp[i].value + } + } + + if (qOutput[i].value != slaveState[i]) { + qOutput[i].value = slaveState[i] + simulationArea.simulationQueue.add(qOutput[i]) + } + } + } +} + +export default class verilogRAM extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + bitWidth = 8, + addressWidth = 10, + memData, + words, + numRead, + numWrite, + rdports, + wrports + ) { + super(x, y, scope, dir, Math.min(Math.max(1, bitWidth), 32)) + /* + this.scope['verilogRAM'].push(this); + */ + this.setDimensions(60, 40) + + this.directionFixed = true + this.labelDirection = 'UP' + + this.addressWidth = Math.min( + Math.max(1, addressWidth), + this.maxAddressWidth + ) + + this.readAddress = [] + for (var i = 0; i < numRead; i++) { + this.readAddress.push( + new Node( + -this.leftDimensionX, + -20, + 0, + this, + this.addressWidth, + 'READ_ADDRESS' + i.toString() + ) + ) + } + + this.writeAddress = [] + for (var i = 0; i < numWrite; i++) { + this.writeAddress.push( + new Node( + -this.leftDimensionX, + -20, + 0, + this, + this.addressWidth, + 'WRITE_ADDRESS' + i.toString() + ) + ) + } + + this.writeDataIn = [] + this.writeEnable = [] + + this.writeDffClock = [] + this.writeDffDInp = [] + this.writeDffQOutput = [] + this.writeDffEn = [] + this.writeDffMasterState = [] + this.writeDffSlaveState = [] + this.writeDffprevClockState = [] + this.writeDffClockPolarity = [] + this.writeDffEnPolarity = [] + + for (var i = 0; i < numWrite; i++) { + var currWriteData = new Node( + -this.leftDimensionX, + 0, + 0, + this, + this.bitWidth, + 'DATA IN' + i.toString() + ) + + var clockInp = new Node(-20, +10, 0, this, 1, 'Clock') + var dInp = new Node(-20, -10, 0, this, this.bitWidth, 'D') + var qOutput = new Node(20, -10, 1, this, this.bitWidth, 'Q') + var en = new Node(-10, 20, 0, this, 1, 'Enable') + var masterState = 0 + var slaveState = 0 + var prevClockState = 0 + var clockPolarity = wrports[i]['clock_polarity'] + var enPolarity = wrports[i]['enable_polarity'] + + if (enPolarity == undefined) { + enPolarity = true + } + + currWriteData.connect(dInp) + + this.writeDffClock.push(clockInp) + this.writeDffDInp.push(dInp) + this.writeDffQOutput.push(qOutput) + this.writeDffEn.push(en) + this.writeDffMasterState.push(masterState) + this.writeDffSlaveState.push(slaveState) + this.writeDffprevClockState.push(prevClockState) + this.writeDffClockPolarity.push(clockPolarity) + this.writeDffEnPolarity.push(enPolarity) + + this.writeDataIn.push(currWriteData) + this.writeEnable.push( + new Node( + -this.leftDimensionX, + 20, + 0, + this, + 1, + 'WRITE_ENABLE' + i.toString() + ) + ) + } + + this.reset = new Node(0, this.downDimensionY, 0, this, 1, 'RESET') + this.coreDump = new Node( + -20, + this.downDimensionY, + 0, + this, + 1, + 'CORE DUMP' + ) + this.dataOut = [] + + this.readDffClock = [] + this.readDffDInp = [] + this.readDffQOutput = [] + this.readDffEn = [] + this.readDffMasterState = [] + this.readDffSlaveState = [] + this.readDffprevClockState = [] + this.readDffClockPolarity = [] + this.readDffEnPolarity = [] + + for (var i = 0; i < numRead; i++) { + var currReadOut = new Node( + this.rightDimensionX, + 0, + 1, + this, + this.bitWidth, + 'DATA OUT' + i.toString() + ) + + var clockInp = new Node(-20, +10, 0, this, 1, 'Clock') + var dInp = new Node(-20, -10, 0, this, this.bitWidth, 'D') + var qOutput = new Node(20, -10, 1, this, this.bitWidth, 'Q') + var en = new Node(-10, 20, 0, this, 1, 'Enable') + var masterState = 0 + var slaveState = 0 + var prevClockState = 0 + var clockPolarity = rdports[i]['clock_polarity'] + var enPolarity = rdports[i]['enable_polarity'] + + this.readDffClock.push(clockInp) + this.readDffDInp.push(dInp) + this.readDffQOutput.push(qOutput) + this.readDffEn.push(en) + this.readDffMasterState.push(masterState) + this.readDffSlaveState.push(slaveState) + this.readDffprevClockState.push(prevClockState) + this.readDffClockPolarity.push(clockPolarity) + this.readDffEnPolarity.push(enPolarity) + + currReadOut.connect(dInp) + + this.dataOut.push(currReadOut) + } + + this.prevCoreDumpValue = undefined + this.words = words + this.numRead = numRead + this.numWrite = numWrite + this.memData = memData + this.rdports = rdports + this.wrports = wrports + this.clearData() + this.fillData(memData) + } + + fillData(memData) { + for (var i = 0; i < this.words; i++) { + this.data[i] = 0 + } + var len = memData.length + var dataIndex = 0 + for (var i = 0; i < len; i++) { + if (Number.isInteger(memData[i])) { + var data = memData[i + 1] + + if (data.startsWith('x')) { + dataIndex += memData[i] + continue + } + + var dataValue = 0 + var power2 = 1 + + for (var j = this.bitWidth - 1; j >= 0; j--) { + if (data[j] == '1') { + dataValue += power2 + } + power2 *= 2 + } + + for (var j = 0; j < memData[i]; j++) { + this.data[dataIndex++] = dataValue + } + i++ + } else { + var data = memData[i] + + if (data.startsWith('x')) { + dataIndex++ + continue + } + + var dataValue = 0 + var power2 = 1 + + for (var j = this.bitWidth - 1; j >= 0; j--) { + if (data[j] == '1') { + dataValue += power2 + } + power2 *= 2 + } + + this.data[dataIndex++] = dataValue + } + } + } + + customSave() { + this.dataOut.map(findNode) + const data = { + // NOTE: data is not persisted since verilogRAMs are volatile. + constructorParamaters: [ + this.direction, + this.bitWidth, + this.addressWidth, + this.memData, + this.words, + this.numRead, + this.numWrite, + this.rdports, + this.wrports, + ], + + nodes: { + readAddress: this.readAddress.map(findNode), + writeAddress: this.writeAddress.map(findNode), + writeDataIn: this.writeDataIn.map(findNode), + writeEnable: this.writeEnable.map(findNode), + dataOut: this.dataOut.map(findNode), + reset: findNode(this.reset), + coreDump: findNode(this.coreDump), + readDffClock: this.readDffClock.map(findNode), + readDffDInp: this.readDffDInp.map(findNode), + readDffQOutput: this.readDffQOutput.map(findNode), + readDffEn: this.readDffEn.map(findNode), + writeDffClock: this.writeDffClock.map(findNode), + writeDffDInp: this.writeDffDInp.map(findNode), + writeDffQOutput: this.writeDffQOutput.map(findNode), + writeDffEn: this.writeDffEn.map(findNode), + }, + } + + return data + } + + newBitWidth(value) { + // value = parseInt(value); + // if (!isNaN(value) && this.bitWidth != value && value >= 1 && value <= 32) { + // this.bitWidth = value; + // this.dataIn.bitWidth = value; + // this.dataOut.bitWidth = value; + // this.clearData(); + // } + } + + changeAddressWidth(value) { + // value = parseInt(value); + // if (!isNaN(value) && this.addressWidth != value && value >= 1 && value <= this.maxAddressWidth) { + // this.addressWidth = value; + // this.address.bitWidth = value; + // this.clearData(); + // } + } + + clearData() { + this.data = new Array(this.words) + this.tooltipText = `${this.memSizeString()} ${this.shortName}` + } + + isResolvable() { + for (var i = 0; i < this.numRead; i++) { + if (this.readAddress[i] != undefined) return true + } + return this.reset.value != undefined || this.coreDump.value != undefined + } + + resolve() { + // resolve write + + customResolve( + this.writeDffClock, + this.writeDffDInp, + this.writeDffQOutput, + this.writeDffEn, + this.writeDffMasterState, + this.writeDffSlaveState, + this.writeDffprevClockState, + this.writeDffClockPolarity, + this.writeDffEnPolarity, + this.numWrite + ) + + for (var i = 0; i < this.numWrite; i++) { + if (this.writeEnable[i].value == 1) { + this.data[this.writeAddress[i].value] = + this.writeDffQOutput[i].value + } + } + + if (this.reset.value == 1) { + this.clearData() + } + + if ( + this.coreDump.value && + this.prevCoreDumpValue != this.coreDump.value + ) { + this.dump() + } + this.prevCoreDumpValue = this.coreDump.value + + for (var i = 0; i < this.numRead; i++) { + this.dataOut[i].value = this.data[this.readAddress[i].value] || 0 + simulationArea.simulationQueue.add(this.dataOut[i]) + } + + customResolve( + this.readDffClock, + this.readDffDInp, + this.readDffQOutput, + this.readDffEn, + this.readDffMasterState, + this.readDffSlaveState, + this.readDffprevClockState, + this.readDffClockPolarity, + this.readDffEnPolarity, + this.numRead + ) + } + + customDraw() { + // var ctx = simulationArea.context; + // // + // var xx = this.x; + // var yy = this.y; + // ctx.beginPath(); + // ctx.strokeStyle = 'gray'; + // ctx.fillStyle = this.write.value ? 'red' : 'lightgreen'; + // ctx.lineWidth = correctWidth(1); + // drawCircle2(ctx, 50, -30, 3, xx, yy, this.direction); + // ctx.fill(); + // ctx.stroke(); + // ctx.beginPath(); + // ctx.textAlign = 'center'; + // ctx.fillStyle = 'black'; + // fillText4(ctx, this.memSizeString(), 0, -10, xx, yy, this.direction, 12); + // fillText4(ctx, this.shortName, 0, 10, xx, yy, this.direction, 12); + // fillText2(ctx, 'A', this.address.x + 12, this.address.y, xx, yy, this.direction); + // fillText2(ctx, 'DI', this.dataIn.x + 12, this.dataIn.y, xx, yy, this.direction); + // fillText2(ctx, 'W', this.write.x + 12, this.write.y, xx, yy, this.direction); + // fillText2(ctx, 'DO', this.dataOut.x - 15, this.dataOut.y, xx, yy, this.direction); + // ctx.fill(); + } + + memSizeString() { + var mag = ['', 'K', 'M'] + var unit = + this.bitWidth == 8 + ? 'B' + : this.bitWidth == 1 + ? 'b' + : ` x ${this.bitWidth}b` + var v = Math.pow(2, this.addressWidth) + var m = 0 + while (v >= 1024 && m < mag.length - 1) { + v /= 1024 + m++ + } + return v + mag[m] + unit + } + + dump() { + var logLabel = console.group && this.label + if (logLabel) { + console.group(this.label) + } + + console.log(this.data) + + if (logLabel) { + console.groupEnd() + } + } +} + +verilogRAM.prototype.tooltipText = 'Random Access Memory' +verilogRAM.prototype.shortName = 'verilogRAM' +verilogRAM.prototype.maxAddressWidth = 20 +verilogRAM.prototype.mutableProperties = { + addressWidth: { + name: 'Address Width', + type: 'number', + max: '20', + min: '1', + func: 'changeAddressWidth', + }, + dump: { + name: 'Core Dump', + type: 'button', + func: 'dump', + }, + reset: { + name: 'Reset', + type: 'button', + func: 'clearData', + }, +} +verilogRAM.prototype.objectType = 'verilogRAM' diff --git a/v0/src/simulator/src/setup.js b/v0/src/simulator/src/setup.js new file mode 100644 index 00000000..6992115c --- /dev/null +++ b/v0/src/simulator/src/setup.js @@ -0,0 +1,211 @@ +/* eslint-disable import/no-cycle */ +/* eslint-disable no-restricted-syntax */ +/* eslint-disable guard-for-in */ + +import { Tooltip } from 'bootstrap' +import metadata from './metadata.json' +import { generateId, showMessage } from './utils' +import backgroundArea from './backgroundArea' +import plotArea from './plotArea' +import simulationArea from './simulationArea' +import { dots } from './canvasApi' +import { update, updateSimulationSet, updateCanvasSet } from './engine' +import { setupUI } from './ux' +import startMainListeners from './listeners' +// import startEmbedListeners from './embedListeners' +import './embed' +import { newCircuit, scopeList } from './circuit' +import load from './data/load' +import save from './data/save' +import { showTourGuide } from './tutorials' +import setupModules from './moduleSetup' +import 'codemirror/lib/codemirror.css' +import 'codemirror/addon/hint/show-hint.css' +import 'codemirror/mode/javascript/javascript' // verilog.js from codemirror is not working because array prototype is changed. +import 'codemirror/addon/edit/closebrackets' +import 'codemirror/addon/hint/anyword-hint' +import 'codemirror/addon/hint/show-hint' +import { setupCodeMirrorEnvironment } from './Verilog2CV' +// import { keyBinder } from '#/components/DialogBox/CustomShortcut.vue' +import '../vendor/jquery-ui.min.css' +import '../vendor/jquery-ui.min' +import { confirmSingleOption } from '#/components/helpers/confirmComponent/ConfirmComponent.vue' +import { getToken } from '#/pages/simulatorHandler.vue' + +/** + * to resize window and setup things it + * sets up new width for the canvas variables. + * Also redraws the grid. + * @category setup + */ +export function resetup() { + DPR = window.devicePixelRatio || 1 + if (lightMode) { + DPR = 1 + } + width = document.getElementById('simulationArea').clientWidth * DPR + if (!embed) { + height = + (document.body.clientHeight - + document.getElementById('toolbar').clientHeight) * + DPR + } else { + height = document.getElementById('simulation').clientHeight * DPR + } + // setup simulationArea and backgroundArea variables used to make changes to canvas. + backgroundArea.setup() + simulationArea.setup() + // redraw grid + dots() + document.getElementById('backgroundArea').style.height = + height / DPR + 100 + 'px' + document.getElementById('backgroundArea').style.width = + width / DPR + 100 + 'px' + document.getElementById('canvasArea').style.height = height / DPR + 'px' + simulationArea.canvas.width = width + simulationArea.canvas.height = height + backgroundArea.canvas.width = width + 100 * DPR + backgroundArea.canvas.height = height + 100 * DPR + if (!embed) { + plotArea.setup() + } + updateCanvasSet(true) + update() // INEFFICIENT, needs to be deprecated + simulationArea.prevScale = 0 + dots() +} + +window.onresize = resetup // listener +window.onorientationchange = resetup // listener + +// for mobiles +window.addEventListener('orientationchange', resetup) // listener + +/** + * function to setup environment variables like projectId and DPR + * @category setup + */ +function setupEnvironment() { + setupModules() + const projectId = generateId() + window.projectId = projectId + updateSimulationSet(true) + // const DPR = window.devicePixelRatio || 1 // unused variable + newCircuit('Main') + window.data = {} + resetup() + setupCodeMirrorEnvironment() +} + +/** + * It initializes some useful array which are helpful + * while simulating, saving and loading project. + * It also draws icons in the sidebar + * @category setup + */ +function setupElementLists() { + // $('#menu').empty() + + window.circuitElementList = metadata.circuitElementList + window.annotationList = metadata.annotationList + window.inputList = metadata.inputList + window.subCircuitInputList = metadata.subCircuitInputList + window.moduleList = [...circuitElementList, ...annotationList] + window.updateOrder = [ + 'wires', + ...circuitElementList, + 'nodes', + ...annotationList, + ] // Order of update + window.renderOrder = [...moduleList.slice().reverse(), 'wires', 'allNodes'] // Order of render +} + +/** + * Fetches project data from API and loads it into the simulator. + * @param {number} projectId The ID of the project to fetch data for + * @category setup + */ +async function fetchProjectData(projectId) { + try { + const response = await fetch( + `/api/v1/projects/${projectId}/circuit_data`, + { + method: 'GET', + headers: { + Accept: 'application/json', + Authorization: `Token ${getToken('cvt')}`, + }, + } + ) + if (response.ok) { + const data = await response.json() + await load(data) + await simulationArea.changeClockTime(data.timePeriod || 500) + $('.loadingIcon').fadeOut() + } else { + throw new Error('API call failed') + } + } catch (error) { + console.error(error) + confirmSingleOption('Error: Could not load.') + $('.loadingIcon').fadeOut() + } +} + +/** + * Load project data immediately when available. + * Improvement to eliminate delay caused by setTimeout in previous implementation revert if issues arise. + * @category setup + */ +async function loadProjectData() { + window.logixProjectId = window.logixProjectId ?? 0 + if (window.logixProjectId !== 0) { + $('.loadingIcon').fadeIn() + await fetchProjectData(window.logixProjectId) + } else if (localStorage.getItem('recover_login') && window.isUserLoggedIn) { + // Restore unsaved data and save + const data = JSON.parse(localStorage.getItem('recover_login')) + await load(data) + localStorage.removeItem('recover') + localStorage.removeItem('recover_login') + await save() + } else if (localStorage.getItem('recover')) { + // Restore unsaved data which didn't get saved due to error + showMessage( + "We have detected that you did not save your last work. Don't worry we have recovered them. Access them using Project->Recover" + ) + } +} + +/** + * Show tour guide if it hasn't been completed yet. + * The tour is shown after a delay of 2 seconds. + * @category setup + */ +function showTour() { + if (!localStorage.tutorials_tour_done && !embed) { + setTimeout(() => { + showTourGuide() + }, 2000) + } +} + +/** + * The first function to be called to setup the whole simulator. + * This function sets up the simulator environment, the UI, the listeners, + * loads the project data, and shows the tour guide. + * @category setup + */ +export function setup() { + // let embed = false + // const startListeners = embed ? startEmbedListeners : startMainListeners + setupElementLists() + setupEnvironment() + if (!embed) { + setupUI() + startMainListeners() + } + // startListeners() + loadProjectData() + showTour() +} diff --git a/v0/src/simulator/src/simulationArea.js b/v0/src/simulator/src/simulationArea.js new file mode 100644 index 00000000..3784988a --- /dev/null +++ b/v0/src/simulator/src/simulationArea.js @@ -0,0 +1,99 @@ +/* eslint-disable import/no-cycle */ +import EventQueue from './eventQueue' +import { clockTick } from './utils' + +/** + * simulation environment object - holds simulation canvas + * @type {Object} simulationArea + * @property {HTMLCanvasElement} canvas + * @property {boolean} selected + * @property {boolean} hover + * @property {number} clockState + * @property {boolean} clockEnabled + * @property {undefined} lastSelected + * @property {Array} stack + * @property {number} prevScale + * @property {number} oldx + * @property {number} oldy + * @property {Array} objectList + * @property {number} maxHeight + * @property {number} maxWidth + * @property {number} minHeight + * @property {number} minWidth + * @property {Array} multipleObjectSelections + * @property {Array} copyList - List of selected elements + * @property {boolean} shiftDown - shift down or not + * @property {boolean} controlDown - contol down or not + * @property {number} timePeriod - time period + * @property {number} mouseX - mouse x + * @property {number} mouseY - mouse y + * @property {number} mouseDownX - mouse click x + * @property {number} mouseDownY - mouse click y + * @property {Array} simulationQueue - simulation queue + * @property {number} clickCount - number of clicks + * @property {string} lock - locked or unlocked + * @property {function} timer - timer + * @property {function} setup - to setup the simulaton area + * @property {function} changeClockTime - change clock time + * @property {function} clear - clear the simulation area + * @category simulationArea + */ +const simulationArea = { + canvas: document.getElementById('simulationArea'), + selected: false, + hover: false, + clockState: 0, + clockEnabled: true, + lastSelected: undefined, + stack: [], + prevScale: 0, + oldx: 0, + oldy: 0, + objectList: [], + maxHeight: 0, + maxWidth: 0, + minHeight: 0, + minWidth: 0, + multipleObjectSelections: [], + copyList: [], + shiftDown: false, + controlDown: false, + timePeriod: 500, + mouseX: 0, + mouseY: 0, + mouseDownX: 0, + mouseDownY: 0, + simulationQueue: undefined, + multiAddElement: false, + + clickCount: 0, // double click + lock: 'unlocked', + timer() { + ckickTimer = setTimeout(() => { + simulationArea.clickCount = 0 + }, 600) + }, + + setup() { + this.canvas = document.getElementById('simulationArea') + this.canvas.width = width + this.canvas.height = height + this.simulationQueue = new EventQueue(10000) + this.context = this.canvas.getContext('2d') + simulationArea.changeClockTime(simulationArea.timePeriod) + this.mouseDown = false + }, + changeClockTime(t) { + if (t < 50) return + clearInterval(simulationArea.ClockInterval) + t = t || prompt('Enter Time Period:') + simulationArea.timePeriod = t + simulationArea.ClockInterval = setInterval(clockTick, t) + }, + clear() { + if (!this.context) return + this.context.clearRect(0, 0, this.canvas.width, this.canvas.height) + }, +} +export const { changeClockTime } = simulationArea +export default simulationArea diff --git a/v0/src/simulator/src/subcircuit.js b/v0/src/simulator/src/subcircuit.js new file mode 100644 index 00000000..85fdc53e --- /dev/null +++ b/v0/src/simulator/src/subcircuit.js @@ -0,0 +1,747 @@ +/* eslint-disable import/no-cycle */ +import Scope, { scopeList, switchCircuit } from './circuit' +import CircuitElement from './circuitElement' +import simulationArea from './simulationArea' +import { scheduleBackup, checkIfBackup } from './data/backupCircuit' +import { + scheduleUpdate, + updateSimulationSet, + updateCanvasSet, + updateSubcircuitSet, + forceResetNodesSet, +} from './engine' +import { loadScope } from './data/load' +import { showError } from './utils' + +import Node, { findNode } from './node' +import { fillText, correctWidth, rect2 } from './canvasApi' +import { colors } from './themer/themer' +import { layoutModeGet } from './layoutMode' +import { verilogModeGet } from './Verilog2CV' +import { sanitizeLabel } from './verilogHelpers' +import { SimulatorStore } from '#/store/SimulatorStore/SimulatorStore' +/** + * Function to load a subcicuit + * @category subcircuit + */ +export function loadSubCircuit(savedData, scope) { + new SubCircuit(savedData.x, savedData.y, scope, savedData.id, savedData) +} + +/** + * Prompt to create subcircuit, shows list of circuits which dont depend on the current circuit + * @param {Scope=} scope + * @category subcircuit + */ +export function createSubCircuitPrompt(scope = globalScope) { + if (verilogModeGet() || layoutModeGet()) { + showError('Subcircuit cannot be inserted in this mode') + return + } + const simulatorStore = SimulatorStore() + simulatorStore.dialogBox.insertsubcircuit_dialog = true + /* + $('#insertSubcircuitDialog').empty() + let flag = true + for (id in scopeList) { + if ( + !scopeList[id].checkDependency(scope.id) && + scopeList[id].isVisible() + ) { + flag = false + $('#insertSubcircuitDialog').append( + `` + ) + } + } + if (flag) + $('#insertSubcircuitDialog').append( + "

Looks like there are no other circuits which doesn't have this circuit as a dependency. Create a new one!

" + ) + $('#insertSubcircuitDialog').dialog({ + resizable: false, + maxHeight: 800, + width: 450, + maxWidth: 800, + minWidth: 250, + buttons: !flag + ? [ + { + text: 'Insert SubCircuit', + click() { + if (!$('input[name=subCircuitId]:checked').val()) + return + simulationArea.lastSelected = new SubCircuit( + undefined, + undefined, + globalScope, + $('input[name=subCircuitId]:checked').val() + ) + $(this).dialog('close') + }, + }, + ] + : [], + }) + */ +} + +/** + * @class + * @extends CircuitElement + * @param {number} x - x coord of subcircuit + * @param {number} y - y coord of subcircuit + * @param {Scope=} scope - the circuit in which subcircuit has been added + * @param {string} id - the id of the subcircuit scope + * @param {JSON} savedData - the saved data + * @category subcircuit + */ +export default class SubCircuit extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + id = undefined, + savedData = undefined + ) { + super(x, y, scope, 'RIGHT', 1) // super call + this.objectType = 'SubCircuit' + this.scope.SubCircuit.push(this) + this.id = id || prompt('Enter Id: ') + this.directionFixed = true + this.fixedBitWidth = true + this.savedData = savedData + this.inputNodes = [] + this.outputNodes = [] + this.localScope = new Scope() + this.preventCircuitSwitch = false // prevents from switching circuit if double clicking element + this.rectangleObject = false + + var subcircuitScope = scopeList[this.id] // Scope of the subcircuit + // Error handing + if (subcircuitScope == undefined) { + // if no such scope for subcircuit exists + showError( + `SubCircuit : ${ + (savedData && savedData.title) || this.id + } Not found` + ) + } else if (!checkIfBackup(subcircuitScope)) { + // if there is no input/output nodes there will be no backup + showError( + `SubCircuit : ${ + (savedData && savedData.title) || subcircuitScope.name + } is an empty circuit` + ) + } else if (subcircuitScope.checkDependency(scope.id)) { + // check for cyclic dependency + showError('Cyclic Circuit Error') + } + // Error handling, cleanup + if ( + subcircuitScope == undefined || + subcircuitScope.checkDependency(scope.id) + ) { + if (savedData) { + for (var i = 0; i < savedData.inputNodes.length; i++) { + scope.allNodes[savedData.inputNodes[i]].deleted = true + } + for (var i = 0; i < savedData.outputNodes.length; i++) { + scope.allNodes[savedData.outputNodes[i]].deleted = true + } + } + return + } + + if (this.savedData != undefined) { + updateSubcircuitSet(true) + scheduleUpdate() + this.version = this.savedData.version || '1.0' + + this.id = this.savedData.id + this.label = this.savedData.label || '' + this.labelDirection = this.savedData.labelDirection || 'RIGHT' + for (var i = 0; i < this.savedData.inputNodes.length; i++) { + this.inputNodes.push( + this.scope.allNodes[this.savedData.inputNodes[i]] + ) + this.inputNodes[i].parent = this + this.inputNodes[i].layout_id = + subcircuitScope.Input[i]?.layoutProperties.id + } + for (var i = 0; i < this.savedData.outputNodes.length; i++) { + this.outputNodes.push( + this.scope.allNodes[this.savedData.outputNodes[i]] + ) + this.outputNodes[i].parent = this + this.outputNodes[i].layout_id = + subcircuitScope.Output[i]?.layoutProperties.id + } + if (this.version == '1.0') { + // For backward compatibility + this.version = '2.0' + this.x -= subcircuitScope.layout.width / 2 + this.y -= subcircuitScope.layout.height / 2 + for (var i = 0; i < this.inputNodes.length; i++) { + this.inputNodes[i].x = + subcircuitScope.Input[i].layoutProperties.x + this.inputNodes[i].y = + subcircuitScope.Input[i].layoutProperties.y + this.inputNodes[i].leftx = this.inputNodes[i].x + this.inputNodes[i].lefty = this.inputNodes[i].y + } + for (var i = 0; i < this.outputNodes.length; i++) { + this.outputNodes[i].x = + subcircuitScope.Output[i].layoutProperties.x + this.outputNodes[i].y = + subcircuitScope.Output[i].layoutProperties.y + this.outputNodes[i].leftx = this.outputNodes[i].x + this.outputNodes[i].lefty = this.outputNodes[i].y + } + } + + if (this.version == '2.0') { + this.leftDimensionX = 0 + this.upDimensionY = 0 + this.rightDimensionX = subcircuitScope.layout.width + this.downDimensionY = subcircuitScope.layout.height + } + + this.nodeList.extend(this.inputNodes) + this.nodeList.extend(this.outputNodes) + } else { + this.version = '2.0' + } + + this.data = JSON.parse(scheduleBackup(subcircuitScope)) + this.buildCircuit() // load the localScope for the subcircuit + this.makeConnections() // which will be wireless + } + + /** + * actually make all connection but are invisible so + * it seems like the simulation is happening in other + * Scope but it actually is not. + */ + makeConnections() { + for (let i = 0; i < this.inputNodes.length; i++) { + this.localScope.Input[i]?.output1.connectWireLess( + this.inputNodes[i] + ) + this.localScope.Input[i].output1.subcircuitOverride = true + } + + for (let i = 0; i < this.outputNodes.length; i++) { + this.localScope.Output[i]?.inp1.connectWireLess(this.outputNodes[i]) + this.outputNodes[i].subcircuitOverride = true + } + } + + /** + * Function to remove wireless connections + */ + removeConnections() { + for (let i = 0; i < this.inputNodes.length; i++) { + this.localScope.Input[i]?.output1.disconnectWireLess( + this.inputNodes[i] + ) + } + + for (let i = 0; i < this.outputNodes.length; i++) { + this.localScope.Output[i]?.inp1.disconnectWireLess( + this.outputNodes[i] + ) + } + } + + /** + * loads the subcircuit and draws all the nodes + */ + buildCircuit() { + var subcircuitScope = scopeList[this.id] + loadScope(this.localScope, this.data) + this.localScope.name = this.data.name + this.lastUpdated = this.localScope.timeStamp + updateSimulationSet(true) + updateCanvasSet(true) + + if (this.savedData == undefined) { + this.leftDimensionX = 0 + this.upDimensionY = 0 + this.rightDimensionX = subcircuitScope.layout.width + this.downDimensionY = subcircuitScope.layout.height + for (var i = 0; i < subcircuitScope.Output.length; i++) { + var a = new Node( + subcircuitScope.Output[i].layoutProperties.x, + subcircuitScope.Output[i].layoutProperties.y, + 1, + this, + subcircuitScope.Output[i].bitWidth + ) + a.layout_id = subcircuitScope.Output[i].layoutProperties.id + this.outputNodes.push(a) + } + for (var i = 0; i < subcircuitScope.Input.length; i++) { + var a = new Node( + subcircuitScope.Input[i].layoutProperties.x, + subcircuitScope.Input[i].layoutProperties.y, + 0, + this, + subcircuitScope.Input[i].bitWidth + ) + a.layout_id = subcircuitScope.Input[i].layoutProperties.id + this.inputNodes.push(a) + } + } + } + + // Needs to be deprecated, removed + reBuild() { + // new SubCircuit(x = this.x, y = this.y, scope = this.scope, this.id); + // this.scope.backups = []; // Because all previous states are invalid now + // this.delete(); + // showMessage('Subcircuit: ' + subcircuitScope.name + ' has been reloaded.'); + } + + /** + * rebuilds the subcircuit if any change to localscope is made + */ + reBuildCircuit() { + this.data = JSON.parse(scheduleBackup(scopeList[this.id])) + this.localScope = new Scope(data.name) + loadScope(this.localScope, this.data) + this.lastUpdated = this.localScope.timeStamp + this.scope.timeStamp = this.localScope.timeStamp + } + + reset() { + this.removeConnections() + + var subcircuitScope = scopeList[this.id] + + for (var i = 0; i < subcircuitScope.SubCircuit.length; i++) { + subcircuitScope.SubCircuit[i].reset() + } + + // No Inputs or Outputs + let emptyCircuit = + subcircuitScope.Input.length == 0 && + subcircuitScope.Output.length == 0 + // No LayoutElements + for (let element of circuitElementList) { + if ( + subcircuitScope[element].length > 0 && + subcircuitScope[element][0].canShowInSubcircuit + ) { + emptyCircuit = false + break + } + } + + if (emptyCircuit) { + showError( + `SubCircuit : ${subcircuitScope.name} is an empty circuit` + ) + } + + subcircuitScope.layout.height = subcircuitScope.layout.height + subcircuitScope.layout.width = subcircuitScope.layout.width + this.leftDimensionX = 0 + this.upDimensionY = 0 + this.rightDimensionX = subcircuitScope.layout.width + this.downDimensionY = subcircuitScope.layout.height + + var temp_map_inp = {} + for (var i = 0; i < subcircuitScope.Input.length; i++) { + temp_map_inp[subcircuitScope.Input[i].layoutProperties.id] = [ + subcircuitScope.Input[i], + undefined, + ] + } + for (var i = 0; i < this.inputNodes.length; i++) { + if (temp_map_inp.hasOwnProperty(this.inputNodes[i].layout_id)) { + temp_map_inp[this.inputNodes[i].layout_id][1] = + this.inputNodes[i] + } else { + this.scope.backups = [] + this.inputNodes[i].delete() + this.nodeList.clean(this.inputNodes[i]) + } + } + + for (id in temp_map_inp) { + if (temp_map_inp[id][1]) { + if ( + temp_map_inp[id][0].layoutProperties.x == + temp_map_inp[id][1].x && + temp_map_inp[id][0].layoutProperties.y == + temp_map_inp[id][1].y + ) { + temp_map_inp[id][1].bitWidth = temp_map_inp[id][0].bitWidth + } else { + this.scope.backups = [] + temp_map_inp[id][1].delete() + this.nodeList.clean(temp_map_inp[id][1]) + temp_map_inp[id][1] = new Node( + temp_map_inp[id][0].layoutProperties.x, + temp_map_inp[id][0].layoutProperties.y, + 0, + this, + temp_map_inp[id][0].bitWidth + ) + temp_map_inp[id][1].layout_id = id + } + } + } + + this.inputNodes = [] + for (var i = 0; i < subcircuitScope.Input.length; i++) { + var input = + temp_map_inp[subcircuitScope.Input[i].layoutProperties.id][0] + if (temp_map_inp[input.layoutProperties.id][1]) { + this.inputNodes.push(temp_map_inp[input.layoutProperties.id][1]) + } else { + var a = new Node( + input.layoutProperties.x, + input.layoutProperties.y, + 0, + this, + input.bitWidth + ) + a.layout_id = input.layoutProperties.id + this.inputNodes.push(a) + } + } + + var temp_map_out = {} + for (var i = 0; i < subcircuitScope.Output.length; i++) { + temp_map_out[subcircuitScope.Output[i].layoutProperties.id] = [ + subcircuitScope.Output[i], + undefined, + ] + } + for (var i = 0; i < this.outputNodes.length; i++) { + if (temp_map_out.hasOwnProperty(this.outputNodes[i].layout_id)) { + temp_map_out[this.outputNodes[i].layout_id][1] = + this.outputNodes[i] + } else { + this.outputNodes[i].delete() + this.nodeList.clean(this.outputNodes[i]) + } + } + + for (id in temp_map_out) { + if (temp_map_out[id][1]) { + if ( + temp_map_out[id][0].layoutProperties.x == + temp_map_out[id][1].x && + temp_map_out[id][0].layoutProperties.y == + temp_map_out[id][1].y + ) { + temp_map_out[id][1].bitWidth = temp_map_out[id][0].bitWidth + } else { + temp_map_out[id][1].delete() + this.nodeList.clean(temp_map_out[id][1]) + temp_map_out[id][1] = new Node( + temp_map_out[id][0].layoutProperties.x, + temp_map_out[id][0].layoutProperties.y, + 1, + this, + temp_map_out[id][0].bitWidth + ) + temp_map_out[id][1].layout_id = id + } + } + } + + this.outputNodes = [] + for (var i = 0; i < subcircuitScope.Output.length; i++) { + var output = + temp_map_out[subcircuitScope.Output[i].layoutProperties.id][0] + if (temp_map_out[output.layoutProperties.id][1]) { + this.outputNodes.push( + temp_map_out[output.layoutProperties.id][1] + ) + } else { + var a = new Node( + output.layoutProperties.x, + output.layoutProperties.y, + 1, + this, + output.bitWidth + ) + a.layout_id = output.layoutProperties.id + this.outputNodes.push(a) + } + } + + if (subcircuitScope.timeStamp > this.lastUpdated) { + this.reBuildCircuit() + } + + // Should this be done here or only when this.reBuildCircuit() is called? + { + this.localScope.reset() + updateSimulationSet(true) + forceResetNodesSet(true) + } + + this.makeConnections() + } + + /** + * Procedure after a element is clicked inside a subcircuit + **/ + click() { + var elementClicked = this.getElementHover() + if (elementClicked) { + this.lastClickedElement = elementClicked + elementClicked.wasClicked = true + } + } + + getElementHover() { + var rX = this.layoutProperties.rightDimensionX + var lX = this.layoutProperties.leftDimensionX + var uY = this.layoutProperties.upDimensionY + var dY = this.layoutProperties.downDimensionY + + for (let el of circuitElementList) { + if (this.localScope[el].length === 0) continue + if (!this.localScope[el][0].canShowInSubcircuit) continue + for (let i = 0; i < this.localScope[el].length; i++) { + var obj = this.localScope[el][i] + if ( + obj.subcircuitMetadata.showInSubcircuit && + obj.isSubcircuitHover(this.x, this.y) + ) { + return obj + } + } + } + } + + /** + * Sets the elements' wasClicked property in the subcircuit to false + **/ + releaseClick() { + if (this.lastClickedElement !== undefined) { + this.lastClickedElement.wasClicked = false + this.lastClickedElement = undefined + } + } + + /** + * adds all local scope inputs to the global scope simulation queue + */ + addInputs() { + for (let i = 0; i < subCircuitInputList.length; i++) { + for ( + let j = 0; + j < this.localScope[subCircuitInputList[i]].length; + j++ + ) { + simulationArea.simulationQueue.add( + this.localScope[subCircuitInputList[i]][j], + 0 + ) + } + } + for (let j = 0; j < this.localScope.SubCircuit.length; j++) { + this.localScope.SubCircuit[j].addInputs() + } + } + + /** + * Procedure if any element is double clicked inside a subcircuit + **/ + dblclick() { + if (this.elementHover) return + switchCircuit(this.id) + } + + /** + * Returns a javascript object of subcircuit data. + * Does not include data of subcircuit elements apart from Input and Output (that is a part of element.subcircuitMetadata) + **/ + saveObject() { + var data = { + x: this.x, + y: this.y, + id: this.id, + label: this.label, + labelDirection: this.labelDirection, + inputNodes: this.inputNodes.map(findNode), + outputNodes: this.outputNodes.map(findNode), + version: this.version, + } + return data + } + + /** + * By design, subcircuit element's input and output nodes are wirelessly + * connected to the localscope (clone of the scope of the subcircuit's + * circuit). So it is almost like the actual circuit is copied in the + * location of the subcircuit element. Therefore no resolve needed. + */ + isResolvable() { + return false + } + + /** + * If element not resolvable (always in subcircuits), removePropagation + * is called on it. + */ + removePropagation() { + // Leave this to the scope of the subcircuit. Do nothing. + } + + verilogName() { + return sanitizeLabel(scopeList[this.id].name) + } + /** + * determines where to show label + */ + determine_label(x, y) { + if (x == 0) return ['left', 5, 5] + if (x == scopeList[this.id].layout.width) return ['right', -5, 5] + if (y == 0) return ['center', 0, 13] + return ['center', 0, -6] + } + + checkHover() { + super.checkHover() + if (this.elementHover) { + this.elementHover.hover = false + this.elementHover = undefined + simulationArea.hover = undefined + } + var elementHover = this.getElementHover() + if (elementHover) { + elementHover.hover = true + this.elementHover = elementHover + this.hover = false + simulationArea.hover = elementHover + } + } + + /** + * Draws the subcircuit (and contained elements) on the screen when the subcircuit is included + in another circuit + **/ + customDraw() { + var subcircuitScope = scopeList[this.id] + + var ctx = simulationArea.context + + ctx.lineWidth = globalScope.scale * 3 + ctx.strokeStyle = colors['stroke'] // ("rgba(0,0,0,1)"); + ctx.fillStyle = colors['fill'] + var xx = this.x + var yy = this.y + + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.lineWidth = correctWidth(3) + ctx.beginPath() + rect2( + ctx, + -this.leftDimensionX, + -this.upDimensionY, + this.leftDimensionX + this.rightDimensionX, + this.upDimensionY + this.downDimensionY, + this.x, + this.y, + [this.direction, 'RIGHT'][+this.directionFixed] + ) + if (!this.elementHover) { + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + } + ctx.fill() + ctx.stroke() + + ctx.beginPath() + + ctx.textAlign = 'center' + ctx.fillStyle = 'black' + if (this.version == '1.0') { + fillText( + ctx, + subcircuitScope.name, + xx, + yy - subcircuitScope.layout.height / 2 + 13, + 11 + ) + } else if (this.version == '2.0') { + if (subcircuitScope.layout.titleEnabled) { + fillText( + ctx, + subcircuitScope.name, + subcircuitScope.layout.title_x + xx, + yy + subcircuitScope.layout.title_y, + 11 + ) + } + } else { + } + + for (var i = 0; i < subcircuitScope.Input.length; i++) { + if (!subcircuitScope.Input[i].label) continue + var info = this.determine_label( + this.inputNodes[i].x, + this.inputNodes[i].y + ) + ctx.textAlign = info[0] + fillText( + ctx, + subcircuitScope.Input[i].label, + this.inputNodes[i].x + info[1] + xx, + yy + this.inputNodes[i].y + info[2], + 12 + ) + } + + for (var i = 0; i < subcircuitScope.Output.length; i++) { + if (!subcircuitScope.Output[i].label) continue + var info = this.determine_label( + this.outputNodes[i].x, + this.outputNodes[i].y + ) + ctx.textAlign = info[0] + fillText( + ctx, + subcircuitScope.Output[i].label, + this.outputNodes[i].x + info[1] + xx, + yy + this.outputNodes[i].y + info[2], + 12 + ) + } + ctx.fill() + for (let i = 0; i < this.outputNodes.length; i++) { + this.outputNodes[i].draw() + } + for (let i = 0; i < this.inputNodes.length; i++) { + this.inputNodes[i].draw() + } + + // draw subcircuitElements + for (let el of circuitElementList) { + if (this.localScope[el].length === 0) continue + if (!this.localScope[el][0].canShowInSubcircuit) continue + for (let i = 0; i < this.localScope[el].length; i++) { + if ( + this.localScope[el][i].subcircuitMetadata.showInSubcircuit + ) { + this.localScope[el][i].drawLayoutMode(this.x, this.y) + } + } + } + } +} +SubCircuit.prototype.centerElement = true // To center subcircuit when new +SubCircuit.prototype.propagationDelayFixed = true diff --git a/v0/src/simulator/src/testCreator.js b/v0/src/simulator/src/testCreator.js new file mode 100644 index 00000000..4a1708c2 --- /dev/null +++ b/v0/src/simulator/src/testCreator.js @@ -0,0 +1,780 @@ +/* + This file contains all javascript related to the test creator UI + at /testbench +*/ + +import _ from '../vendor/table2csv' + +const CREATORMODE = { + NORMAL: 0, + SIMULATOR_POPUP: 1, +} + +var testMode = 'comb' +var groupIndex = 0 +var inputCount = 0 +var nextInputIndex = 0 +var outputCount = 0 +var nextOutputIndex = 0 +var cases = [0] +var creatorMode = CREATORMODE.NORMAL +var circuitScopeID + +function dataReset() { + groupIndex = -1 + cases = [0] +} + +/** + * Onload, check if it is opened in a popup. + * Check if test is being edited, or created + */ +window.onload = () => { + const query = new URLSearchParams(window.location.search) + if (query.has('popUp')) { + if (query.get('popUp') == 'true') { + creatorMode = CREATORMODE.SIMULATOR_POPUP + $('.right-button-group').append( + '' + ) + } + } + if (query.has('data')) { + $('#tb-creator-head').html('Edit Test') + circuitScopeID = query.get('scopeID') + loadData(query.get('data')) + return + } + + if (query.has('result')) { + $('#tb-creator-head').html('Test Result') + loadResult(query.get('result')) + readOnlyUI() + return + } + + circuitScopeID = query.get('scopeID') + addInput() + addOutput() + makeSortable() +} + +/* Change UI testMode between Combinational(comb) and Sequential(seq) */ +function changeTestMode(m) { + if (testMode === m) return false + dataReset() + testMode = m + $('#combSelect').removeClass('tab-selected') + $('#seqSelect').removeClass('tab-selected') + $('#tb-new-group').css('visibility', m === 'seq' ? 'visible' : 'hidden') + $(`#${m}Select`).addClass('tab-selected') + $('#dataGroup').empty() + + return true +} + +/* Adds case to a group */ +function addCase(grp) { + const currentGroupTable = $(`#data-table-${grp + 1}`) + + let s = + '
\n' + for (let i = 0; i < inputCount + outputCount; i++) + s += '0' + s += '' + + // Sortable hack + currentGroupTable.find('tbody').remove() + currentGroupTable.append(s) +} + +/* Deletes case from a group */ +function deleteCase(element) { + const row = element.parent().parent() + const grp = Number(row.parent().attr('id').split('-').pop()) + + row.remove() +} + +/* Adds group with default name 'Group N' or name supplied in @param groupName */ +/* Used without params by UI, used with params by loadData() */ +function addGroup( + groupName = `${testMode === 'comb' ? 'Group' : 'Set'} ${groupIndex + 2}` +) { + $('.plus-button').removeClass('latest-button') + groupIndex++ + + const s = ` +
+

${escapeHtml(groupName)}

+
Click + to add tests to the ${ + testMode === 'comb' ? 'group' : 'set' + }
+ + +
+ +
+ ` + cases[groupIndex] = 0 + $('#dataGroup').append(s) + + makeSortable() +} + +/* Deletes a group */ +function deleteGroup(element) { + const groupDiv = element.parent() + const grp = Number(groupDiv.attr('id').split('-').pop()) + groupDiv.remove() +} + +/* Adds input with default value 0 or values supplied in @param inputData */ +/* Used without params for UI, used with params by loadData() */ +function addInput( + label = `inp${nextInputIndex + 1}`, + bitwidth = 1, + inputData = [] +) { + nextInputIndex++ + inputCount++ + // Change head table contents + const sHead = `${escapeHtml( + label + )} ` + const sData = `${escapeHtml( + bitwidth.toString() + )}` + $('#testBenchTable') + .find('tr') + .eq(1) + .find('th') + .eq(inputCount - 1) + .after(sHead) + $('#testBenchTable') + .find('tr') + .eq(2) + .find('td') + .eq(inputCount - 1) + .after(sData) + $('#tb-inputs-head').attr('colspan', inputCount) + + // Change data tables' contents + $('#dataGroup') + .find('table') + .each(function (group_i) { + $(this) + .find('tr') + .each(function (case_i) { + const s = `${ + inputData.length + ? escapeHtml(inputData[group_i][case_i]) + : 0 + }` + $(this) + .find('td') + .eq(inputCount - 1) + .after(s) + }) + }) +} + +/* Adds output with default value 0 or values supplied in @param outputData */ +/* Used without params for UI, used with params by loadData() */ +/* Used with resultData and result=true for setting result */ +function addOutput( + label = `out${nextOutputIndex + 1}`, + bitwidth = 1, + outputData = [], + result = false, + resultData = [] +) { + nextOutputIndex++ + outputCount++ + // Change head table contents + let sHead = `${escapeHtml( + label + )} ` + let sData = `${escapeHtml( + bitwidth.toString() + )}` + + // If result then set colspan to 2 + if (result) { + sHead = `${escapeHtml( + label + )} ` + sData = `${escapeHtml( + bitwidth.toString() + )}` + } + + $('#testBenchTable') + .find('tr') + .eq(1) + .find('th') + .eq(inputCount + outputCount - 1) + .after(sHead) + $('#testBenchTable') + .find('tr') + .eq(2) + .find('td') + .eq(inputCount + outputCount - 1) + .after(sData) + // If not result then colspan is outputCount + $('#tb-outputs-head').attr('colspan', outputCount) + // else it's 2*outputCount + if (result) { + $('#tb-outputs-head').attr('colspan', 2 * outputCount) + } + + // Change data tables' contents + + // If not result just add the outputs + if (!result) { + $('#dataGroup') + .find('table') + .each(function (group_i) { + $(this) + .find('tr') + .each(function (case_i) { + const s = `${ + outputData.length + ? escapeHtml(outputData[group_i][case_i]) + : 0 + }` + $(this) + .find('td') + .eq(inputCount + outputCount - 1) + .after(s) + }) + }) + + // If result then add results besides the outputs + // Hacky + } else { + $('#dataGroup') + .find('table') + .each(function (group_i) { + $(this) + .find('tr') + .each(function (case_i) { + // Add the outputs (expected values) + const outputCellData = `${escapeHtml( + outputData[group_i][case_i] + )}` + $(this) + .find('td') + .eq(inputCount + 2 * (outputCount - 1)) + .after(outputCellData) + + // Add the actual values + const resultColor = + resultData[group_i][case_i] === + outputData[group_i][case_i] + ? 'green' + : 'red' + const resultCellData = `${escapeHtml( + resultData[group_i][case_i] + )}` + $(this) + .find('td') + .eq(inputCount + 2 * outputCount - 1) + .after(resultCellData) + }) + }) + } +} + +/* Deletes input unless there's only one input */ +function deleteInput(element) { + if (inputCount === 1) return + const columnIndex = element.parent().eq(0).index() + + $('#testBenchTable tr, .data-group tr') + .slice(1) + .each(function () { + $(this).find('td, th').eq(columnIndex).remove() + }) + + inputCount-- + $('#tb-inputs-head').attr('colspan', inputCount) +} + +/* Deletes output unless there's only one output */ +function deleteOutput(element) { + if (outputCount === 1) return + const columnIndex = element.parent().eq(0).index() + + $('#testBenchTable tr, .data-group tr') + .slice(1) + .each(function () { + $(this).find('td, th').eq(columnIndex).remove() + }) + + outputCount-- + $('#tb-outputs-head').attr('colspan', outputCount) +} + +/* Returns input/output(keys) and their bitwidths(values) */ +/* Called by getData() */ +function getBitWidths() { + const bitwidths = {} + $('#testBenchTable') + .find('tr') + .eq(1) + .find('th') + .slice(1) + .each(function (index) { + const inp = $(this).text() + const bw = $('#testBenchTable') + .find('tr') + .eq(2) + .find('td') + .slice(1) + .eq(index) + .html() + bitwidths[inp] = Number(bw) + }) + return bitwidths +} + +/* Returns data for all the groups for all inputs and outputs */ +/* Called by parse() */ +function getData() { + const bitwidths = getBitWidths() + const groups = [] + const groupCount = $('#dataGroup').children().length + for (let group_i = 0; group_i < groupCount; group_i++) { + const group = {} + group.label = getGroupTitle(group_i) + group.inputs = [] + group.outputs = [] + + const group_table = $(`#data-table-${group_i + 1}`) + group.n = group_table.find('tr').length + + // Push all the inputs in the group + for (let inp_i = 0; inp_i < inputCount; inp_i++) { + const label = Object.keys(bitwidths)[inp_i] + const input = { + label: label.slice(0, label.length - 1), + bitWidth: bitwidths[label], + values: [], + } + group_table.find('tr').each(function () { + input.values.push($(this).find('td').slice(1).eq(inp_i).html()) + }) + + group.inputs.push(input) + } + + // Push all the outputs in the group + for (let out_i = 0; out_i < outputCount; out_i++) { + const label = Object.keys(bitwidths)[inputCount + out_i] + const output = { + label: label.slice(0, label.length - 1), + bitWidth: bitwidths[label], + values: [], + } + group_table.find('tr').each(function () { + output.values.push( + $(this) + .find('td') + .slice(1) + .eq(inputCount + out_i) + .html() + ) + }) + + group.outputs.push(output) + } + + groups.push(group) + } + + return groups +} + +function getTestTitle() { + return $('#test-title-label').text() +} + +function getGroupTitle(group_i) { + return $(`#data-group-title-${group_i + 1}`).text() +} + +/* Parse UI table into Javascript Object */ +function parse() { + const data = {} + const tableData = getData() + data.type = testMode + data.title = getTestTitle() + data.groups = tableData + return data +} + +/* Export test data as a CSV file */ +function exportAsCSV() { + let csvData = '' + csvData += 'Title,Test Type,Input Count,Output Count\n' + csvData += `${getTestTitle()},${testMode},${inputCount},${outputCount}\n\n\n` + csvData += $('table').eq(0).table2CSV() + csvData += '\n\n' + $('table') + .slice(1) + .each(function (group_i) { + csvData += getGroupTitle(group_i) + csvData += '\n' + csvData += $(this).table2CSV() + csvData += '\n\n' + }) + + download(`${getTestTitle()}.csv`, csvData) + return csvData +} + +/* + Imports data from CSV and loads into the table + To achieve this, first converts to JSON then uses request param to load json to table +*/ +function importFromCSV() { + const file = $('#csvFileInput').prop('files')[0] + const reader = new FileReader() + + // If circuitScopeID exists, ie. if popup opened from testbench, then use that to redirect + const query = new URLSearchParams(window.location.search) + // Preserve popup status while redirecting + const isPopup = query.get('popUp') || false + + // When the file is read, redirect to the data location + reader.onload = () => { + const csvContent = reader.result + const jsonData = csv2json(csvContent, 1, 1) + + window.location = `/testbench?scopeID=${ + circuitScopeID || '' + }&data=${jsonData}&popUp=${isPopup}` + } + + reader.readAsText(file) +} + +// Clicks the hidden upload file button, entrypoint into importFromCSV() +// The hidden button in-turn calls importFromCSV() +function clickUpload() { + $('#csvFileInput').click() +} + +/* Converts CSV to JSON to be loaded into the table */ +function csv2json(csvFileData) { + const stripQuotes = (str) => str.replaceAll('"', '') + + /* Extracts bitwidths from the csv data */ + const getBitWidthsCSV = (csvDataBW) => { + const testMetadata = csvDataBW.split('\n\n')[0].split('\n') + const labels = testMetadata[1] + .split(',') + .slice(1) + .map((label) => stripQuotes(label)) + const bitWidths = testMetadata[2] + .split(',') + .slice(1) + .map((bw) => Number(stripQuotes(bw))) + + return { labels, bitWidths } + } + + const csvMetadata = csvFileData.split('\n\n\n')[0].split('\n')[1].split(',') + const csvData = csvFileData.split('\n\n\n')[1] + const jsonData = {} + + jsonData.title = csvMetadata[0] + jsonData.type = csvMetadata[1] + const inputCountCSV = Number(csvMetadata[2]) + const outputCountCSV = Number(csvMetadata[3]) + + jsonData.groups = [] + const { labels, bitWidths } = getBitWidthsCSV(csvData) + + const groups = csvData.split('\n\n').slice(1) + for (let group_i = 0; group_i < groups.length - 1; group_i++) { + const rows = groups[group_i].split('\n') + jsonData.groups[group_i] = { + label: rows[0], + n: rows.length - 1, + inputs: [], + outputs: [], + } + + // Parse Inputs + for (let input_i = 0; input_i < inputCountCSV; input_i++) { + const thisInput = { + label: labels[input_i], + bitWidth: bitWidths[input_i], + values: [], + } + for (let case_i = 1; case_i < rows.length; case_i++) + thisInput.values.push( + stripQuotes(rows[case_i].split(',')[input_i + 1]) + ) + + jsonData.groups[group_i].inputs.push(thisInput) + } + + // Parse Outputs + for ( + let output_i = inputCountCSV; + output_i < inputCountCSV + outputCountCSV; + output_i++ + ) { + const thisOutput = { + label: labels[output_i], + bitWidth: bitWidths[output_i], + values: [], + } + for (let case_i = 1; case_i < rows.length; case_i++) { + thisOutput.values.push( + stripQuotes(rows[case_i].split(',')[output_i + 1]) + ) + } + + jsonData.groups[group_i].outputs.push(thisOutput) + } + } + + return JSON.stringify(jsonData) +} + +/* Helper function to download generated file */ +function download(filename, text) { + var element = document.createElement('a') + element.setAttribute( + 'href', + `data:text/plain;charset=utf-8,${encodeURIComponent(text)}` + ) + element.setAttribute('download', filename) + + element.style.display = 'none' + document.body.appendChild(element) + + element.click() + + document.body.removeChild(element) +} + +/** + * Called when Save is clicked. If opened in popup, sends message to parent window + * to attach test to the testbench. + */ +function saveData() { + const testData = parse() + + if (creatorMode === CREATORMODE.SIMULATOR_POPUP) { + const postData = { scopeID: circuitScopeID, testData } + window.opener.postMessage( + { type: 'testData', data: JSON.stringify(postData) }, + '*' + ) + window.close() + } +} + +/* Loads data from JSON string into the table */ +function loadData(dataJSON) { + const data = JSON.parse(dataJSON) + if (data.title) $('#test-title-label').text(data.title) + changeTestMode() + changeTestMode(data.type) + for (let group_i = 0; group_i < data.groups.length; group_i++) { + const group = data.groups[group_i] + addGroup(group.label) + for (let case_i = 0; case_i < group.inputs[0].values.length; case_i++) { + addCase(group_i) + } + } + + // Add input values + for (let input_i = 0; input_i < data.groups[0].inputs.length; input_i++) { + const input = data.groups[0].inputs[input_i] + const values = data.groups.map((group) => group.inputs[input_i].values) + + addInput(input.label, input.bitWidth, values) + } + + // Add output values + for ( + let output_i = 0; + output_i < data.groups[0].outputs.length; + output_i++ + ) { + const output = data.groups[0].outputs[output_i] + const values = data.groups.map( + (group) => group.outputs[output_i].values + ) + + addOutput(output.label, output.bitWidth, values) + } +} + +/** + * Loads result from JSON string into the testbench creator UI + */ +function loadResult(dataJSON) { + const data = JSON.parse(dataJSON) + if (data.title) $('#test-title-label').text(data.title) + changeTestMode() + changeTestMode(data.type) + for (let group_i = 0; group_i < data.groups.length; group_i++) { + const group = data.groups[group_i] + addGroup(group.label) + for (let case_i = 0; case_i < group.inputs[0].values.length; case_i++) { + addCase(group_i) + } + } + + // Add input values + for (let input_i = 0; input_i < data.groups[0].inputs.length; input_i++) { + const input = data.groups[0].inputs[input_i] + const values = data.groups.map((group) => group.inputs[input_i].values) + + addInput(input.label, input.bitWidth, values) + } + + // Add output values + for ( + let output_i = 0; + output_i < data.groups[0].outputs.length; + output_i++ + ) { + const output = data.groups[0].outputs[output_i] + const values = data.groups.map( + (group) => group.outputs[output_i].values + ) + const results = data.groups.map( + (group) => group.outputs[output_i].results + ) + const expectedOutputs = [] + const actualOutputs = [] + + for (let group_i = 0; group_i < values.length; group_i++) { + const groupExpectedOuts = [] + const groupActualOuts = [] + for (let val_i = 0; val_i < values[group_i].length; val_i++) { + groupExpectedOuts.push(values[group_i][val_i]) + groupActualOuts.push(results[group_i][val_i]) + } + + expectedOutputs.push(groupExpectedOuts) + actualOutputs.push(groupActualOuts) + } + addOutput( + `${output.label}`, + output.bitWidth, + expectedOutputs, + true, + actualOutputs + ) + } +} + +/** + * Makes the UI read only for displaying results + */ +function readOnlyUI() { + makeContentUneditable() + makeUnsortable() + $('.lower-button, .table-button, .tb-minus').hide() + $('.tablink').attr('disabled', 'disabled') + $('.tablink').removeClass('tablink-no-override') + $('.data-group-info').text('') +} + +function makeContentUneditable() { + $('body') + .find('td, th, span, h3, div') + .each(function () { + $(this).attr('contenteditable', 'false') + }) +} + +function makeSortable() { + const helper = function (e, ui) { + const helperE = ui.clone() + helperE.children().each(function (child_i) { + $(this).width(ui.children().eq(child_i).width()) + }) + + return helperE + } + + function makePlaceholder(e, ui) { + ui.placeholder.children().each(function () { + $(this).css('border', '0px') + }) + } + + /* + Sortable hack: To allow sorting inside empty tables, the tables should have some height. + But it is not possible to give tables height without having rows, so we add a tbody. + tbody gives the table height but messes up all the other things. So we only keep tbody + if the table has no rows, and once table gets rows, we remove that tbody + */ + function removeTbody(e, ui) { + $(e.target).find('tbody').remove() + } + + function createTbody(e, ui) { + if ($(e.target).find('tr, tbody').length === 0) { + $(e.target).append('') + } + } + + $('.data-group table').sortable({ + handle: '.tb-handle', + helper, + start: makePlaceholder, + placeholder: 'clone', + connectWith: 'table', + receive: removeTbody, // For sortable hack + remove: createTbody, // For sortable hack + items: 'tr', + revert: 50, + scroll: false, + }) +} + +function makeUnsortable() { + $('.data-group table').sortable({ disabled: true }) +} + +function escapeHtml(unsafe) { + return unsafe + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, ''') +} + +// Making HTML called functions global + +window.addGroup = addGroup +window.deleteGroup = deleteGroup +window.addCase = addCase +window.deleteCase = deleteCase +window.addInput = addInput +window.deleteInput = deleteInput +window.addOutput = addOutput +window.deleteOutput = deleteOutput +window.parse = parse +window.saveData = saveData +window.changeTestMode = changeTestMode +window.exportAsCSV = exportAsCSV +window.importFromCSV = importFromCSV +window.csv2json = csv2json +window.clickUpload = clickUpload diff --git a/v0/src/simulator/src/testbench.js b/v0/src/simulator/src/testbench.js new file mode 100644 index 00000000..d6f8a070 --- /dev/null +++ b/v0/src/simulator/src/testbench.js @@ -0,0 +1,1140 @@ +/** + * This file contains all functions related the the testbench + * Contains the the testbench engine and UI modules + */ + +import { scheduleBackup } from './data/backupCircuit' +import { changeClockEnable } from './sequential' +import { play } from './engine' +import Scope from './circuit' +import { showMessage, escapeHtml } from './utils' +import { confirmOption } from '#/components/helpers/confirmComponent/ConfirmComponent.vue' + +/** + * @typedef {number} RunContext + */ +const CONTEXT = { + CONTEXT_SIMULATOR: 0, + CONTEXT_ASSIGNMENTS: 1, +} + +const VALIDATION_ERRORS = { + NOTPRESENT: 0, // Element is not present in the circuit + WRONGBITWIDTH: 1, // Element is present but has incorrect bitwidth + DUPLICATE_ID_DATA: 2, // Duplicate identifiers in test data + DUPLICATE_ID_SCOPE: 3, // Duplicate identifiers in scope + NO_RST: 4, // Sequential circuit but no reset(RST) in scope +} + +const TESTBENCH_CREATOR_PATH = '/testbench' + +// Do we have any other function to do this? +// Utility function. Converts decimal number to binary string +function dec2bin(dec, bitWidth = undefined) { + if (dec === undefined) return 'X' + const bin = (dec >>> 0).toString(2) + if (!bitWidth) return bin + + return '0'.repeat(bitWidth - bin.length) + bin +} + +/** + * Class to store all data related to the testbench and functions to use it + * @param {Object} data - Javascript object of the test data + * @param {number=} currentGroup - Current group index in the test + * @param {number=} currentCase - Current case index in the group + */ +export class TestbenchData { + constructor(data, currentGroup = 0, currentCase = 0) { + this.currentCase = currentCase + this.currentGroup = currentGroup + this.testData = data + } + + /** + * Checks whether given case-group pair exists in the test + */ + isCaseValid() { + if ( + this.currentGroup >= this.data.groups.length || + this.currentGroup < 0 + ) + return false + const caseCount = + this.testData.groups[this.currentGroup].inputs[0].values.length + if (this.currentCase >= caseCount || this.currentCase < 0) return false + + return true + } + + /** + * Validate and set case and group in the test + * @param {number} groupIndex - Group index to set + * @param {number} caseIndex - Case index to set + */ + setCase(groupIndex, caseIndex) { + const newCase = new TestbenchData(this.testData, groupIndex, caseIndex) + if (newCase.isCaseValid()) { + this.currentGroup = groupIndex + this.currentCase = caseIndex + return true + } + + return false + } + + /** + * Validate and go to the next group. + * Skips over empty groups + */ + groupNext() { + const newCase = new TestbenchData(this.testData, this.currentGroup, 0) + const groupCount = newCase.testData.groups.length + let caseCount = + newCase.testData.groups[newCase.currentGroup].inputs[0].values + .length + + while (caseCount === 0 || this.currentGroup === newCase.currentGroup) { + newCase.currentGroup++ + if (newCase.currentGroup >= groupCount) return false + caseCount = + newCase.testData.groups[newCase.currentGroup].inputs[0].values + .length + } + + this.currentGroup = newCase.currentGroup + this.currentCase = newCase.currentCase + return true + } + + /** + * Validate and go to the previous group. + * Skips over empty groups + */ + groupPrev() { + const newCase = new TestbenchData(this.testData, this.currentGroup, 0) + const groupCount = newCase.testData.groups.length + let caseCount = + newCase.testData.groups[newCase.currentGroup].inputs[0].values + .length + + while (caseCount === 0 || this.currentGroup === newCase.currentGroup) { + newCase.currentGroup-- + if (newCase.currentGroup < 0) return false + caseCount = + newCase.testData.groups[newCase.currentGroup].inputs[0].values + .length + } + + this.currentGroup = newCase.currentGroup + this.currentCase = newCase.currentCase + return true + } + + /** + * Validate and go to the next case + */ + caseNext() { + const caseCount = + this.testData.groups[this.currentGroup].inputs[0].values.length + if (this.currentCase >= caseCount - 1) return this.groupNext() + this.currentCase++ + return true + } + + /** + * Validate and go to the previous case + */ + casePrev() { + if (this.currentCase <= 0) { + if (!this.groupPrev()) return false + const caseCount = + this.testData.groups[this.currentGroup].inputs[0].values.length + this.currentCase = caseCount - 1 + return true + } + + this.currentCase-- + return true + } + + /** + * Finds and switches to the first non empty group to start the test from + */ + goToFirstValidGroup() { + const newCase = new TestbenchData(this.testData, 0, 0) + const caseCount = + newCase.testData.groups[this.currentGroup].inputs[0].values.length + + // If the first group is not empty, do nothing + if (caseCount > 0) return true + + // Otherwise go next until non empty group + const validExists = newCase.groupNext() + + // If all groups empty return false + if (!validExists) return false + + // else set case to the non empty group + this.currentGroup = newCase.currentGroup + this.currentCase = newCase.currentCase + return true + } +} + +/** + * UI Function + * Create prompt for the testbench UI when creator is opened + */ +function creatorOpenPrompt(creatorWindow) { + scheduleBackup() + const windowSVG = ` + + + + + ` + + const s = ` +
+
+ ${windowSVG} +
+

A browser pop-up is opened to create the test

+

Please save the test to open it here

+
+ ` + + $('#setTestbenchData').dialog({ + resizable: false, + width: 'auto', + buttons: [ + { + text: 'Close Pop-Up', + click() { + $(this).dialog('close') + creatorWindow.close() + }, + }, + ], + }) + + $('#setTestbenchData').empty() + $('#setTestbenchData').append(s) +} + +/** + * Interface function to run testbench. Called by testbench prompt on simulator or assignments + * @param {Object} data - Object containing Test Data + * @param {RunContext=} runContext - Whether simulator or Assignment called this function + * @param {Scope=} scope - the circuit + */ +export function runTestBench( + data, + scope = globalScope, + runContext = CONTEXT.CONTEXT_SIMULATOR +) { + const isValid = validate(data, scope) + if (!isValid.ok) { + showMessage( + 'Testbench: Some elements missing from circuit. Click Validate to know more' + ) + } + + if (runContext === CONTEXT.CONTEXT_SIMULATOR) { + const tempTestbenchData = new TestbenchData(data) + if (!tempTestbenchData.goToFirstValidGroup()) { + showMessage('Testbench: The test is empty') + return + } + + globalScope.testbenchData = tempTestbenchData + + updateTestbenchUI() + return + } + + if (runContext === CONTEXT.CONTEXT_ASSIGNMENTS) { + // Not implemented + } +} + +/** + * Updates the TestBench UI on the simulator with the current test attached + * If no test is attached then shows the 'No test attached' screen + * Called by runTestBench() when test is set, also called by UX/setupPanelListeners() + * whenever ux change requires this UI to update(such as clicking on a different circuit or + * loading a saved circuit) + */ +export function updateTestbenchUI() { + // Remove all listeners from buttons + $('.tb-dialog-button').off('click') + $('.tb-case-button').off('click') + + setupTestbenchUI() + if (globalScope.testbenchData != undefined) { + const { testbenchData } = globalScope + + // Initialize the UI + setUITableHeaders(testbenchData) + + // Add listeners to buttons + $('.tb-case-button#prev-case-btn').on( + 'click', + buttonListenerFunctions.previousCaseButton + ) + $('.tb-case-button#next-case-btn').on( + 'click', + buttonListenerFunctions.nextCaseButton + ) + $('.tb-case-button#prev-group-btn').on( + 'click', + buttonListenerFunctions.previousGroupButton + ) + $('.tb-case-button#next-group-btn').on( + 'click', + buttonListenerFunctions.nextGroupButton + ) + $('.tb-dialog-button#change-test-btn').on( + 'click', + buttonListenerFunctions.changeTestButton + ) + $('.tb-dialog-button#runall-btn').on( + 'click', + buttonListenerFunctions.runAllButton + ) + $('.tb-dialog-button#edit-test-btn').on( + 'click', + buttonListenerFunctions.editTestButton + ) + $('.tb-dialog-button#validate-btn').on( + 'click', + buttonListenerFunctions.validateButton + ) + $('.tb-dialog-button#remove-test-btn').on( + 'click', + buttonListenerFunctions.removeTestButton + ) + } + + // Add listener to attach test button + $('.tb-dialog-button#attach-test-btn').on( + 'click', + buttonListenerFunctions.attachTestButton + ) +} + +/** + * Defines all the functions called as event listeners for buttons on the UI + */ +const buttonListenerFunctions = { + previousCaseButton: () => { + const isValid = validate( + globalScope.testbenchData.testData, + globalScope + ) + if (!isValid.ok) { + showMessage( + 'Testbench: Some elements missing from circuit. Click Validate to know more' + ) + return + } + globalScope.testbenchData.casePrev() + buttonListenerFunctions.computeCase() + }, + + nextCaseButton: () => { + const isValid = validate( + globalScope.testbenchData.testData, + globalScope + ) + if (!isValid.ok) { + showMessage( + 'Testbench: Some elements missing from circuit. Click Validate to know more' + ) + return + } + globalScope.testbenchData.caseNext() + buttonListenerFunctions.computeCase() + }, + + previousGroupButton: () => { + const isValid = validate( + globalScope.testbenchData.testData, + globalScope + ) + if (!isValid.ok) { + showMessage( + 'Testbench: Some elements missing from circuit. Click Validate to know more' + ) + return + } + globalScope.testbenchData.groupPrev() + buttonListenerFunctions.computeCase() + }, + + nextGroupButton: () => { + const isValid = validate( + globalScope.testbenchData.testData, + globalScope + ) + if (!isValid.ok) { + showMessage( + 'Testbench: Some elements missing from circuit. Click Validate to know more' + ) + return + } + globalScope.testbenchData.groupNext() + buttonListenerFunctions.computeCase() + }, + + changeTestButton: () => { + openCreator('create') + }, + + runAllButton: () => { + const isValid = validate( + globalScope.testbenchData.testData, + globalScope + ) + if (!isValid.ok) { + showMessage( + 'Testbench: Some elements missing from circuit. Click Validate to know more' + ) + return + } + const results = runAll(globalScope.testbenchData.testData, globalScope) + const { passed } = results.summary + const { total } = results.summary + const resultString = JSON.stringify(results.detailed) + $('#runall-summary').text(`${passed} out of ${total}`) + $('#runall-detailed-link').on('click', () => { + openCreator('result', resultString) + }) + $('.testbench-runall-label').css('display', 'table-cell') + $('.testbench-runall-label').delay(5000).fadeOut('slow') + }, + + editTestButton: () => { + const editDataString = JSON.stringify( + globalScope.testbenchData.testData + ) + openCreator('edit', editDataString) + }, + + validateButton: () => { + const isValid = validate( + globalScope.testbenchData.testData, + globalScope + ) + showValidationUI(isValid) + }, + + removeTestButton: async () => { + if ( + await confirmOption( + 'Are you sure you want to remove the test from the circuit?' + ) + ) { + globalScope.testbenchData = undefined + setupTestbenchUI() + } + }, + + attachTestButton: () => { + openCreator('create') + }, + + rerunTestButton: () => { + buttonListenerFunctions.computeCase() + }, + + computeCase: () => { + setUICurrentCase(globalScope.testbenchData) + const result = runSingleTest(globalScope.testbenchData, globalScope) + setUIResult(globalScope.testbenchData, result) + }, +} + +/** + * UI Function + * Checks whether test is attached to the scope and switches UI accordingly + */ +export function setupTestbenchUI() { + // Don't change UI if UI is minimized (because hide() and show() are recursive) + if ($('.testbench-manual-panel .minimize').css('display') === 'none') return + + if (globalScope.testbenchData === undefined) { + $('.tb-test-not-null').hide() + $('.tb-test-null').show() + return + } + + $('.tb-test-null').hide() + $('.tb-test-not-null').show() +} + +/** + * Run all the tests automatically. Called by runTestBench() + * @param {Object} data - Object containing Test Data + * @param {Scope=} scope - the circuit + */ +export function runAll(data, scope = globalScope) { + // Stop the clocks + // TestBench will now take over clock toggling + changeClockEnable(false) + + const { inputs, outputs, reset } = bindIO(data, scope) + let totalCases = 0 + let passedCases = 0 + + data.groups.forEach((group) => { + // for (const output of group.outputs) output.results = []; + group.outputs.forEach((output) => (output.results = [])) + for (let case_i = 0; case_i < group.n; case_i++) { + totalCases++ + // Set and propagate the inputs + setInputValues(inputs, group, case_i, scope) + // If sequential, trigger clock now + if (data.type === 'seq') tickClock(scope) + // Get output values + const caseResult = getOutputValues(data, outputs) + // Put the results in the data + + let casePassed = true // Tracks if current case passed or failed + + caseResult.forEach((_, outName) => { + // TODO: find() is not the best idea because of O(n) + const output = group.outputs.find( + (dataOutput) => dataOutput.label === outName + ) + output.results.push(caseResult.get(outName)) + + if (output.values[case_i] !== caseResult.get(outName)) + casePassed = false + }) + + // If current case passed, then increment passedCases + if (casePassed) passedCases++ + } + + // If sequential, trigger reset at the end of group (set) + if (data.type === 'seq') triggerReset(reset) + }) + + // Tests done, restart the clocks + changeClockEnable(true) + + // Return results + const results = {} + results.detailed = data + results.summary = { passed: passedCases, total: totalCases } + return results +} + +/** + * Runs single test + * @param {Object} data - Object containing Test Data + * @param {number} groupIndex - Index of the group to be tested + * @param {number} caseIndex - Index of the case inside the group + * @param {Scope} scope - The circuit + */ +function runSingleTest(testbenchData, scope) { + const data = testbenchData.testData + + let result + if (data.type === 'comb') { + result = runSingleCombinational(testbenchData, scope) + } else if (data.type === 'seq') { + result = runSingleSequential(testbenchData, scope) + } + + return result +} + +/** + * Runs single combinational test + * @param {Object} data - Object containing Test Data + * @param {number} groupIndex - Index of the group to be tested + * @param {number} caseIndex - Index of the case inside the group + * @param {Scope} scope - The circuit + */ +function runSingleCombinational(testbenchData, scope) { + const data = testbenchData.testData + const groupIndex = testbenchData.currentGroup + const caseIndex = testbenchData.currentCase + + const { inputs, outputs } = bindIO(data, scope) + const group = data.groups[groupIndex] + + // Stop the clocks + changeClockEnable(false) + + // Set input values according to the test + setInputValues(inputs, group, caseIndex, scope) + // Check output values + const result = getOutputValues(data, outputs) + // Restart the clocks + changeClockEnable(true) + return result +} + +/** + * Runs single sequential test and all tests above it in the group + * Used in MANUAL mode + * @param {Object} data - Object containing Test Data + * @param {number} groupIndex - Index of the group to be tested + * @param {number} caseIndex - Index of the case inside the group + * @param {Scope} scope - The circuit + */ +function runSingleSequential(testbenchData, scope) { + const data = testbenchData.testData + const groupIndex = testbenchData.currentGroup + const caseIndex = testbenchData.currentCase + + const { inputs, outputs, reset } = bindIO(data, scope) + const group = data.groups[groupIndex] + + // Stop the clocks + changeClockEnable(false) + + // Trigger reset + triggerReset(reset, scope) + + // Run the test and tests above in the same group + for (let case_i = 0; case_i <= caseIndex; case_i++) { + setInputValues(inputs, group, case_i, scope) + tickClock(scope) + } + + const result = getOutputValues(data, outputs) + + // Restart the clocks + changeClockEnable(true) + + return result +} + +/** + * Set and propogate the input values according to the testcase. + * Called by runSingle() and runAll() + * @param {Object} inputs - Object with keys as input names and values as inputs + * @param {Object} group - Test group + * @param {number} caseIndex - Index of the case in the group + * @param {Scope} scope - the circuit + */ +function setInputValues(inputs, group, caseIndex, scope) { + group.inputs.forEach((input) => { + inputs[input.label].state = parseInt(input.values[caseIndex], 2) + }) + + // Propagate inputs + play(scope) +} + +/** + * Gets Output values as a Map with keys as output name and value as output state + * @param {Object} outputs - Object with keys as output names and values as outputs + */ +function getOutputValues(data, outputs) { + const values = new Map() + + data.groups[0].outputs.forEach((dataOutput) => { + // Using node value because output state only changes on rendering + const resultValue = outputs[dataOutput.label].nodeList[0].value + const resultBW = outputs[dataOutput.label].nodeList[0].bitWidth + values.set(dataOutput.label, dec2bin(resultValue, resultBW)) + }) + + return values +} + +/** + * UI Function + * Shows validation UI + * @param {Object} validationErrors - Object with errors returned by validate() + */ +function showValidationUI(validationErrors) { + const checkSVG = ` + + + + ` + + let s = ` +
+
+ ${checkSVG} +
+ All good. No validation errors +
+ ` + + if (!validationErrors.ok) { + s = ` +
+

Please fix these errors to run tests

+ + + + + + ` + + validationErrors.invalids.forEach((vError) => { + s += ` + + + + + ` + }) + + s += '
IdentifierError
${vError.identifier}${vError.message}
' + } + + $('#testbenchValidate').dialog({ + resizable: false, + width: 'auto', + buttons: [ + { + text: 'Ok', + click() { + $(this).dialog('close') + }, + }, + { + text: 'Auto Fix', + click() { + const fixes = validationAutoFix(validationErrors) + showMessage(`Testbench: Auto fixed ${fixes} errors`) + $(this).dialog('close') + }, + }, + ], + }) + + $('#testbenchValidate').empty() + $('#testbenchValidate').append(s) +} + +/** + * Validate if all inputs and output elements are present with correct bitwidths + * @param {Object} data - Object containing Test Data + * @param {Scope} scope - the circuit + */ +function validate(data, scope) { + let invalids = [] + + // Check for duplicate identifiers + if (!checkDistinctIdentifiersData(data)) { + invalids.push({ + type: VALIDATION_ERRORS.DUPLICATE_ID_DATA, + identifier: '-', + message: 'Duplicate identifiers in test data', + }) + } + + if (!checkDistinctIdentifiersScope(scope)) { + invalids.push({ + type: VALIDATION_ERRORS.DUPLICATE_ID_SCOPE, + identifier: '-', + message: 'Duplicate identifiers in circuit', + }) + } + + // Don't do further checks if duplicates + if (invalids.length > 0) return { ok: false, invalids } + + // Validate inputs and outputs + const inputsValid = validateInputs(data, scope) + const outputsValid = validateOutputs(data, scope) + + invalids = inputsValid.ok ? invalids : invalids.concat(inputsValid.invalids) + invalids = outputsValid.ok + ? invalids + : invalids.concat(outputsValid.invalids) + + // Validate presence of reset if test is sequential + if (data.type === 'seq') { + const resetPresent = scope.Input.some( + (simulatorReset) => + simulatorReset.label === 'RST' && + simulatorReset.bitWidth === 1 && + simulatorReset.objectType === 'Input' + ) + + if (!resetPresent) { + invalids.push({ + type: VALIDATION_ERRORS.NO_RST, + identifier: 'RST', + message: 'Reset(RST) not present in circuit', + }) + } + } + + if (invalids.length > 0) return { ok: false, invalids } + return { ok: true } +} + +/** + * Autofix whatever is possible in validation errors. + * returns number of autofixed errors + * @param {Object} validationErrors - Object with errors returned by validate() + */ +function validationAutoFix(validationErrors) { + // Currently only autofixes bitwidths + let fixedErrors = 0 + // Return if no errors + if (validationErrors.ok) return fixedErrors + + const bitwidthErrors = validationErrors.invalids.filter( + (vError) => vError.type === VALIDATION_ERRORS.WRONGBITWIDTH + ) + + bitwidthErrors.forEach((bwError) => { + const { element, expectedBitWidth } = bwError.extraInfo + element.newBitWidth(expectedBitWidth) + fixedErrors++ + }) + + return fixedErrors +} + +/** + * Checks if all the labels in the test data are unique. Called by validate() + * @param {Object} data - Object containing Test Data + */ +function checkDistinctIdentifiersData(data) { + const inputIdentifiersData = data.groups[0].inputs.map( + (input) => input.label + ) + const outputIdentifiersData = data.groups[0].outputs.map( + (output) => output.label + ) + const identifiersData = inputIdentifiersData.concat(outputIdentifiersData) + + return new Set(identifiersData).size === identifiersData.length +} + +/** + * Checks if all the input/output labels in the scope are unique. Called by validate() + * TODO: Replace with identifiers + * @param {Scope} scope - the circuit + */ +function checkDistinctIdentifiersScope(scope) { + const inputIdentifiersScope = scope.Input.map((input) => input.label) + const outputIdentifiersScope = scope.Output.map((output) => output.label) + let identifiersScope = inputIdentifiersScope.concat(outputIdentifiersScope) + + // Remove identifiers which have not been set yet (ie. empty strings) + identifiersScope = identifiersScope.filter((identifer) => identifer != '') + + return new Set(identifiersScope).size === identifiersScope.length +} + +/** + * Validates presence and bitwidths of test inputs in the circuit. + * Called by validate() + * @param {Object} data - Object containing Test Data + * @param {Scope} scope - the circuit + */ +function validateInputs(data, scope) { + const invalids = [] + + data.groups[0].inputs.forEach((dataInput) => { + const matchInput = scope.Input.find( + (simulatorInput) => simulatorInput.label === dataInput.label + ) + + if (matchInput === undefined) { + invalids.push({ + type: VALIDATION_ERRORS.NOTPRESENT, + identifier: dataInput.label, + message: 'Input is not present in the circuit', + }) + } else if (matchInput.bitWidth !== dataInput.bitWidth) { + invalids.push({ + type: VALIDATION_ERRORS.WRONGBITWIDTH, + identifier: dataInput.label, + extraInfo: { + element: matchInput, + expectedBitWidth: dataInput.bitWidth, + }, + message: `Input bitwidths don't match in circuit and test (${matchInput.bitWidth} vs ${dataInput.bitWidth})`, + }) + } + }) + + if (invalids.length > 0) return { ok: false, invalids } + return { ok: true } +} + +/** + * Validates presence and bitwidths of test outputs in the circuit. + * Called by validate() + * @param {Object} data - Object containing Test Data + * @param {Scope} scope - the circuit + */ +function validateOutputs(data, scope) { + const invalids = [] + + data.groups[0].outputs.forEach((dataOutput) => { + const matchOutput = scope.Output.find( + (simulatorOutput) => simulatorOutput.label === dataOutput.label + ) + + if (matchOutput === undefined) { + invalids.push({ + type: VALIDATION_ERRORS.NOTPRESENT, + identifier: dataOutput.label, + message: 'Output is not present in the circuit', + }) + } else if (matchOutput.bitWidth !== dataOutput.bitWidth) { + invalids.push({ + type: VALIDATION_ERRORS.WRONGBITWIDTH, + identifier: dataOutput.label, + extraInfo: { + element: matchOutput, + expectedBitWidth: dataOutput.bitWidth, + }, + message: `Output bitwidths don't match in circuit and test (${matchOutput.bitWidth} vs ${dataOutput.bitWidth})`, + }) + } + }) + + if (invalids.length > 0) return { ok: false, invalids } + return { ok: true } +} + +/** + * Returns object of scope inputs and outputs keyed by their labels + * @param {Object} data - Object containing Test Data + * @param {Scope=} scope - the circuit + */ +function bindIO(data, scope) { + const inputs = {} + const outputs = {} + let reset + + data.groups[0].inputs.forEach((dataInput) => { + inputs[dataInput.label] = scope.Input.find( + (simulatorInput) => simulatorInput.label === dataInput.label + ) + }) + + data.groups[0].outputs.forEach((dataOutput) => { + outputs[dataOutput.label] = scope.Output.find( + (simulatorOutput) => simulatorOutput.label === dataOutput.label + ) + }) + + if (data.type === 'seq') { + reset = scope.Input.find( + (simulatorOutput) => simulatorOutput.label === 'RST' + ) + } + + return { inputs, outputs, reset } +} + +/** + * Ticks clock recursively one full cycle (Only used in testbench context) + * @param {Scope} scope - the circuit whose clock to be ticked + */ +function tickClock(scope) { + scope.clockTick() + play(scope) + scope.clockTick() + play(scope) +} + +/** + * Triggers reset (Only used in testbench context) + * @param {Input} reset - reset pin to be triggered + * @param {Scope} scope - the circuit + */ +function triggerReset(reset, scope) { + reset.state = 1 + play(scope) + reset.state = 0 + play(scope) +} + +/** + * UI Function + * Sets IO labels and bitwidths on UI table + * Called by simulatorRunTestbench() + * @param {Object} data - Object containing the test data + */ +function setUITableHeaders(testbenchData) { + const data = testbenchData.testData + const inputCount = data.groups[0].inputs.length + const outputCount = data.groups[0].outputs.length + + $('#tb-manual-table-inputs-head').attr('colspan', inputCount) + $('#tb-manual-table-outputs-head').attr('colspan', outputCount) + + $('.testbench-runall-label').css('display', 'none') + + $('.tb-data#data-title') + .children() + .eq(1) + .text(data.title || 'Untitled') + $('.tb-data#data-type') + .children() + .eq(1) + .text(data.type === 'comb' ? 'Combinational' : 'Sequential') + + $('#tb-manual-table-labels').html('LABELS') + $('#tb-manual-table-bitwidths').html('Bitwidth') + + data.groups[0].inputs.concat(data.groups[0].outputs).forEach((io) => { + const label = `${escapeHtml(io.label)}` + const bw = `${escapeHtml(io.bitWidth.toString())}` + $('#tb-manual-table-labels').append(label) + $('#tb-manual-table-bitwidths').append(bw) + }) + + setUICurrentCase(testbenchData) +} + +/** + * UI Function + * Set current test case data on the UI + * @param {Object} data - Object containing the test data + * @param {number} groupIndex - Index of the group of current case + * @param {number} caseIndex - Index of the case within the group + */ +function setUICurrentCase(testbenchData) { + const data = testbenchData.testData + const groupIndex = testbenchData.currentGroup + const caseIndex = testbenchData.currentCase + + const currCaseElement = $('#tb-manual-table-current-case') + currCaseElement.empty() + currCaseElement.append('Current Case') + $('#tb-manual-table-test-result').empty() + $('#tb-manual-table-test-result').append('Result') + + data.groups[groupIndex].inputs.forEach((input) => { + currCaseElement.append( + `${escapeHtml(input.values[caseIndex])}` + ) + }) + + data.groups[groupIndex].outputs.forEach((output) => { + currCaseElement.append( + `${escapeHtml(output.values[caseIndex])}` + ) + }) + + $('.testbench-manual-panel .group-label').text( + data.groups[groupIndex].label + ) + $('.testbench-manual-panel .case-label').text(caseIndex + 1) +} + +/** + * UI Function + * Set the current test case result on the UI + * @param {Object} data - Object containing the test data + * @param {Map} result - Map containing the output values (returned by getOutputValues()) + */ +function setUIResult(testbenchData, result) { + const data = testbenchData.testData + const groupIndex = testbenchData.currentGroup + const caseIndex = testbenchData.currentCase + const resultElement = $('#tb-manual-table-test-result') + let inputCount = data.groups[0].inputs.length + resultElement.empty() + resultElement.append('Result') + while (inputCount--) { + resultElement.append(' - ') + } + + for (const output of result.keys()) { + const resultValue = result.get(output) + const expectedValue = data.groups[groupIndex].outputs.find( + (dataOutput) => dataOutput.label === output + ).values[caseIndex] + const color = resultValue === expectedValue ? '#17FC12' : '#FF1616' + resultElement.append( + `${escapeHtml(resultValue)}` + ) + } +} + +/** + * Use this function to navigate to test creator. This function starts the storage listener + * so the test is loaded directly into the simulator + * @param {string} type - 'create', 'edit' or 'result' + * @param {String} dataString - data in JSON string to load in case of 'edit' and 'result' + */ +function openCreator(type, dataString) { + const popupHeight = 800 + const popupWidth = 1200 + const popupTop = (window.height - popupHeight) / 2 + const popupLeft = (window.width - popupWidth) / 2 + const POPUP_STYLE_STRING = `height=${popupHeight},width=${popupWidth},top=${popupTop},left=${popupLeft}` + let popUp + + /* Listener to catch testData from pop up and load it onto the testbench */ + const dataListener = (message) => { + if ( + message.origin !== window.origin || + message.data.type !== 'testData' + ) + return + + // Check if the current scope requested the creator pop up + const data = JSON.parse(message.data.data) + + // Unbind event listener + window.removeEventListener('message', dataListener) + + // If scopeID does not match, do nothing and return + if (data.scopeID != globalScope.id) return + + // Load test data onto the scope + runTestBench(data.testData, globalScope, CONTEXT.CONTEXT_SIMULATOR) + + // Close the 'Pop up is open' dialog + $('#setTestbenchData').dialog('close') + } + + if (type === 'create') { + const url = `${TESTBENCH_CREATOR_PATH}?scopeID=${globalScope.id}&popUp=true` + popUp = window.open(url, 'popupWindow', POPUP_STYLE_STRING) + creatorOpenPrompt(popUp) + window.addEventListener('message', dataListener) + } + + if (type === 'edit') { + const url = `${TESTBENCH_CREATOR_PATH}?scopeID=${globalScope.id}&data=${dataString}&popUp=true` + popUp = window.open(url, 'popupWindow', POPUP_STYLE_STRING) + creatorOpenPrompt(popUp) + window.addEventListener('message', dataListener) + } + + if (type === 'result') { + const url = `${TESTBENCH_CREATOR_PATH}?scopeID=${globalScope.id}&result=${dataString}&popUp=true` + popUp = window.open(url, 'popupWindow', POPUP_STYLE_STRING) + } + + // Check if popup was closed (in case it was closed by window's X button), + // then close 'popup open' dialog + if (popUp && type !== 'result') { + const checkPopUp = setInterval(() => { + if (popUp.closed) { + // Close the dialog if it's open + if ($('#setTestbenchData').dialog('isOpen')) + $('#setTestbenchData').dialog('close') + + // Remove the event listener that listens for data from popup + window.removeEventListener('message', dataListener) + clearInterval(checkPopUp) + } + }, 1000) + } +} diff --git a/v0/src/simulator/src/testbench/ForceGate.js b/v0/src/simulator/src/testbench/ForceGate.js new file mode 100644 index 00000000..5da82c6f --- /dev/null +++ b/v0/src/simulator/src/testbench/ForceGate.js @@ -0,0 +1,92 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { fillText4 } from '../canvasApi' +/** + * @class + * ForceGate + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @category testbench + */ +export default class ForceGate extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT', bitWidth = 1) { + super(x, y, scope, dir, bitWidth) + this.setDimensions(20, 10) + this.objectType = 'ForceGate' + this.scope.ForceGate.push(this) + this.inp1 = new Node(-20, 0, 0, this) + this.inp2 = new Node(0, 0, 0, this) + this.output1 = new Node(20, 0, 1, this) + } + + /** + * @memberof ForceGate + * Checks if the element is resolvable + * @return {boolean} + */ + isResolvable() { + return this.inp1.value !== undefined || this.inp2.value !== undefined + } + + /** + * @memberof ForceGate + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.direction, this.bitWidth], + nodes: { + output1: findNode(this.output1), + inp1: findNode(this.inp1), + inp2: findNode(this.inp2), + }, + } + return data + } + + /** + * @memberof ForceGate + * resolve output values based on inputData + */ + resolve() { + if (this.inp2.value !== undefined) { + this.output1.value = this.inp2.value + } else { + this.output1.value = this.inp1.value + } + simulationArea.simulationQueue.add(this.output1) + } + + /** + * @memberof ForceGate + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + const xx = this.x + const yy = this.y + + ctx.beginPath() + ctx.fillStyle = 'Black' + ctx.textAlign = 'center' + + fillText4(ctx, 'I', -10, 0, xx, yy, this.direction, 10) + fillText4(ctx, 'O', 10, 0, xx, yy, this.direction, 10) + ctx.fill() + } +} + +/** + * @memberof ForceGate + * Help Tip + * @type {string} + * @category testbench + */ +ForceGate.prototype.tooltipText = 'Force Gate ToolTip : ForceGate Selected.' +ForceGate.prototype.objectType = 'ForceGate' diff --git a/v0/src/simulator/src/testbench/testbenchInput.js b/v0/src/simulator/src/testbench/testbenchInput.js new file mode 100644 index 00000000..bdf7452b --- /dev/null +++ b/v0/src/simulator/src/testbench/testbenchInput.js @@ -0,0 +1,351 @@ +import CircuitElement from '../circuitElement' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, fillText } from '../canvasApi' +import Node, { findNode } from '../node' +import plotArea from '../plotArea' + +/** + * TestBench Input has a node for it's clock input. + * this.testData - the data of all test cases. + * Every testbench has a uniq identifier. + * @class + * @extends CircuitElement + * @param {number} x - the x coord of TB + * @param {number} y - the y coord of TB + * @param {Scope=} scope - the circuit on which TB is drawn + * @param {string} dir - direction + * @param {string} identifier - id to identify tests + * @param {JSON=} testData - input, output and number of tests + * @category testbench + */ +export default class TB_Input extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + identifier, + testData + ) { + super(x, y, scope, dir, 1) + this.objectType = 'TB_Input' + this.scope.TB_Input.push(this) + this.setIdentifier(identifier || 'Test1') + this.testData = testData || { inputs: [], outputs: [], n: 0 } + this.clockInp = new Node(0, 20, 0, this, 1) + this.outputs = [] + this.running = false // if tests are undergo + this.iteration = 0 + this.setup() + } + + /** + * @memberof TB_Input + * Takes input when double clicked. For help on generation of input refer to TB_Input.helplink + */ + dblclick() { + this.testData = JSON.parse(prompt('Enter TestBench Json')) + this.setup() + } + + setDimensions() { + this.leftDimensionX = 0 + this.rightDimensionX = 120 + + this.upDimensionY = 0 + this.downDimensionY = 40 + this.testData.inputs.length * 20 + } + + /** + * @memberof TB_Input + * setups the Test by parsing through the testbench data. + */ + setup() { + this.iteration = 0 + this.running = false + this.nodeList.clean(this.clockInp) + this.deleteNodes() + this.nodeList = [] + this.nodeList.push(this.clockInp) + this.testData = this.testData || { inputs: [], outputs: [], n: 0 } + // this.clockInp = new Node(0,20, 0,this,1); + + this.setDimensions() + + this.prevClockState = 0 + this.outputs = [] + + for (var i = 0; i < this.testData.inputs.length; i++) { + this.outputs.push( + new Node( + this.rightDimensionX, + 30 + i * 20, + 1, + this, + this.testData.inputs[i].bitWidth, + this.testData.inputs[i].label + ) + ) + } + + for (var i = 0; i < this.scope.TB_Output.length; i++) { + if (this.scope.TB_Output[i].identifier == this.identifier) { + this.scope.TB_Output[i].setup() + } + } + } + + /** + * @memberof TB_Input + * toggles state by simply negating this.running so that test cases stop + */ + toggleState() { + this.running = !this.running + this.prevClockState = 0 + } + + /** + * @memberof TB_Input + * function to run from test case 0 again + */ + resetIterations() { + this.iteration = 0 + this.prevClockState = 0 + } + + /** + * @memberof TB_Input + * function to resolve the testbench input adds + */ + resolve() { + if (this.clockInp.value != this.prevClockState) { + this.prevClockState = this.clockInp.value + if (this.clockInp.value == 1 && this.running) { + if (this.iteration < this.testData.n) { + this.iteration++ + } else { + this.running = false + } + } + } + if (this.running && this.iteration) { + for (var i = 0; i < this.testData.inputs.length; i++) { + this.outputs[i].value = parseInt( + this.testData.inputs[i].values[this.iteration - 1], + 2 + ) + simulationArea.simulationQueue.add(this.outputs[i]) + } + } + } + + /** + * @memberof TB_Input + * was a function to plot values incase any flag used as output to this element + */ + setPlotValue() { + return + var time = plotArea.stopWatch.ElapsedMilliseconds + if ( + this.plotValues.length && + this.plotValues[this.plotValues.length - 1][0] == time + ) { + this.plotValues.pop() + } + + if (this.plotValues.length == 0) { + this.plotValues.push([time, this.inp1.value]) + return + } + + if (this.plotValues[this.plotValues.length - 1][1] == this.inp1.value) { + return + } + this.plotValues.push([time, this.inp1.value]) + } + + customSave() { + var data = { + constructorParamaters: [ + this.direction, + this.identifier, + this.testData, + ], + nodes: { + outputs: this.outputs.map(findNode), + clockInp: findNode(this.clockInp), + }, + } + return data + } + + /** + * This function is used to set a uniq identifier to every testbench + * @memberof TB_Input + */ + setIdentifier(id = '') { + if (id.length == 0 || id == this.identifier) return + + for (var i = 0; i < this.scope.TB_Output.length; i++) { + this.scope.TB_Output[i].checkPairing() + } + + for (var i = 0; i < this.scope.TB_Output.length; i++) { + if (this.scope.TB_Output[i].identifier == this.identifier) { + this.scope.TB_Output[i].identifier = id + } + } + + this.identifier = id + + this.checkPaired() + } + + /** + * Check if there is a output tester paired with input TB. + * @memberof TB_Input + */ + checkPaired() { + for (var i = 0; i < this.scope.TB_Output.length; i++) { + if (this.scope.TB_Output[i].identifier == this.identifier) { + this.scope.TB_Output[i].checkPairing() + } + } + } + + delete() { + super.delete() + this.checkPaired() + } + + customDraw() { + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = 'grey' + ctx.fillStyle = '#fcfcfc' + ctx.lineWidth = correctWidth(1) + var xx = this.x + var yy = this.y + + var xRotate = 0 + var yRotate = 0 + if (this.direction == 'LEFT') { + xRotate = 0 + yRotate = 0 + } else if (this.direction == 'RIGHT') { + xRotate = 120 - this.xSize + yRotate = 0 + } else if (this.direction == 'UP') { + xRotate = 60 - this.xSize / 2 + yRotate = -20 + } else { + xRotate = 60 - this.xSize / 2 + yRotate = 20 + } + + ctx.beginPath() + ctx.textAlign = 'center' + ctx.fillStyle = 'black' + fillText( + ctx, + `${this.identifier} [INPUT]`, + xx + this.rightDimensionX / 2, + yy + 14, + 10 + ) + + fillText( + ctx, + ['Not Running', 'Running'][+this.running], + xx + this.rightDimensionX / 2, + yy + 14 + 10 + 20 * this.testData.inputs.length, + 10 + ) + fillText( + ctx, + `Case: ${this.iteration}`, + xx + this.rightDimensionX / 2, + yy + 14 + 20 + 20 * this.testData.inputs.length, + 10 + ) + // fillText(ctx, "Case: "+this.iteration, xx , yy + 20+14, 10); + ctx.fill() + + ctx.font = '30px Raleway' + ctx.textAlign = 'right' + ctx.fillStyle = 'blue' + ctx.beginPath() + for (var i = 0; i < this.testData.inputs.length; i++) { + // ctx.beginPath(); + fillText( + ctx, + this.testData.inputs[i].label, + this.rightDimensionX - 5 + xx, + 30 + i * 20 + yy + 4, + 10 + ) + } + + ctx.fill() + if (this.running && this.iteration) { + ctx.font = '30px Raleway' + ctx.textAlign = 'left' + ctx.fillStyle = 'blue' + ctx.beginPath() + for (var i = 0; i < this.testData.inputs.length; i++) { + fillText( + ctx, + this.testData.inputs[i].values[this.iteration - 1], + 5 + xx, + 30 + i * 20 + yy + 4, + 10 + ) + } + + ctx.fill() + } + + ctx.beginPath() + ctx.strokeStyle = 'rgba(0,0,0,1)' + ctx.lineWidth = correctWidth(3) + var xx = this.x + var yy = this.y + // rect(ctx, xx - 20, yy - 20, 40, 40); + moveTo(ctx, 0, 15, xx, yy, this.direction) + lineTo(ctx, 5, 20, xx, yy, this.direction) + lineTo(ctx, 0, 25, xx, yy, this.direction) + + ctx.stroke() + } +} + +TB_Input.prototype.tooltipText = 'Test Bench Input Selected' + +/** + * @memberof TB_Input + * different algo for drawing center elements + * @category testbench + */ +TB_Input.prototype.centerElement = true + +TB_Input.prototype.helplink = 'https://docs.circuitverse.org/#/chapter7/3testcircuits' + +TB_Input.prototype.mutableProperties = { + identifier: { + name: 'TestBench Name:', + type: 'text', + maxlength: '10', + func: 'setIdentifier', + }, + iteration: { + name: 'Reset Iterations', + type: 'button', + func: 'resetIterations', + }, + toggleState: { + name: 'Toggle State', + type: 'button', + func: 'toggleState', + }, +} +TB_Input.prototype.objectType = 'TB_Input' diff --git a/v0/src/simulator/src/testbench/testbenchOutput.js b/v0/src/simulator/src/testbench/testbenchOutput.js new file mode 100644 index 00000000..865ec258 --- /dev/null +++ b/v0/src/simulator/src/testbench/testbenchOutput.js @@ -0,0 +1,324 @@ +import CircuitElement from '../circuitElement' +import simulationArea from '../simulationArea' +import { correctWidth, fillText } from '../canvasApi' +import Node, { findNode } from '../node' + +// helper function to convert decimal to binary +function dec2bin(dec, bitWidth = undefined) { + // only for positive nos + var bin = dec.toString(2) + if (bitWidth == undefined) return bin + return '0'.repeat(bitWidth - bin.length) + bin +} + +/** + * TestBench Output has a node for it's input which is + * compared to desired output according tp testData of + * input TB Every TB_output has a uniq identifier matching + * it's TB_Input + * @class + * @extends CircuitElement + * @param {number} x - the x coord of TB + * @param {number} y - the y coord of TB + * @param {Scope=} scope - the circuit on which TB is drawn + * @param {string} dir - direction + * @param {string} identifier - id to identify tests + * @category testbench + */ + +export default class TB_Output extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT', identifier) { + super(x, y, scope, dir, 1) + // this.setDimensions(60,20); + this.objectType = 'TB_Output' + this.scope.TB_Output.push(this) + + // this.xSize=10; + + // this.plotValues = []; + // this.inp1 = new Node(0, 0, 0, this); + // this.inp1 = new Node(100, 100, 0, this); + this.setIdentifier(identifier || 'Test1') + this.inputs = [] + this.testBenchInput = undefined + + this.setup() + } + + // TB_Output.prototype.dblclick=function(){ + // this.testData=JSON.parse(prompt("Enter TestBench Json")); + // this.setup(); + // } + setDimensions() { + this.leftDimensionX = 0 + this.rightDimensionX = 160 + this.upDimensionY = 0 + this.downDimensionY = 40 + if (this.testBenchInput) { + this.downDimensionY = + 40 + this.testBenchInput.testData.outputs.length * 20 + } + } + + setup() { + // this.iteration = 0; + // this.running = false; + // this.nodeList.clean(this.clockInp); + this.deleteNodes() // deletes all nodes whenever setup is called. + this.nodeList = [] + + this.inputs = [] + this.testBenchInput = undefined + // find it's pair input + for (var i = 0; i < this.scope.TB_Input.length; i++) { + if (this.scope.TB_Input[i].identifier == this.identifier) { + this.testBenchInput = this.scope.TB_Input[i] + break + } + } + + this.setDimensions() + + if (this.testBenchInput) { + for ( + var i = 0; + i < this.testBenchInput.testData.outputs.length; + i++ + ) { + this.inputs.push( + new Node( + 0, + 30 + i * 20, + NODE_INPUT, + this, + this.testBenchInput.testData.outputs[i].bitWidth, + this.testBenchInput.testData.outputs[i].label + ) + ) + } + } + } + + customSave() { + var data = { + constructorParamaters: [this.direction, this.identifier], + nodes: { + inputs: this.inputs.map(findNode), + }, + } + return data + } + + /** + * @memberof TB_output + * set identifier for this testbench + */ + setIdentifier(id = '') { + if (id.length == 0 || id == this.identifier) return + this.identifier = id + this.setup() + } + + /** + * @memberof TB_output + * Function to check if the input for this TB exist + */ + checkPairing(id = '') { + if (this.testBenchInput) { + if ( + this.testBenchInput.deleted || + this.testBenchInput.identifier != this.identifier + ) { + this.setup() + } + } else { + this.setup() + } + } + + customDraw() { + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = 'grey' + ctx.fillStyle = '#fcfcfc' + ctx.lineWidth = correctWidth(1) + var xx = this.x + var yy = this.y + + var xRotate = 0 + var yRotate = 0 + if (this.direction == 'LEFT') { + xRotate = 0 + yRotate = 0 + } else if (this.direction == 'RIGHT') { + xRotate = 120 - this.xSize + yRotate = 0 + } else if (this.direction == 'UP') { + xRotate = 60 - this.xSize / 2 + yRotate = -20 + } else { + xRotate = 60 - this.xSize / 2 + yRotate = 20 + } + + // rect2(ctx, -120+xRotate+this.xSize, -20+yRotate, 120-this.xSize, 40, xx, yy, "RIGHT"); + // if ((this.hover && !simulationArea.shiftDown) || simulationArea.lastSelected == this || simulationArea.multipleObjectSelections.contains(this)) + // ctx.fillStyle = "rgba(255, 255, 32,0.8)"; + // ctx.fill(); + // ctx.stroke(); + // + // ctx.font = "14px Raleway"; + // this.xOff = ctx.measureText(this.identifier).width; + // ctx.beginPath(); + // rect2(ctx, -105+xRotate+this.xSize, -11+yRotate,this.xOff + 10, 23, xx, yy, "RIGHT"); + // ctx.fillStyle = "#eee" + // ctx.strokeStyle = "#ccc"; + // ctx.fill(); + // ctx.stroke(); + // + + ctx.beginPath() + ctx.textAlign = 'center' + ctx.fillStyle = 'black' + fillText( + ctx, + `${this.identifier} [OUTPUT]`, + xx + this.rightDimensionX / 2, + yy + 14, + 10 + ) + + // fillText(ctx, ["Not Running","Running"][+this.running], xx + this.rightDimensionX/ 2 , yy + 14 + 10 + 20*this.testData.inputs.length, 10); + // fillText(ctx, "Case: "+(this.iteration), xx + this.rightDimensionX/ 2 , yy + 14 + 20 + 20*this.testData.inputs.length, 10); + fillText( + ctx, + ['Unpaired', 'Paired'][+(this.testBenchInput != undefined)], + xx + this.rightDimensionX / 2, + yy + this.downDimensionY - 5, + 10 + ) + ctx.fill() + + if (this.testBenchInput) { + ctx.beginPath() + ctx.font = '30px Raleway' + ctx.textAlign = 'left' + ctx.fillStyle = 'blue' + for ( + var i = 0; + i < this.testBenchInput.testData.outputs.length; + i++ + ) { + // ctx.beginPath(); + fillText( + ctx, + this.testBenchInput.testData.outputs[i].label, + 5 + xx, + 30 + i * 20 + yy + 4, + 10 + ) + } + ctx.fill() + + if (this.testBenchInput.running && this.testBenchInput.iteration) { + ctx.beginPath() + ctx.font = '30px Raleway' + ctx.textAlign = 'right' + ctx.fillStyle = 'blue' + ctx.beginPath() + for ( + var i = 0; + i < this.testBenchInput.testData.outputs.length; + i++ + ) { + fillText( + ctx, + this.testBenchInput.testData.outputs[i].values[ + this.testBenchInput.iteration - 1 + ], + xx + this.rightDimensionX - 5, + 30 + i * 20 + yy + 4, + 10 + ) + } + + ctx.fill() + } + + if (this.testBenchInput.running && this.testBenchInput.iteration) { + ctx.beginPath() + ctx.font = '30px Raleway' + ctx.textAlign = 'center' + ctx.fillStyle = 'blue' + + for ( + var i = 0; + i < this.testBenchInput.testData.outputs.length; + i++ + ) { + if (this.inputs[i].value != undefined) { + ctx.beginPath() + if ( + this.testBenchInput.testData.outputs[i].values[ + this.testBenchInput.iteration - 1 + ] == 'x' || + parseInt( + this.testBenchInput.testData.outputs[i].values[ + this.testBenchInput.iteration - 1 + ], + 2 + ) == this.inputs[i].value + ) { + ctx.fillStyle = 'green' + } else { + ctx.fillStyle = 'red' + } + fillText( + ctx, + dec2bin( + this.inputs[i].value, + this.inputs[i].bitWidth + ), + xx + this.rightDimensionX / 2, + 30 + i * 20 + yy + 4, + 10 + ) + ctx.fill() + } else { + ctx.beginPath() + if ( + this.testBenchInput.testData.outputs[i].values[ + this.testBenchInput.iteration - 1 + ] == 'x' + ) { + ctx.fillStyle = 'green' + } else { + ctx.fillStyle = 'red' + } + fillText( + ctx, + 'X', + xx + this.rightDimensionX / 2, + 30 + i * 20 + yy + 4, + 10 + ) + ctx.fill() + } + } + } + } + } +} + +TB_Output.prototype.tooltipText = 'Test Bench Output Selected' +TB_Output.prototype.helplink = 'https://docs.circuitverse.org/#/chapter7/3testcircuits' +TB_Output.prototype.centerElement = true +TB_Output.prototype.mutableProperties = { + identifier: { + name: 'TestBench Name:', + type: 'text', + maxlength: '10', + func: 'setIdentifier', + }, +} +TB_Output.prototype.objectType = 'TB_Output' diff --git a/v0/src/simulator/src/themer/customThemeAbstraction.js b/v0/src/simulator/src/themer/customThemeAbstraction.js new file mode 100644 index 00000000..399134ea --- /dev/null +++ b/v0/src/simulator/src/themer/customThemeAbstraction.js @@ -0,0 +1,44 @@ +/** + * CreateAbstraction + * @param {*} themeOptions + * @returns an Object + */ +export const CreateAbstraction = (themeOptions) => { + return { + Navbar: { + color: themeOptions['--bg-navbar'], + description: 'navbar background', + ref: ['--bg-navbar'], + }, + Primary: { + color: themeOptions['--primary'], + description: 'modals background', + ref: ['--primary'], + }, + Secondary: { + color: themeOptions['--bg-tabs'], + description: 'tabBar background', + ref: ['--bg-tabs'], + }, + Canvas: { + color: themeOptions['--canvas-fill'], + description: 'canvas background', + ref: ['--canvas-fill'], + }, + Stroke: { + color: themeOptions['--canvas-stroke'], + description: 'canvas grid color', + ref: ['--canvas-stroke'], + }, + Text: { + color: themeOptions['--text-lite'], + description: 'text color', + ref: ['--text-lite', '--text-panel', '--text-dark'], + }, + Borders: { + color: themeOptions['--br-secondary'], + description: 'borders color', + ref: ['--br-secondary'], + }, + } +} diff --git a/v0/src/simulator/src/themer/customThemer.js b/v0/src/simulator/src/themer/customThemer.js new file mode 100644 index 00000000..51a7e0b2 --- /dev/null +++ b/v0/src/simulator/src/themer/customThemer.js @@ -0,0 +1,154 @@ +// /* eslint-disable import/prefer-default-export */ +// /* eslint-disable import/no-cycle */ +// import { dots } from '../canvasApi' +// import themeOptions from './themes' +// import { updateThemeForStyle } from './themer' +// import { CreateAbstraction } from './customThemeAbstraction' + +// /** +// * +// */ +// var customTheme = CreateAbstraction(themeOptions['Custom Theme']) + +// const updateBG = () => dots(true, false, true) + +// /** +// * Generates Custom theme card HTML +// * return Html Element Theme card html (properties_container) +// */ +// // const getCustomThemeCard = () => { +// // var propertiesContainer = document.createElement('form') +// // const keys = Object.keys(customTheme) +// // keys.forEach((key) => { +// // const property = document.createElement('div') +// // const newPropertyLabel = document.createElement('label') +// // newPropertyLabel.textContent = `${key} (${customTheme[key].description})` +// // newPropertyLabel.setAttribute('for', key) +// // const newPropertyInput = document.createElement('input') +// // newPropertyInput.setAttribute('type', 'color') +// // newPropertyInput.setAttribute('name', key) +// // newPropertyInput.setAttribute('value', customTheme[key].color) +// // newPropertyInput.classList.add('customColorInput') +// // property.append(newPropertyLabel) +// // property.append(newPropertyInput) +// // propertiesContainer.append(property) +// // }) +// // const downloadAnchor = document.createElement('a') +// // downloadAnchor.setAttribute('id', 'downloadThemeFile') +// // downloadAnchor.setAttribute('style', 'display:none') +// // propertiesContainer.appendChild(downloadAnchor) +// // return propertiesContainer +// // } + +// /** +// * Create Custom Color Themes Dialog +// */ +// // export const CustomColorThemes = () => { +// // $('#CustomColorThemesDialog').empty() +// // $('#CustomColorThemesDialog').append(getCustomThemeCard()) +// // $('#CustomColorThemesDialog').dialog({ +// // resizable: false, +// // close() { +// // themeOptions['Custom Theme'] = +// // JSON.parse(localStorage.getItem('Custom Theme')) || +// // themeOptions['Default Theme'] // hack for closing dialog box without saving +// // // Rollback to previous theme +// // updateThemeForStyle(localStorage.getItem('theme')) +// // updateBG() +// // }, +// // buttons: [ +// // { +// // text: 'Apply Theme', +// // click() { +// // // update theme to Custom Theme +// // localStorage.setItem('theme', 'Custom Theme') +// // // add Custom theme to custom theme object +// // localStorage.setItem( +// // 'Custom Theme', +// // JSON.stringify(themeOptions['Custom Theme']) +// // ) +// // $('.set').removeClass('set') +// // $('.selected').addClass('set') +// // $(this).dialog('close') +// // }, +// // }, +// // { +// // text: 'Import Theme', +// // click() { +// // $('#importThemeFile').click() +// // }, +// // }, +// // { +// // text: 'Export Theme', +// // click() { +// // const dlAnchorElem = +// // document.getElementById('downloadThemeFile') +// // dlAnchorElem.setAttribute( +// // 'href', +// // `data:text/json;charset=utf-8,${encodeURIComponent( +// // JSON.stringify(themeOptions['Custom Theme']) +// // )}` +// // ) +// // dlAnchorElem.setAttribute('download', 'CV_CustomTheme.json') +// // dlAnchorElem.click() +// // }, +// // }, +// // ], +// // }) + +// // $('#CustomColorThemesDialog').focus() + +// // /** +// // * To preview the changes +// // */ +// // // function setColorEvent() { +// // // $('.customColorInput').on('input', (e) => { +// // // customTheme[e.target.name].color = e.target.value +// // // customTheme[e.target.name].ref.forEach((property) => { +// // // themeOptions['Custom Theme'][property] = e.target.value +// // // }) +// // // updateThemeForStyle('Custom Theme') +// // // updateBG() +// // // }) +// // // } +// // // setColorEvent() + +// // // hack for updating current theme to the saved custom theme +// // setTimeout(() => { +// // updateThemeForStyle('Custom Theme') +// // updateBG() +// // }, 50) + +// // /** +// // * Read JSON file and +// // * set Custom theme to the Content of the JSON file +// // * */ +// // // function receivedText(e) { +// // // const lines = JSON.parse(e.target.result) +// // // customTheme = CreateAbstraction(lines) +// // // themeOptions['Custom Theme'] = lines +// // // // preview theme +// // // updateThemeForStyle('Custom Theme') +// // // updateBG() +// // // // update colors in dialog box +// // // $('#CustomColorThemesDialog').empty() +// // // $('#CustomColorThemesDialog').append(getCustomThemeCard()) +// // // setColorEvent() +// // // } + +// // /** +// // * Add listener for file input +// // * Read imported JSON file +// // */ +// // // $('#importThemeFile').on('change', (event) => { +// // // var File = event.target.files[0] +// // // if (File !== null && File.name.split('.')[1] === 'json') { +// // // var fr = new FileReader() +// // // fr.onload = receivedText +// // // fr.readAsText(File) +// // // $('#importThemeFile').val('') +// // // } else { +// // // alert('File Not Supported !') +// // // } +// // // }) +// // } diff --git a/v0/src/simulator/src/themer/themeCardSvg.js b/v0/src/simulator/src/themer/themeCardSvg.js new file mode 100644 index 00000000..5e1776cb --- /dev/null +++ b/v0/src/simulator/src/themer/themeCardSvg.js @@ -0,0 +1,102 @@ +export default ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +` diff --git a/v0/src/simulator/src/themer/themer.js b/v0/src/simulator/src/themer/themer.js new file mode 100644 index 00000000..2c61865e --- /dev/null +++ b/v0/src/simulator/src/themer/themer.js @@ -0,0 +1,223 @@ +import { dots } from '../canvasApi' +import themeOptions from './themes' +import themeCardSvg from './themeCardSvg' +import { SimulatorStore } from '#/store/SimulatorStore/SimulatorStore' + +/** + * Extracts canvas theme colors from CSS-Variables and returns a JSON Object + * @returns {object} + */ +const getCanvasColors = () => { + let colors = {} + colors['hover_select'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--hover-and-sel') + colors['fill'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--fill') + colors['mini_fill'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--mini-map') + colors['mini_stroke'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--mini-map-stroke') + colors['stroke'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--stroke') + colors['stroke_alt'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--secondary-stroke') + colors['input_text'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--input-text') + colors['color_wire_draw'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--wire-draw') + colors['color_wire_con'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--wire-cnt') + colors['color_wire_pow'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--wire-pow') + colors['color_wire_sel'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--wire-sel') + colors['color_wire_lose'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--wire-lose') + colors['color_wire'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--wire-norm') + colors['text'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--text') + colors['node'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--node') + colors['node_norm'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--node-norm') + colors['splitter'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--splitter') + colors['out_rect'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--output-rect') + colors['canvas_stroke'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--canvas-stroke') + colors['canvas_fill'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--canvas-fill') + return colors +} + +/** + * Common canvas theme color object, used for rendering canvas elements + */ +export let colors = getCanvasColors() + +/** + * Updates theme + * 1) Sets CSS Variables for UI elements + * 2) Sets color variable for Canvas elements + */ +export function updateThemeForStyle(themeName) { + const selectedTheme = themeOptions[themeName] + if (selectedTheme === undefined) return + const html = document.getElementsByTagName('html')[0] + Object.keys(selectedTheme).forEach((property, i) => { + html.style.setProperty(property, selectedTheme[property]) + }) + colors = getCanvasColors() +} + +/** + * Theme Preview Card SVG + * Sets the SVG colors according to theme + * @param {string} themeName Name of theme + * @returns {SVG} + */ +export const getThemeCardSvg = (themeName) => { + const colors = themeOptions[themeName] + let svgIcon = $(themeCardSvg) + + // Dynamically set the colors according to the theme + $('.svgText', svgIcon).attr('fill', colors['--text-panel']) + + $('.svgNav', svgIcon).attr('fill', colors['--bg-tab']) + $('.svgNav', svgIcon).attr('stroke', colors['--br-primary']) + + $('.svgGridBG', svgIcon).attr('fill', colors['--canvas-fill']) + $('.svgGrid', svgIcon).attr('fill', colors['--canvas-stroke']) + + $('.svgPanel', svgIcon).attr('fill', colors['--primary']) + $('.svgPanel', svgIcon).attr('stroke', colors['--br-primary']) + + $('.svgChev', svgIcon).attr('stroke', colors['--br-secondary']) + + $('.svgHeader', svgIcon).attr('fill', colors['--primary']) + let temp = svgIcon.prop('outerHTML') + return svgIcon.prop('outerHTML') +} + +/** + * Generates theme card HTML + * @param {string} themeName Name of theme + * @param {boolean} selected Flag variable for currently selected theme + * @return {string} Theme card html + */ +export const getThemeCard = (themeName, selected) => { + if (themeName === 'Custom Theme') return '
' + let themeId = themeName.replace(' ', '') + let selectedClass = selected ? 'selected set' : '' + // themeSel is the hit area + return ` +
+
+ ${getThemeCardSvg(themeName)} + + + + +
+ ` +} + +/** + * Create Color Themes Dialog + */ +export const colorThemes = () => { + const simulatorStore = SimulatorStore() + simulatorStore.dialogBox.theme_dialog = true + + // const selectedTheme = localStorage.getItem('theme') + // $('#colorThemesDialog').empty() + // const themes = Object.keys(themeOptions) + // themes.forEach((theme) => { + // if (theme === selectedTheme) { + // $('#colorThemesDialog').append(getThemeCard(theme, true)) + // } else { + // $('#colorThemesDialog').append(getThemeCard(theme, false)) + // } + // }) + + // $('.selected label').trigger('click') + // $('#colorThemesDialog').dialog({ + // resizable: false, + // close() { + // // Rollback to previous theme + // updateThemeForStyle(localStorage.getItem('theme')) + // updateBG() + // }, + // buttons: [ + // { + // text: 'Apply Theme', + // click() { + // // check if any theme is selected or not + // if ($('.selected label').text()) { + // localStorage.removeItem('Custom Theme') + // localStorage.setItem( + // 'theme', + // $('.selected label').text() + // ) + // } + // $('.set').removeClass('set') + // $('.selected').addClass('set') + // $(this).dialog('close') + // }, + // }, + // { + // text: 'Custom Theme', + // click() { + // CustomColorThemes() + // $(this).dialog('close') + // }, + // }, + // ], + // }) + + $('#colorThemesDialog').focus() + $('.ui-dialog[aria-describedby="colorThemesDialog"]').on('click', () => + $('#colorThemesDialog').focus() + ) //hack for losing focus + + $('.themeSel').on('mousedown', (e) => { + e.preventDefault() + $('.selected').removeClass('selected') + let themeCard = $(e.target.parentElement) + themeCard.addClass('selected') + // Extract radio button + var radioButton = themeCard.find('input[type=radio]') + radioButton.trigger('click') // Mark as selected + updateThemeForStyle(themeCard.find('label').text()) // Extract theme name and set + updateBG() + }) +} + +export const updateBG = () => dots(true, false, true) +;(() => { + if (!localStorage.getItem('theme')) + localStorage.setItem('theme', 'Default Theme') + updateThemeForStyle(localStorage.getItem('theme')) +})() diff --git a/v0/src/simulator/src/themer/themes.js b/v0/src/simulator/src/themer/themes.js new file mode 100644 index 00000000..d4f8caa1 --- /dev/null +++ b/v0/src/simulator/src/themer/themes.js @@ -0,0 +1,382 @@ +export default { + 'Default Theme': { + '--text-navbar--alt': '#000', + '--br-secondary': '#7d7d7d', + '--br-circuit-cur': '#fff', + '--br-circuit': '#fff', + '--cus-radio_label': '#656565', + '--primary': '#454545', + '--text-lite': '#fff', + '--text-dark': '#000', + '--text-panel': 'white', + '--bg-navbar': '#454545', + '--qp-br-tl': '#333333', + '--qp-br-rd': '#535353', + '--qp-box-shadow-1': '#3b3b3b', + '--qp-box-shadow-2': '#4f4f4f', + '--bg-circuit': '#ddd', + '--br-circuit': '#454545', + '--br-primary': '#fff', + '--bg-primary-moz': '#454545e6', + '--bg-primary-chr': '#454545b3', + '--bg-tabs': '#8b8b8b', + '--bg-icons': '#7d7d7d', + '--bg-text': '#cacaca', + '--bg-secondary': '#bbbbbb', + '--canvas-stroke': '#eee', + '--canvas-fill': 'white', + '--context-text': 'white', + '--bg-toggle-btn-primary': '#42b983', + '--primary-btn-hov': '#3ca877', + '--btn-danger': '#dc5656', + '--btn-danger-darken': '#b03662', + '--disable': '#6c8b93', + '--cus-btn-hov--bg': '#ddd', + '--cus-btn-hov-text': '#000', + '--node': 'green', + '--stroke': 'black', + '--fill': 'white', + '--hover-and-sel': 'rgba(255, 255, 32, 0.8)', + '--wire-draw': 'black', + '--wire-cnt': 'green', + '--wire-pow': 'lightgreen', + '--wire-sel': 'blue', + '--wire-lose': 'red', + '--mini-map': 'green', + '--mini-map-stroke': 'darkgreen', + '--input-text': 'green', + '--secondary-stroke': 'red', + '--text': 'black', + '--wire-norm': 'black', + '--node-norm': 'green', + '--splitter': 'black', + '--output-rect': 'blue', + '--table-head-dark': '#3d3d3d', + }, + 'Night Sky': { + '--text-navbar--alt': '#fff', + '--br-secondary': '#665627', + '--cus-radio_label': '#0F111A', + '--primary': '#0F111A', //header bg, panels bg + '--text-lite': '#FFF', //normal state text + '--text-dark': 'white', //text state on hover, on drop down menu , context menu + '--text-panel': 'white', + '--bg-navbar': '#0F111A', + '--br-circuit': '#0F111A', //panel border, tabbar circuit border + '--br-primary': '#665627', //panel border, tabbar circuit border + '--br-circuit-cur': '#cccccc', + '--bg-circuit': '#bdc2ca', + '--bg-primary-moz': '#0f111ae6', //dialog bg + '--bg-primary-chr': '#0f111ab3', //dialog bg + '--bg-tabs': '#727d8d', //tabs bar primary bg, + '--bg-icons': '#4d647a', //ce icon bg + '--bg-text': '#727d8d', //drop down, content menu, text bg on hover + '--bg-secondary': '#536c84', //border color input button, + '--canvas-fill': '#1B2C33', //canvas bg + '--canvas-stroke': '#6A7980', //canvas stroke + '--context-text': 'white', + '--bg-toggle-btn-primary': '#48a69d', + '--primary-btn-hov': '#3f9189', + '--btn-danger': '#c33c6c', + '--btn-danger-darken': '#b03662', + '--qp-br-tl': '#282d46', //more ligthen than qp box shadow 1 + '--qp-br-rd': '#1d2132', + '--qp-box-shadow-1': '#1d2132', //lil lighten base + '--qp-box-shadow-2': '#0a0b11', //lil darken base + '--cus-btn-hov--bg': '#48a69d', + '--cus-btn-hov-text': '#fff', + '--node': '#285963', + '--stroke': '#35aea9', + '--fill': '#DEFFFE', + '--hover-and-sel': '#E3B924', + '--wire-draw': '#77878C', + '--wire-cnt': '#3B7F58', + '--wire-pow': '#75FFB0', + '--wire-sel': '#208CC9', + '--wire-lose': '#BF0426', + '--mini-map': '#3B7F58', + '--mini-map-stroke': '#607F6E', + '--input-text': '#3B7F58', + '--output-rect': '#0487D9', + '--secondary-stroke': '#BF0426', + '--text': '#E9FBF8', + '--wire-norm': '#277F7C', + '--node-norm': '#FFC231', + '--splitter': '#0284A8', + '--disable': '#4F74B0', + '--table-head-dark': '#000000', + }, + 'Lite-born Spring': { + '--text-navbar--alt': '#000', + '--br-secondary': '#6B6B6B', + '--cus-radio_label': '#6B6B6B', + '--primary': '#EAEAEB', //header bg + '--text-dark': '#6B6B6B', //normal state text + '--text-lite': 'white', //text state on hover, on drop down menu , context menu + '--text-panel': '#6B6B6B', + '--bg-navbar': '#6b6b6b', + '--qp-br-tl': '#969696', //more ligthen than qp box shadow 1 + '--qp-br-rd': '#545454', + '--qp-box-shadow-1': '#747474', //lil lighten base .. top left shadow + '--qp-box-shadow-2': '#5f5f5f', //lil darken base //down right shadow + // "--bg-tabs": "#EAEAEB", //tabs bar primary bg, + '--bg-tabs': '#A4A4A4', //tabs bar primary bg, + '--br-circuit-cur': '#42B983', + '--bg-circuit': '#D7D7D7', + '--br-circuit': '#42B983', + '--br-primary': '#42B983', //panel border, tabbar circuit border + '--context-text-hov': '#6B6B6B', + '--context-text': 'white', + '--bg-primary-moz': 'rgba(107, 107, 107, 0.904)', //dialog bg, navbar dropwdown //.9 opacity of nav + '--bg-primary-chr': 'rgba(107, 107, 107, 0.704)', //dialog bg navbar dropwdown // .7 opacity of nav + '--bg-icons': '#DDDDDD', //ce icon bg + '--bg-text': '#ddd', //drop down, content menu, text bg on hover + '--bg-secondary': '#6B6B6B', //border color input button, + '--bg-toggle-btn-primary': '#42B983', + '--primary-btn-hov': '#66C89C', + '--btn-danger': '#BF2424', + '--btn-danger-darken': '#BF414C', + '--cus-btn-hov--bg': '#42B983', + '--cus-btn-hov-text': '#fff', + '--canvas-fill': 'white', //canvas bg + '--canvas-stroke': '#BABABA', //canvas stroke + '--node': '#42B983', + '--stroke': '#6B6B6B', + '--fill': '#EAEAEB', + '--hover-and-sel': '#FFE99B', //yellow + '--wire-draw': '#6B6B6B', //black + '--wire-cnt': '#42B983', // + '--wire-pow': '#52E539', + '--wire-sel': '#0FB2F2', + '--wire-lose': '#F10530', + '--mini-map': '#42B983', + '--mini-map-stroke': '#0FB2F2', + '--input-text': '#42B983', + '--output-rect': '#0487D9', + '--secondary-stroke': '#F10530', + '--text': '#454545', + '--wire-norm': '#006839', + '--node-norm': '#FFC231', + '--splitter': '#00B462', + '--disable': '#656565', + '--table-head-dark': '#ffffff', + }, + 'G&W': { + '--text-navbar--alt': '#000', + '--br-secondary': '#6B6B6B', + '--cus-radio_label': '#6B6B6B', + '--primary': '#EAEAEB', //header bg + '--text-dark': '#6B6B6B', //normal state text + '--text-lite': 'white', //text state on hover, on drop down menu , context menu + '--text-panel': '#656565', + '--bg-navbar': '#6b6b6b', + '--qp-br-tl': '#969696', //more ligthen than qp box shadow 1 + '--qp-br-rd': '#545454', + '--qp-box-shadow-1': '#747474', //lil lighten base .. top left shadow + '--qp-box-shadow-2': '#5f5f5f', //lil darken base //down right shadow + // "--bg-tabs": "#EAEAEB", //tabs bar primary bg, + '--bg-tabs': '#A4A4A4', //tabs bar primary bg, + '--br-circuit-cur': '#6b6b6b', + '--bg-circuit': '#D7D7D7', + '--br-circuit': '#6b6b6b', + '--br-primary': '#6B6B6B', //panel border, tabbar circuit border + '--context-text-hov': '#6B6B6B', + '--context-text': 'white', + '--bg-primary-moz': 'rgba(107, 107, 107, 0.904)', //dialog bg, navbar dropwdown //.9 opacity of nav + '--bg-primary-chr': 'rgba(107, 107, 107, 0.704)', //dialog bg navbar dropwdown // .7 opacity of nav + '--bg-icons': '#DDDDDD', //ce icon bg + '--bg-text': '#ddd', //drop down, content menu, text bg on hover + '--bg-secondary': '#6B6B6B', //border color input button, + '--bg-toggle-btn-primary': '#3ac8a4', + '--primary-btn-hov': '#71D7BD', + '--btn-danger': '#fc8771', + '--btn-danger-darken': '#FDB2A4', + '--cus-btn-hov--bg': '#3ac8a4', + '--cus-btn-hov-text': '#fff', + '--canvas-fill': 'white', //canvas bg + '--canvas-stroke': '#BABABA', //canvas stroke + '--node': '#42B983', + '--stroke': '#6B6B6B', + '--fill': '#EAEAEB', + '--hover-and-sel': '#FFE99B', //yellow + '--wire-draw': '#6B6B6B', //black + '--wire-cnt': '#42B983', // + '--wire-pow': '#52E539', + '--wire-sel': '#0FB2F2', + '--wire-lose': '#F10530', + '--mini-map': '#42B983', + '--mini-map-stroke': '#0FB2F2', + '--input-text': '#42B983', + '--output-rect': '#0487D9', + '--secondary-stroke': '#F10530', + '--text': '#454545', + '--wire-norm': '#006839', + '--node-norm': '#FFC231', + '--splitter': '#00B462', + '--disable': '#656565', + '--table-head-dark': '#ffffff', + }, + 'High Contrast': { + '--text-navbar--alt': '#000', + '--br-secondary': '#F38518', + '--cus-radio_label': 'black', + '--primary': 'black', //header bg + '--text-dark': 'black', //normal state text + '--text-lite': 'white', //text state on hover, on drop down menu , context menu + '--text-panel': 'white', + '--bg-navbar': 'black', + '--qp-br-tl': '#F38518', //more ligthen than qp box shadow 1 + '--qp-br-rd': '#F38518', + '--qp-box-shadow-1': '#0D0D0D', //lil lighten base .. top left shadow + '--qp-box-shadow-2': '#0D0D0D', //lil darken base //down right shadow + '--bg-tabs': '#616161', //tabs bar primary bg, + '--text-circuit': 'black', + '--br-circuit-cur': '#F38518', + '--bg-circuit': '#B6B6B6', + '--br-circuit': '#F38518', + '--br-primary': '#F38518', //panel border, tabbar circuit border + '--context-text-hov': 'black', + '--context-text': 'white', + '--bg-primary-moz': 'rgba(0, 0, 0, 0.904)', //dialog bg, navbar dropwdown //.9 opacity of nav + '--bg-primary-chr': 'rgba(0, 0, 0, 0.704)', //dialog bg navbar dropwdown // .7 opacity of nav + '--bg-icons': '#262626', //ce icon bg + '--bg-text': '#ddd', //drop down, content menu, text bg on hover + '--bg-secondary': '#6B6B6B', //border color input button, + '--bg-toggle-btn-primary': '#009599', + '--primary-btn-hov': '#00b1b6', + '--btn-danger': '#E45605', + '--btn-danger-darken': '#fa792f', + '--cus-btn-hov--bg': '#009599', + '--cus-btn-hov-text': '#fff', + '--canvas-fill': 'black', //canvas bg + '--canvas-stroke': '#9ad5e9', //canvas stroke + '--node': '#26a841', + '--stroke': '#626262', + '--fill': '#d4d4d4', + '--hover-and-sel': '#ffdf6c', //yellow + '--wire-draw': '#909090', //black + '--wire-cnt': '#3bb15b', // + '--wire-pow': '#3ac53c', + '--wire-sel': '#049ffb', + '--wire-lose': '#f42f0b', + '--mini-map': '#1c2427', + '--mini-map-stroke': '#000', + '--input-text': '#42B983', + '--output-rect': '#005682', + '--secondary-stroke': '#f7081e', + '--text': '#fff', + '--wire-norm': '#00b965', + '--node-norm': '#F38518', + '--splitter': '#21de5a', + '--disable': '#262626', + '--table-head-dark': '#000000', + }, + 'Color Blind': { + '--text-navbar--alt': '#000', + '--br-secondary': '#e2dad1', + '--cus-radio_label': '#2e2b21', + '--primary': '#2e2b21', //header bg + '--text-dark': 'black', //normal state text + '--text-lite': 'white', //text state on hover, on drop down menu , context menu + '--text-panel': 'white', + '--bg-navbar': '#2e2b21', + '--qp-br-tl': '#716950', //more ligthen than qp box shadow 1 + '--qp-br-rd': '#6c654d', + '--qp-box-shadow-1': '#4f4a38', //lil lighten base .. top left shadow + '--qp-box-shadow-2': '#302d23', //lil darken base //down right shadow + '--bg-tabs': '#9a9a9a', //tabs bar primary bg, + '--text-circuit': 'black', + '--br-circuit-cur': '#e2dad1', + '--bg-circuit': '#d6d6d6', + '--br-circuit': '#e2dad1', + '--br-primary': '#e2dad1', //panel border, tabbar circuit border + '--context-text-hov': 'black', + '--context-text': 'white', + '--bg-primary-moz': 'rgba(46, 43, 33, 1)', //dialog bg, navbar dropwdown //.9 opacity of nav + '--bg-primary-chr': 'rgba(46, 43, 33, 1)', //dialog bg navbar dropwdown // .7 opacity of nav + '--bg-icons': '#9c7762', //ce icon bg + '--bg-text': '#ddd', //drop down, content menu, text bg on hover + '--bg-secondary': '#6B6B6B', //border color input button, + '--bg-toggle-btn-primary': '#bfaac1', + '--primary-btn-hov': '#ccbbcd', + '--btn-danger': '#b66e43', + '--btn-danger-darken': '#ba7144', + '--cus-btn-hov--bg': '#b66e43', + '--cus-btn-hov-text': '#fff', + '--canvas-fill': '#fff', //canvas bg + '--canvas-stroke': '#8d88ad', //canvas stroke + '--node': '#c59434', + '--stroke': '#342a1f', + '--fill': '#e0dcd3', + '--hover-and-sel': '#f4d4d4', //yellow + '--wire-draw': '#4c4c4c', //black + '--wire-cnt': '#908eb9', // + '--wire-pow': '#b3b1cf', + '--wire-sel': '#a7b8f8', + '--wire-lose': '#f42f0b', + '--mini-map': '#4b4636', + '--mini-map-stroke': '#000', + '--input-text': '#756d54', + '--output-rect': '#092c48', + '--secondary-stroke': '#cdb1ad', + '--text': '#000', + '--wire-norm': '#7f7cae', + '--node-norm': '#c59434', + '--splitter': '#836222', + '--disable': '#956c6a', + '--table-head-dark': '#2e2b21', + }, + 'Custom Theme': JSON.parse(localStorage.getItem('Custom Theme')) || { + '--text-navbar--alt': '#000', + '--br-secondary': '#7d7d7d', + '--br-circuit-cur': '#ffffff', + '--br-circuit': '#ffffff', + '--cus-radio_label': '#656565', + '--primary': '#454545', + '--text-lite': '#ffffff', + '--text-dark': '#000', + '--text-panel': '#ffffff', + '--bg-navbar': '#454545', + '--qp-br-tl': '#333333', + '--qp-br-rd': '#535353', + '--qp-box-shadow-1': '#3b3b3b', + '--qp-box-shadow-2': '#4f4f4f', + '--bg-circuit': '#ddd', + '--br-circuit': '#454545', + '--br-primary': '#ffffff', + '--bg-primary-moz': '#454545e6', + '--bg-primary-chr': '#454545b3', + '--bg-tabs': '#8b8b8b', + '--bg-icons': '#7d7d7d', + '--bg-text': '#cacaca', + '--bg-secondary': '#bbbbbb', + '--canvas-stroke': '#eee', + '--canvas-fill': '#ffffff', + '--context-text': '#ffffff', + '--bg-toggle-btn-primary': '#42b983', + '--primary-btn-hov': '#3ca877', + '--btn-danger': '#dc5656', + '--btn-danger-darken': '#b03662', + '--disable': '#6c8b93', + '--cus-btn-hov--bg': '#ddd', + '--cus-btn-hov-text': '#000', + '--node': '#008000', + '--stroke': '#000', + '--fill': '#ffffff', + '--hover-and-sel': '#ffff20cc', + '--wire-draw': '#000', + '--wire-cnt': '#008000', + '--wire-pow': '#90ee90', + '--wire-sel': '#0000ff', + '--wire-lose': '#ff0000', + '--mini-map': '#008000', + '--mini-map-stroke': '#006400', + '--input-text': '#008000', + '--secondary-stroke': '#ff0000', + '--text': '#000', + '--wire-norm': '#000', + '--node-norm': '#008000', + '--splitter': '#000', + '--output-rect': '#0000ff', + }, +} diff --git a/v0/src/simulator/src/tutorials.js b/v0/src/simulator/src/tutorials.js new file mode 100644 index 00000000..a1f10d67 --- /dev/null +++ b/v0/src/simulator/src/tutorials.js @@ -0,0 +1,143 @@ +import Driver from 'driver.js' + +export const tour = [ + { + element: '#guide_1', + className: 'guide_1', + popover: { + className: '', + title: 'Circuit Elements panel', + description: + 'This is where you can find all the circuit elements that are offered to build amazing circuits.', + position: 'right', + offset: 160, + }, + }, + { + element: '.guide_2', + popover: { + title: 'Properties Panel', + description: + 'This panel lets you change element properties as they are selected. When no elements are selected, the panel displays project properties.', + position: 'left', + offset: 200, + }, + }, + { + element: '.quick-btn', + popover: { + title: 'Quick Access Panel', + description: + 'This movable panel offers to perform some actions like Save Online, Open, Download quickly. Hover over the icons and see for yourself', + position: 'bottom', + // offset: 750, + }, + }, + // { + // element: '.forum-tab', + // popover: { + // className: "", + // title: 'Forum Tab', + // description: "The forums can help you report issues & bugs, feature requests, and discussing about circuits with the community!", + // position: 'right', + // // offset: -25, + // }, + // }, + { + element: '#tabsBar', + popover: { + title: 'Circuit Tabs', + description: + 'This section displays all the circuits you have in your project. You can easily add and delete circuits.', + position: 'bottom', + offset: 250, + }, + }, + { + element: '.timing-diagram-panel', + popover: { + title: 'Timing Diagram Panel (Waveform)', + description: + 'This panel displays the waveform created by circuits and can be used for resolving race conditions and debugging circuits.', + position: 'bottom', + offset: 0, + }, + }, + + // { + // element: '#delCirGuide', + // popover: { + // title: 'Delete sub-circuit button', + // description: "You can make delete sub-circuits by pressing the cross *Note that main circuit cannot be deleted.", + // position: 'right', + // // offset: 250, + // }, + // }, + { + element: '.report-sidebar a', + popover: { + className: 'bug-guide', + title: 'Report System', + description: + 'You can report any issues/bugs you face through this issue reporting button there and then quickly.', + position: 'left', + offset: -105, + }, + }, + { + element: '.tour-help', + popover: { + className: 'tourHelpStep', + title: 'Restart tutorial anytime', + description: + 'You can restart this tutorial anytime by clicking on "Tutorial Guide" under this dropdown.', + position: 'right', + offset: 0, + }, + }, +] + +// Not used currently +export const tutorialWrapper = () => { + const panelHighlight = new Driver() + document.querySelector('.panelHeader').addEventListener('click', (e) => { + if (localStorage.tutorials === 'next') { + panelHighlight.highlight({ + element: '#guide_1', + showButtons: false, + popover: { + title: 'Here are the elements', + description: + 'Select any element by clicking on it & then click anywhere on the grid to place the element.', + position: 'right', + offset: + e.target.nextElementSibling.offsetHeight + + e.target.offsetTop - + 45, + }, + }) + localStorage.setItem('tutorials', 'done') + } + }, { + once: true, + }) + document.querySelector('.icon').addEventListener('click', () => { + panelHighlight.reset(true) + }) +} + +const animatedTourDriver = new Driver({ + animate: true, + opacity: 0.8, + padding: 5, + showButtons: true, +}) + +export function showTourGuide() { + document.querySelector('.draggable-panel .maximize').click(); + animatedTourDriver.defineSteps(tour) + animatedTourDriver.start() + localStorage.setItem('tutorials_tour_done', true) +} + +export default showTourGuide diff --git a/v0/src/simulator/src/utils.js b/v0/src/simulator/src/utils.js new file mode 100644 index 00000000..9d6a32b9 --- /dev/null +++ b/v0/src/simulator/src/utils.js @@ -0,0 +1,277 @@ +import simulationArea from './simulationArea' +import { + scheduleUpdate, + play, + updateCanvasSet, + errorDetectedSet, + errorDetectedGet, +} from './engine' +import { layoutModeGet } from './layoutMode' +import plotArea from './plotArea' +import { SimulatorStore } from '#/store/SimulatorStore/SimulatorStore' + +window.globalScope = undefined +window.lightMode = false // To be deprecated +window.projectId = undefined +window.id = undefined +window.loading = false // Flag - all assets are loaded + +var prevErrorMessage // Global variable for error messages +var prevShowMessage // Global variable for error messages +export function generateId() { + var id = '' + var possible = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' + + for (var i = 0; i < 20; i++) { + id += possible.charAt(Math.floor(Math.random() * possible.length)) + } + + return id +} + +// To strip tags from input +export function stripTags(string = '') { + return string.replace(/(<([^>]+)>)/gi, '').trim() +} + +export function clockTick() { + if (!simulationArea.clockEnabled) return + if (errorDetectedGet()) return + if (layoutModeGet()) return + updateCanvasSet(true) + globalScope.clockTick() + plotArea.nextCycle() + play() + scheduleUpdate(0, 20) +} + +/** + * Helper function to show error + * @param {string} error -The error to be shown + * @category utils + */ +export function showError(error) { + errorDetectedSet(true) + // if error ha been shown return + if (error === prevErrorMessage) return + prevErrorMessage = error + var id = Math.floor(Math.random() * 10000) + $('#MessageDiv').append( + `` + ) + setTimeout(() => { + prevErrorMessage = undefined + $(`#${id}`).fadeOut() + }, 1500) +} + +// Helper function to show message +export function showMessage(mes) { + if (mes === prevShowMessage) return + prevShowMessage = mes + var id = Math.floor(Math.random() * 10000) + $('#MessageDiv').append( + `` + ) + setTimeout(() => { + prevShowMessage = undefined + $(`#${id}`).fadeOut() + }, 2500) +} + +export function distance(x1, y1, x2, y2) { + return Math.sqrt((x2 - x1) ** 2) + (y2 - y1) ** 2 +} + +/** + * Helper function to return unique list + * @param {Array} a - any array + * @category utils + */ +export function uniq(a) { + var seen = {} + const tmp = a.filter((item) => + seen.hasOwnProperty(item) ? false : (seen[item] = true) + ) + return tmp +} + +// Generates final verilog code for each element +// Gate = &/|/^ +// Invert is true for xNor, Nor, Nand +export function gateGenerateVerilog(gate, invert = false) { + var inputs = [] + var outputs = [] + + for (var i = 0; i < this.nodeList.length; i++) { + if (this.nodeList[i].type == NODE_INPUT) { + inputs.push(this.nodeList[i]) + } else { + if (this.nodeList[i].connections.length > 0) + outputs.push(this.nodeList[i]) + else outputs.push('') // Don't create a wire + } + } + + var res = 'assign ' + if (outputs.length == 1) res += outputs[0].verilogLabel + else res += `{${outputs.map((x) => x.verilogLabel).join(', ')}}` + + res += ' = ' + + var inputParams = inputs.map((x) => x.verilogLabel).join(` ${gate} `) + if (invert) { + res += `~(${inputParams});` + } else { + res += inputParams + ';' + } + return res +} + +// Helper function to download text +export function download(filename, text) { + var pom = document.createElement('a') + pom.setAttribute( + 'href', + 'data:text/plain;charset=utf-8,' + encodeURIComponent(text) + ) + pom.setAttribute('download', filename) + + if (document.createEvent) { + var event = document.createEvent('MouseEvents') + event.initEvent('click', true, true) + pom.dispatchEvent(event) + } else { + pom.click() + } +} + +// Helper function to open a new tab +export function openInNewTab(url) { + var win = window.open(url, '_blank') + win.focus() +} + +export function copyToClipboard(text) { + const textarea = document.createElement('textarea') + + // Move it off-screen. + textarea.style.cssText = 'position: absolute; left: -99999em' + + // Set to readonly to prevent mobile devices opening a keyboard when + // text is .select()'ed. + textarea.setAttribute('readonly', true) + + document.body.appendChild(textarea) + textarea.value = text + + // Check if there is any content selected previously. + const selected = + document.getSelection().rangeCount > 0 + ? document.getSelection().getRangeAt(0) + : false + + // iOS Safari blocks programmatic execCommand copying normally, without this hack. + // https://stackoverflow.com/questions/34045777/copy-to-clipboard-using-javascript-in-ios + if (navigator.userAgent.match(/ipad|ipod|iphone/i)) { + const editable = textarea.contentEditable + textarea.contentEditable = true + const range = document.createRange() + range.selectNodeContents(textarea) + const sel = window.getSelection() + sel.removeAllRanges() + sel.addRange(range) + textarea.setSelectionRange(0, 999999) + textarea.contentEditable = editable + } else { + textarea.select() + } + + try { + const result = document.execCommand('copy') + + // Restore previous selection. + if (selected) { + document.getSelection().removeAllRanges() + document.getSelection().addRange(selected) + } + textarea.remove() + return result + } catch (err) { + console.error(err) + textarea.remove() + return false + } +} + +export function truncateString(str, num) { + // If the length of str is less than or equal to num + // just return str--don't truncate it. + if (str.length <= num) { + return str + } + // Return str truncated with '...' concatenated to the end of str. + return str.slice(0, num) + '...' +} + +export function bitConverterDialog() { + const simulatorStore = SimulatorStore(); + simulatorStore.dialogBox.hex_bin_dec_converter_dialog = true; +} + +export function getImageDimensions(file) { + return new Promise(function (resolved, rejected) { + var i = new Image() + i.onload = function () { + resolved({ w: i.width, h: i.height }) + } + i.src = file + }) +} + +// convertors +export var convertors = { + dec2bin: (x) => '0b' + x.toString(2), + dec2hex: (x) => '0x' + x.toString(16), + dec2octal: (x) => '0' + x.toString(8), + dec2bcd: (x) => parseInt(x.toString(10), 16).toString(2), +} + +export function parseNumber(num) { + if (num instanceof Number) return num + if (num.slice(0, 2).toLocaleLowerCase() == '0b') + return parseInt(num.slice(2), 2) + if (num.slice(0, 2).toLocaleLowerCase() == '0x') + return parseInt(num.slice(2), 16) + if (num.slice(0, 1).toLocaleLowerCase() == '0') return parseInt(num, 8) + return parseInt(num) +} + +export function promptFile(contentType, multiple) { + var input = document.createElement('input') + input.type = 'file' + input.multiple = multiple + input.accept = contentType + return new Promise(function (resolve) { + document.activeElement.onfocus = function () { + document.activeElement.onfocus = null + setTimeout(resolve, 500) + } + input.onchange = function () { + var files = Array.from(input.files) + if (multiple) return resolve(files) + resolve(files[0]) + } + input.click() + }) +} + +export function escapeHtml(unsafe) { + return unsafe + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, ''') +} diff --git a/v0/src/simulator/src/ux.js b/v0/src/simulator/src/ux.js new file mode 100644 index 00000000..aaad33cd --- /dev/null +++ b/v0/src/simulator/src/ux.js @@ -0,0 +1,803 @@ +/* eslint-disable import/no-cycle */ +/* eslint-disable guard-for-in */ +/* eslint-disable no-restricted-syntax */ +/* eslint-disable no-restricted-syntax */ +/* eslint-disable guard-for-in */ + +import { layoutModeGet } from './layoutMode' +import { + scheduleUpdate, + wireToBeCheckedSet, + updateCanvasSet, + update, + updateSimulationSet, +} from './engine' +import simulationArea from './simulationArea' +import logixFunction from './data' +import { newCircuit, circuitProperty } from './circuit' +import modules from './modules' +import { updateRestrictedElementsInScope } from './restrictedElementDiv' +import { paste } from './events' +import { setProjectName, getProjectName } from './data/save' +import { changeScale } from './canvasApi' +import { generateImage, generateSaveData } from './data/save' +import { setupVerilogExportCodeWindow } from './verilog' +import { updateTestbenchUI, setupTestbenchUI } from './testbench' +import { applyVerilogTheme } from './Verilog2CV' +import { dragging } from './drag' + +export const uxvar = { + smartDropXX: 50, + smartDropYY: 80, +} +/** + * @type {number} - Is used to calculate the position where an element from sidebar is dropped + * @category ux + */ +uxvar.smartDropXX = 50 + +/** + * @type {number} - Is used to calculate the position where an element from sidebar is dropped + * @category ux + */ +uxvar.smartDropYY = 80 + +// ------------------------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------------------------ +/** + * @type {Object} - Object stores the position of context menu; + * @category ux + */ +var ctxPos = { + x: 0, + y: 0, + visible: false, +} +// FUNCITON TO SHOW AND HIDE CONTEXT MENU +function hideContextMenu() { + var el = document.getElementById('contextMenu') + el.style = 'opacity:0;' + setTimeout(() => { + el.style = 'visibility:hidden;' + ctxPos.visible = false + }, 200) // Hide after 2 sec +} +/** + * Function displays context menu + * @category ux + */ +function showContextMenu() { + if (layoutModeGet()) return false // Hide context menu when it is in Layout Mode + $('#contextMenu').css({ + visibility: 'visible', + opacity: 1, + }) + + var windowHeight = + $('#simulationArea').height() - $('#contextMenu').height() - 10 + var windowWidth = + $('#simulationArea').width() - $('#contextMenu').width() - 10 + // for top, left, right, bottom + var topPosition + var leftPosition + var rightPosition + var bottomPosition + if (ctxPos.y > windowHeight && ctxPos.x <= windowWidth) { + //When user click on bottom-left part of window + leftPosition = ctxPos.x + bottomPosition = $(window).height() - ctxPos.y + $('#contextMenu').css({ + left: `${leftPosition}px`, + bottom: `${bottomPosition}px`, + right: 'auto', + top: 'auto', + }) + } else if (ctxPos.y > windowHeight && ctxPos.x > windowWidth) { + //When user click on bottom-right part of window + bottomPosition = $(window).height() - ctxPos.y + rightPosition = $(window).width() - ctxPos.x + $('#contextMenu').css({ + left: 'auto', + bottom: `${bottomPosition}px`, + right: `${rightPosition}px`, + top: 'auto', + }) + } else if (ctxPos.y <= windowHeight && ctxPos.x <= windowWidth) { + //When user click on top-left part of window + leftPosition = ctxPos.x + topPosition = ctxPos.y + $('#contextMenu').css({ + left: `${leftPosition}px`, + bottom: 'auto', + right: 'auto', + top: `${topPosition}px`, + }) + } else { + //When user click on top-right part of window + rightPosition = $(window).width() - ctxPos.x + topPosition = ctxPos.y + $('#contextMenu').css({ + left: 'auto', + bottom: 'auto', + right: `${rightPosition}px`, + top: `${topPosition}px`, + }) + } + ctxPos.visible = true + return false +} + +/** + * adds some UI elements to side bar and + * menu also attaches listeners to sidebar + * @category ux + */ +export function setupUI() { + var ctxEl = document.getElementById('contextMenu') + document.addEventListener('mousedown', (e) => { + // Check if mouse is not inside the context menu and menu is visible + if ( + !( + e.clientX >= ctxPos.x && + e.clientX <= ctxPos.x + ctxEl.offsetWidth && + e.clientY >= ctxPos.y && + e.clientY <= ctxPos.y + ctxEl.offsetHeight + ) && + ctxPos.visible && + e.which !== 3 + ) { + hideContextMenu() + } + + // Change the position of context whenever mouse is clicked + ctxPos.x = e.clientX + ctxPos.y = e.clientY + }) + document.getElementById('canvasArea').oncontextmenu = showContextMenu + + // commenting jquery-ui (not working) + // $('#sideBar').resizable({ + // handles: 'e', + // // minWidth:270, + // }); + // $('#menu, #subcircuitMenu').accordion({ + // collapsible: true, + // active: false, + // heightStyle: 'content', + // }); + + $('.logixButton').on('click', function () { + logixFunction[this.id]() + }) + // var dummyCounter=0; + + // calling apply on select theme in dropdown + + // $('#saveAsImg').on('click',function(){ + // saveAsImg(); + // }); + // $('#Save').on('click',function(){ + // Save(); + // }); + // $('#moduleProperty').draggable(); + setupPanels() + // setupVerilogExportCodeWindow() +} + +/** + * Keeps in check which property is being displayed + * @category ux + */ +var prevPropertyObj + +export function prevPropertyObjSet(param) { + prevPropertyObj = param +} + +export function prevPropertyObjGet() { + return prevPropertyObj +} + +function checkValidBitWidth() { + const selector = $("[name='newBitWidth']") + if ( + selector === undefined || + selector.val() > 32 || + selector.val() < 1 || + !$.isNumeric(selector.val()) + ) { + // fallback to previously saves state + selector.val(selector.attr('old-val')) + } else { + selector.attr('old-val', selector.val()) + } +} + +export function objectPropertyAttributeUpdate() { + checkValidBitWidth() + scheduleUpdate() + updateCanvasSet(true) + wireToBeCheckedSet(1) + let { value } = this + if (this.type === 'number') { + value = parseFloat(value) + } + if (simulationArea.lastSelected && simulationArea.lastSelected[this.name]) { + simulationArea.lastSelected[this.name](value) + // Commented out due to property menu refresh bug + // prevPropertyObjSet(simulationArea.lastSelected[this.name](this.value)) || prevPropertyObjGet(); + } else { + circuitProperty[this.name](value) + } +} + +export function objectPropertyAttributeCheckedUpdate() { + if (this.name === 'toggleLabelInLayoutMode') return // Hack to prevent toggleLabelInLayoutMode from toggling twice + scheduleUpdate() + updateCanvasSet(true) + wireToBeCheckedSet(1) + if (simulationArea.lastSelected && simulationArea.lastSelected[this.name]) { + simulationArea.lastSelected[this.name](this.value) + // Commented out due to property menu refresh bug + // prevPropertyObjSet(simulationArea.lastSelected[this.name](this.value)) || prevPropertyObjGet(); + } else { + circuitProperty[this.name](this.checked) + } +} + +export function checkPropertiesUpdate(value = 0) { + $('.objectPropertyAttribute').off( + 'change keyup paste click', + objectPropertyAttributeUpdate + ) + $('.objectPropertyAttribute').on( + 'change keyup paste click', + objectPropertyAttributeUpdate + ) + + $('.objectPropertyAttributeChecked').off( + 'change keyup paste click', + objectPropertyAttributeCheckedUpdate + ) + $('.objectPropertyAttributeChecked').on( + 'change keyup paste click', + objectPropertyAttributeCheckedUpdate + ) +} + +/** + * show properties of an object. + * @param {CircuiElement} obj - the object whose properties we want to be shown in sidebar + * @category ux + */ +export function showProperties(obj) { + if (obj === prevPropertyObjGet()) return + + /* + hideProperties() + prevPropertyObjSet(obj) + if (layoutModeGet()) { + // if an element is selected, show its properties instead of the layout dialog + if ( + simulationArea.lastSelected === undefined || + ['Wire', 'CircuitElement', 'Node'].indexOf( + simulationArea.lastSelected.objectType + ) !== -1 + ) { + $('#moduleProperty').hide() + $('#layoutDialog').show() + return + } + + $('#moduleProperty').show() + $('#layoutDialog').hide() + $('#moduleProperty-inner').append( + "
" + obj.objectType + '
' + ) + + if (obj.subcircuitMutableProperties && obj.canShowInSubcircuit) { + for (let attr in obj.subcircuitMutableProperties) { + var prop = obj.subcircuitMutableProperties[attr] + if (obj.subcircuitMutableProperties[attr].type == 'number') { + var s = + '

' + + prop.name + + "

" + $('#moduleProperty-inner').append(s) + } + } + if (!obj.labelDirectionFixed) { + if (!obj.subcircuitMetadata.labelDirection) + obj.subcircuitMetadata.labelDirection = obj.labelDirection + var s = $( + "' + ) + s.val(obj.subcircuitMetadata.labelDirection) + $('#moduleProperty-inner').append( + '

Label Direction: ' + $(s).prop('outerHTML') + '

' + ) + } + } + } else if ( + simulationArea.lastSelected === undefined || + ['Wire', 'CircuitElement', 'Node'].indexOf( + simulationArea.lastSelected.objectType + ) !== -1 + ) { + $('#moduleProperty').show() + + $('#moduleProperty-inner').append( + `

Project:

` + ) + $('#moduleProperty-inner').append( + `

Circuit:

` + ) + $('#moduleProperty-inner').append( + `

Clock Time (ms):

` + ) + $('#moduleProperty-inner').append( + `

Clock Enabled:

` + ) + $('#moduleProperty-inner').append( + `

Lite Mode:

` + ) + $('#moduleProperty-inner').append( + "

" + ) + // $('#moduleProperty-inner').append("

"); + } else { + $('#moduleProperty').show() + + $('#moduleProperty-inner').append( + `

${obj.objectType}
` + ) + // $('#moduleProperty').append(""); + if (!obj.fixedBitWidth) { + $('#moduleProperty-inner').append( + `

BitWidth:

` + ) + } + + if (obj.changeInputSize) { + $('#moduleProperty-inner').append( + `

Input Size:

` + ) + } + + if (!obj.propagationDelayFixed) { + $('#moduleProperty-inner').append( + `

Delay:

` + ) + } + + if (!obj.disableLabel) + $('#moduleProperty-inner').append( + `

Label:

` + ) + + var s + if (!obj.labelDirectionFixed) { + s = $( + `${ + "' + ) + s.val(obj.labelDirection) + $('#moduleProperty-inner').append( + `

Label Direction: ${$(s).prop('outerHTML')}

` + ) + } + + if (!obj.directionFixed) { + s = $( + `${ + "' + ) + $('#moduleProperty-inner').append( + `

Direction: ${$(s).prop('outerHTML')}

` + ) + } else if (!obj.orientationFixed) { + s = $( + `${ + "' + ) + $('#moduleProperty-inner').append( + `

Orientation: ${$(s).prop('outerHTML')}

` + ) + } + + if (obj.mutableProperties) { + for (const attr in obj.mutableProperties) { + var prop = obj.mutableProperties[attr] + if (obj.mutableProperties[attr].type === 'number') { + s = `

${ + prop.name + }

` + $('#moduleProperty-inner').append(s) + } else if (obj.mutableProperties[attr].type === 'text') { + s = `

${ + prop.name + }

` + $('#moduleProperty-inner').append(s) + } else if (obj.mutableProperties[attr].type === 'button') { + s = `

` + $('#moduleProperty-inner').append(s) + } else if (obj.mutableProperties[attr].type === 'textarea') { + s = `

${prop.name}

` + $('#moduleProperty-inner').append(s) + } + } + } + } + + var helplink = obj && obj.helplink + if (helplink) { + $('#moduleProperty-inner').append( + '

' + ) + $('#HelpButton').on('click', () => { + window.open(helplink) + }) + } +*/ + checkPropertiesUpdate(this) + + // $(".moduleProperty input[type='number']").inputSpinner(); +} + +/** + * Hides the properties in sidebar. + * @category ux + */ +export function hideProperties() { + $('#moduleProperty-inner').empty() + $('#moduleProperty').hide() + prevPropertyObjSet(undefined) + $('.objectPropertyAttribute').unbind('change keyup paste click') +} +/** + * checkss the input is safe or not + * @param {HTML} unsafe - the html which we wants to escape + * @category ux + */ +function escapeHtml(unsafe) { + return unsafe + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, ''') +} + +export function deleteSelected() { + if ( + simulationArea.lastSelected && + !( + simulationArea.lastSelected.objectType === 'Node' && + simulationArea.lastSelected.type !== 2 + ) + ) { + simulationArea.lastSelected.delete() + } + + for (var i = 0; i < simulationArea.multipleObjectSelections.length; i++) { + if ( + !( + simulationArea.multipleObjectSelections[i].objectType === + 'Node' && + simulationArea.multipleObjectSelections[i].type !== 2 + ) + ) + simulationArea.multipleObjectSelections[i].cleanDelete() + } + + simulationArea.multipleObjectSelections = [] + simulationArea.lastSelected = undefined + showProperties(simulationArea.lastSelected) + // Updated restricted elements + updateCanvasSet(true) + scheduleUpdate() + updateRestrictedElementsInScope() +} + +export function setupPanels() { + // $('#dragQPanel') + // .on('mousedown', () => + // $('.quick-btn').draggable({ + // disabled: false, + // containment: 'window', + // }) + // ) + // .on('mouseup', () => $('.quick-btn').draggable({ disabled: true })) + + // let position = { x: 0, y: 0 } + // interact('.quick-btn').draggable({ + // allowFrom: '#dragQPanel', + // listeners: { + // move(event) { + // position.x = position.x + event.dx + // position.y = position.y + event.dy + // event.target.style.transform = `translate(${position.x}px, ${position.y}px)` + // }, + // }, + // }) + + dragging('#dragQPanel', '.quick-btn') + + setupPanelListeners('.elementPanel') + setupPanelListeners('.layoutElementPanel') + setupPanelListeners('#moduleProperty') + setupPanelListeners('#layoutDialog') + setupPanelListeners('#verilogEditorPanel') + setupPanelListeners('.timing-diagram-panel') + setupPanelListeners('.testbench-manual-panel') + + // Minimize Timing Diagram (takes too much space) + $('.timing-diagram-panel .minimize').trigger('click') + + // Update the Testbench Panel UI + updateTestbenchUI() + // Minimize Testbench UI + $('.testbench-manual-panel .minimize').trigger('click') + + // Hack because minimizing panel then maximizing sets visibility recursively + // updateTestbenchUI calls some hide()s which are undone by maximization + // TODO: Remove hack + $('.testbench-manual-panel .maximize').on('click', setupTestbenchUI) + + $('#projectName').on('click', () => { + $("input[name='setProjectName']").focus().select() + }) +} + +function setupPanelListeners(panelSelector) { + var headerSelector = `${panelSelector} .panel-header` + var minimizeSelector = `${panelSelector} .minimize` + var maximizeSelector = `${panelSelector} .maximize` + var bodySelector = `${panelSelector} > .panel-body` + + dragging(headerSelector, panelSelector) + // let position = { x: 0, y: 0 } + // Drag Start + // $(headerSelector).on('mousedown', () => + // $(panelSelector).draggable({ disabled: false, containment: 'window' }) + // interact(panelSelector).draggable({ + // allowFrom: headerSelector, + // listeners: { + // move(event) { + // position.x += event.dx + // position.y += event.dy + + // event.target.style.transform = `translate(${position.x}px, ${position.y}px)` + // }, + // }, + // }) + // ) + // // Drag End + // $(headerSelector).on('mouseup', () => + // $(panelSelector).draggable({ disabled: true }) + // ) + // Current Panel on Top + var minimized = false + $(headerSelector).on('dblclick', () => + minimized + ? $(maximizeSelector).trigger('click') + : $(minimizeSelector).trigger('click') + ) + // Minimize + $(minimizeSelector).on('click', () => { + $(bodySelector).hide() + $(minimizeSelector).hide() + $(maximizeSelector).show() + minimized = true + }) + // Maximize + $(maximizeSelector).on('click', () => { + $(bodySelector).show() + $(minimizeSelector).show() + $(maximizeSelector).hide() + minimized = false + }) +} + +export function exitFullView() { + const exitViewBtn = document.querySelector('#exitViewBtn') + if (exitViewBtn) exitViewBtn.remove() + + const elements = document.querySelectorAll( + '.navbar, .modules, .report-sidebar, #tabsBar, #moduleProperty, .timing-diagram-panel, .testbench-manual-panel, .quick-btn' + ) + elements.forEach((element) => { + if (element instanceof HTMLElement) { + element.style.display = '' + } + }) +} + +export function fullView() { + const app = document.querySelector('#app') + + const exitViewEl = document.createElement('button') + exitViewEl.id = 'exitViewBtn' + exitViewEl.textContent = 'Exit Full Preview' + + const elements = document.querySelectorAll( + '.navbar, .modules, .report-sidebar, #tabsBar, #moduleProperty, .timing-diagram-panel, .testbench-manual-panel, .quick-btn' + ) + elements.forEach((element) => { + if (element instanceof HTMLElement) { + element.style.display = 'none' + } + }) + + app.appendChild(exitViewEl) + exitViewEl.addEventListener('click', exitFullView) +} + +/** + Fills the elements that can be displayed in the subcircuit, in the subcircuit menu +**/ +export function fillSubcircuitElements() { + $('#subcircuitMenu').empty() + var subCircuitElementExists = false + for (let el of circuitElementList) { + if (globalScope[el].length === 0) continue + if (!globalScope[el][0].canShowInSubcircuit) continue + let tempHTML = '' + + // add a panel for each existing group + tempHTML += `
${el}s
` + tempHTML += `
` + + let available = false + + // add an SVG for each element + for (let i = 0; i < globalScope[el].length; i++) { + if (!globalScope[el][i].subcircuitMetadata.showInSubcircuit) { + tempHTML += `
` + tempHTML += `` + tempHTML += `

${ + globalScope[el][i].label !== '' + ? globalScope[el][i].label + : 'unlabeled' + }

` + tempHTML += '
' + available = true + } + } + tempHTML += '
' + subCircuitElementExists = subCircuitElementExists || available + if (available) $('#subcircuitMenu').append(tempHTML) + } + + if (subCircuitElementExists) { + // $('#subcircuitMenu').accordion('refresh') + } else { + $('#subcircuitMenu').append('

No layout elements available

') + } + + $('.subcircuitModule').mousedown(function () { + let elementName = this.dataset.elementName + let elementIndex = this.dataset.elementId + + let element = globalScope[elementName][elementIndex] + + element.subcircuitMetadata.showInSubcircuit = true + element.newElement = true + simulationArea.lastSelected = element + this.parentElement.removeChild(this) + }) +} diff --git a/v0/src/simulator/src/verilog.js b/v0/src/simulator/src/verilog.js new file mode 100644 index 00000000..d0811b2c --- /dev/null +++ b/v0/src/simulator/src/verilog.js @@ -0,0 +1,580 @@ +/* + # Primary Developers + 1) James H-J Yeh, Ph.D. + 2) Satvik Ramaprasad + + refer verilog_documentation.md +*/ +import { scopeList } from './circuit' +import { errorDetectedGet } from './engine' +import { download } from './utils' +import { getProjectName } from './data/save' +import modules from './modules' +import { sanitizeLabel } from './verilogHelpers' +import CodeMirror from 'codemirror/lib/codemirror.js' +import 'codemirror/lib/codemirror.css' +import 'codemirror/addon/hint/show-hint.css' +import 'codemirror/mode/verilog/verilog.js' +import 'codemirror/addon/edit/closebrackets.js' +import 'codemirror/addon/hint/anyword-hint.js' +import 'codemirror/addon/hint/show-hint.js' +import 'codemirror/addon/display/autorefresh.js' +import { openInNewTab, copyToClipboard, showMessage } from './utils' +import { SimulatorStore } from '#/store/SimulatorStore/SimulatorStore' +var editor + +export function generateVerilog() { + const simulatorStore = SimulatorStore() + simulatorStore.dialogBox.exportverilog_dialog = true + // var dialog = $('#verilog-export-code-window-div') + // var data = verilog.exportVerilog() + // editor.setValue(data) + // $('#verilog-export-code-window-div .CodeMirror').css( + // 'height', + // $(window).height() - 200 + // ) + // dialog.dialog({ + // resizable: false, + // width: '90%', + // height: 'auto', + // position: { my: 'center', at: 'center', of: window }, + // buttons: [ + // { + // text: 'Download Verilog File', + // click() { + // var fileName = getProjectName() || 'Untitled' + // download(fileName + '.v', editor.getValue()) + // }, + // }, + // { + // text: 'Copy to Clipboard', + // click() { + // copyToClipboard(editor.getValue()) + // showMessage('Code has been copied') + // }, + // }, + // { + // text: 'Try in EDA Playground', + // click() { + // copyToClipboard(editor.getValue()) + // openInNewTab('https://www.edaplayground.com/x/XZpY') + // }, + // }, + // ], + // }) +} + +export function setupVerilogExportCodeWindow() { + var myTextarea = document.getElementById('verilog-export-code-window') + editor = CodeMirror.fromTextArea(myTextarea, { + mode: 'verilog', + autoRefresh: true, + styleActiveLine: true, + lineNumbers: true, + autoCloseBrackets: true, + smartIndent: true, + indentWithTabs: true, + extraKeys: { 'Ctrl-Space': 'autocomplete' }, + }) +} + +export var verilog = { + // Entry point to verilog generation + // scope = undefined means export all circuits + exportVerilog: function (scope = undefined) { + var dependencyList = {} + // Reset Verilog Element State + for (var elem in modules) { + // Not sure if globalScope here is correct. + if (modules[elem].resetVerilog) { + modules[elem].resetVerilog() + } + } + + // List of devices under test for which testbench needs to be created + var DUTs = [] + var SubCircuitIds = new Set() + + // Generate SubCircuit Dependency Graph + for (id in scopeList) { + dependencyList[id] = scopeList[id].getDependencies() + for (var i = 0; i < scopeList[id].SubCircuit.length; i++) { + SubCircuitIds.add(scopeList[id].SubCircuit[i].id) + } + } + + for (id in scopeList) { + if (!SubCircuitIds.has(id)) DUTs.push(scopeList[id]) + } + + // DFS on SubCircuit Dependency Graph + var visited = {} + var elementTypesUsed = {} + var output = '' + if (scope) { + // generate verilog only for scope + output += this.exportVerilogScope( + scope.id, + visited, + dependencyList, + elementTypesUsed + ) + } else { + // generate verilog for everything + for (id in scopeList) { + output += this.exportVerilogScope( + id, + visited, + dependencyList, + elementTypesUsed + ) + } + } + // Add Circuit Element - Module Specific Verilog Code + for (var element in elementTypesUsed) { + // If element has custom verilog + if (modules[element] && modules[element].moduleVerilog) { + output += modules[element].moduleVerilog() + } + } + + var report = this.generateReport(elementTypesUsed) + '\n' + var testbench = this.generateTestBenchCode(DUTs) + + return report + testbench + output + }, + generateReport: function (elementTypesUsed) { + var output = '' + output += '/**\n' + output += + ' * This is an autogenerated netlist code from CircuitVerse. Verilog Code can be\n' + output += + ' * tested on https://www.edaplayground.com/ using Icarus Verilog 0.9.7. This is an\n' + output += + ' * experimental module and some manual changes make need to be done in order for\n' + output += ' * this to work.\n' + output += ' *\n' + output += + ' * If you have any ideas/suggestions or bug fixes, raise an issue\n' + output += + ' * on https://github.com/CircuitVerse/CircuitVerse/issues/new/choose\n' + output += ' */\n' + output += '\n' + output += '/*\n' + output += sp(1) + 'Element Usage Report\n' + for (var elem in elementTypesUsed) { + if (elem == 'Node') continue + output += `${sp(2)}${elem} - ${elementTypesUsed[elem]} times\n` + } + output += '*/\n' + output += '\n' + var instructions = '' + output += '/*\n' + output += sp(1) + 'Usage Instructions and Tips\n' + instructions += + sp(2) + + 'Labels - Ensure unique label names and avoid using verilog keywords\n' + instructions += + sp(2) + + 'Warnings - Connect all optional inputs to remove warnings\n' + for (var elem in elementTypesUsed) { + // If element has custom instructions + if (modules[elem] && modules[elem].verilogInstructions) { + instructions += indent(2, modules[elem].verilogInstructions()) + } + } + output += instructions + output += '*/\n' + return output + }, + generateTestBenchCode: function (DUTs) { + if (DUTs.length == 0) return '' + var output = '// Sample Testbench Code - Uncomment to use\n' + + output += '\n/*\n' + output += 'module TestBench();\n' + var registers = {} + var wires = {} + for (var i = 1; i <= 32; i++) registers[i] = new Set() + for (var i = 1; i <= 32; i++) wires[i] = new Set() + + var clocks = new Set() + var inputs = new Set() + var outputs = new Set() + var deviceInstantiations = '' + for (var i = 0; i < DUTs.length; i++) { + var DUT = DUTs[i] + for (var j = 0; j < DUT.Input.length; j++) { + var inp = DUT.Input[j] + registers[inp.bitWidth].add(inp.label) + inputs.add(inp.label) + } + for (var j = 0; j < DUT.Output.length; j++) { + var out = DUT.Output[j] + wires[out.bitWidth].add(out.label) + outputs.add(out.label) + } + for (var j = 0; j < DUT.Clock.length; j++) { + var inp = DUT.Clock[j] + registers[1].add(inp.label) + clocks.add(inp.label) + } + var circuitName = sanitizeLabel(DUT.name) + var dutHeader = this.generateHeaderHelper(DUT) + deviceInstantiations += `${sp( + 1 + )}${circuitName} DUT${i}${dutHeader}\n` + } + output += '\n' + // Generate Reg Initialization Code + for (var bitWidth = 1; bitWidth <= 32; bitWidth++) { + if (registers[bitWidth].size == 0) continue + var regArray = [...registers[bitWidth]] + if (bitWidth == 1) output += `${sp(1)}reg ${regArray.join(', ')};\n` + else + output += `${sp(1)}reg [${bitWidth - 1}:0] ${regArray.join( + ', ' + )};\n` + } + output += '\n' + // Generate Wire Initialization Code + for (var bitWidth = 1; bitWidth <= 32; bitWidth++) { + if (wires[bitWidth].size == 0) continue + var wireArray = [...wires[bitWidth]] + if (bitWidth == 1) + output += `${sp(1)}wire ${wireArray.join(', ')};\n` + else + output += `${sp(1)}wire [${bitWidth - 1}:0] ${wireArray.join( + ', ' + )};\n` + } + output += '\n' + + output += deviceInstantiations + + if (clocks.size) { + output += `${sp(1)}always begin\n` + output += `${sp(2)}#10\n` + for (var clk of clocks) output += `${sp(2)}${clk} = 0;\n` + output += `${sp(2)}#10\n` + for (var clk of clocks) output += `${sp(2)}${clk} = 1;\n` + output += `${sp(1)}end\n` + output += '\n' + } + + output += `${sp(1)}initial begin\n` + + // Reset inputs to 0 + for (var inp of inputs) { + output += `${sp(2)}${inp} = 0;\n` + } + output += '\n' + output += `${sp(2)}#15\n` + for (var out of outputs) { + output += `${sp(2)}$display("${out} = %b", ${out});\n` + } + output += '\n' + output += `${sp(2)}#10\n` + for (var out of outputs) { + output += `${sp(2)}$display("${out} = %b", ${out});\n` + } + output += '\n' + output += `${sp(2)}$finish;\n\n` + output += `${sp(1)}end\n` + + output += 'endmodule\n' + + output += '\n*/\n' + + return output + }, + // Recursive DFS function + exportVerilogScope: function ( + id, + visited, + dependencyList, + elementTypesUsed + ) { + // Already Visited + if (visited[id]) return '' + // Mark as Visited + visited[id] = true + + var output = '' + // DFS on dependencies + for (var i = 0; i < dependencyList[id].length; i++) + output += + this.exportVerilogScope( + dependencyList[id][i], + visited, + dependencyList, + elementTypesUsed + ) + '\n' + + var scope = scopeList[id] + // Initialize labels for all elements + this.resetLabels(scope) + this.setLabels(scope) + + output += this.generateHeader(scope) + output += this.generateOutputList(scope) // generate output first to be consistent + output += this.generateInputList(scope) + + // Note: processGraph function populates scope.verilogWireList + var res = this.processGraph(scope, elementTypesUsed) + + // Generate Wire Initialization Code + for (var bitWidth = 1; bitWidth <= 32; bitWidth++) { + var wireList = scope.verilogWireList[bitWidth] + // Hack for splitter + wireList = wireList.filter((x) => !x.includes('[')) + if (wireList.length == 0) continue + if (bitWidth == 1) output += ' wire ' + wireList.join(', ') + ';\n' + else + output += + ' wire [' + + (bitWidth - 1) + + ':0] ' + + wireList.join(', ') + + ';\n' + } + + // Append Wire connections and module instantiations + output += res + + // Append footer + output += 'endmodule\n' + + return output + }, + // Performs DFS on the graph and generates netlist of wires and connections + processGraph: function (scope, elementTypesUsed) { + // Initializations + var res = '' + scope.stack = [] + scope.verilogWireList = [] + for (var i = 0; i <= 32; i++) scope.verilogWireList.push(new Array()) + + var verilogResolvedSet = new Set() + + // Start DFS from inputs + for (var i = 0; i < inputList.length; i++) { + for (var j = 0; j < scope[inputList[i]].length; j++) { + scope.stack.push(scope[inputList[i]][j]) + } + } + + // Iterative DFS on circuit graph + while (scope.stack.length) { + if (errorDetectedGet()) return + var elem = scope.stack.pop() + + if (verilogResolvedSet.has(elem)) continue + + // Process verilog creates variable names and adds elements to DFS stack + elem.processVerilog() + + // Record usage of element type + if (elem.objectType != 'Node') { + if (elementTypesUsed[elem.objectType]) + elementTypesUsed[elem.objectType]++ + else elementTypesUsed[elem.objectType] = 1 + } + + if ( + elem.objectType != 'Node' && + elem.objectType != 'Input' && + elem.objectType != 'Clock' + ) { + verilogResolvedSet.add(elem) + } + } + + // Generate connection verilog code and module instantiations + for (var elem of verilogResolvedSet) { + res += ' ' + elem.generateVerilog() + '\n' + } + return res + }, + + resetLabels: function (scope) { + for (var i = 0; i < scope.allNodes.length; i++) { + scope.allNodes[i].verilogLabel = '' + } + }, + // Sets labels for all Circuit Elements elements + setLabels: function (scope = globalScope) { + /** + * Sets a name for each element. If element is already labeled, + * the element is used directly, otherwise an automated label is provided + * sanitizeLabel is a helper function to escape white spaces + */ + for (var i = 0; i < scope.Input.length; i++) { + if (scope.Input[i].label == '') scope.Input[i].label = 'inp_' + i + else scope.Input[i].label = sanitizeLabel(scope.Input[i].label) + // copy label to node + scope.Input[i].output1.verilogLabel = scope.Input[i].label + } + for (var i = 0; i < scope.ConstantVal.length; i++) { + if (scope.ConstantVal[i].label == '') + scope.ConstantVal[i].label = 'const_' + i + else + scope.ConstantVal[i].label = sanitizeLabel( + scope.ConstantVal[i].label + ) + // copy label to node + scope.ConstantVal[i].output1.verilogLabel = + scope.ConstantVal[i].label + } + + // copy label to clock + for (var i = 0; i < scope.Clock.length; i++) { + if (scope.Clock[i].label == '') scope.Clock[i].label = 'clk_' + i + else scope.Clock[i].label = sanitizeLabel(scope.Clock[i].label) + scope.Clock[i].output1.verilogLabel = scope.Clock[i].label + } + + for (var i = 0; i < scope.Output.length; i++) { + if (scope.Output[i].label == '') scope.Output[i].label = 'out_' + i + else scope.Output[i].label = sanitizeLabel(scope.Output[i].label) + } + for (var i = 0; i < scope.SubCircuit.length; i++) { + if (scope.SubCircuit[i].label == '') + scope.SubCircuit[i].label = + scope.SubCircuit[i].data.name + '_' + i + else + scope.SubCircuit[i].label = sanitizeLabel( + scope.SubCircuit[i].label + ) + } + for (var i = 0; i < moduleList.length; i++) { + var m = moduleList[i] + for (var j = 0; j < scope[m].length; j++) { + scope[m][j].verilogLabel = + sanitizeLabel(scope[m][j].label) || + scope[m][j].verilogName() + '_' + j + } + } + }, + generateHeader: function (scope = globalScope) { + // Example: module HalfAdder (a,b,s,c); + var res = '\nmodule ' + sanitizeLabel(scope.name) + res += this.generateHeaderHelper(scope) + return res + }, + generateHeaderHelper: function (scope = globalScope) { + // Example: (a,b,s,c); + var res = '(' + var pins = [] + for (var i = 0; i < scope.Output.length; i++) { + pins.push(scope.Output[i].label) + } + for (var i = 0; i < scope.Clock.length; i++) { + pins.push(scope.Clock[i].label) + } + for (var i = 0; i < scope.Input.length; i++) { + pins.push(scope.Input[i].label) + } + res += pins.join(', ') + res += ');\n' + return res + }, + generateInputList: function (scope = globalScope) { + var inputs = {} + for (var i = 1; i <= 32; i++) inputs[i] = [] + + for (var i = 0; i < scope.Input.length; i++) { + inputs[scope.Input[i].bitWidth].push(scope.Input[i].label) + } + + for (var i = 0; i < scope.Clock.length; i++) { + inputs[scope.Clock[i].bitWidth].push(scope.Clock[i].label) + } + + var res = '' + for (var bitWidth in inputs) { + if (inputs[bitWidth].length == 0) continue + if (bitWidth == 1) res += ' input ' + inputs[1].join(', ') + ';\n' + else + res += + ' input [' + + (bitWidth - 1) + + ':0] ' + + inputs[bitWidth].join(', ') + + ';\n' + } + + return res + }, + generateOutputList: function (scope = globalScope) { + // Example 1: output s,cout; + var outputs = {} + for (var i = 0; i < scope.Output.length; i++) { + if (outputs[scope.Output[i].bitWidth]) + outputs[scope.Output[i].bitWidth].push(scope.Output[i].label) + else outputs[scope.Output[i].bitWidth] = [scope.Output[i].label] + } + var res = '' + for (var bitWidth in outputs) { + if (bitWidth == 1) + res += ' output ' + outputs[1].join(', ') + ';\n' + else + res += + ' output [' + + (bitWidth - 1) + + ':0] ' + + outputs[bitWidth].join(', ') + + ';\n' + } + + return res + }, + /* + sanitizeLabel: function(name){ + // Replace spaces by "_" + name = name.replace(/ /g , "_"); + // Replace Hyphens by "_" + name = name.replace(/-/g , "_"); + // Replace Colons by "_" + name = name.replace(/:/g , "_"); + // replace ~ with inv_ + name = name.replace(/~/g , "inv_"); + // Shorten Inverse to inv + name = name.replace(/Inverse/g , "inv"); + + // If first character is a number + if(name.substring(0, 1).search(/[0-9]/g) > -1) { + name = "w_" + name; + } + + // if first character is not \ already + if (name[0] != '\\') { + //if there are non-alphanum_ character, add \ + if (name.search(/[\W]/g) > -1) + name = "\\" + name; + } + return name; + }, + */ +} + +/* + Helper function to generate spaces for indentation +*/ +function sp(indentation) { + return ' '.repeat(indentation * 2) +} + +/* + Helper function to indent paragraph +*/ +function indent(indentation, string) { + var result = string.split('\n') + if (result[result.length - 1] == '') { + result.pop() + result = result.map((x) => sp(indentation) + x).join('\n') + result += '\n' + return result + } + return result.map((x) => sp(indentation) + x).join('\n') +} diff --git a/v0/src/simulator/src/verilogHelpers.js b/v0/src/simulator/src/verilogHelpers.js new file mode 100644 index 00000000..3f9a4123 --- /dev/null +++ b/v0/src/simulator/src/verilogHelpers.js @@ -0,0 +1,41 @@ +export function sanitizeLabel(name) { + // return name.replace(/ Inverse/g, "_inv").replace(/ /g , "_"); + var temp = name + // if there is a space anywhere but the last place + // replace spaces by "_" + // last space is required for escaped id + if (temp.search(/ /g) < temp.length - 1 && temp.search(/ /g) >= 0) { + temp = temp.replace(/ Inverse/g, '_inv') + temp = temp.replace(/ /g, '_') + } + // if first character is not \ already + if (temp.substring(0, 1).search(/\\/g) < 0) { + // if there are non-alphanum_ character, or first character is num, add \ + if ( + temp.search(/[\W]/g) > -1 || + temp.substring(0, 1).search(/[0-9]/g) > -1 + ) + temp = '\\' + temp + ' ' + } + return temp +} + +export function generateNodeName(node, currentCount, totalCount) { + if (node.verilogLabel) return node.verilogLabel + var parentVerilogLabel = node.parent.verilogLabel + var nodeName + if (node.label) { + nodeName = sanitizeLabel(node.label) + } else { + nodeName = totalCount > 1 ? 'out_' + currentCount : 'out' + } + if (parentVerilogLabel.substring(0, 1).search(/\\/g) < 0) + return parentVerilogLabel + '_' + nodeName + else + return ( + parentVerilogLabel.substring(0, parentVerilogLabel.length - 1) + + '_' + + nodeName + + ' ' + ) +} diff --git a/v0/src/simulator/src/wire.js b/v0/src/simulator/src/wire.js new file mode 100644 index 00000000..7e06e7d2 --- /dev/null +++ b/v0/src/simulator/src/wire.js @@ -0,0 +1,240 @@ +/* eslint-disable no-multi-assign */ +// wire object +import { drawLine } from './canvasApi' +import simulationArea from './simulationArea' +import Node from './node' +import { updateSimulationSet, forceResetNodesSet } from './engine' +import { colors } from './themer/themer' + +/** + * Wire - To connect two nodes. + * @class + * @memberof module:wire + * @param {Node} node1 + * @param {Node} node2 + * @param {Scope} scope - The circuit in which wire has to be drawn + * @category wire + */ +export default class Wire { + constructor(node1, node2, scope) { + this.objectType = 'Wire' + this.node1 = node1 + this.scope = scope + this.node2 = node2 + this.type = 'horizontal' + + this.updateData() + this.scope.wires.push(this) + forceResetNodesSet(true) + } + + // if data changes + updateData() { + this.x1 = this.node1.absX() + this.y1 = this.node1.absY() + this.x2 = this.node2.absX() + this.y2 = this.node2.absY() + if (this.x1 === this.x2) this.type = 'vertical' + } + + updateScope(scope) { + this.scope = scope + this.checkConnections() + } + + // to check if nodes are disconnected + checkConnections() { + var check = + this.node1.deleted || + this.node2.deleted || + !this.node1.connections.contains(this.node2) || + !this.node2.connections.contains(this.node1) + if (check) this.delete() + return check + } + + dblclick() { + if ( + this.node1.parent == globalScope.root && + this.node2.parent == globalScope.root + ) { + simulationArea.multipleObjectSelections = [this.node1, this.node2] + simulationArea.lastSelected = undefined + } + } + + update() { + var updated = false + if (embed) return updated + + if (this.node1.absX() === this.node2.absX()) { + this.x1 = this.x2 = this.node1.absX() + this.type = 'vertical' + } else if (this.node1.absY() === this.node2.absY()) { + this.y1 = this.y2 = this.node1.absY() + this.type = 'horizontal' + } + + // if (wireToBeChecked && this.checkConnections()) { + // this.delete(); + // return updated; + // } // SLOW , REMOVE + if ( + simulationArea.shiftDown === false && + simulationArea.mouseDown === true && + simulationArea.selected === false && + this.checkWithin( + simulationArea.mouseDownX, + simulationArea.mouseDownY + ) + ) { + simulationArea.selected = true + simulationArea.lastSelected = this + updated = true + } else if ( + simulationArea.mouseDown && + simulationArea.lastSelected === this && + !this.checkWithin(simulationArea.mouseX, simulationArea.mouseY) + ) { + var n = new Node( + simulationArea.mouseDownX, + simulationArea.mouseDownY, + 2, + this.scope.root + ) + n.clicked = true + n.wasClicked = true + simulationArea.lastSelected = n + this.converge(n) + } + // eslint-disable-next-line no-empty + if (simulationArea.lastSelected === this) { + } + + if (this.node1.deleted || this.node2.deleted) { + this.delete() + return updated + } // if either of the nodes are deleted + + if (simulationArea.mouseDown === false) { + if (this.type === 'horizontal') { + if (this.node1.absY() !== this.y1) { + // if(this.checkConnections()){this.delete();return;} + n = new Node(this.node1.absX(), this.y1, 2, this.scope.root) + this.converge(n) + updated = true + } else if (this.node2.absY() !== this.y2) { + // if(this.checkConnections()){this.delete();return;} + n = new Node(this.node2.absX(), this.y2, 2, this.scope.root) + this.converge(n) + updated = true + } + } else if (this.type === 'vertical') { + if (this.node1.absX() !== this.x1) { + // if(this.checkConnections()){this.delete();return;} + n = new Node(this.x1, this.node1.absY(), 2, this.scope.root) + this.converge(n) + updated = true + } else if (this.node2.absX() !== this.x2) { + // if(this.checkConnections()){this.delete();return;} + n = new Node(this.x2, this.node2.absY(), 2, this.scope.root) + this.converge(n) + updated = true + } + } + } + return updated + } + + draw() { + // for calculating min-max Width,min-max Height + // + const ctx = simulationArea.context + + var color + if (simulationArea.lastSelected == this) { + color = colors['color_wire_sel'] + } else if ( + this.node1.value == undefined || + this.node2.value == undefined + ) { + color = colors['color_wire_lose'] + } else if (this.node1.bitWidth == 1) { + color = [ + colors['color_wire_lose'], + colors['color_wire_con'], + colors['color_wire_pow'], + ][this.node1.value + 1] + } else { + color = colors['color_wire'] + } + drawLine( + ctx, + this.node1.absX(), + this.node1.absY(), + this.node2.absX(), + this.node2.absY(), + color, + 3 + ) + } + + // checks if node lies on wire + checkConvergence(n) { + return this.checkWithin(n.absX(), n.absY()) + } + + // fn checks if coordinate lies on wire + checkWithin(x, y) { + if ( + this.type === 'horizontal' && + this.node1.absX() < this.node2.absX() && + x > this.node1.absX() && + x < this.node2.absX() && + y === this.node2.absY() + ) + return true + if ( + this.type === 'horizontal' && + this.node1.absX() > this.node2.absX() && + x < this.node1.absX() && + x > this.node2.absX() && + y === this.node2.absY() + ) + return true + if ( + this.type === 'vertical' && + this.node1.absY() < this.node2.absY() && + y > this.node1.absY() && + y < this.node2.absY() && + x === this.node2.absX() + ) + return true + if ( + this.type === 'vertical' && + this.node1.absY() > this.node2.absY() && + y < this.node1.absY() && + y > this.node2.absY() && + x === this.node2.absX() + ) + return true + return false + } + + // add intermediate node between these 2 nodes + converge(n) { + this.node1.connect(n) + this.node2.connect(n) + this.delete() + } + + delete() { + forceResetNodesSet(true) + updateSimulationSet(true) + this.node1.connections.clean(this.node2) + this.node2.connections.clean(this.node1) + this.scope.wires.clean(this) + this.node1.checkDeleted() + this.node2.checkDeleted() + } +} diff --git a/v0/src/simulator/vendor/canvas2svg.js b/v0/src/simulator/vendor/canvas2svg.js new file mode 100644 index 00000000..73dae81d --- /dev/null +++ b/v0/src/simulator/vendor/canvas2svg.js @@ -0,0 +1,1469 @@ +/*!! + * Canvas 2 Svg v1.0.19 + * A low level canvas to SVG converter. Uses a mock canvas context to build an SVG document. + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/mit-license.php + * + * Author: + * Kerry Liu + * + * Copyright (c) 2014 Gliffy Inc. + */ + +;(function () { + 'use strict' + + var STYLES, ctx, CanvasGradient, CanvasPattern, namedEntities + + //helper function to format a string + function format(str, args) { + var keys = Object.keys(args), + i + for (i = 0; i < keys.length; i++) { + str = str.replace( + new RegExp('\\{' + keys[i] + '\\}', 'gi'), + args[keys[i]] + ) + } + return str + } + + //helper function that generates a random string + function randomString(holder) { + var chars, randomstring, i + if (!holder) { + throw new Error( + 'cannot create a random attribute name for an undefined object' + ) + } + chars = 'ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz' + randomstring = '' + do { + randomstring = '' + for (i = 0; i < 12; i++) { + randomstring += chars[Math.floor(Math.random() * chars.length)] + } + } while (holder[randomstring]) + return randomstring + } + + //helper function to map named to numbered entities + function createNamedToNumberedLookup(items, radix) { + var i, + entity, + lookup = {}, + base10, + base16 + items = items.split(',') + radix = radix || 10 + // Map from named to numbered entities. + for (i = 0; i < items.length; i += 2) { + entity = '&' + items[i + 1] + ';' + base10 = parseInt(items[i], radix) + lookup[entity] = '&#' + base10 + ';' + } + //FF and IE need to create a regex from hex values ie   == \xa0 + lookup['\\xa0'] = ' ' + return lookup + } + + //helper function to map canvas-textAlign to svg-textAnchor + function getTextAnchor(textAlign) { + //TODO: support rtl languages + var mapping = { + left: 'start', + right: 'end', + center: 'middle', + start: 'start', + end: 'end', + } + return mapping[textAlign] || mapping.start + } + + //helper function to map canvas-textBaseline to svg-dominantBaseline + function getDominantBaseline(textBaseline) { + //INFO: not supported in all browsers + var mapping = { + alphabetic: 'alphabetic', + hanging: 'hanging', + top: 'text-before-edge', + bottom: 'text-after-edge', + middle: 'central', + } + return mapping[textBaseline] || mapping.alphabetic + } + + // Unpack entities lookup where the numbers are in radix 32 to reduce the size + // entity mapping courtesy of tinymce + namedEntities = createNamedToNumberedLookup( + '50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,' + + '5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,' + + '5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,' + + '5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,' + + '68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,' + + '6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,' + + '6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,' + + '75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,' + + '7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,' + + '7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,' + + 'sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,' + + 'st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,' + + 't9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,' + + 'tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,' + + 'u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,' + + '81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,' + + '8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,' + + '8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,' + + '8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,' + + '8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,' + + 'nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,' + + 'rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,' + + 'Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,' + + '80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,' + + '811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro', + 32 + ) + + //Some basic mappings for attributes and default values. + STYLES = { + strokeStyle: { + svgAttr: 'stroke', //corresponding svg attribute + canvas: '#000000', //canvas default + svg: 'none', //svg default + apply: 'stroke', //apply on stroke() or fill() + }, + fillStyle: { + svgAttr: 'fill', + canvas: '#000000', + svg: null, //svg default is black, but we need to special case this to handle canvas stroke without fill + apply: 'fill', + }, + lineCap: { + svgAttr: 'stroke-linecap', + canvas: 'butt', + svg: 'butt', + apply: 'stroke', + }, + lineJoin: { + svgAttr: 'stroke-linejoin', + canvas: 'miter', + svg: 'miter', + apply: 'stroke', + }, + miterLimit: { + svgAttr: 'stroke-miterlimit', + canvas: 10, + svg: 4, + apply: 'stroke', + }, + lineWidth: { + svgAttr: 'stroke-width', + canvas: 1, + svg: 1, + apply: 'stroke', + }, + globalAlpha: { + svgAttr: 'opacity', + canvas: 1, + svg: 1, + apply: 'fill stroke', + }, + font: { + //font converts to multiple svg attributes, there is custom logic for this + canvas: '10px sans-serif', + }, + shadowColor: { + canvas: '#000000', + }, + shadowOffsetX: { + canvas: 0, + }, + shadowOffsetY: { + canvas: 0, + }, + shadowBlur: { + canvas: 0, + }, + textAlign: { + canvas: 'start', + }, + textBaseline: { + canvas: 'alphabetic', + }, + lineDash: { + svgAttr: 'stroke-dasharray', + canvas: [], + svg: null, + apply: 'stroke', + }, + } + + /** + * + * @param gradientNode - reference to the gradient + * @constructor + */ + CanvasGradient = function (gradientNode, ctx) { + this.__root = gradientNode + this.__ctx = ctx + } + + /** + * Adds a color stop to the gradient root + */ + CanvasGradient.prototype.addColorStop = function (offset, color) { + var stop = this.__ctx.__createElement('stop'), + regex, + matches + stop.setAttribute('offset', offset) + if (color.indexOf('rgba') !== -1) { + //separate alpha value, since webkit can't handle it + regex = + /rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d?\.?\d*)\s*\)/gi + matches = regex.exec(color) + stop.setAttribute( + 'stop-color', + format('rgb({r},{g},{b})', { + r: matches[1], + g: matches[2], + b: matches[3], + }) + ) + stop.setAttribute('stop-opacity', matches[4]) + } else { + stop.setAttribute('stop-color', color) + } + this.__root.appendChild(stop) + } + + CanvasPattern = function (pattern, ctx) { + this.__root = pattern + this.__ctx = ctx + } + + /** + * The mock canvas context + * @param o - options include: + * ctx - existing Context2D to wrap around + * width - width of your canvas (defaults to 500) + * height - height of your canvas (defaults to 500) + * enableMirroring - enables canvas mirroring (get image data) (defaults to false) + * document - the document object (defaults to the current document) + */ + ctx = function (o) { + var defaultOptions = { + width: 500, + height: 500, + enableMirroring: false, + }, + options + + //keep support for this way of calling C2S: new C2S(width,height) + if (arguments.length > 1) { + options = defaultOptions + options.width = arguments[0] + options.height = arguments[1] + } else if (!o) { + options = defaultOptions + } else { + options = o + } + + if (!(this instanceof ctx)) { + //did someone call this without new? + return new ctx(options) + } + + //setup options + this.width = options.width || defaultOptions.width + this.height = options.height || defaultOptions.height + this.enableMirroring = + options.enableMirroring !== undefined + ? options.enableMirroring + : defaultOptions.enableMirroring + + this.canvas = this ///point back to this instance! + this.__document = options.document || document + + // allow passing in an existing context to wrap around + // if a context is passed in, we know a canvas already exist + if (options.ctx) { + this.__ctx = options.ctx + } else { + this.__canvas = this.__document.createElement('canvas') + this.__ctx = this.__canvas.getContext('2d') + } + + this.__setDefaultStyles() + this.__stack = [this.__getStyleState()] + this.__groupStack = [] + + //the root svg element + this.__root = this.__document.createElementNS( + 'http://www.w3.org/2000/svg', + 'svg' + ) + this.__root.setAttribute('version', 1.1) + this.__root.setAttribute('xmlns', 'http://www.w3.org/2000/svg') + this.__root.setAttributeNS( + 'http://www.w3.org/2000/xmlns/', + 'xmlns:xlink', + 'http://www.w3.org/1999/xlink' + ) + this.__root.setAttribute('width', this.width) + this.__root.setAttribute('height', this.height) + + //make sure we don't generate the same ids in defs + this.__ids = {} + + //defs tag + this.__defs = this.__document.createElementNS( + 'http://www.w3.org/2000/svg', + 'defs' + ) + this.__root.appendChild(this.__defs) + + //also add a group child. the svg element can't use the transform attribute + this.__currentElement = this.__document.createElementNS( + 'http://www.w3.org/2000/svg', + 'g' + ) + this.__root.appendChild(this.__currentElement) + } + + /** + * Creates the specified svg element + * @private + */ + ctx.prototype.__createElement = function ( + elementName, + properties, + resetFill + ) { + if (typeof properties === 'undefined') { + properties = {} + } + + var element = this.__document.createElementNS( + 'http://www.w3.org/2000/svg', + elementName + ), + keys = Object.keys(properties), + i, + key + if (resetFill) { + //if fill or stroke is not specified, the svg element should not display. By default SVG's fill is black. + element.setAttribute('fill', 'none') + element.setAttribute('stroke', 'none') + } + for (i = 0; i < keys.length; i++) { + key = keys[i] + element.setAttribute(key, properties[key]) + } + return element + } + + /** + * Applies default canvas styles to the context + * @private + */ + ctx.prototype.__setDefaultStyles = function () { + //default 2d canvas context properties see:http://www.w3.org/TR/2dcontext/ + var keys = Object.keys(STYLES), + i, + key + for (i = 0; i < keys.length; i++) { + key = keys[i] + this[key] = STYLES[key].canvas + } + } + + /** + * Applies styles on restore + * @param styleState + * @private + */ + ctx.prototype.__applyStyleState = function (styleState) { + var keys = Object.keys(styleState), + i, + key + for (i = 0; i < keys.length; i++) { + key = keys[i] + this[key] = styleState[key] + } + } + + /** + * Gets the current style state + * @return {Object} + * @private + */ + ctx.prototype.__getStyleState = function () { + var i, + styleState = {}, + keys = Object.keys(STYLES), + key + for (i = 0; i < keys.length; i++) { + key = keys[i] + styleState[key] = this[key] + } + return styleState + } + + /** + * Apples the current styles to the current SVG element. On "ctx.fill" or "ctx.stroke" + * @param type + * @private + */ + ctx.prototype.__applyStyleToCurrentElement = function (type) { + var currentElement = this.__currentElement + var currentStyleGroup = this.__currentElementsToStyle + if (currentStyleGroup) { + currentElement.setAttribute(type, '') + currentElement = currentStyleGroup.element + currentStyleGroup.children.forEach(function (node) { + node.setAttribute(type, '') + }) + } + + var keys = Object.keys(STYLES), + i, + style, + value, + id, + regex, + matches + for (i = 0; i < keys.length; i++) { + style = STYLES[keys[i]] + value = this[keys[i]] + if (style.apply) { + //is this a gradient or pattern? + if (value instanceof CanvasPattern) { + //pattern + if (value.__ctx) { + //copy over defs + while (value.__ctx.__defs.childNodes.length) { + id = + value.__ctx.__defs.childNodes[0].getAttribute( + 'id' + ) + this.__ids[id] = id + this.__defs.appendChild( + value.__ctx.__defs.childNodes[0] + ) + } + } + currentElement.setAttribute( + style.apply, + format('url(#{id})', { + id: value.__root.getAttribute('id'), + }) + ) + } else if (value instanceof CanvasGradient) { + //gradient + currentElement.setAttribute( + style.apply, + format('url(#{id})', { + id: value.__root.getAttribute('id'), + }) + ) + } else if ( + style.apply.indexOf(type) !== -1 && + style.svg !== value + ) { + if ( + (style.svgAttr === 'stroke' || + style.svgAttr === 'fill') && + value.indexOf('rgba') !== -1 + ) { + //separate alpha value, since illustrator can't handle it + regex = + /rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d?\.?\d*)\s*\)/gi + matches = regex.exec(value) + currentElement.setAttribute( + style.svgAttr, + format('rgb({r},{g},{b})', { + r: matches[1], + g: matches[2], + b: matches[3], + }) + ) + //should take globalAlpha here + var opacity = matches[4] + var globalAlpha = this.globalAlpha + if (globalAlpha != null) { + opacity *= globalAlpha + } + currentElement.setAttribute( + style.svgAttr + '-opacity', + opacity + ) + } else { + var attr = style.svgAttr + if (keys[i] === 'globalAlpha') { + attr = type + '-' + style.svgAttr + if (currentElement.getAttribute(attr)) { + //fill-opacity or stroke-opacity has already been set by stroke or fill. + continue + } + } + //otherwise only update attribute if right type, and not svg default + currentElement.setAttribute(attr, value) + } + } + } + } + } + + /** + * Will return the closest group or svg node. May return the current element. + * @private + */ + ctx.prototype.__closestGroupOrSvg = function (node) { + node = node || this.__currentElement + if (node.nodeName === 'g' || node.nodeName === 'svg') { + return node + } else { + return this.__closestGroupOrSvg(node.parentNode) + } + } + + /** + * Returns the serialized value of the svg so far + * @param fixNamedEntities - Standalone SVG doesn't support named entities, which document.createTextNode encodes. + * If true, we attempt to find all named entities and encode it as a numeric entity. + * @return serialized svg + */ + ctx.prototype.getSerializedSvg = function (fixNamedEntities) { + var serialized = new XMLSerializer().serializeToString(this.__root), + keys, + i, + key, + value, + regexp, + xmlns + + //IE search for a duplicate xmnls because they didn't implement setAttributeNS correctly + xmlns = + /xmlns="http:\/\/www\.w3\.org\/2000\/svg".+xmlns="http:\/\/www\.w3\.org\/2000\/svg/gi + if (xmlns.test(serialized)) { + serialized = serialized.replace( + 'xmlns="http://www.w3.org/2000/svg', + 'xmlns:xlink="http://www.w3.org/1999/xlink' + ) + } + + if (fixNamedEntities) { + keys = Object.keys(namedEntities) + //loop over each named entity and replace with the proper equivalent. + for (i = 0; i < keys.length; i++) { + key = keys[i] + value = namedEntities[key] + regexp = new RegExp(key, 'gi') + if (regexp.test(serialized)) { + serialized = serialized.replace(regexp, value) + } + } + } + + return serialized + } + + /** + * Returns the root svg + * @return + */ + ctx.prototype.getSvg = function () { + return this.__root + } + /** + * Will generate a group tag. + */ + ctx.prototype.save = function () { + var group = this.__createElement('g') + var parent = this.__closestGroupOrSvg() + this.__groupStack.push(parent) + parent.appendChild(group) + this.__currentElement = group + this.__stack.push(this.__getStyleState()) + } + /** + * Sets current element to parent, or just root if already root + */ + ctx.prototype.restore = function () { + this.__currentElement = this.__groupStack.pop() + this.__currentElementsToStyle = null + //Clearing canvas will make the poped group invalid, currentElement is set to the root group node. + if (!this.__currentElement) { + this.__currentElement = this.__root.childNodes[1] + } + var state = this.__stack.pop() + this.__applyStyleState(state) + } + + /** + * Helper method to add transform + * @private + */ + ctx.prototype.__addTransform = function (t) { + //if the current element has siblings, add another group + var parent = this.__closestGroupOrSvg() + if (parent.childNodes.length > 0) { + if (this.__currentElement.nodeName === 'path') { + if (!this.__currentElementsToStyle) + this.__currentElementsToStyle = { + element: parent, + children: [], + } + this.__currentElementsToStyle.children.push( + this.__currentElement + ) + this.__applyCurrentDefaultPath() + } + + var group = this.__createElement('g') + parent.appendChild(group) + this.__currentElement = group + } + + var transform = this.__currentElement.getAttribute('transform') + if (transform) { + transform += ' ' + } else { + transform = '' + } + transform += t + this.__currentElement.setAttribute('transform', transform) + } + + /** + * scales the current element + */ + ctx.prototype.scale = function (x, y) { + if (y === undefined) { + y = x + } + this.__addTransform(format('scale({x},{y})', { x: x, y: y })) + } + + /** + * rotates the current element + */ + ctx.prototype.rotate = function (angle) { + var degrees = (angle * 180) / Math.PI + this.__addTransform( + format('rotate({angle},{cx},{cy})', { + angle: degrees, + cx: 0, + cy: 0, + }) + ) + } + + /** + * translates the current element + */ + ctx.prototype.translate = function (x, y) { + this.__addTransform(format('translate({x},{y})', { x: x, y: y })) + } + + /** + * applies a transform to the current element + */ + ctx.prototype.transform = function (a, b, c, d, e, f) { + this.__addTransform( + format('matrix({a},{b},{c},{d},{e},{f})', { + a: a, + b: b, + c: c, + d: d, + e: e, + f: f, + }) + ) + } + + /** + * Create a new Path Element + */ + ctx.prototype.beginPath = function () { + var path, parent + + // Note that there is only one current default path, it is not part of the drawing state. + // See also: https://html.spec.whatwg.org/multipage/scripting.html#current-default-path + this.__currentDefaultPath = '' + this.__currentPosition = {} + + path = this.__createElement('path', {}, true) + parent = this.__closestGroupOrSvg() + parent.appendChild(path) + this.__currentElement = path + } + + /** + * Helper function to apply currentDefaultPath to current path element + * @private + */ + ctx.prototype.__applyCurrentDefaultPath = function () { + var currentElement = this.__currentElement + if (currentElement.nodeName === 'path') { + currentElement.setAttribute('d', this.__currentDefaultPath) + } else { + console.error( + 'Attempted to apply path command to node', + currentElement.nodeName + ) + } + } + + /** + * Helper function to add path command + * @private + */ + ctx.prototype.__addPathCommand = function (command) { + this.__currentDefaultPath += ' ' + this.__currentDefaultPath += command + } + + /** + * Adds the move command to the current path element, + * if the currentPathElement is not empty create a new path element + */ + ctx.prototype.moveTo = function (x, y) { + if (this.__currentElement.nodeName !== 'path') { + this.beginPath() + } + + // creates a new subpath with the given point + this.__currentPosition = { x: x, y: y } + this.__addPathCommand(format('M {x} {y}', { x: x, y: y })) + } + + /** + * Closes the current path + */ + ctx.prototype.closePath = function () { + if (this.__currentDefaultPath) { + this.__addPathCommand('Z') + } + } + + /** + * Adds a line to command + */ + ctx.prototype.lineTo = function (x, y) { + this.__currentPosition = { x: x, y: y } + if (this.__currentDefaultPath.indexOf('M') > -1) { + this.__addPathCommand(format('L {x} {y}', { x: x, y: y })) + } else { + this.__addPathCommand(format('M {x} {y}', { x: x, y: y })) + } + } + + /** + * Add a bezier command + */ + ctx.prototype.bezierCurveTo = function (cp1x, cp1y, cp2x, cp2y, x, y) { + this.__currentPosition = { x: x, y: y } + this.__addPathCommand( + format('C {cp1x} {cp1y} {cp2x} {cp2y} {x} {y}', { + cp1x: cp1x, + cp1y: cp1y, + cp2x: cp2x, + cp2y: cp2y, + x: x, + y: y, + }) + ) + } + + /** + * Adds a quadratic curve to command + */ + ctx.prototype.quadraticCurveTo = function (cpx, cpy, x, y) { + this.__currentPosition = { x: x, y: y } + this.__addPathCommand( + format('Q {cpx} {cpy} {x} {y}', { cpx: cpx, cpy: cpy, x: x, y: y }) + ) + } + + /** + * Return a new normalized vector of given vector + */ + var normalize = function (vector) { + var len = Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1]) + return [vector[0] / len, vector[1] / len] + } + + /** + * Adds the arcTo to the current path + * + * @see http://www.w3.org/TR/2015/WD-2dcontext-20150514/#dom-context-2d-arcto + */ + ctx.prototype.arcTo = function (x1, y1, x2, y2, radius) { + // Let the point (x0, y0) be the last point in the subpath. + var x0 = this.__currentPosition && this.__currentPosition.x + var y0 = this.__currentPosition && this.__currentPosition.y + + // First ensure there is a subpath for (x1, y1). + if (typeof x0 == 'undefined' || typeof y0 == 'undefined') { + return + } + + // Negative values for radius must cause the implementation to throw an IndexSizeError exception. + if (radius < 0) { + throw new Error( + 'IndexSizeError: The radius provided (' + + radius + + ') is negative.' + ) + } + + // If the point (x0, y0) is equal to the point (x1, y1), + // or if the point (x1, y1) is equal to the point (x2, y2), + // or if the radius radius is zero, + // then the method must add the point (x1, y1) to the subpath, + // and connect that point to the previous point (x0, y0) by a straight line. + if ( + (x0 === x1 && y0 === y1) || + (x1 === x2 && y1 === y2) || + radius === 0 + ) { + this.lineTo(x1, y1) + return + } + + // Otherwise, if the points (x0, y0), (x1, y1), and (x2, y2) all lie on a single straight line, + // then the method must add the point (x1, y1) to the subpath, + // and connect that point to the previous point (x0, y0) by a straight line. + var unit_vec_p1_p0 = normalize([x0 - x1, y0 - y1]) + var unit_vec_p1_p2 = normalize([x2 - x1, y2 - y1]) + if ( + unit_vec_p1_p0[0] * unit_vec_p1_p2[1] === + unit_vec_p1_p0[1] * unit_vec_p1_p2[0] + ) { + this.lineTo(x1, y1) + return + } + + // Otherwise, let The Arc be the shortest arc given by circumference of the circle that has radius radius, + // and that has one point tangent to the half-infinite line that crosses the point (x0, y0) and ends at the point (x1, y1), + // and that has a different point tangent to the half-infinite line that ends at the point (x1, y1), and crosses the point (x2, y2). + // The points at which this circle touches these two lines are called the start and end tangent points respectively. + + // note that both vectors are unit vectors, so the length is 1 + var cos = + unit_vec_p1_p0[0] * unit_vec_p1_p2[0] + + unit_vec_p1_p0[1] * unit_vec_p1_p2[1] + var theta = Math.acos(Math.abs(cos)) + + // Calculate origin + var unit_vec_p1_origin = normalize([ + unit_vec_p1_p0[0] + unit_vec_p1_p2[0], + unit_vec_p1_p0[1] + unit_vec_p1_p2[1], + ]) + var len_p1_origin = radius / Math.sin(theta / 2) + var x = x1 + len_p1_origin * unit_vec_p1_origin[0] + var y = y1 + len_p1_origin * unit_vec_p1_origin[1] + + // Calculate start angle and end angle + // rotate 90deg clockwise (note that y axis points to its down) + var unit_vec_origin_start_tangent = [ + -unit_vec_p1_p0[1], + unit_vec_p1_p0[0], + ] + // rotate 90deg counter clockwise (note that y axis points to its down) + var unit_vec_origin_end_tangent = [ + unit_vec_p1_p2[1], + -unit_vec_p1_p2[0], + ] + var getAngle = function (vector) { + // get angle (clockwise) between vector and (1, 0) + var x = vector[0] + var y = vector[1] + if (y >= 0) { + // note that y axis points to its down + return Math.acos(x) + } else { + return -Math.acos(x) + } + } + var startAngle = getAngle(unit_vec_origin_start_tangent) + var endAngle = getAngle(unit_vec_origin_end_tangent) + + // Connect the point (x0, y0) to the start tangent point by a straight line + this.lineTo( + x + unit_vec_origin_start_tangent[0] * radius, + y + unit_vec_origin_start_tangent[1] * radius + ) + + // Connect the start tangent point to the end tangent point by arc + // and adding the end tangent point to the subpath. + this.arc(x, y, radius, startAngle, endAngle) + } + + /** + * Sets the stroke property on the current element + */ + ctx.prototype.stroke = function () { + if (this.__currentElement.nodeName === 'path') { + this.__currentElement.setAttribute( + 'paint-order', + 'fill stroke markers' + ) + } + this.__applyCurrentDefaultPath() + this.__applyStyleToCurrentElement('stroke') + } + + /** + * Sets fill properties on the current element + */ + ctx.prototype.fill = function () { + if (this.__currentElement.nodeName === 'path') { + this.__currentElement.setAttribute( + 'paint-order', + 'stroke fill markers' + ) + } + this.__applyCurrentDefaultPath() + this.__applyStyleToCurrentElement('fill') + } + + /** + * Adds a rectangle to the path. + */ + ctx.prototype.rect = function (x, y, width, height) { + if (this.__currentElement.nodeName !== 'path') { + this.beginPath() + } + this.moveTo(x, y) + this.lineTo(x + width, y) + this.lineTo(x + width, y + height) + this.lineTo(x, y + height) + this.lineTo(x, y) + this.closePath() + } + + /** + * adds a rectangle element + */ + ctx.prototype.fillRect = function (x, y, width, height) { + var rect, parent + rect = this.__createElement( + 'rect', + { + x: x, + y: y, + width: width, + height: height, + }, + true + ) + parent = this.__closestGroupOrSvg() + parent.appendChild(rect) + this.__currentElement = rect + this.__applyStyleToCurrentElement('fill') + } + + /** + * Draws a rectangle with no fill + * @param x + * @param y + * @param width + * @param height + */ + ctx.prototype.strokeRect = function (x, y, width, height) { + var rect, parent + rect = this.__createElement( + 'rect', + { + x: x, + y: y, + width: width, + height: height, + }, + true + ) + parent = this.__closestGroupOrSvg() + parent.appendChild(rect) + this.__currentElement = rect + this.__applyStyleToCurrentElement('stroke') + } + + /** + * Clear entire canvas: + * 1. save current transforms + * 2. remove all the childNodes of the root g element + */ + ctx.prototype.__clearCanvas = function () { + var current = this.__closestGroupOrSvg(), + transform = current.getAttribute('transform') + var rootGroup = this.__root.childNodes[1] + var childNodes = rootGroup.childNodes + for (var i = childNodes.length - 1; i >= 0; i--) { + if (childNodes[i]) { + rootGroup.removeChild(childNodes[i]) + } + } + this.__currentElement = rootGroup + //reset __groupStack as all the child group nodes are all removed. + this.__groupStack = [] + if (transform) { + this.__addTransform(transform) + } + } + + /** + * "Clears" a canvas by just drawing a white rectangle in the current group. + */ + ctx.prototype.clearRect = function (x, y, width, height) { + //clear entire canvas + if ( + x === 0 && + y === 0 && + width === this.width && + height === this.height + ) { + this.__clearCanvas() + return + } + var rect, + parent = this.__closestGroupOrSvg() + rect = this.__createElement( + 'rect', + { + x: x, + y: y, + width: width, + height: height, + fill: '#FFFFFF', + }, + true + ) + parent.appendChild(rect) + } + + /** + * Adds a linear gradient to a defs tag. + * Returns a canvas gradient object that has a reference to it's parent def + */ + ctx.prototype.createLinearGradient = function (x1, y1, x2, y2) { + var grad = this.__createElement( + 'linearGradient', + { + id: randomString(this.__ids), + x1: x1 + 'px', + x2: x2 + 'px', + y1: y1 + 'px', + y2: y2 + 'px', + gradientUnits: 'userSpaceOnUse', + }, + false + ) + this.__defs.appendChild(grad) + return new CanvasGradient(grad, this) + } + + /** + * Adds a radial gradient to a defs tag. + * Returns a canvas gradient object that has a reference to it's parent def + */ + ctx.prototype.createRadialGradient = function (x0, y0, r0, x1, y1, r1) { + var grad = this.__createElement( + 'radialGradient', + { + id: randomString(this.__ids), + cx: x1 + 'px', + cy: y1 + 'px', + r: r1 + 'px', + fx: x0 + 'px', + fy: y0 + 'px', + gradientUnits: 'userSpaceOnUse', + }, + false + ) + this.__defs.appendChild(grad) + return new CanvasGradient(grad, this) + } + + /** + * Parses the font string and returns svg mapping + * @private + */ + ctx.prototype.__parseFont = function () { + var regex = + /^\s*(?=(?:(?:[-a-z]+\s*){0,2}(italic|oblique))?)(?=(?:(?:[-a-z]+\s*){0,2}(small-caps))?)(?=(?:(?:[-a-z]+\s*){0,2}(bold(?:er)?|lighter|[1-9]00))?)(?:(?:normal|\1|\2|\3)\s*){0,3}((?:xx?-)?(?:small|large)|medium|smaller|larger|[.\d]+(?:\%|in|[cem]m|ex|p[ctx]))(?:\s*\/\s*(normal|[.\d]+(?:\%|in|[cem]m|ex|p[ctx])))?\s*([-,\'\"\sa-z0-9]+?)\s*$/i + var fontPart = regex.exec(this.font) + var data = { + style: fontPart[1] || 'normal', + size: fontPart[4] || '10px', + family: fontPart[6] || 'sans-serif', + weight: fontPart[3] || 'normal', + decoration: fontPart[2] || 'normal', + href: null, + } + + //canvas doesn't support underline natively, but we can pass this attribute + if (this.__fontUnderline === 'underline') { + data.decoration = 'underline' + } + + //canvas also doesn't support linking, but we can pass this as well + if (this.__fontHref) { + data.href = this.__fontHref + } + + return data + } + + /** + * Helper to link text fragments + * @param font + * @param element + * @return {*} + * @private + */ + ctx.prototype.__wrapTextLink = function (font, element) { + if (font.href) { + var a = this.__createElement('a') + a.setAttributeNS( + 'http://www.w3.org/1999/xlink', + 'xlink:href', + font.href + ) + a.appendChild(element) + return a + } + return element + } + + /** + * Fills or strokes text + * @param text + * @param x + * @param y + * @param action - stroke or fill + * @private + */ + ctx.prototype.__applyText = function (text, x, y, action) { + var font = this.__parseFont(), + parent = this.__closestGroupOrSvg(), + textElement = this.__createElement( + 'text', + { + 'font-family': font.family, + 'font-size': font.size, + 'font-style': font.style, + 'font-weight': font.weight, + 'text-decoration': font.decoration, + x: x, + y: y, + 'text-anchor': getTextAnchor(this.textAlign), + 'dominant-baseline': getDominantBaseline(this.textBaseline), + }, + true + ) + + textElement.appendChild(this.__document.createTextNode(text)) + this.__currentElement = textElement + this.__applyStyleToCurrentElement(action) + parent.appendChild(this.__wrapTextLink(font, textElement)) + } + + /** + * Creates a text element + * @param text + * @param x + * @param y + */ + ctx.prototype.fillText = function (text, x, y) { + this.__applyText(text, x, y, 'fill') + } + + /** + * Strokes text + * @param text + * @param x + * @param y + */ + ctx.prototype.strokeText = function (text, x, y) { + this.__applyText(text, x, y, 'stroke') + } + + /** + * No need to implement this for svg. + * @param text + * @return {TextMetrics} + */ + ctx.prototype.measureText = function (text) { + this.__ctx.font = this.font + return this.__ctx.measureText(text) + } + + /** + * Arc command! + */ + ctx.prototype.arc = function ( + x, + y, + radius, + startAngle, + endAngle, + counterClockwise + ) { + // in canvas no circle is drawn if no angle is provided. + if (startAngle === endAngle) { + return + } + startAngle = startAngle % (2 * Math.PI) + endAngle = endAngle % (2 * Math.PI) + if (startAngle === endAngle) { + //circle time! subtract some of the angle so svg is happy (svg elliptical arc can't draw a full circle) + endAngle = + (endAngle + 2 * Math.PI - 0.001 * (counterClockwise ? -1 : 1)) % + (2 * Math.PI) + } + var endX = x + radius * Math.cos(endAngle), + endY = y + radius * Math.sin(endAngle), + startX = x + radius * Math.cos(startAngle), + startY = y + radius * Math.sin(startAngle), + sweepFlag = counterClockwise ? 0 : 1, + largeArcFlag = 0, + diff = endAngle - startAngle + + // https://github.com/gliffy/canvas2svg/issues/4 + if (diff < 0) { + diff += 2 * Math.PI + } + + if (counterClockwise) { + largeArcFlag = diff > Math.PI ? 0 : 1 + } else { + largeArcFlag = diff > Math.PI ? 1 : 0 + } + + this.lineTo(startX, startY) + this.__addPathCommand( + format( + 'A {rx} {ry} {xAxisRotation} {largeArcFlag} {sweepFlag} {endX} {endY}', + { + rx: radius, + ry: radius, + xAxisRotation: 0, + largeArcFlag: largeArcFlag, + sweepFlag: sweepFlag, + endX: endX, + endY: endY, + } + ) + ) + + this.__currentPosition = { x: endX, y: endY } + } + + /** + * Generates a ClipPath from the clip command. + */ + ctx.prototype.clip = function () { + var group = this.__closestGroupOrSvg(), + clipPath = this.__createElement('clipPath'), + id = randomString(this.__ids), + newGroup = this.__createElement('g') + + this.__applyCurrentDefaultPath() + group.removeChild(this.__currentElement) + clipPath.setAttribute('id', id) + clipPath.appendChild(this.__currentElement) + + this.__defs.appendChild(clipPath) + + //set the clip path to this group + group.setAttribute('clip-path', format('url(#{id})', { id: id })) + + //clip paths can be scaled and transformed, we need to add another wrapper group to avoid later transformations + // to this path + group.appendChild(newGroup) + + this.__currentElement = newGroup + } + + /** + * Draws a canvas, image or mock context to this canvas. + * Note that all svg dom manipulation uses node.childNodes rather than node.children for IE support. + * http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-drawimage + */ + ctx.prototype.drawImage = function () { + //convert arguments to a real array + var args = Array.prototype.slice.call(arguments), + image = args[0], + dx, + dy, + dw, + dh, + sx = 0, + sy = 0, + sw, + sh, + parent, + svg, + defs, + group, + currentElement, + svgImage, + canvas, + context, + id + + if (args.length === 3) { + dx = args[1] + dy = args[2] + sw = image.width + sh = image.height + dw = sw + dh = sh + } else if (args.length === 5) { + dx = args[1] + dy = args[2] + dw = args[3] + dh = args[4] + sw = image.width + sh = image.height + } else if (args.length === 9) { + sx = args[1] + sy = args[2] + sw = args[3] + sh = args[4] + dx = args[5] + dy = args[6] + dw = args[7] + dh = args[8] + } else { + throw new Error( + 'Invalid number of arguments passed to drawImage: ' + + arguments.length + ) + } + + parent = this.__closestGroupOrSvg() + currentElement = this.__currentElement + var translateDirective = 'translate(' + dx + ', ' + dy + ')' + if (image instanceof ctx) { + //canvas2svg mock canvas context. In the future we may want to clone nodes instead. + //also I'm currently ignoring dw, dh, sw, sh, sx, sy for a mock context. + svg = image.getSvg().cloneNode(true) + if (svg.childNodes && svg.childNodes.length > 1) { + defs = svg.childNodes[0] + while (defs.childNodes.length) { + id = defs.childNodes[0].getAttribute('id') + this.__ids[id] = id + this.__defs.appendChild(defs.childNodes[0]) + } + group = svg.childNodes[1] + if (group) { + //save original transform + var originTransform = group.getAttribute('transform') + var transformDirective + if (originTransform) { + transformDirective = + originTransform + ' ' + translateDirective + } else { + transformDirective = translateDirective + } + group.setAttribute('transform', transformDirective) + parent.appendChild(group) + } + } + } else if (image.nodeName === 'CANVAS' || image.nodeName === 'IMG') { + //canvas or image + svgImage = this.__createElement('image') + svgImage.setAttribute('width', dw) + svgImage.setAttribute('height', dh) + svgImage.setAttribute('preserveAspectRatio', 'none') + + if (sx || sy || sw !== image.width || sh !== image.height) { + //crop the image using a temporary canvas + canvas = this.__document.createElement('canvas') + canvas.width = dw + canvas.height = dh + context = canvas.getContext('2d') + context.drawImage(image, sx, sy, sw, sh, 0, 0, dw, dh) + image = canvas + } + svgImage.setAttribute('transform', translateDirective) + svgImage.setAttributeNS( + 'http://www.w3.org/1999/xlink', + 'xlink:href', + image.nodeName === 'CANVAS' + ? image.toDataURL() + : image.getAttribute('src') + ) + parent.appendChild(svgImage) + } + } + + /** + * Generates a pattern tag + */ + ctx.prototype.createPattern = function (image, repetition) { + var pattern = this.__document.createElementNS( + 'http://www.w3.org/2000/svg', + 'pattern' + ), + id = randomString(this.__ids), + img + pattern.setAttribute('id', id) + pattern.setAttribute('width', image.width) + pattern.setAttribute('height', image.height) + if (image.nodeName === 'CANVAS' || image.nodeName === 'IMG') { + img = this.__document.createElementNS( + 'http://www.w3.org/2000/svg', + 'image' + ) + img.setAttribute('width', image.width) + img.setAttribute('height', image.height) + img.setAttributeNS( + 'http://www.w3.org/1999/xlink', + 'xlink:href', + image.nodeName === 'CANVAS' + ? image.toDataURL() + : image.getAttribute('src') + ) + pattern.appendChild(img) + this.__defs.appendChild(pattern) + } else if (image instanceof ctx) { + pattern.appendChild(image.__root.childNodes[1]) + this.__defs.appendChild(pattern) + } + return new CanvasPattern(pattern, this) + } + + ctx.prototype.setLineDash = function (dashArray) { + if (dashArray && dashArray.length > 0) { + this.lineDash = dashArray.join(',') + } else { + this.lineDash = null + } + } + + /** + * Not yet implemented + */ + ctx.prototype.drawFocusRing = function () {} + ctx.prototype.createImageData = function () {} + ctx.prototype.getImageData = function () {} + ctx.prototype.putImageData = function () {} + ctx.prototype.globalCompositeOperation = function () {} + ctx.prototype.setTransform = function () {} + + //add options for alternative namespace + if (typeof window === 'object') { + window.C2S = ctx + } + + // CommonJS/Browserify + if (typeof module === 'object' && typeof module.exports === 'object') { + module.exports = ctx + } +})() diff --git a/v0/src/simulator/vendor/images/ui-icons_444444_256x240.png b/v0/src/simulator/vendor/images/ui-icons_444444_256x240.png new file mode 100644 index 00000000..8cc6f5db Binary files /dev/null and b/v0/src/simulator/vendor/images/ui-icons_444444_256x240.png differ diff --git a/v0/src/simulator/vendor/images/ui-icons_555555_256x240.png b/v0/src/simulator/vendor/images/ui-icons_555555_256x240.png new file mode 100644 index 00000000..c852a056 Binary files /dev/null and b/v0/src/simulator/vendor/images/ui-icons_555555_256x240.png differ diff --git a/v0/src/simulator/vendor/images/ui-icons_777620_256x240.png b/v0/src/simulator/vendor/images/ui-icons_777620_256x240.png new file mode 100644 index 00000000..3136fd07 Binary files /dev/null and b/v0/src/simulator/vendor/images/ui-icons_777620_256x240.png differ diff --git a/v0/src/simulator/vendor/images/ui-icons_777777_256x240.png b/v0/src/simulator/vendor/images/ui-icons_777777_256x240.png new file mode 100644 index 00000000..e055acb9 Binary files /dev/null and b/v0/src/simulator/vendor/images/ui-icons_777777_256x240.png differ diff --git a/v0/src/simulator/vendor/images/ui-icons_cc0000_256x240.png b/v0/src/simulator/vendor/images/ui-icons_cc0000_256x240.png new file mode 100644 index 00000000..acb51425 Binary files /dev/null and b/v0/src/simulator/vendor/images/ui-icons_cc0000_256x240.png differ diff --git a/v0/src/simulator/vendor/images/ui-icons_ffffff_256x240.png b/v0/src/simulator/vendor/images/ui-icons_ffffff_256x240.png new file mode 100644 index 00000000..7e4d1d08 Binary files /dev/null and b/v0/src/simulator/vendor/images/ui-icons_ffffff_256x240.png differ diff --git a/v0/src/simulator/vendor/jquery-ui.min.css b/v0/src/simulator/vendor/jquery-ui.min.css new file mode 100644 index 00000000..4c702d0f --- /dev/null +++ b/v0/src/simulator/vendor/jquery-ui.min.css @@ -0,0 +1,1137 @@ +/*! jQuery UI - v1.13.1 - 2022-01-22 +* http://jqueryui.com +* Includes: draggable.css, core.css, resizable.css, sortable.css, accordion.css, button.css, controlgroup.css, checkboxradio.css, dialog.css, theme.css +* To view and modify this theme, visit http://jqueryui.com/themeroller/?scope=&folderName=base&cornerRadiusShadow=8px&offsetLeftShadow=0px&offsetTopShadow=0px&thicknessShadow=5px&opacityShadow=30&bgImgOpacityShadow=0&bgTextureShadow=flat&bgColorShadow=666666&opacityOverlay=30&bgImgOpacityOverlay=0&bgTextureOverlay=flat&bgColorOverlay=aaaaaa&iconColorError=cc0000&fcError=5f3f3f&borderColorError=f1a899&bgTextureError=flat&bgColorError=fddfdf&iconColorHighlight=777620&fcHighlight=777620&borderColorHighlight=dad55e&bgTextureHighlight=flat&bgColorHighlight=fffa90&iconColorActive=ffffff&fcActive=ffffff&borderColorActive=003eff&bgTextureActive=flat&bgColorActive=007fff&iconColorHover=555555&fcHover=2b2b2b&borderColorHover=cccccc&bgTextureHover=flat&bgColorHover=ededed&iconColorDefault=777777&fcDefault=454545&borderColorDefault=c5c5c5&bgTextureDefault=flat&bgColorDefault=f6f6f6&iconColorContent=444444&fcContent=333333&borderColorContent=dddddd&bgTextureContent=flat&bgColorContent=ffffff&iconColorHeader=444444&fcHeader=333333&borderColorHeader=dddddd&bgTextureHeader=flat&bgColorHeader=e9e9e9&cornerRadius=3px&fwDefault=normal&fsDefault=1em&ffDefault=Arial%2CHelvetica%2Csans-serif +* Copyright jQuery Foundation and other contributors; Licensed MIT */ + +.ui-draggable-handle { + -ms-touch-action: none; + touch-action: none; +} +.ui-helper-hidden { + display: none; +} +.ui-helper-hidden-accessible { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} +.ui-helper-reset { + margin: 0; + padding: 0; + border: 0; + outline: 0; + line-height: 1.3; + text-decoration: none; + font-size: 100%; + list-style: none; +} +.ui-helper-clearfix:before, +.ui-helper-clearfix:after { + content: ''; + display: table; + border-collapse: collapse; +} +.ui-helper-clearfix:after { + clear: both; +} +.ui-helper-zfix { + width: 100%; + height: 100%; + top: 0; + left: 0; + position: absolute; + opacity: 0; + -ms-filter: 'alpha(opacity=0)'; +} +.ui-front { + z-index: 100; +} +.ui-state-disabled { + cursor: default !important; + pointer-events: none; +} +.ui-icon { + display: inline-block; + vertical-align: middle; + margin-top: -0.25em; + position: relative; + text-indent: -99999px; + overflow: hidden; + background-repeat: no-repeat; +} +.ui-widget-icon-block { + left: 50%; + margin-left: -8px; + display: block; +} +.ui-widget-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +.ui-resizable { + position: relative; +} +.ui-resizable-handle { + position: absolute; + font-size: 0.1px; + display: block; + -ms-touch-action: none; + touch-action: none; +} +.ui-resizable-disabled .ui-resizable-handle, +.ui-resizable-autohide .ui-resizable-handle { + display: none; +} +.ui-resizable-n { + cursor: n-resize; + height: 7px; + width: 100%; + top: -5px; + left: 0; +} +.ui-resizable-s { + cursor: s-resize; + height: 7px; + width: 100%; + bottom: -5px; + left: 0; +} +.ui-resizable-e { + cursor: e-resize; + width: 7px; + right: -5px; + top: 0; + height: 100%; +} +.ui-resizable-w { + cursor: w-resize; + width: 7px; + left: -5px; + top: 0; + height: 100%; +} +.ui-resizable-se { + cursor: se-resize; + width: 12px; + height: 12px; + right: 1px; + bottom: 1px; +} +.ui-resizable-sw { + cursor: sw-resize; + width: 9px; + height: 9px; + left: -5px; + bottom: -5px; +} +.ui-resizable-nw { + cursor: nw-resize; + width: 9px; + height: 9px; + left: -5px; + top: -5px; +} +.ui-resizable-ne { + cursor: ne-resize; + width: 9px; + height: 9px; + right: -5px; + top: -5px; +} +.ui-sortable-handle { + -ms-touch-action: none; + touch-action: none; +} +.ui-accordion .ui-accordion-header { + display: block; + cursor: pointer; + position: relative; + margin: 2px 0 0 0; + padding: 0.5em 0.5em 0.5em 0.7em; + font-size: 100%; +} +.ui-accordion .ui-accordion-content { + padding: 1em 2.2em; + border-top: 0; + overflow: auto; +} +.ui-button { + padding: 0.4em 1em; + display: inline-block; + position: relative; + line-height: normal; + margin-right: 0.1em; + cursor: pointer; + vertical-align: middle; + text-align: center; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + overflow: visible; +} +.ui-button, +.ui-button:link, +.ui-button:visited, +.ui-button:hover, +.ui-button:active { + text-decoration: none; +} +.ui-button-icon-only { + width: 2em; + box-sizing: border-box; + text-indent: -9999px; + white-space: nowrap; +} +input.ui-button.ui-button-icon-only { + text-indent: 0; +} +.ui-button-icon-only .ui-icon { + position: absolute; + top: 50%; + left: 50%; + margin-top: -8px; + margin-left: -8px; +} +.ui-button.ui-icon-notext .ui-icon { + padding: 0; + width: 2.1em; + height: 2.1em; + text-indent: -9999px; + white-space: nowrap; +} +input.ui-button.ui-icon-notext .ui-icon { + width: auto; + height: auto; + text-indent: 0; + white-space: normal; + padding: 0.4em 1em; +} +input.ui-button::-moz-focus-inner, +button.ui-button::-moz-focus-inner { + border: 0; + padding: 0; +} +.ui-controlgroup { + vertical-align: middle; + display: inline-block; +} +.ui-controlgroup > .ui-controlgroup-item { + float: left; + margin-left: 0; + margin-right: 0; +} +.ui-controlgroup > .ui-controlgroup-item:focus, +.ui-controlgroup > .ui-controlgroup-item.ui-visual-focus { + z-index: 9999; +} +.ui-controlgroup-vertical > .ui-controlgroup-item { + display: block; + float: none; + width: 100%; + margin-top: 0; + margin-bottom: 0; + text-align: left; +} +.ui-controlgroup-vertical .ui-controlgroup-item { + box-sizing: border-box; +} +.ui-controlgroup .ui-controlgroup-label { + padding: 0.4em 1em; +} +.ui-controlgroup .ui-controlgroup-label span { + font-size: 80%; +} +.ui-controlgroup-horizontal .ui-controlgroup-label + .ui-controlgroup-item { + border-left: none; +} +.ui-controlgroup-vertical .ui-controlgroup-label + .ui-controlgroup-item { + border-top: none; +} +.ui-controlgroup-horizontal .ui-controlgroup-label.ui-widget-content { + border-right: none; +} +.ui-controlgroup-vertical .ui-controlgroup-label.ui-widget-content { + border-bottom: none; +} +.ui-controlgroup-vertical .ui-spinner-input { + width: 75%; + width: calc(100% - 2.4em); +} +.ui-controlgroup-vertical .ui-spinner .ui-spinner-up { + border-top-style: solid; +} +.ui-checkboxradio-label .ui-icon-background { + box-shadow: inset 1px 1px 1px #ccc; + border-radius: 0.12em; + border: none; +} +.ui-checkboxradio-radio-label .ui-icon-background { + width: 16px; + height: 16px; + border-radius: 1em; + overflow: visible; + border: none; +} +.ui-checkboxradio-radio-label.ui-checkboxradio-checked .ui-icon, +.ui-checkboxradio-radio-label.ui-checkboxradio-checked:hover .ui-icon { + background-image: none; + width: 8px; + height: 8px; + border-width: 4px; + border-style: solid; +} +.ui-checkboxradio-disabled { + pointer-events: none; +} +.ui-dialog { + position: absolute; + top: 0; + left: 0; + padding: 0.2em; + outline: 0; +} +.ui-dialog .ui-dialog-titlebar { + padding: 0.4em 1em; + position: relative; +} +.ui-dialog .ui-dialog-title { + float: left; + margin: 0.1em 0; + white-space: nowrap; + width: 90%; + overflow: hidden; + text-overflow: ellipsis; +} +.ui-dialog .ui-dialog-titlebar-close { + position: absolute; + right: 0.3em; + top: 50%; + width: 20px; + margin: -10px 0 0 0; + padding: 1px; + height: 20px; +} +.ui-dialog .ui-dialog-content { + position: relative; + border: 0; + padding: 0.5em 1em; + background: none; + overflow: auto; +} +.ui-dialog .ui-dialog-buttonpane { + text-align: left; + border-width: 1px 0 0 0; + background-image: none; + margin-top: 0.5em; + padding: 0.3em 1em 0.5em 0.4em; +} +.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { + float: right; +} +.ui-dialog .ui-dialog-buttonpane button { + margin: 0.5em 0.4em 0.5em 0; + cursor: pointer; +} +.ui-dialog .ui-resizable-n { + height: 2px; + top: 0; +} +.ui-dialog .ui-resizable-e { + width: 2px; + right: 0; +} +.ui-dialog .ui-resizable-s { + height: 2px; + bottom: 0; +} +.ui-dialog .ui-resizable-w { + width: 2px; + left: 0; +} +.ui-dialog .ui-resizable-se, +.ui-dialog .ui-resizable-sw, +.ui-dialog .ui-resizable-ne, +.ui-dialog .ui-resizable-nw { + width: 7px; + height: 7px; +} +.ui-dialog .ui-resizable-se { + right: 0; + bottom: 0; +} +.ui-dialog .ui-resizable-sw { + left: 0; + bottom: 0; +} +.ui-dialog .ui-resizable-ne { + right: 0; + top: 0; +} +.ui-dialog .ui-resizable-nw { + left: 0; + top: 0; +} +.ui-draggable .ui-dialog-titlebar { + cursor: move; +} +.ui-widget { + font-family: Arial, Helvetica, sans-serif; + font-size: 1em; +} +.ui-widget .ui-widget { + font-size: 1em; +} +.ui-widget input, +.ui-widget select, +.ui-widget textarea, +.ui-widget button { + font-family: Arial, Helvetica, sans-serif; + font-size: 1em; +} +.ui-widget.ui-widget-content { + border: 1px solid #c5c5c5; +} +.ui-widget-content { + border: 1px solid #ddd; + background: #fff; + color: #333; +} +.ui-widget-content a { + color: #333; +} +.ui-widget-header { + border: 1px solid #ddd; + background: #e9e9e9; + color: #333; + font-weight: bold; +} +.ui-widget-header a { + color: #333; +} +.ui-state-default, +.ui-widget-content .ui-state-default, +.ui-widget-header .ui-state-default, +.ui-button, +html .ui-button.ui-state-disabled:hover, +html .ui-button.ui-state-disabled:active { + border: 1px solid #c5c5c5; + background: #f6f6f6; + font-weight: normal; + color: #454545; +} +.ui-state-default a, +.ui-state-default a:link, +.ui-state-default a:visited, +a.ui-button, +a:link.ui-button, +a:visited.ui-button, +.ui-button { + color: #454545; + text-decoration: none; +} +.ui-state-hover, +.ui-widget-content .ui-state-hover, +.ui-widget-header .ui-state-hover, +.ui-state-focus, +.ui-widget-content .ui-state-focus, +.ui-widget-header .ui-state-focus, +.ui-button:hover, +.ui-button:focus { + border: 1px solid #ccc; + background: #ededed; + font-weight: normal; + color: #2b2b2b; +} +.ui-state-hover a, +.ui-state-hover a:hover, +.ui-state-hover a:link, +.ui-state-hover a:visited, +.ui-state-focus a, +.ui-state-focus a:hover, +.ui-state-focus a:link, +.ui-state-focus a:visited, +a.ui-button:hover, +a.ui-button:focus { + color: #2b2b2b; + text-decoration: none; +} +.ui-visual-focus { + box-shadow: 0 0 3px 1px rgb(94, 158, 214); +} +.ui-state-active, +.ui-widget-content .ui-state-active, +.ui-widget-header .ui-state-active, +a.ui-button:active, +.ui-button:active, +.ui-button.ui-state-active:hover { + border: 1px solid #003eff; + background: #007fff; + font-weight: normal; + color: #fff; +} +.ui-icon-background, +.ui-state-active .ui-icon-background { + border: #003eff; + background-color: #fff; +} +.ui-state-active a, +.ui-state-active a:link, +.ui-state-active a:visited { + color: #fff; + text-decoration: none; +} +.ui-state-highlight, +.ui-widget-content .ui-state-highlight, +.ui-widget-header .ui-state-highlight { + border: 1px solid #dad55e; + background: #fffa90; + color: #777620; +} +.ui-state-checked { + border: 1px solid #dad55e; + background: #fffa90; +} +.ui-state-highlight a, +.ui-widget-content .ui-state-highlight a, +.ui-widget-header .ui-state-highlight a { + color: #777620; +} +.ui-state-error, +.ui-widget-content .ui-state-error, +.ui-widget-header .ui-state-error { + border: 1px solid #f1a899; + background: #fddfdf; + color: #5f3f3f; +} +.ui-state-error a, +.ui-widget-content .ui-state-error a, +.ui-widget-header .ui-state-error a { + color: #5f3f3f; +} +.ui-state-error-text, +.ui-widget-content .ui-state-error-text, +.ui-widget-header .ui-state-error-text { + color: #5f3f3f; +} +.ui-priority-primary, +.ui-widget-content .ui-priority-primary, +.ui-widget-header .ui-priority-primary { + font-weight: bold; +} +.ui-priority-secondary, +.ui-widget-content .ui-priority-secondary, +.ui-widget-header .ui-priority-secondary { + opacity: 0.7; + -ms-filter: 'alpha(opacity=70)'; + font-weight: normal; +} +.ui-state-disabled, +.ui-widget-content .ui-state-disabled, +.ui-widget-header .ui-state-disabled { + opacity: 0.35; + -ms-filter: 'alpha(opacity=35)'; + background-image: none; +} +.ui-state-disabled .ui-icon { + -ms-filter: 'alpha(opacity=35)'; +} +.ui-icon { + width: 16px; + height: 16px; +} +.ui-icon, +.ui-widget-content .ui-icon { + background-image: url('images/ui-icons_444444_256x240.png'); +} +.ui-widget-header .ui-icon { + background-image: url('images/ui-icons_444444_256x240.png'); +} +.ui-state-hover .ui-icon, +.ui-state-focus .ui-icon, +.ui-button:hover .ui-icon, +.ui-button:focus .ui-icon { + background-image: url('images/ui-icons_555555_256x240.png'); +} +.ui-state-active .ui-icon, +.ui-button:active .ui-icon { + background-image: url('images/ui-icons_ffffff_256x240.png'); +} +.ui-state-highlight .ui-icon, +.ui-button .ui-state-highlight.ui-icon { + background-image: url('images/ui-icons_777620_256x240.png'); +} +.ui-state-error .ui-icon, +.ui-state-error-text .ui-icon { + background-image: url('images/ui-icons_cc0000_256x240.png'); +} +.ui-button .ui-icon { + background-image: url('images/ui-icons_777777_256x240.png'); +} +.ui-icon-blank.ui-icon-blank.ui-icon-blank { + background-image: none; +} +.ui-icon-caret-1-n { + background-position: 0 0; +} +.ui-icon-caret-1-ne { + background-position: -16px 0; +} +.ui-icon-caret-1-e { + background-position: -32px 0; +} +.ui-icon-caret-1-se { + background-position: -48px 0; +} +.ui-icon-caret-1-s { + background-position: -65px 0; +} +.ui-icon-caret-1-sw { + background-position: -80px 0; +} +.ui-icon-caret-1-w { + background-position: -96px 0; +} +.ui-icon-caret-1-nw { + background-position: -112px 0; +} +.ui-icon-caret-2-n-s { + background-position: -128px 0; +} +.ui-icon-caret-2-e-w { + background-position: -144px 0; +} +.ui-icon-triangle-1-n { + background-position: 0 -16px; +} +.ui-icon-triangle-1-ne { + background-position: -16px -16px; +} +.ui-icon-triangle-1-e { + background-position: -32px -16px; +} +.ui-icon-triangle-1-se { + background-position: -48px -16px; +} +.ui-icon-triangle-1-s { + background-position: -65px -16px; +} +.ui-icon-triangle-1-sw { + background-position: -80px -16px; +} +.ui-icon-triangle-1-w { + background-position: -96px -16px; +} +.ui-icon-triangle-1-nw { + background-position: -112px -16px; +} +.ui-icon-triangle-2-n-s { + background-position: -128px -16px; +} +.ui-icon-triangle-2-e-w { + background-position: -144px -16px; +} +.ui-icon-arrow-1-n { + background-position: 0 -32px; +} +.ui-icon-arrow-1-ne { + background-position: -16px -32px; +} +.ui-icon-arrow-1-e { + background-position: -32px -32px; +} +.ui-icon-arrow-1-se { + background-position: -48px -32px; +} +.ui-icon-arrow-1-s { + background-position: -65px -32px; +} +.ui-icon-arrow-1-sw { + background-position: -80px -32px; +} +.ui-icon-arrow-1-w { + background-position: -96px -32px; +} +.ui-icon-arrow-1-nw { + background-position: -112px -32px; +} +.ui-icon-arrow-2-n-s { + background-position: -128px -32px; +} +.ui-icon-arrow-2-ne-sw { + background-position: -144px -32px; +} +.ui-icon-arrow-2-e-w { + background-position: -160px -32px; +} +.ui-icon-arrow-2-se-nw { + background-position: -176px -32px; +} +.ui-icon-arrowstop-1-n { + background-position: -192px -32px; +} +.ui-icon-arrowstop-1-e { + background-position: -208px -32px; +} +.ui-icon-arrowstop-1-s { + background-position: -224px -32px; +} +.ui-icon-arrowstop-1-w { + background-position: -240px -32px; +} +.ui-icon-arrowthick-1-n { + background-position: 1px -48px; +} +.ui-icon-arrowthick-1-ne { + background-position: -16px -48px; +} +.ui-icon-arrowthick-1-e { + background-position: -32px -48px; +} +.ui-icon-arrowthick-1-se { + background-position: -48px -48px; +} +.ui-icon-arrowthick-1-s { + background-position: -64px -48px; +} +.ui-icon-arrowthick-1-sw { + background-position: -80px -48px; +} +.ui-icon-arrowthick-1-w { + background-position: -96px -48px; +} +.ui-icon-arrowthick-1-nw { + background-position: -112px -48px; +} +.ui-icon-arrowthick-2-n-s { + background-position: -128px -48px; +} +.ui-icon-arrowthick-2-ne-sw { + background-position: -144px -48px; +} +.ui-icon-arrowthick-2-e-w { + background-position: -160px -48px; +} +.ui-icon-arrowthick-2-se-nw { + background-position: -176px -48px; +} +.ui-icon-arrowthickstop-1-n { + background-position: -192px -48px; +} +.ui-icon-arrowthickstop-1-e { + background-position: -208px -48px; +} +.ui-icon-arrowthickstop-1-s { + background-position: -224px -48px; +} +.ui-icon-arrowthickstop-1-w { + background-position: -240px -48px; +} +.ui-icon-arrowreturnthick-1-w { + background-position: 0 -64px; +} +.ui-icon-arrowreturnthick-1-n { + background-position: -16px -64px; +} +.ui-icon-arrowreturnthick-1-e { + background-position: -32px -64px; +} +.ui-icon-arrowreturnthick-1-s { + background-position: -48px -64px; +} +.ui-icon-arrowreturn-1-w { + background-position: -64px -64px; +} +.ui-icon-arrowreturn-1-n { + background-position: -80px -64px; +} +.ui-icon-arrowreturn-1-e { + background-position: -96px -64px; +} +.ui-icon-arrowreturn-1-s { + background-position: -112px -64px; +} +.ui-icon-arrowrefresh-1-w { + background-position: -128px -64px; +} +.ui-icon-arrowrefresh-1-n { + background-position: -144px -64px; +} +.ui-icon-arrowrefresh-1-e { + background-position: -160px -64px; +} +.ui-icon-arrowrefresh-1-s { + background-position: -176px -64px; +} +.ui-icon-arrow-4 { + background-position: 0 -80px; +} +.ui-icon-arrow-4-diag { + background-position: -16px -80px; +} +.ui-icon-extlink { + background-position: -32px -80px; +} +.ui-icon-newwin { + background-position: -48px -80px; +} +.ui-icon-refresh { + background-position: -64px -80px; +} +.ui-icon-shuffle { + background-position: -80px -80px; +} +.ui-icon-transfer-e-w { + background-position: -96px -80px; +} +.ui-icon-transferthick-e-w { + background-position: -112px -80px; +} +.ui-icon-folder-collapsed { + background-position: 0 -96px; +} +.ui-icon-folder-open { + background-position: -16px -96px; +} +.ui-icon-document { + background-position: -32px -96px; +} +.ui-icon-document-b { + background-position: -48px -96px; +} +.ui-icon-note { + background-position: -64px -96px; +} +.ui-icon-mail-closed { + background-position: -80px -96px; +} +.ui-icon-mail-open { + background-position: -96px -96px; +} +.ui-icon-suitcase { + background-position: -112px -96px; +} +.ui-icon-comment { + background-position: -128px -96px; +} +.ui-icon-person { + background-position: -144px -96px; +} +.ui-icon-print { + background-position: -160px -96px; +} +.ui-icon-trash { + background-position: -176px -96px; +} +.ui-icon-locked { + background-position: -192px -96px; +} +.ui-icon-unlocked { + background-position: -208px -96px; +} +.ui-icon-bookmark { + background-position: -224px -96px; +} +.ui-icon-tag { + background-position: -240px -96px; +} +.ui-icon-home { + background-position: 0 -112px; +} +.ui-icon-flag { + background-position: -16px -112px; +} +.ui-icon-calendar { + background-position: -32px -112px; +} +.ui-icon-cart { + background-position: -48px -112px; +} +.ui-icon-pencil { + background-position: -64px -112px; +} +.ui-icon-clock { + background-position: -80px -112px; +} +.ui-icon-disk { + background-position: -96px -112px; +} +.ui-icon-calculator { + background-position: -112px -112px; +} +.ui-icon-zoomin { + background-position: -128px -112px; +} +.ui-icon-zoomout { + background-position: -144px -112px; +} +.ui-icon-search { + background-position: -160px -112px; +} +.ui-icon-wrench { + background-position: -176px -112px; +} +.ui-icon-gear { + background-position: -192px -112px; +} +.ui-icon-heart { + background-position: -208px -112px; +} +.ui-icon-star { + background-position: -224px -112px; +} +.ui-icon-link { + background-position: -240px -112px; +} +.ui-icon-cancel { + background-position: 0 -128px; +} +.ui-icon-plus { + background-position: -16px -128px; +} +.ui-icon-plusthick { + background-position: -32px -128px; +} +.ui-icon-minus { + background-position: -48px -128px; +} +.ui-icon-minusthick { + background-position: -64px -128px; +} +.ui-icon-close { + background-position: -80px -128px; +} +.ui-icon-closethick { + background-position: -96px -128px; +} +.ui-icon-key { + background-position: -112px -128px; +} +.ui-icon-lightbulb { + background-position: -128px -128px; +} +.ui-icon-scissors { + background-position: -144px -128px; +} +.ui-icon-clipboard { + background-position: -160px -128px; +} +.ui-icon-copy { + background-position: -176px -128px; +} +.ui-icon-contact { + background-position: -192px -128px; +} +.ui-icon-image { + background-position: -208px -128px; +} +.ui-icon-video { + background-position: -224px -128px; +} +.ui-icon-script { + background-position: -240px -128px; +} +.ui-icon-alert { + background-position: 0 -144px; +} +.ui-icon-info { + background-position: -16px -144px; +} +.ui-icon-notice { + background-position: -32px -144px; +} +.ui-icon-help { + background-position: -48px -144px; +} +.ui-icon-check { + background-position: -64px -144px; +} +.ui-icon-bullet { + background-position: -80px -144px; +} +.ui-icon-radio-on { + background-position: -96px -144px; +} +.ui-icon-radio-off { + background-position: -112px -144px; +} +.ui-icon-pin-w { + background-position: -128px -144px; +} +.ui-icon-pin-s { + background-position: -144px -144px; +} +.ui-icon-play { + background-position: 0 -160px; +} +.ui-icon-pause { + background-position: -16px -160px; +} +.ui-icon-seek-next { + background-position: -32px -160px; +} +.ui-icon-seek-prev { + background-position: -48px -160px; +} +.ui-icon-seek-end { + background-position: -64px -160px; +} +.ui-icon-seek-start { + background-position: -80px -160px; +} +.ui-icon-seek-first { + background-position: -80px -160px; +} +.ui-icon-stop { + background-position: -96px -160px; +} +.ui-icon-eject { + background-position: -112px -160px; +} +.ui-icon-volume-off { + background-position: -128px -160px; +} +.ui-icon-volume-on { + background-position: -144px -160px; +} +.ui-icon-power { + background-position: 0 -176px; +} +.ui-icon-signal-diag { + background-position: -16px -176px; +} +.ui-icon-signal { + background-position: -32px -176px; +} +.ui-icon-battery-0 { + background-position: -48px -176px; +} +.ui-icon-battery-1 { + background-position: -64px -176px; +} +.ui-icon-battery-2 { + background-position: -80px -176px; +} +.ui-icon-battery-3 { + background-position: -96px -176px; +} +.ui-icon-circle-plus { + background-position: 0 -192px; +} +.ui-icon-circle-minus { + background-position: -16px -192px; +} +.ui-icon-circle-close { + background-position: -32px -192px; +} +.ui-icon-circle-triangle-e { + background-position: -48px -192px; +} +.ui-icon-circle-triangle-s { + background-position: -64px -192px; +} +.ui-icon-circle-triangle-w { + background-position: -80px -192px; +} +.ui-icon-circle-triangle-n { + background-position: -96px -192px; +} +.ui-icon-circle-arrow-e { + background-position: -112px -192px; +} +.ui-icon-circle-arrow-s { + background-position: -128px -192px; +} +.ui-icon-circle-arrow-w { + background-position: -144px -192px; +} +.ui-icon-circle-arrow-n { + background-position: -160px -192px; +} +.ui-icon-circle-zoomin { + background-position: -176px -192px; +} +.ui-icon-circle-zoomout { + background-position: -192px -192px; +} +.ui-icon-circle-check { + background-position: -208px -192px; +} +.ui-icon-circlesmall-plus { + background-position: 0 -208px; +} +.ui-icon-circlesmall-minus { + background-position: -16px -208px; +} +.ui-icon-circlesmall-close { + background-position: -32px -208px; +} +.ui-icon-squaresmall-plus { + background-position: -48px -208px; +} +.ui-icon-squaresmall-minus { + background-position: -64px -208px; +} +.ui-icon-squaresmall-close { + background-position: -80px -208px; +} +.ui-icon-grip-dotted-vertical { + background-position: 0 -224px; +} +.ui-icon-grip-dotted-horizontal { + background-position: -16px -224px; +} +.ui-icon-grip-solid-vertical { + background-position: -32px -224px; +} +.ui-icon-grip-solid-horizontal { + background-position: -48px -224px; +} +.ui-icon-gripsmall-diagonal-se { + background-position: -64px -224px; +} +.ui-icon-grip-diagonal-se { + background-position: -80px -224px; +} +.ui-corner-all, +.ui-corner-top, +.ui-corner-left, +.ui-corner-tl { + border-top-left-radius: 3px; +} +.ui-corner-all, +.ui-corner-top, +.ui-corner-right, +.ui-corner-tr { + border-top-right-radius: 3px; +} +.ui-corner-all, +.ui-corner-bottom, +.ui-corner-left, +.ui-corner-bl { + border-bottom-left-radius: 3px; +} +.ui-corner-all, +.ui-corner-bottom, +.ui-corner-right, +.ui-corner-br { + border-bottom-right-radius: 3px; +} +.ui-widget-overlay { + background: #aaa; + opacity: 0.3; + -ms-filter: Alpha(Opacity=30); +} +.ui-widget-shadow { + -webkit-box-shadow: 0 0 5px #666; + box-shadow: 0 0 5px #666; +} diff --git a/v0/src/simulator/vendor/jquery-ui.min.js b/v0/src/simulator/vendor/jquery-ui.min.js new file mode 100644 index 00000000..8453fa2f --- /dev/null +++ b/v0/src/simulator/vendor/jquery-ui.min.js @@ -0,0 +1,7887 @@ +/*! jQuery UI - v1.13.1 - 2022-01-22 + * http://jqueryui.com + * Includes: widget.js, position.js, data.js, disable-selection.js, focusable.js, form-reset-mixin.js, jquery-patch.js, keycode.js, labels.js, scroll-parent.js, tabbable.js, unique-id.js, widgets/draggable.js, widgets/resizable.js, widgets/sortable.js, widgets/accordion.js, widgets/button.js, widgets/checkboxradio.js, widgets/controlgroup.js, widgets/dialog.js, widgets/mouse.js, effect.js, effects/effect-blind.js, effects/effect-bounce.js, effects/effect-clip.js, effects/effect-drop.js, effects/effect-explode.js, effects/effect-fade.js, effects/effect-fold.js, effects/effect-highlight.js, effects/effect-puff.js, effects/effect-pulsate.js, effects/effect-scale.js, effects/effect-shake.js, effects/effect-size.js, effects/effect-slide.js, effects/effect-transfer.js + * Copyright jQuery Foundation and other contributors; Licensed MIT */ + +!(function (t) { + 'use strict' + 'function' == typeof define && define.amd + ? define(['jquery'], t) + : t(jQuery) +})(function (x) { + 'use strict' + x.ui = x.ui || {} + x.ui.version = '1.13.1' + var o, + i = 0, + r = Array.prototype.hasOwnProperty, + a = Array.prototype.slice + ;(x.cleanData = + ((o = x.cleanData), + function (t) { + for (var e, i, s = 0; null != (i = t[s]); s++) + (e = x._data(i, 'events')) && + e.remove && + x(i).triggerHandler('remove') + o(t) + })), + (x.widget = function (t, i, e) { + var s, + o, + n, + r = {}, + a = t.split('.')[0], + h = a + '-' + (t = t.split('.')[1]) + return ( + e || ((e = i), (i = x.Widget)), + Array.isArray(e) && (e = x.extend.apply(null, [{}].concat(e))), + (x.expr.pseudos[h.toLowerCase()] = function (t) { + return !!x.data(t, h) + }), + (x[a] = x[a] || {}), + (s = x[a][t]), + (o = x[a][t] = + function (t, e) { + if (!this || !this._createWidget) return new o(t, e) + arguments.length && this._createWidget(t, e) + }), + x.extend(o, s, { + version: e.version, + _proto: x.extend({}, e), + _childConstructors: [], + }), + ((n = new i()).options = x.widget.extend({}, n.options)), + x.each(e, function (e, s) { + function o() { + return i.prototype[e].apply(this, arguments) + } + function n(t) { + return i.prototype[e].apply(this, t) + } + r[e] = + 'function' == typeof s + ? function () { + var t, + e = this._super, + i = this._superApply + return ( + (this._super = o), + (this._superApply = n), + (t = s.apply(this, arguments)), + (this._super = e), + (this._superApply = i), + t + ) + } + : s + }), + (o.prototype = x.widget.extend( + n, + { widgetEventPrefix: (s && n.widgetEventPrefix) || t }, + r, + { + constructor: o, + namespace: a, + widgetName: t, + widgetFullName: h, + } + )), + s + ? (x.each(s._childConstructors, function (t, e) { + var i = e.prototype + x.widget( + i.namespace + '.' + i.widgetName, + o, + e._proto + ) + }), + delete s._childConstructors) + : i._childConstructors.push(o), + x.widget.bridge(t, o), + o + ) + }), + (x.widget.extend = function (t) { + for ( + var e, i, s = a.call(arguments, 1), o = 0, n = s.length; + o < n; + o++ + ) + for (e in s[o]) + (i = s[o][e]), + r.call(s[o], e) && + void 0 !== i && + (x.isPlainObject(i) + ? (t[e] = x.isPlainObject(t[e]) + ? x.widget.extend({}, t[e], i) + : x.widget.extend({}, i)) + : (t[e] = i)) + return t + }), + (x.widget.bridge = function (n, e) { + var r = e.prototype.widgetFullName || n + x.fn[n] = function (i) { + var t = 'string' == typeof i, + s = a.call(arguments, 1), + o = this + return ( + t + ? this.length || 'instance' !== i + ? this.each(function () { + var t, + e = x.data(this, r) + return 'instance' === i + ? ((o = e), !1) + : e + ? 'function' != typeof e[i] || + '_' === i.charAt(0) + ? x.error( + "no such method '" + + i + + "' for " + + n + + ' widget instance' + ) + : (t = e[i].apply(e, s)) !== e && + void 0 !== t + ? ((o = + t && t.jquery + ? o.pushStack(t.get()) + : t), + !1) + : void 0 + : x.error( + 'cannot call methods on ' + + n + + " prior to initialization; attempted to call method '" + + i + + "'" + ) + }) + : (o = void 0) + : (s.length && + (i = x.widget.extend.apply(null, [i].concat(s))), + this.each(function () { + var t = x.data(this, r) + t + ? (t.option(i || {}), t._init && t._init()) + : x.data(this, r, new e(i, this)) + })), + o + ) + } + }), + (x.Widget = function () {}), + (x.Widget._childConstructors = []), + (x.Widget.prototype = { + widgetName: 'widget', + widgetEventPrefix: '', + defaultElement: '
', + options: { classes: {}, disabled: !1, create: null }, + _createWidget: function (t, e) { + ;(e = x(e || this.defaultElement || this)[0]), + (this.element = x(e)), + (this.uuid = i++), + (this.eventNamespace = '.' + this.widgetName + this.uuid), + (this.bindings = x()), + (this.hoverable = x()), + (this.focusable = x()), + (this.classesElementLookup = {}), + e !== this && + (x.data(e, this.widgetFullName, this), + this._on(!0, this.element, { + remove: function (t) { + t.target === e && this.destroy() + }, + }), + (this.document = x( + e.style ? e.ownerDocument : e.document || e + )), + (this.window = x( + this.document[0].defaultView || + this.document[0].parentWindow + ))), + (this.options = x.widget.extend( + {}, + this.options, + this._getCreateOptions(), + t + )), + this._create(), + this.options.disabled && + this._setOptionDisabled(this.options.disabled), + this._trigger('create', null, this._getCreateEventData()), + this._init() + }, + _getCreateOptions: function () { + return {} + }, + _getCreateEventData: x.noop, + _create: x.noop, + _init: x.noop, + destroy: function () { + var i = this + this._destroy(), + x.each(this.classesElementLookup, function (t, e) { + i._removeClass(e, t) + }), + this.element + .off(this.eventNamespace) + .removeData(this.widgetFullName), + this.widget() + .off(this.eventNamespace) + .removeAttr('aria-disabled'), + this.bindings.off(this.eventNamespace) + }, + _destroy: x.noop, + widget: function () { + return this.element + }, + option: function (t, e) { + var i, + s, + o, + n = t + if (0 === arguments.length) + return x.widget.extend({}, this.options) + if ('string' == typeof t) + if ( + ((n = {}), (t = (i = t.split('.')).shift()), i.length) + ) { + for ( + s = n[t] = x.widget.extend({}, this.options[t]), + o = 0; + o < i.length - 1; + o++ + ) + (s[i[o]] = s[i[o]] || {}), (s = s[i[o]]) + if (((t = i.pop()), 1 === arguments.length)) + return void 0 === s[t] ? null : s[t] + s[t] = e + } else { + if (1 === arguments.length) + return void 0 === this.options[t] + ? null + : this.options[t] + n[t] = e + } + return this._setOptions(n), this + }, + _setOptions: function (t) { + for (var e in t) this._setOption(e, t[e]) + return this + }, + _setOption: function (t, e) { + return ( + 'classes' === t && this._setOptionClasses(e), + (this.options[t] = e), + 'disabled' === t && this._setOptionDisabled(e), + this + ) + }, + _setOptionClasses: function (t) { + var e, i, s + for (e in t) + (s = this.classesElementLookup[e]), + t[e] !== this.options.classes[e] && + s && + s.length && + ((i = x(s.get())), + this._removeClass(s, e), + i.addClass( + this._classes({ + element: i, + keys: e, + classes: t, + add: !0, + }) + )) + }, + _setOptionDisabled: function (t) { + this._toggleClass( + this.widget(), + this.widgetFullName + '-disabled', + null, + !!t + ), + t && + (this._removeClass( + this.hoverable, + null, + 'ui-state-hover' + ), + this._removeClass( + this.focusable, + null, + 'ui-state-focus' + )) + }, + enable: function () { + return this._setOptions({ disabled: !1 }) + }, + disable: function () { + return this._setOptions({ disabled: !0 }) + }, + _classes: function (o) { + var n = [], + r = this + function t(t, e) { + for (var i, s = 0; s < t.length; s++) + (i = r.classesElementLookup[t[s]] || x()), + (i = o.add + ? ((function () { + var i = [] + o.element.each(function (t, e) { + x + .map( + r.classesElementLookup, + function (t) { + return t + } + ) + .some(function (t) { + return t.is(e) + }) || i.push(e) + }), + r._on(x(i), { + remove: '_untrackClassesElement', + }) + })(), + x( + x.uniqueSort( + i.get().concat(o.element.get()) + ) + )) + : x(i.not(o.element).get())), + (r.classesElementLookup[t[s]] = i), + n.push(t[s]), + e && o.classes[t[s]] && n.push(o.classes[t[s]]) + } + return ( + (o = x.extend( + { + element: this.element, + classes: this.options.classes || {}, + }, + o + )).keys && t(o.keys.match(/\S+/g) || [], !0), + o.extra && t(o.extra.match(/\S+/g) || []), + n.join(' ') + ) + }, + _untrackClassesElement: function (i) { + var s = this + x.each(s.classesElementLookup, function (t, e) { + ;-1 !== x.inArray(i.target, e) && + (s.classesElementLookup[t] = x(e.not(i.target).get())) + }), + this._off(x(i.target)) + }, + _removeClass: function (t, e, i) { + return this._toggleClass(t, e, i, !1) + }, + _addClass: function (t, e, i) { + return this._toggleClass(t, e, i, !0) + }, + _toggleClass: function (t, e, i, s) { + var o = 'string' == typeof t || null === t, + i = { + extra: o ? e : i, + keys: o ? t : e, + element: o ? this.element : t, + add: (s = 'boolean' == typeof s ? s : i), + } + return i.element.toggleClass(this._classes(i), s), this + }, + _on: function (o, n, t) { + var r, + a = this + 'boolean' != typeof o && ((t = n), (n = o), (o = !1)), + t + ? ((n = r = x(n)), + (this.bindings = this.bindings.add(n))) + : ((t = n), (n = this.element), (r = this.widget())), + x.each(t, function (t, e) { + function i() { + if ( + o || + (!0 !== a.options.disabled && + !x(this).hasClass('ui-state-disabled')) + ) + return ('string' == typeof e ? a[e] : e).apply( + a, + arguments + ) + } + 'string' != typeof e && + (i.guid = e.guid = e.guid || i.guid || x.guid++) + var s = t.match(/^([\w:-]*)\s*(.*)$/), + t = s[1] + a.eventNamespace, + s = s[2] + s ? r.on(t, s, i) : n.on(t, i) + }) + }, + _off: function (t, e) { + ;(e = + (e || '').split(' ').join(this.eventNamespace + ' ') + + this.eventNamespace), + t.off(e), + (this.bindings = x(this.bindings.not(t).get())), + (this.focusable = x(this.focusable.not(t).get())), + (this.hoverable = x(this.hoverable.not(t).get())) + }, + _delay: function (t, e) { + var i = this + return setTimeout(function () { + return ('string' == typeof t ? i[t] : t).apply(i, arguments) + }, e || 0) + }, + _hoverable: function (t) { + ;(this.hoverable = this.hoverable.add(t)), + this._on(t, { + mouseenter: function (t) { + this._addClass( + x(t.currentTarget), + null, + 'ui-state-hover' + ) + }, + mouseleave: function (t) { + this._removeClass( + x(t.currentTarget), + null, + 'ui-state-hover' + ) + }, + }) + }, + _focusable: function (t) { + ;(this.focusable = this.focusable.add(t)), + this._on(t, { + focusin: function (t) { + this._addClass( + x(t.currentTarget), + null, + 'ui-state-focus' + ) + }, + focusout: function (t) { + this._removeClass( + x(t.currentTarget), + null, + 'ui-state-focus' + ) + }, + }) + }, + _trigger: function (t, e, i) { + var s, + o, + n = this.options[t] + if ( + ((i = i || {}), + ((e = x.Event(e)).type = ( + t === this.widgetEventPrefix + ? t + : this.widgetEventPrefix + t + ).toLowerCase()), + (e.target = this.element[0]), + (o = e.originalEvent)) + ) + for (s in o) s in e || (e[s] = o[s]) + return ( + this.element.trigger(e, i), + !( + ('function' == typeof n && + !1 === n.apply(this.element[0], [e].concat(i))) || + e.isDefaultPrevented() + ) + ) + }, + }), + x.each({ show: 'fadeIn', hide: 'fadeOut' }, function (n, r) { + x.Widget.prototype['_' + n] = function (e, t, i) { + var s, + o = (t = 'string' == typeof t ? { effect: t } : t) + ? (!0 !== t && 'number' != typeof t && t.effect) || r + : n + 'number' == typeof (t = t || {}) + ? (t = { duration: t }) + : !0 === t && (t = {}), + (s = !x.isEmptyObject(t)), + (t.complete = i), + t.delay && e.delay(t.delay), + s && x.effects && x.effects.effect[o] + ? e[n](t) + : o !== n && e[o] + ? e[o](t.duration, t.easing, i) + : e.queue(function (t) { + x(this)[n](), i && i.call(e[0]), t() + }) + } + }) + var s, P, C, n, h, l, c, p, z + x.widget + function T(t, e, i) { + return [ + parseFloat(t[0]) * (p.test(t[0]) ? e / 100 : 1), + parseFloat(t[1]) * (p.test(t[1]) ? i / 100 : 1), + ] + } + function k(t, e) { + return parseInt(x.css(t, e), 10) || 0 + } + function H(t) { + return null != t && t === t.window + } + ;(P = Math.max), + (C = Math.abs), + (n = /left|center|right/), + (h = /top|center|bottom/), + (l = /[\+\-]\d+(\.[\d]+)?%?/), + (c = /^\w+/), + (p = /%$/), + (z = x.fn.position), + (x.position = { + scrollbarWidth: function () { + if (void 0 !== s) return s + var t, + e = x( + "
" + ), + i = e.children()[0] + return ( + x('body').append(e), + (t = i.offsetWidth), + e.css('overflow', 'scroll'), + t === (i = i.offsetWidth) && (i = e[0].clientWidth), + e.remove(), + (s = t - i) + ) + }, + getScrollInfo: function (t) { + var e = + t.isWindow || t.isDocument + ? '' + : t.element.css('overflow-x'), + i = + t.isWindow || t.isDocument + ? '' + : t.element.css('overflow-y'), + e = + 'scroll' === e || + ('auto' === e && t.width < t.element[0].scrollWidth) + return { + width: + 'scroll' === i || + ('auto' === i && t.height < t.element[0].scrollHeight) + ? x.position.scrollbarWidth() + : 0, + height: e ? x.position.scrollbarWidth() : 0, + } + }, + getWithinInfo: function (t) { + var e = x(t || window), + i = H(e[0]), + s = !!e[0] && 9 === e[0].nodeType + return { + element: e, + isWindow: i, + isDocument: s, + offset: !i && !s ? x(t).offset() : { left: 0, top: 0 }, + scrollLeft: e.scrollLeft(), + scrollTop: e.scrollTop(), + width: e.outerWidth(), + height: e.outerHeight(), + } + }, + }), + (x.fn.position = function (p) { + if (!p || !p.of) return z.apply(this, arguments) + var u, + d, + f, + g, + m, + t, + v = + 'string' == typeof (p = x.extend({}, p)).of + ? x(document).find(p.of) + : x(p.of), + _ = x.position.getWithinInfo(p.within), + b = x.position.getScrollInfo(_), + y = (p.collision || 'flip').split(' '), + w = {}, + e = + 9 === (t = (e = v)[0]).nodeType + ? { + width: e.width(), + height: e.height(), + offset: { top: 0, left: 0 }, + } + : H(t) + ? { + width: e.width(), + height: e.height(), + offset: { + top: e.scrollTop(), + left: e.scrollLeft(), + }, + } + : t.preventDefault + ? { + width: 0, + height: 0, + offset: { top: t.pageY, left: t.pageX }, + } + : { + width: e.outerWidth(), + height: e.outerHeight(), + offset: e.offset(), + } + return ( + v[0].preventDefault && (p.at = 'left top'), + (d = e.width), + (f = e.height), + (m = x.extend({}, (g = e.offset))), + x.each(['my', 'at'], function () { + var t, + e, + i = (p[this] || '').split(' ') + ;((i = + 1 === i.length + ? n.test(i[0]) + ? i.concat(['center']) + : h.test(i[0]) + ? ['center'].concat(i) + : ['center', 'center'] + : i)[0] = n.test(i[0]) ? i[0] : 'center'), + (i[1] = h.test(i[1]) ? i[1] : 'center'), + (t = l.exec(i[0])), + (e = l.exec(i[1])), + (w[this] = [t ? t[0] : 0, e ? e[0] : 0]), + (p[this] = [c.exec(i[0])[0], c.exec(i[1])[0]]) + }), + 1 === y.length && (y[1] = y[0]), + 'right' === p.at[0] + ? (m.left += d) + : 'center' === p.at[0] && (m.left += d / 2), + 'bottom' === p.at[1] + ? (m.top += f) + : 'center' === p.at[1] && (m.top += f / 2), + (u = T(w.at, d, f)), + (m.left += u[0]), + (m.top += u[1]), + this.each(function () { + var i, + t, + r = x(this), + a = r.outerWidth(), + h = r.outerHeight(), + e = k(this, 'marginLeft'), + s = k(this, 'marginTop'), + o = a + e + k(this, 'marginRight') + b.width, + n = h + s + k(this, 'marginBottom') + b.height, + l = x.extend({}, m), + c = T(w.my, r.outerWidth(), r.outerHeight()) + 'right' === p.my[0] + ? (l.left -= a) + : 'center' === p.my[0] && (l.left -= a / 2), + 'bottom' === p.my[1] + ? (l.top -= h) + : 'center' === p.my[1] && (l.top -= h / 2), + (l.left += c[0]), + (l.top += c[1]), + (i = { marginLeft: e, marginTop: s }), + x.each(['left', 'top'], function (t, e) { + x.ui.position[y[t]] && + x.ui.position[y[t]][e](l, { + targetWidth: d, + targetHeight: f, + elemWidth: a, + elemHeight: h, + collisionPosition: i, + collisionWidth: o, + collisionHeight: n, + offset: [u[0] + c[0], u[1] + c[1]], + my: p.my, + at: p.at, + within: _, + elem: r, + }) + }), + p.using && + (t = function (t) { + var e = g.left - l.left, + i = e + d - a, + s = g.top - l.top, + o = s + f - h, + n = { + target: { + element: v, + left: g.left, + top: g.top, + width: d, + height: f, + }, + element: { + element: r, + left: l.left, + top: l.top, + width: a, + height: h, + }, + horizontal: + i < 0 + ? 'left' + : 0 < e + ? 'right' + : 'center', + vertical: + o < 0 + ? 'top' + : 0 < s + ? 'bottom' + : 'middle', + } + d < a && + C(e + i) < d && + (n.horizontal = 'center'), + f < h && + C(s + o) < f && + (n.vertical = 'middle'), + P(C(e), C(i)) > P(C(s), C(o)) + ? (n.important = 'horizontal') + : (n.important = 'vertical'), + p.using.call(this, t, n) + }), + r.offset(x.extend(l, { using: t })) + }) + ) + }), + (x.ui.position = { + fit: { + left: function (t, e) { + var i = e.within, + s = i.isWindow ? i.scrollLeft : i.offset.left, + o = i.width, + n = t.left - e.collisionPosition.marginLeft, + r = s - n, + a = n + e.collisionWidth - o - s + e.collisionWidth > o + ? 0 < r && a <= 0 + ? ((i = t.left + r + e.collisionWidth - o - s), + (t.left += r - i)) + : (t.left = + !(0 < a && r <= 0) && a < r + ? s + o - e.collisionWidth + : s) + : 0 < r + ? (t.left += r) + : 0 < a + ? (t.left -= a) + : (t.left = P(t.left - n, t.left)) + }, + top: function (t, e) { + var i = e.within, + s = i.isWindow ? i.scrollTop : i.offset.top, + o = e.within.height, + n = t.top - e.collisionPosition.marginTop, + r = s - n, + a = n + e.collisionHeight - o - s + e.collisionHeight > o + ? 0 < r && a <= 0 + ? ((i = t.top + r + e.collisionHeight - o - s), + (t.top += r - i)) + : (t.top = + !(0 < a && r <= 0) && a < r + ? s + o - e.collisionHeight + : s) + : 0 < r + ? (t.top += r) + : 0 < a + ? (t.top -= a) + : (t.top = P(t.top - n, t.top)) + }, + }, + flip: { + left: function (t, e) { + var i = e.within, + s = i.offset.left + i.scrollLeft, + o = i.width, + n = i.isWindow ? i.scrollLeft : i.offset.left, + r = t.left - e.collisionPosition.marginLeft, + a = r - n, + h = r + e.collisionWidth - o - n, + l = + 'left' === e.my[0] + ? -e.elemWidth + : 'right' === e.my[0] + ? e.elemWidth + : 0, + i = + 'left' === e.at[0] + ? e.targetWidth + : 'right' === e.at[0] + ? -e.targetWidth + : 0, + r = -2 * e.offset[0] + a < 0 + ? ((s = t.left + l + i + r + e.collisionWidth - o - s) < + 0 || + s < C(a)) && + (t.left += l + i + r) + : 0 < h && + (0 < + (n = + t.left - + e.collisionPosition.marginLeft + + l + + i + + r - + n) || + C(n) < h) && + (t.left += l + i + r) + }, + top: function (t, e) { + var i = e.within, + s = i.offset.top + i.scrollTop, + o = i.height, + n = i.isWindow ? i.scrollTop : i.offset.top, + r = t.top - e.collisionPosition.marginTop, + a = r - n, + h = r + e.collisionHeight - o - n, + l = + 'top' === e.my[1] + ? -e.elemHeight + : 'bottom' === e.my[1] + ? e.elemHeight + : 0, + i = + 'top' === e.at[1] + ? e.targetHeight + : 'bottom' === e.at[1] + ? -e.targetHeight + : 0, + r = -2 * e.offset[1] + a < 0 + ? ((s = t.top + l + i + r + e.collisionHeight - o - s) < + 0 || + s < C(a)) && + (t.top += l + i + r) + : 0 < h && + (0 < + (n = + t.top - + e.collisionPosition.marginTop + + l + + i + + r - + n) || + C(n) < h) && + (t.top += l + i + r) + }, + }, + flipfit: { + left: function () { + x.ui.position.flip.left.apply(this, arguments), + x.ui.position.fit.left.apply(this, arguments) + }, + top: function () { + x.ui.position.flip.top.apply(this, arguments), + x.ui.position.fit.top.apply(this, arguments) + }, + }, + }) + var t + x.ui.position, + x.extend(x.expr.pseudos, { + data: x.expr.createPseudo + ? x.expr.createPseudo(function (e) { + return function (t) { + return !!x.data(t, e) + } + }) + : function (t, e, i) { + return !!x.data(t, i[3]) + }, + }), + x.fn.extend({ + disableSelection: + ((t = + 'onselectstart' in document.createElement('div') + ? 'selectstart' + : 'mousedown'), + function () { + return this.on(t + '.ui-disableSelection', function (t) { + t.preventDefault() + }) + }), + enableSelection: function () { + return this.off('.ui-disableSelection') + }, + }) + ;(x.ui.focusable = function (t, e) { + var i, + s, + o, + n, + r = t.nodeName.toLowerCase() + return 'area' === r + ? ((s = (i = t.parentNode).name), + !(!t.href || !s || 'map' !== i.nodeName.toLowerCase()) && + 0 < (s = x("img[usemap='#" + s + "']")).length && + s.is(':visible')) + : (/^(input|select|textarea|button|object)$/.test(r) + ? (o = !t.disabled) && + (n = x(t).closest('fieldset')[0]) && + (o = !n.disabled) + : (o = ('a' === r && t.href) || e), + o && + x(t).is(':visible') && + (function (t) { + var e = t.css('visibility') + for (; 'inherit' === e; ) + (t = t.parent()), (e = t.css('visibility')) + return 'visible' === e + })(x(t))) + }), + x.extend(x.expr.pseudos, { + focusable: function (t) { + return x.ui.focusable(t, null != x.attr(t, 'tabindex')) + }, + }) + var e, u + x.ui.focusable, + (x.fn._form = function () { + return 'string' == typeof this[0].form + ? this.closest('form') + : x(this[0].form) + }), + (x.ui.formResetMixin = { + _formResetHandler: function () { + var e = x(this) + setTimeout(function () { + var t = e.data('ui-form-reset-instances') + x.each(t, function () { + this.refresh() + }) + }) + }, + _bindFormResetHandler: function () { + var t + ;(this.form = this.element._form()), + this.form.length && + ((t = this.form.data('ui-form-reset-instances') || []) + .length || + this.form.on( + 'reset.ui-form-reset', + this._formResetHandler + ), + t.push(this), + this.form.data('ui-form-reset-instances', t)) + }, + _unbindFormResetHandler: function () { + var t + this.form.length && + ((t = this.form.data('ui-form-reset-instances')).splice( + x.inArray(this, t), + 1 + ), + t.length + ? this.form.data('ui-form-reset-instances', t) + : this.form + .removeData('ui-form-reset-instances') + .off('reset.ui-form-reset')) + }, + }) + x.expr.pseudos || (x.expr.pseudos = x.expr[':']), + x.uniqueSort || (x.uniqueSort = x.unique), + x.escapeSelector || + ((e = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g), + (u = function (t, e) { + return e + ? '\0' === t + ? '�' + : t.slice(0, -1) + + '\\' + + t.charCodeAt(t.length - 1).toString(16) + + ' ' + : '\\' + t + }), + (x.escapeSelector = function (t) { + return (t + '').replace(e, u) + })), + (x.fn.even && x.fn.odd) || + x.fn.extend({ + even: function () { + return this.filter(function (t) { + return t % 2 == 0 + }) + }, + odd: function () { + return this.filter(function (t) { + return t % 2 == 1 + }) + }, + }) + ;(x.ui.keyCode = { + BACKSPACE: 8, + COMMA: 188, + DELETE: 46, + DOWN: 40, + END: 35, + ENTER: 13, + ESCAPE: 27, + HOME: 36, + LEFT: 37, + PAGE_DOWN: 34, + PAGE_UP: 33, + PERIOD: 190, + RIGHT: 39, + SPACE: 32, + TAB: 9, + UP: 38, + }), + (x.fn.labels = function () { + var t, e, i + return this.length + ? this[0].labels && this[0].labels.length + ? this.pushStack(this[0].labels) + : ((e = this.eq(0).parents('label')), + (t = this.attr('id')) && + ((i = (i = this.eq(0).parents().last()).add( + (i.length ? i : this).siblings() + )), + (t = "label[for='" + x.escapeSelector(t) + "']"), + (e = e.add(i.find(t).addBack(t)))), + this.pushStack(e)) + : this.pushStack([]) + }), + (x.fn.scrollParent = function (t) { + var e = this.css('position'), + i = 'absolute' === e, + s = t ? /(auto|scroll|hidden)/ : /(auto|scroll)/, + t = this.parents() + .filter(function () { + var t = x(this) + return ( + (!i || 'static' !== t.css('position')) && + s.test( + t.css('overflow') + + t.css('overflow-y') + + t.css('overflow-x') + ) + ) + }) + .eq(0) + return 'fixed' !== e && t.length + ? t + : x(this[0].ownerDocument || document) + }), + x.extend(x.expr.pseudos, { + tabbable: function (t) { + var e = x.attr(t, 'tabindex'), + i = null != e + return (!i || 0 <= e) && x.ui.focusable(t, i) + }, + }), + x.fn.extend({ + uniqueId: + ((d = 0), + function () { + return this.each(function () { + this.id || (this.id = 'ui-id-' + ++d) + }) + }), + removeUniqueId: function () { + return this.each(function () { + ;/^ui-id-\d+$/.test(this.id) && x(this).removeAttr('id') + }) + }, + }), + (x.ui.ie = !!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase())) + var d, + f = !1 + x(document).on('mouseup', function () { + f = !1 + }) + x.widget('ui.mouse', { + version: '1.13.1', + options: { + cancel: 'input, textarea, button, select, option', + distance: 1, + delay: 0, + }, + _mouseInit: function () { + var e = this + this.element + .on('mousedown.' + this.widgetName, function (t) { + return e._mouseDown(t) + }) + .on('click.' + this.widgetName, function (t) { + if ( + !0 === + x.data(t.target, e.widgetName + '.preventClickEvent') + ) + return ( + x.removeData( + t.target, + e.widgetName + '.preventClickEvent' + ), + t.stopImmediatePropagation(), + !1 + ) + }), + (this.started = !1) + }, + _mouseDestroy: function () { + this.element.off('.' + this.widgetName), + this._mouseMoveDelegate && + this.document + .off( + 'mousemove.' + this.widgetName, + this._mouseMoveDelegate + ) + .off( + 'mouseup.' + this.widgetName, + this._mouseUpDelegate + ) + }, + _mouseDown: function (t) { + if (!f) { + ;(this._mouseMoved = !1), + this._mouseStarted && this._mouseUp(t), + (this._mouseDownEvent = t) + var e = this, + i = 1 === t.which, + s = + !( + 'string' != typeof this.options.cancel || + !t.target.nodeName + ) && x(t.target).closest(this.options.cancel).length + return i && !s && this._mouseCapture(t) + ? ((this.mouseDelayMet = !this.options.delay), + this.mouseDelayMet || + (this._mouseDelayTimer = setTimeout(function () { + e.mouseDelayMet = !0 + }, this.options.delay)), + this._mouseDistanceMet(t) && + this._mouseDelayMet(t) && + ((this._mouseStarted = !1 !== this._mouseStart(t)), + !this._mouseStarted) + ? (t.preventDefault(), !0) + : (!0 === + x.data( + t.target, + this.widgetName + '.preventClickEvent' + ) && + x.removeData( + t.target, + this.widgetName + '.preventClickEvent' + ), + (this._mouseMoveDelegate = function (t) { + return e._mouseMove(t) + }), + (this._mouseUpDelegate = function (t) { + return e._mouseUp(t) + }), + this.document + .on( + 'mousemove.' + this.widgetName, + this._mouseMoveDelegate + ) + .on( + 'mouseup.' + this.widgetName, + this._mouseUpDelegate + ), + t.preventDefault(), + (f = !0))) + : !0 + } + }, + _mouseMove: function (t) { + if (this._mouseMoved) { + if ( + x.ui.ie && + (!document.documentMode || document.documentMode < 9) && + !t.button + ) + return this._mouseUp(t) + if (!t.which) + if ( + t.originalEvent.altKey || + t.originalEvent.ctrlKey || + t.originalEvent.metaKey || + t.originalEvent.shiftKey + ) + this.ignoreMissingWhich = !0 + else if (!this.ignoreMissingWhich) return this._mouseUp(t) + } + return ( + (t.which || t.button) && (this._mouseMoved = !0), + this._mouseStarted + ? (this._mouseDrag(t), t.preventDefault()) + : (this._mouseDistanceMet(t) && + this._mouseDelayMet(t) && + ((this._mouseStarted = + !1 !== this._mouseStart(this._mouseDownEvent, t)), + this._mouseStarted + ? this._mouseDrag(t) + : this._mouseUp(t)), + !this._mouseStarted) + ) + }, + _mouseUp: function (t) { + this.document + .off('mousemove.' + this.widgetName, this._mouseMoveDelegate) + .off('mouseup.' + this.widgetName, this._mouseUpDelegate), + this._mouseStarted && + ((this._mouseStarted = !1), + t.target === this._mouseDownEvent.target && + x.data( + t.target, + this.widgetName + '.preventClickEvent', + !0 + ), + this._mouseStop(t)), + this._mouseDelayTimer && + (clearTimeout(this._mouseDelayTimer), + delete this._mouseDelayTimer), + (this.ignoreMissingWhich = !1), + (f = !1), + t.preventDefault() + }, + _mouseDistanceMet: function (t) { + return ( + Math.max( + Math.abs(this._mouseDownEvent.pageX - t.pageX), + Math.abs(this._mouseDownEvent.pageY - t.pageY) + ) >= this.options.distance + ) + }, + _mouseDelayMet: function () { + return this.mouseDelayMet + }, + _mouseStart: function () {}, + _mouseDrag: function () {}, + _mouseStop: function () {}, + _mouseCapture: function () { + return !0 + }, + }), + (x.ui.plugin = { + add: function (t, e, i) { + var s, + o = x.ui[t].prototype + for (s in i) + (o.plugins[s] = o.plugins[s] || []), + o.plugins[s].push([e, i[s]]) + }, + call: function (t, e, i, s) { + var o, + n = t.plugins[e] + if ( + n && + (s || + (t.element[0].parentNode && + 11 !== t.element[0].parentNode.nodeType)) + ) + for (o = 0; o < n.length; o++) + t.options[n[o][0]] && n[o][1].apply(t.element, i) + }, + }), + (x.ui.safeActiveElement = function (e) { + var i + try { + i = e.activeElement + } catch (t) { + i = e.body + } + return (i = !(i = i || e.body).nodeName ? e.body : i) + }), + (x.ui.safeBlur = function (t) { + t && 'body' !== t.nodeName.toLowerCase() && x(t).trigger('blur') + }) + x.widget('ui.draggable', x.ui.mouse, { + version: '1.13.1', + widgetEventPrefix: 'drag', + options: { + addClasses: !0, + appendTo: 'parent', + axis: !1, + connectToSortable: !1, + containment: !1, + cursor: 'auto', + cursorAt: !1, + grid: !1, + handle: !1, + helper: 'original', + iframeFix: !1, + opacity: !1, + refreshPositions: !1, + revert: !1, + revertDuration: 500, + scope: 'default', + scroll: !0, + scrollSensitivity: 20, + scrollSpeed: 20, + snap: !1, + snapMode: 'both', + snapTolerance: 20, + stack: !1, + zIndex: !1, + drag: null, + start: null, + stop: null, + }, + _create: function () { + 'original' === this.options.helper && this._setPositionRelative(), + this.options.addClasses && this._addClass('ui-draggable'), + this._setHandleClassName(), + this._mouseInit() + }, + _setOption: function (t, e) { + this._super(t, e), + 'handle' === t && + (this._removeHandleClassName(), this._setHandleClassName()) + }, + _destroy: function () { + ;(this.helper || this.element).is('.ui-draggable-dragging') + ? (this.destroyOnClear = !0) + : (this._removeHandleClassName(), this._mouseDestroy()) + }, + _mouseCapture: function (t) { + var e = this.options + return ( + !( + this.helper || + e.disabled || + 0 < x(t.target).closest('.ui-resizable-handle').length + ) && + ((this.handle = this._getHandle(t)), + !!this.handle && + (this._blurActiveElement(t), + this._blockFrames( + !0 === e.iframeFix ? 'iframe' : e.iframeFix + ), + !0)) + ) + }, + _blockFrames: function (t) { + this.iframeBlocks = this.document.find(t).map(function () { + var t = x(this) + return x('
') + .css('position', 'absolute') + .appendTo(t.parent()) + .outerWidth(t.outerWidth()) + .outerHeight(t.outerHeight()) + .offset(t.offset())[0] + }) + }, + _unblockFrames: function () { + this.iframeBlocks && + (this.iframeBlocks.remove(), delete this.iframeBlocks) + }, + _blurActiveElement: function (t) { + var e = x.ui.safeActiveElement(this.document[0]) + x(t.target).closest(e).length || x.ui.safeBlur(e) + }, + _mouseStart: function (t) { + var e = this.options + return ( + (this.helper = this._createHelper(t)), + this._addClass(this.helper, 'ui-draggable-dragging'), + this._cacheHelperProportions(), + x.ui.ddmanager && (x.ui.ddmanager.current = this), + this._cacheMargins(), + (this.cssPosition = this.helper.css('position')), + (this.scrollParent = this.helper.scrollParent(!0)), + (this.offsetParent = this.helper.offsetParent()), + (this.hasFixedAncestor = + 0 < + this.helper.parents().filter(function () { + return 'fixed' === x(this).css('position') + }).length), + (this.positionAbs = this.element.offset()), + this._refreshOffsets(t), + (this.originalPosition = this.position = + this._generatePosition(t, !1)), + (this.originalPageX = t.pageX), + (this.originalPageY = t.pageY), + e.cursorAt && this._adjustOffsetFromHelper(e.cursorAt), + this._setContainment(), + !1 === this._trigger('start', t) + ? (this._clear(), !1) + : (this._cacheHelperProportions(), + x.ui.ddmanager && + !e.dropBehaviour && + x.ui.ddmanager.prepareOffsets(this, t), + this._mouseDrag(t, !0), + x.ui.ddmanager && x.ui.ddmanager.dragStart(this, t), + !0) + ) + }, + _refreshOffsets: function (t) { + ;(this.offset = { + top: this.positionAbs.top - this.margins.top, + left: this.positionAbs.left - this.margins.left, + scroll: !1, + parent: this._getParentOffset(), + relative: this._getRelativeOffset(), + }), + (this.offset.click = { + left: t.pageX - this.offset.left, + top: t.pageY - this.offset.top, + }) + }, + _mouseDrag: function (t, e) { + if ( + (this.hasFixedAncestor && + (this.offset.parent = this._getParentOffset()), + (this.position = this._generatePosition(t, !0)), + (this.positionAbs = this._convertPositionTo('absolute')), + !e) + ) { + e = this._uiHash() + if (!1 === this._trigger('drag', t, e)) + return this._mouseUp(new x.Event('mouseup', t)), !1 + this.position = e.position + } + return ( + (this.helper[0].style.left = this.position.left + 'px'), + (this.helper[0].style.top = this.position.top + 'px'), + x.ui.ddmanager && x.ui.ddmanager.drag(this, t), + !1 + ) + }, + _mouseStop: function (t) { + var e = this, + i = !1 + return ( + x.ui.ddmanager && + !this.options.dropBehaviour && + (i = x.ui.ddmanager.drop(this, t)), + this.dropped && ((i = this.dropped), (this.dropped = !1)), + ('invalid' === this.options.revert && !i) || + ('valid' === this.options.revert && i) || + !0 === this.options.revert || + ('function' == typeof this.options.revert && + this.options.revert.call(this.element, i)) + ? x(this.helper).animate( + this.originalPosition, + parseInt(this.options.revertDuration, 10), + function () { + !1 !== e._trigger('stop', t) && e._clear() + } + ) + : !1 !== this._trigger('stop', t) && this._clear(), + !1 + ) + }, + _mouseUp: function (t) { + return ( + this._unblockFrames(), + x.ui.ddmanager && x.ui.ddmanager.dragStop(this, t), + this.handleElement.is(t.target) && + this.element.trigger('focus'), + x.ui.mouse.prototype._mouseUp.call(this, t) + ) + }, + cancel: function () { + return ( + this.helper.is('.ui-draggable-dragging') + ? this._mouseUp( + new x.Event('mouseup', { target: this.element[0] }) + ) + : this._clear(), + this + ) + }, + _getHandle: function (t) { + return ( + !this.options.handle || + !!x(t.target).closest(this.element.find(this.options.handle)) + .length + ) + }, + _setHandleClassName: function () { + ;(this.handleElement = this.options.handle + ? this.element.find(this.options.handle) + : this.element), + this._addClass(this.handleElement, 'ui-draggable-handle') + }, + _removeHandleClassName: function () { + this._removeClass(this.handleElement, 'ui-draggable-handle') + }, + _createHelper: function (t) { + var e = this.options, + i = 'function' == typeof e.helper, + t = i + ? x(e.helper.apply(this.element[0], [t])) + : 'clone' === e.helper + ? this.element.clone().removeAttr('id') + : this.element + return ( + t.parents('body').length || + t.appendTo( + 'parent' === e.appendTo + ? this.element[0].parentNode + : e.appendTo + ), + i && t[0] === this.element[0] && this._setPositionRelative(), + t[0] === this.element[0] || + /(fixed|absolute)/.test(t.css('position')) || + t.css('position', 'absolute'), + t + ) + }, + _setPositionRelative: function () { + ;/^(?:r|a|f)/.test(this.element.css('position')) || + (this.element[0].style.position = 'relative') + }, + _adjustOffsetFromHelper: function (t) { + 'string' == typeof t && (t = t.split(' ')), + 'left' in + (t = Array.isArray(t) + ? { left: +t[0], top: +t[1] || 0 } + : t) && + (this.offset.click.left = t.left + this.margins.left), + 'right' in t && + (this.offset.click.left = + this.helperProportions.width - + t.right + + this.margins.left), + 'top' in t && + (this.offset.click.top = t.top + this.margins.top), + 'bottom' in t && + (this.offset.click.top = + this.helperProportions.height - + t.bottom + + this.margins.top) + }, + _isRootNode: function (t) { + return /(html|body)/i.test(t.tagName) || t === this.document[0] + }, + _getParentOffset: function () { + var t = this.offsetParent.offset(), + e = this.document[0] + return ( + 'absolute' === this.cssPosition && + this.scrollParent[0] !== e && + x.contains(this.scrollParent[0], this.offsetParent[0]) && + ((t.left += this.scrollParent.scrollLeft()), + (t.top += this.scrollParent.scrollTop())), + { + top: + (t = this._isRootNode(this.offsetParent[0]) + ? { top: 0, left: 0 } + : t).top + + (parseInt( + this.offsetParent.css('borderTopWidth'), + 10 + ) || 0), + left: + t.left + + (parseInt( + this.offsetParent.css('borderLeftWidth'), + 10 + ) || 0), + } + ) + }, + _getRelativeOffset: function () { + if ('relative' !== this.cssPosition) return { top: 0, left: 0 } + var t = this.element.position(), + e = this._isRootNode(this.scrollParent[0]) + return { + top: + t.top - + (parseInt(this.helper.css('top'), 10) || 0) + + (e ? 0 : this.scrollParent.scrollTop()), + left: + t.left - + (parseInt(this.helper.css('left'), 10) || 0) + + (e ? 0 : this.scrollParent.scrollLeft()), + } + }, + _cacheMargins: function () { + this.margins = { + left: parseInt(this.element.css('marginLeft'), 10) || 0, + top: parseInt(this.element.css('marginTop'), 10) || 0, + right: parseInt(this.element.css('marginRight'), 10) || 0, + bottom: parseInt(this.element.css('marginBottom'), 10) || 0, + } + }, + _cacheHelperProportions: function () { + this.helperProportions = { + width: this.helper.outerWidth(), + height: this.helper.outerHeight(), + } + }, + _setContainment: function () { + var t, + e, + i, + s = this.options, + o = this.document[0] + ;(this.relativeContainer = null), + s.containment + ? 'window' !== s.containment + ? 'document' !== s.containment + ? s.containment.constructor !== Array + ? ('parent' === s.containment && + (s.containment = + this.helper[0].parentNode), + (i = (e = x(s.containment))[0]) && + ((t = /(scroll|auto)/.test( + e.css('overflow') + )), + (this.containment = [ + (parseInt( + e.css('borderLeftWidth'), + 10 + ) || 0) + + (parseInt( + e.css('paddingLeft'), + 10 + ) || 0), + (parseInt( + e.css('borderTopWidth'), + 10 + ) || 0) + + (parseInt( + e.css('paddingTop'), + 10 + ) || 0), + (t + ? Math.max( + i.scrollWidth, + i.offsetWidth + ) + : i.offsetWidth) - + (parseInt( + e.css('borderRightWidth'), + 10 + ) || 0) - + (parseInt( + e.css('paddingRight'), + 10 + ) || 0) - + this.helperProportions.width - + this.margins.left - + this.margins.right, + (t + ? Math.max( + i.scrollHeight, + i.offsetHeight + ) + : i.offsetHeight) - + (parseInt( + e.css('borderBottomWidth'), + 10 + ) || 0) - + (parseInt( + e.css('paddingBottom'), + 10 + ) || 0) - + this.helperProportions.height - + this.margins.top - + this.margins.bottom, + ]), + (this.relativeContainer = e))) + : (this.containment = s.containment) + : (this.containment = [ + 0, + 0, + x(o).width() - + this.helperProportions.width - + this.margins.left, + (x(o).height() || + o.body.parentNode.scrollHeight) - + this.helperProportions.height - + this.margins.top, + ]) + : (this.containment = [ + x(window).scrollLeft() - + this.offset.relative.left - + this.offset.parent.left, + x(window).scrollTop() - + this.offset.relative.top - + this.offset.parent.top, + x(window).scrollLeft() + + x(window).width() - + this.helperProportions.width - + this.margins.left, + x(window).scrollTop() + + (x(window).height() || + o.body.parentNode.scrollHeight) - + this.helperProportions.height - + this.margins.top, + ]) + : (this.containment = null) + }, + _convertPositionTo: function (t, e) { + e = e || this.position + var i = 'absolute' === t ? 1 : -1, + t = this._isRootNode(this.scrollParent[0]) + return { + top: + e.top + + this.offset.relative.top * i + + this.offset.parent.top * i - + ('fixed' === this.cssPosition + ? -this.offset.scroll.top + : t + ? 0 + : this.offset.scroll.top) * + i, + left: + e.left + + this.offset.relative.left * i + + this.offset.parent.left * i - + ('fixed' === this.cssPosition + ? -this.offset.scroll.left + : t + ? 0 + : this.offset.scroll.left) * + i, + } + }, + _generatePosition: function (t, e) { + var i, + s = this.options, + o = this._isRootNode(this.scrollParent[0]), + n = t.pageX, + r = t.pageY + return ( + (o && this.offset.scroll) || + (this.offset.scroll = { + top: this.scrollParent.scrollTop(), + left: this.scrollParent.scrollLeft(), + }), + e && + (this.containment && + ((i = this.relativeContainer + ? ((i = this.relativeContainer.offset()), + [ + this.containment[0] + i.left, + this.containment[1] + i.top, + this.containment[2] + i.left, + this.containment[3] + i.top, + ]) + : this.containment), + t.pageX - this.offset.click.left < i[0] && + (n = i[0] + this.offset.click.left), + t.pageY - this.offset.click.top < i[1] && + (r = i[1] + this.offset.click.top), + t.pageX - this.offset.click.left > i[2] && + (n = i[2] + this.offset.click.left), + t.pageY - this.offset.click.top > i[3] && + (r = i[3] + this.offset.click.top)), + s.grid && + ((t = s.grid[1] + ? this.originalPageY + + Math.round((r - this.originalPageY) / s.grid[1]) * + s.grid[1] + : this.originalPageY), + (r = + !i || + t - this.offset.click.top >= i[1] || + t - this.offset.click.top > i[3] + ? t + : t - this.offset.click.top >= i[1] + ? t - s.grid[1] + : t + s.grid[1]), + (t = s.grid[0] + ? this.originalPageX + + Math.round((n - this.originalPageX) / s.grid[0]) * + s.grid[0] + : this.originalPageX), + (n = + !i || + t - this.offset.click.left >= i[0] || + t - this.offset.click.left > i[2] + ? t + : t - this.offset.click.left >= i[0] + ? t - s.grid[0] + : t + s.grid[0])), + 'y' === s.axis && (n = this.originalPageX), + 'x' === s.axis && (r = this.originalPageY)), + { + top: + r - + this.offset.click.top - + this.offset.relative.top - + this.offset.parent.top + + ('fixed' === this.cssPosition + ? -this.offset.scroll.top + : o + ? 0 + : this.offset.scroll.top), + left: + n - + this.offset.click.left - + this.offset.relative.left - + this.offset.parent.left + + ('fixed' === this.cssPosition + ? -this.offset.scroll.left + : o + ? 0 + : this.offset.scroll.left), + } + ) + }, + _clear: function () { + this._removeClass(this.helper, 'ui-draggable-dragging'), + this.helper[0] === this.element[0] || + this.cancelHelperRemoval || + this.helper.remove(), + (this.helper = null), + (this.cancelHelperRemoval = !1), + this.destroyOnClear && this.destroy() + }, + _trigger: function (t, e, i) { + return ( + (i = i || this._uiHash()), + x.ui.plugin.call(this, t, [e, i, this], !0), + /^(drag|start|stop)/.test(t) && + ((this.positionAbs = this._convertPositionTo('absolute')), + (i.offset = this.positionAbs)), + x.Widget.prototype._trigger.call(this, t, e, i) + ) + }, + plugins: {}, + _uiHash: function () { + return { + helper: this.helper, + position: this.position, + originalPosition: this.originalPosition, + offset: this.positionAbs, + } + }, + }), + x.ui.plugin.add('draggable', 'connectToSortable', { + start: function (e, t, i) { + var s = x.extend({}, t, { item: i.element }) + ;(i.sortables = []), + x(i.options.connectToSortable).each(function () { + var t = x(this).sortable('instance') + t && + !t.options.disabled && + (i.sortables.push(t), + t.refreshPositions(), + t._trigger('activate', e, s)) + }) + }, + stop: function (e, t, i) { + var s = x.extend({}, t, { item: i.element }) + ;(i.cancelHelperRemoval = !1), + x.each(i.sortables, function () { + var t = this + t.isOver + ? ((t.isOver = 0), + (i.cancelHelperRemoval = !0), + (t.cancelHelperRemoval = !1), + (t._storedCSS = { + position: t.placeholder.css('position'), + top: t.placeholder.css('top'), + left: t.placeholder.css('left'), + }), + t._mouseStop(e), + (t.options.helper = t.options._helper)) + : ((t.cancelHelperRemoval = !0), + t._trigger('deactivate', e, s)) + }) + }, + drag: function (i, s, o) { + x.each(o.sortables, function () { + var t = !1, + e = this + ;(e.positionAbs = o.positionAbs), + (e.helperProportions = o.helperProportions), + (e.offset.click = o.offset.click), + e._intersectsWith(e.containerCache) && + ((t = !0), + x.each(o.sortables, function () { + return ( + (this.positionAbs = o.positionAbs), + (this.helperProportions = + o.helperProportions), + (this.offset.click = o.offset.click), + (t = + this !== e && + this._intersectsWith( + this.containerCache + ) && + x.contains( + e.element[0], + this.element[0] + ) + ? !1 + : t) + ) + })), + t + ? (e.isOver || + ((e.isOver = 1), + (o._parent = s.helper.parent()), + (e.currentItem = s.helper + .appendTo(e.element) + .data('ui-sortable-item', !0)), + (e.options._helper = e.options.helper), + (e.options.helper = function () { + return s.helper[0] + }), + (i.target = e.currentItem[0]), + e._mouseCapture(i, !0), + e._mouseStart(i, !0, !0), + (e.offset.click.top = o.offset.click.top), + (e.offset.click.left = o.offset.click.left), + (e.offset.parent.left -= + o.offset.parent.left - + e.offset.parent.left), + (e.offset.parent.top -= + o.offset.parent.top - + e.offset.parent.top), + o._trigger('toSortable', i), + (o.dropped = e.element), + x.each(o.sortables, function () { + this.refreshPositions() + }), + (o.currentItem = o.element), + (e.fromOutside = o)), + e.currentItem && + (e._mouseDrag(i), (s.position = e.position))) + : e.isOver && + ((e.isOver = 0), + (e.cancelHelperRemoval = !0), + (e.options._revert = e.options.revert), + (e.options.revert = !1), + e._trigger('out', i, e._uiHash(e)), + e._mouseStop(i, !0), + (e.options.revert = e.options._revert), + (e.options.helper = e.options._helper), + e.placeholder && e.placeholder.remove(), + s.helper.appendTo(o._parent), + o._refreshOffsets(i), + (s.position = o._generatePosition(i, !0)), + o._trigger('fromSortable', i), + (o.dropped = !1), + x.each(o.sortables, function () { + this.refreshPositions() + })) + }) + }, + }), + x.ui.plugin.add('draggable', 'cursor', { + start: function (t, e, i) { + var s = x('body'), + i = i.options + s.css('cursor') && (i._cursor = s.css('cursor')), + s.css('cursor', i.cursor) + }, + stop: function (t, e, i) { + i = i.options + i._cursor && x('body').css('cursor', i._cursor) + }, + }), + x.ui.plugin.add('draggable', 'opacity', { + start: function (t, e, i) { + ;(e = x(e.helper)), (i = i.options) + e.css('opacity') && (i._opacity = e.css('opacity')), + e.css('opacity', i.opacity) + }, + stop: function (t, e, i) { + i = i.options + i._opacity && x(e.helper).css('opacity', i._opacity) + }, + }), + x.ui.plugin.add('draggable', 'scroll', { + start: function (t, e, i) { + i.scrollParentNotHidden || + (i.scrollParentNotHidden = i.helper.scrollParent(!1)), + i.scrollParentNotHidden[0] !== i.document[0] && + 'HTML' !== i.scrollParentNotHidden[0].tagName && + (i.overflowOffset = i.scrollParentNotHidden.offset()) + }, + drag: function (t, e, i) { + var s = i.options, + o = !1, + n = i.scrollParentNotHidden[0], + r = i.document[0] + n !== r && 'HTML' !== n.tagName + ? ((s.axis && 'x' === s.axis) || + (i.overflowOffset.top + n.offsetHeight - t.pageY < + s.scrollSensitivity + ? (n.scrollTop = o = n.scrollTop + s.scrollSpeed) + : t.pageY - i.overflowOffset.top < + s.scrollSensitivity && + (n.scrollTop = o = + n.scrollTop - s.scrollSpeed)), + (s.axis && 'y' === s.axis) || + (i.overflowOffset.left + n.offsetWidth - t.pageX < + s.scrollSensitivity + ? (n.scrollLeft = o = + n.scrollLeft + s.scrollSpeed) + : t.pageX - i.overflowOffset.left < + s.scrollSensitivity && + (n.scrollLeft = o = + n.scrollLeft - s.scrollSpeed))) + : ((s.axis && 'x' === s.axis) || + (t.pageY - x(r).scrollTop() < s.scrollSensitivity + ? (o = x(r).scrollTop( + x(r).scrollTop() - s.scrollSpeed + )) + : x(window).height() - + (t.pageY - x(r).scrollTop()) < + s.scrollSensitivity && + (o = x(r).scrollTop( + x(r).scrollTop() + s.scrollSpeed + ))), + (s.axis && 'y' === s.axis) || + (t.pageX - x(r).scrollLeft() < s.scrollSensitivity + ? (o = x(r).scrollLeft( + x(r).scrollLeft() - s.scrollSpeed + )) + : x(window).width() - + (t.pageX - x(r).scrollLeft()) < + s.scrollSensitivity && + (o = x(r).scrollLeft( + x(r).scrollLeft() + s.scrollSpeed + )))), + !1 !== o && + x.ui.ddmanager && + !s.dropBehaviour && + x.ui.ddmanager.prepareOffsets(i, t) + }, + }), + x.ui.plugin.add('draggable', 'snap', { + start: function (t, e, i) { + var s = i.options + ;(i.snapElements = []), + x( + s.snap.constructor !== String + ? s.snap.items || ':data(ui-draggable)' + : s.snap + ).each(function () { + var t = x(this), + e = t.offset() + this !== i.element[0] && + i.snapElements.push({ + item: this, + width: t.outerWidth(), + height: t.outerHeight(), + top: e.top, + left: e.left, + }) + }) + }, + drag: function (t, e, i) { + for ( + var s, + o, + n, + r, + a, + h, + l, + c, + p, + u = i.options, + d = u.snapTolerance, + f = e.offset.left, + g = f + i.helperProportions.width, + m = e.offset.top, + v = m + i.helperProportions.height, + _ = i.snapElements.length - 1; + 0 <= _; + _-- + ) + (h = + (a = i.snapElements[_].left - i.margins.left) + + i.snapElements[_].width), + (c = + (l = i.snapElements[_].top - i.margins.top) + + i.snapElements[_].height), + g < a - d || + h + d < f || + v < l - d || + c + d < m || + !x.contains( + i.snapElements[_].item.ownerDocument, + i.snapElements[_].item + ) + ? (i.snapElements[_].snapping && + i.options.snap.release && + i.options.snap.release.call( + i.element, + t, + x.extend(i._uiHash(), { + snapItem: i.snapElements[_].item, + }) + ), + (i.snapElements[_].snapping = !1)) + : ('inner' !== u.snapMode && + ((s = Math.abs(l - v) <= d), + (o = Math.abs(c - m) <= d), + (n = Math.abs(a - g) <= d), + (r = Math.abs(h - f) <= d), + s && + (e.position.top = i._convertPositionTo( + 'relative', + { + top: + l - + i.helperProportions.height, + left: 0, + } + ).top), + o && + (e.position.top = i._convertPositionTo( + 'relative', + { top: c, left: 0 } + ).top), + n && + (e.position.left = i._convertPositionTo( + 'relative', + { + top: 0, + left: + a - i.helperProportions.width, + } + ).left), + r && + (e.position.left = i._convertPositionTo( + 'relative', + { top: 0, left: h } + ).left)), + (p = s || o || n || r), + 'outer' !== u.snapMode && + ((s = Math.abs(l - m) <= d), + (o = Math.abs(c - v) <= d), + (n = Math.abs(a - f) <= d), + (r = Math.abs(h - g) <= d), + s && + (e.position.top = i._convertPositionTo( + 'relative', + { top: l, left: 0 } + ).top), + o && + (e.position.top = i._convertPositionTo( + 'relative', + { + top: + c - + i.helperProportions.height, + left: 0, + } + ).top), + n && + (e.position.left = i._convertPositionTo( + 'relative', + { top: 0, left: a } + ).left), + r && + (e.position.left = i._convertPositionTo( + 'relative', + { + top: 0, + left: + h - i.helperProportions.width, + } + ).left)), + !i.snapElements[_].snapping && + (s || o || n || r || p) && + i.options.snap.snap && + i.options.snap.snap.call( + i.element, + t, + x.extend(i._uiHash(), { + snapItem: i.snapElements[_].item, + }) + ), + (i.snapElements[_].snapping = + s || o || n || r || p)) + }, + }), + x.ui.plugin.add('draggable', 'stack', { + start: function (t, e, i) { + var s, + i = i.options, + i = x.makeArray(x(i.stack)).sort(function (t, e) { + return ( + (parseInt(x(t).css('zIndex'), 10) || 0) - + (parseInt(x(e).css('zIndex'), 10) || 0) + ) + }) + i.length && + ((s = parseInt(x(i[0]).css('zIndex'), 10) || 0), + x(i).each(function (t) { + x(this).css('zIndex', s + t) + }), + this.css('zIndex', s + i.length)) + }, + }), + x.ui.plugin.add('draggable', 'zIndex', { + start: function (t, e, i) { + ;(e = x(e.helper)), (i = i.options) + e.css('zIndex') && (i._zIndex = e.css('zIndex')), + e.css('zIndex', i.zIndex) + }, + stop: function (t, e, i) { + i = i.options + i._zIndex && x(e.helper).css('zIndex', i._zIndex) + }, + }) + x.ui.draggable + x.widget('ui.resizable', x.ui.mouse, { + version: '1.13.1', + widgetEventPrefix: 'resize', + options: { + alsoResize: !1, + animate: !1, + animateDuration: 'slow', + animateEasing: 'swing', + aspectRatio: !1, + autoHide: !1, + classes: { + 'ui-resizable-se': 'ui-icon ui-icon-gripsmall-diagonal-se', + }, + containment: !1, + ghost: !1, + grid: !1, + handles: 'e,s,se', + helper: !1, + maxHeight: null, + maxWidth: null, + minHeight: 10, + minWidth: 10, + zIndex: 90, + resize: null, + start: null, + stop: null, + }, + _num: function (t) { + return parseFloat(t) || 0 + }, + _isNumber: function (t) { + return !isNaN(parseFloat(t)) + }, + _hasScroll: function (t, e) { + if ('hidden' === x(t).css('overflow')) return !1 + var i = e && 'left' === e ? 'scrollLeft' : 'scrollTop', + e = !1 + if (0 < t[i]) return !0 + try { + ;(t[i] = 1), (e = 0 < t[i]), (t[i] = 0) + } catch (t) {} + return e + }, + _create: function () { + var t, + e = this.options, + i = this + this._addClass('ui-resizable'), + x.extend(this, { + _aspectRatio: !!e.aspectRatio, + aspectRatio: e.aspectRatio, + originalElement: this.element, + _proportionallyResizeElements: [], + _helper: + e.helper || e.ghost || e.animate + ? e.helper || 'ui-resizable-helper' + : null, + }), + this.element[0].nodeName.match( + /^(canvas|textarea|input|select|button|img)$/i + ) && + (this.element.wrap( + x("
").css({ + overflow: 'hidden', + position: this.element.css('position'), + width: this.element.outerWidth(), + height: this.element.outerHeight(), + top: this.element.css('top'), + left: this.element.css('left'), + }) + ), + (this.element = this.element + .parent() + .data( + 'ui-resizable', + this.element.resizable('instance') + )), + (this.elementIsWrapper = !0), + (t = { + marginTop: this.originalElement.css('marginTop'), + marginRight: this.originalElement.css('marginRight'), + marginBottom: this.originalElement.css('marginBottom'), + marginLeft: this.originalElement.css('marginLeft'), + }), + this.element.css(t), + this.originalElement.css('margin', 0), + (this.originalResizeStyle = + this.originalElement.css('resize')), + this.originalElement.css('resize', 'none'), + this._proportionallyResizeElements.push( + this.originalElement.css({ + position: 'static', + zoom: 1, + display: 'block', + }) + ), + this.originalElement.css(t), + this._proportionallyResize()), + this._setupHandles(), + e.autoHide && + x(this.element) + .on('mouseenter', function () { + e.disabled || + (i._removeClass('ui-resizable-autohide'), + i._handles.show()) + }) + .on('mouseleave', function () { + e.disabled || + i.resizing || + (i._addClass('ui-resizable-autohide'), + i._handles.hide()) + }), + this._mouseInit() + }, + _destroy: function () { + this._mouseDestroy(), this._addedHandles.remove() + function t(t) { + x(t) + .removeData('resizable') + .removeData('ui-resizable') + .off('.resizable') + } + var e + return ( + this.elementIsWrapper && + (t(this.element), + (e = this.element), + this.originalElement + .css({ + position: e.css('position'), + width: e.outerWidth(), + height: e.outerHeight(), + top: e.css('top'), + left: e.css('left'), + }) + .insertAfter(e), + e.remove()), + this.originalElement.css('resize', this.originalResizeStyle), + t(this.originalElement), + this + ) + }, + _setOption: function (t, e) { + switch ((this._super(t, e), t)) { + case 'handles': + this._removeHandles(), this._setupHandles() + break + case 'aspectRatio': + this._aspectRatio = !!e + } + }, + _setupHandles: function () { + var t, + e, + i, + s, + o, + n = this.options, + r = this + if ( + ((this.handles = + n.handles || + (x('.ui-resizable-handle', this.element).length + ? { + n: '.ui-resizable-n', + e: '.ui-resizable-e', + s: '.ui-resizable-s', + w: '.ui-resizable-w', + se: '.ui-resizable-se', + sw: '.ui-resizable-sw', + ne: '.ui-resizable-ne', + nw: '.ui-resizable-nw', + } + : 'e,s,se')), + (this._handles = x()), + (this._addedHandles = x()), + this.handles.constructor === String) + ) + for ( + 'all' === this.handles && + (this.handles = 'n,e,s,w,se,sw,ne,nw'), + i = this.handles.split(','), + this.handles = {}, + e = 0; + e < i.length; + e++ + ) + (s = + 'ui-resizable-' + + (t = String.prototype.trim.call(i[e]))), + (o = x('
')), + this._addClass(o, 'ui-resizable-handle ' + s), + o.css({ zIndex: n.zIndex }), + (this.handles[t] = '.ui-resizable-' + t), + this.element.children(this.handles[t]).length || + (this.element.append(o), + (this._addedHandles = this._addedHandles.add(o))) + ;(this._renderAxis = function (t) { + var e, i, s + for (e in ((t = t || this.element), this.handles)) + this.handles[e].constructor === String + ? (this.handles[e] = this.element + .children(this.handles[e]) + .first() + .show()) + : (this.handles[e].jquery || + this.handles[e].nodeType) && + ((this.handles[e] = x(this.handles[e])), + this._on(this.handles[e], { + mousedown: r._mouseDown, + })), + this.elementIsWrapper && + this.originalElement[0].nodeName.match( + /^(textarea|input|select|button)$/i + ) && + ((i = x(this.handles[e], this.element)), + (s = /sw|ne|nw|se|n|s/.test(e) + ? i.outerHeight() + : i.outerWidth()), + (i = [ + 'padding', + /ne|nw|n/.test(e) + ? 'Top' + : /se|sw|s/.test(e) + ? 'Bottom' + : /^e$/.test(e) + ? 'Right' + : 'Left', + ].join('')), + t.css(i, s), + this._proportionallyResize()), + (this._handles = this._handles.add(this.handles[e])) + }), + this._renderAxis(this.element), + (this._handles = this._handles.add( + this.element.find('.ui-resizable-handle') + )), + this._handles.disableSelection(), + this._handles.on('mouseover', function () { + r.resizing || + (this.className && + (o = this.className.match( + /ui-resizable-(se|sw|ne|nw|n|e|s|w)/i + )), + (r.axis = o && o[1] ? o[1] : 'se')) + }), + n.autoHide && + (this._handles.hide(), + this._addClass('ui-resizable-autohide')) + }, + _removeHandles: function () { + this._addedHandles.remove() + }, + _mouseCapture: function (t) { + var e, + i, + s = !1 + for (e in this.handles) + ((i = x(this.handles[e])[0]) !== t.target && + !x.contains(i, t.target)) || + (s = !0) + return !this.options.disabled && s + }, + _mouseStart: function (t) { + var e, + i, + s = this.options, + o = this.element + return ( + (this.resizing = !0), + this._renderProxy(), + (e = this._num(this.helper.css('left'))), + (i = this._num(this.helper.css('top'))), + s.containment && + ((e += x(s.containment).scrollLeft() || 0), + (i += x(s.containment).scrollTop() || 0)), + (this.offset = this.helper.offset()), + (this.position = { left: e, top: i }), + (this.size = this._helper + ? { + width: this.helper.width(), + height: this.helper.height(), + } + : { width: o.width(), height: o.height() }), + (this.originalSize = this._helper + ? { width: o.outerWidth(), height: o.outerHeight() } + : { width: o.width(), height: o.height() }), + (this.sizeDiff = { + width: o.outerWidth() - o.width(), + height: o.outerHeight() - o.height(), + }), + (this.originalPosition = { left: e, top: i }), + (this.originalMousePosition = { left: t.pageX, top: t.pageY }), + (this.aspectRatio = + 'number' == typeof s.aspectRatio + ? s.aspectRatio + : this.originalSize.width / this.originalSize.height || + 1), + (s = x('.ui-resizable-' + this.axis).css('cursor')), + x('body').css( + 'cursor', + 'auto' === s ? this.axis + '-resize' : s + ), + this._addClass('ui-resizable-resizing'), + this._propagate('start', t), + !0 + ) + }, + _mouseDrag: function (t) { + var e = this.originalMousePosition, + i = this.axis, + s = t.pageX - e.left || 0, + e = t.pageY - e.top || 0, + i = this._change[i] + return ( + this._updatePrevProperties(), + i && + ((e = i.apply(this, [t, s, e])), + this._updateVirtualBoundaries(t.shiftKey), + (this._aspectRatio || t.shiftKey) && + (e = this._updateRatio(e, t)), + (e = this._respectSize(e, t)), + this._updateCache(e), + this._propagate('resize', t), + (e = this._applyChanges()), + !this._helper && + this._proportionallyResizeElements.length && + this._proportionallyResize(), + x.isEmptyObject(e) || + (this._updatePrevProperties(), + this._trigger('resize', t, this.ui()), + this._applyChanges())), + !1 + ) + }, + _mouseStop: function (t) { + this.resizing = !1 + var e, + i, + s, + o = this.options, + n = this + return ( + this._helper && + ((s = + (e = + (i = this._proportionallyResizeElements).length && + /textarea/i.test(i[0].nodeName)) && + this._hasScroll(i[0], 'left') + ? 0 + : n.sizeDiff.height), + (i = e ? 0 : n.sizeDiff.width), + (e = { + width: n.helper.width() - i, + height: n.helper.height() - s, + }), + (i = + parseFloat(n.element.css('left')) + + (n.position.left - n.originalPosition.left) || + null), + (s = + parseFloat(n.element.css('top')) + + (n.position.top - n.originalPosition.top) || null), + o.animate || + this.element.css(x.extend(e, { top: s, left: i })), + n.helper.height(n.size.height), + n.helper.width(n.size.width), + this._helper && !o.animate && this._proportionallyResize()), + x('body').css('cursor', 'auto'), + this._removeClass('ui-resizable-resizing'), + this._propagate('stop', t), + this._helper && this.helper.remove(), + !1 + ) + }, + _updatePrevProperties: function () { + ;(this.prevPosition = { + top: this.position.top, + left: this.position.left, + }), + (this.prevSize = { + width: this.size.width, + height: this.size.height, + }) + }, + _applyChanges: function () { + var t = {} + return ( + this.position.top !== this.prevPosition.top && + (t.top = this.position.top + 'px'), + this.position.left !== this.prevPosition.left && + (t.left = this.position.left + 'px'), + this.size.width !== this.prevSize.width && + (t.width = this.size.width + 'px'), + this.size.height !== this.prevSize.height && + (t.height = this.size.height + 'px'), + this.helper.css(t), + t + ) + }, + _updateVirtualBoundaries: function (t) { + var e, + i, + s = this.options, + o = { + minWidth: this._isNumber(s.minWidth) ? s.minWidth : 0, + maxWidth: this._isNumber(s.maxWidth) ? s.maxWidth : 1 / 0, + minHeight: this._isNumber(s.minHeight) ? s.minHeight : 0, + maxHeight: this._isNumber(s.maxHeight) + ? s.maxHeight + : 1 / 0, + } + ;(this._aspectRatio || t) && + ((e = o.minHeight * this.aspectRatio), + (i = o.minWidth / this.aspectRatio), + (s = o.maxHeight * this.aspectRatio), + (t = o.maxWidth / this.aspectRatio), + e > o.minWidth && (o.minWidth = e), + i > o.minHeight && (o.minHeight = i), + s < o.maxWidth && (o.maxWidth = s), + t < o.maxHeight && (o.maxHeight = t)), + (this._vBoundaries = o) + }, + _updateCache: function (t) { + ;(this.offset = this.helper.offset()), + this._isNumber(t.left) && (this.position.left = t.left), + this._isNumber(t.top) && (this.position.top = t.top), + this._isNumber(t.height) && (this.size.height = t.height), + this._isNumber(t.width) && (this.size.width = t.width) + }, + _updateRatio: function (t) { + var e = this.position, + i = this.size, + s = this.axis + return ( + this._isNumber(t.height) + ? (t.width = t.height * this.aspectRatio) + : this._isNumber(t.width) && + (t.height = t.width / this.aspectRatio), + 'sw' === s && + ((t.left = e.left + (i.width - t.width)), (t.top = null)), + 'nw' === s && + ((t.top = e.top + (i.height - t.height)), + (t.left = e.left + (i.width - t.width))), + t + ) + }, + _respectSize: function (t) { + var e = this._vBoundaries, + i = this.axis, + s = + this._isNumber(t.width) && + e.maxWidth && + e.maxWidth < t.width, + o = + this._isNumber(t.height) && + e.maxHeight && + e.maxHeight < t.height, + n = + this._isNumber(t.width) && + e.minWidth && + e.minWidth > t.width, + r = + this._isNumber(t.height) && + e.minHeight && + e.minHeight > t.height, + a = this.originalPosition.left + this.originalSize.width, + h = this.originalPosition.top + this.originalSize.height, + l = /sw|nw|w/.test(i), + i = /nw|ne|n/.test(i) + return ( + n && (t.width = e.minWidth), + r && (t.height = e.minHeight), + s && (t.width = e.maxWidth), + o && (t.height = e.maxHeight), + n && l && (t.left = a - e.minWidth), + s && l && (t.left = a - e.maxWidth), + r && i && (t.top = h - e.minHeight), + o && i && (t.top = h - e.maxHeight), + t.width || t.height || t.left || !t.top + ? t.width || t.height || t.top || !t.left || (t.left = null) + : (t.top = null), + t + ) + }, + _getPaddingPlusBorderDimensions: function (t) { + for ( + var e = 0, + i = [], + s = [ + t.css('borderTopWidth'), + t.css('borderRightWidth'), + t.css('borderBottomWidth'), + t.css('borderLeftWidth'), + ], + o = [ + t.css('paddingTop'), + t.css('paddingRight'), + t.css('paddingBottom'), + t.css('paddingLeft'), + ]; + e < 4; + e++ + ) + (i[e] = parseFloat(s[e]) || 0), (i[e] += parseFloat(o[e]) || 0) + return { height: i[0] + i[2], width: i[1] + i[3] } + }, + _proportionallyResize: function () { + if (this._proportionallyResizeElements.length) + for ( + var t, e = 0, i = this.helper || this.element; + e < this._proportionallyResizeElements.length; + e++ + ) + (t = this._proportionallyResizeElements[e]), + this.outerDimensions || + (this.outerDimensions = + this._getPaddingPlusBorderDimensions(t)), + t.css({ + height: + i.height() - this.outerDimensions.height || 0, + width: i.width() - this.outerDimensions.width || 0, + }) + }, + _renderProxy: function () { + var t = this.element, + e = this.options + ;(this.elementOffset = t.offset()), + this._helper + ? ((this.helper = + this.helper || + x('
').css({ overflow: 'hidden' })), + this._addClass(this.helper, this._helper), + this.helper.css({ + width: this.element.outerWidth(), + height: this.element.outerHeight(), + position: 'absolute', + left: this.elementOffset.left + 'px', + top: this.elementOffset.top + 'px', + zIndex: ++e.zIndex, + }), + this.helper.appendTo('body').disableSelection()) + : (this.helper = this.element) + }, + _change: { + e: function (t, e) { + return { width: this.originalSize.width + e } + }, + w: function (t, e) { + var i = this.originalSize + return { + left: this.originalPosition.left + e, + width: i.width - e, + } + }, + n: function (t, e, i) { + var s = this.originalSize + return { + top: this.originalPosition.top + i, + height: s.height - i, + } + }, + s: function (t, e, i) { + return { height: this.originalSize.height + i } + }, + se: function (t, e, i) { + return x.extend( + this._change.s.apply(this, arguments), + this._change.e.apply(this, [t, e, i]) + ) + }, + sw: function (t, e, i) { + return x.extend( + this._change.s.apply(this, arguments), + this._change.w.apply(this, [t, e, i]) + ) + }, + ne: function (t, e, i) { + return x.extend( + this._change.n.apply(this, arguments), + this._change.e.apply(this, [t, e, i]) + ) + }, + nw: function (t, e, i) { + return x.extend( + this._change.n.apply(this, arguments), + this._change.w.apply(this, [t, e, i]) + ) + }, + }, + _propagate: function (t, e) { + x.ui.plugin.call(this, t, [e, this.ui()]), + 'resize' !== t && this._trigger(t, e, this.ui()) + }, + plugins: {}, + ui: function () { + return { + originalElement: this.originalElement, + element: this.element, + helper: this.helper, + position: this.position, + size: this.size, + originalSize: this.originalSize, + originalPosition: this.originalPosition, + } + }, + }), + x.ui.plugin.add('resizable', 'animate', { + stop: function (e) { + var i = x(this).resizable('instance'), + t = i.options, + s = i._proportionallyResizeElements, + o = s.length && /textarea/i.test(s[0].nodeName), + n = o && i._hasScroll(s[0], 'left') ? 0 : i.sizeDiff.height, + r = o ? 0 : i.sizeDiff.width, + o = { width: i.size.width - r, height: i.size.height - n }, + r = + parseFloat(i.element.css('left')) + + (i.position.left - i.originalPosition.left) || null, + n = + parseFloat(i.element.css('top')) + + (i.position.top - i.originalPosition.top) || null + i.element.animate( + x.extend(o, n && r ? { top: n, left: r } : {}), + { + duration: t.animateDuration, + easing: t.animateEasing, + step: function () { + var t = { + width: parseFloat(i.element.css('width')), + height: parseFloat(i.element.css('height')), + top: parseFloat(i.element.css('top')), + left: parseFloat(i.element.css('left')), + } + s && + s.length && + x(s[0]).css({ + width: t.width, + height: t.height, + }), + i._updateCache(t), + i._propagate('resize', e) + }, + } + ) + }, + }), + x.ui.plugin.add('resizable', 'containment', { + start: function () { + var i, + s, + o = x(this).resizable('instance'), + t = o.options, + e = o.element, + n = t.containment, + r = + n instanceof x + ? n.get(0) + : /parent/.test(n) + ? e.parent().get(0) + : n + r && + ((o.containerElement = x(r)), + /document/.test(n) || n === document + ? ((o.containerOffset = { left: 0, top: 0 }), + (o.containerPosition = { left: 0, top: 0 }), + (o.parentData = { + element: x(document), + left: 0, + top: 0, + width: x(document).width(), + height: + x(document).height() || + document.body.parentNode.scrollHeight, + })) + : ((i = x(r)), + (s = []), + x(['Top', 'Right', 'Left', 'Bottom']).each(function ( + t, + e + ) { + s[t] = o._num(i.css('padding' + e)) + }), + (o.containerOffset = i.offset()), + (o.containerPosition = i.position()), + (o.containerSize = { + height: i.innerHeight() - s[3], + width: i.innerWidth() - s[1], + }), + (t = o.containerOffset), + (e = o.containerSize.height), + (n = o.containerSize.width), + (n = o._hasScroll(r, 'left') ? r.scrollWidth : n), + (e = o._hasScroll(r) ? r.scrollHeight : e), + (o.parentData = { + element: r, + left: t.left, + top: t.top, + width: n, + height: e, + }))) + }, + resize: function (t) { + var e = x(this).resizable('instance'), + i = e.options, + s = e.containerOffset, + o = e.position, + n = e._aspectRatio || t.shiftKey, + r = { top: 0, left: 0 }, + a = e.containerElement, + t = !0 + a[0] !== document && + /static/.test(a.css('position')) && + (r = s), + o.left < (e._helper ? s.left : 0) && + ((e.size.width = + e.size.width + + (e._helper + ? e.position.left - s.left + : e.position.left - r.left)), + n && + ((e.size.height = e.size.width / e.aspectRatio), + (t = !1)), + (e.position.left = i.helper ? s.left : 0)), + o.top < (e._helper ? s.top : 0) && + ((e.size.height = + e.size.height + + (e._helper + ? e.position.top - s.top + : e.position.top)), + n && + ((e.size.width = e.size.height * e.aspectRatio), + (t = !1)), + (e.position.top = e._helper ? s.top : 0)), + (i = + e.containerElement.get(0) === + e.element.parent().get(0)), + (o = /relative|absolute/.test( + e.containerElement.css('position') + )), + i && o + ? ((e.offset.left = + e.parentData.left + e.position.left), + (e.offset.top = e.parentData.top + e.position.top)) + : ((e.offset.left = e.element.offset().left), + (e.offset.top = e.element.offset().top)), + (o = Math.abs( + e.sizeDiff.width + + (e._helper + ? e.offset.left - r.left + : e.offset.left - s.left) + )), + (s = Math.abs( + e.sizeDiff.height + + (e._helper + ? e.offset.top - r.top + : e.offset.top - s.top) + )), + o + e.size.width >= e.parentData.width && + ((e.size.width = e.parentData.width - o), + n && + ((e.size.height = e.size.width / e.aspectRatio), + (t = !1))), + s + e.size.height >= e.parentData.height && + ((e.size.height = e.parentData.height - s), + n && + ((e.size.width = e.size.height * e.aspectRatio), + (t = !1))), + t || + ((e.position.left = e.prevPosition.left), + (e.position.top = e.prevPosition.top), + (e.size.width = e.prevSize.width), + (e.size.height = e.prevSize.height)) + }, + stop: function () { + var t = x(this).resizable('instance'), + e = t.options, + i = t.containerOffset, + s = t.containerPosition, + o = t.containerElement, + n = x(t.helper), + r = n.offset(), + a = n.outerWidth() - t.sizeDiff.width, + n = n.outerHeight() - t.sizeDiff.height + t._helper && + !e.animate && + /relative/.test(o.css('position')) && + x(this).css({ + left: r.left - s.left - i.left, + width: a, + height: n, + }), + t._helper && + !e.animate && + /static/.test(o.css('position')) && + x(this).css({ + left: r.left - s.left - i.left, + width: a, + height: n, + }) + }, + }), + x.ui.plugin.add('resizable', 'alsoResize', { + start: function () { + var t = x(this).resizable('instance').options + x(t.alsoResize).each(function () { + var t = x(this) + t.data('ui-resizable-alsoresize', { + width: parseFloat(t.width()), + height: parseFloat(t.height()), + left: parseFloat(t.css('left')), + top: parseFloat(t.css('top')), + }) + }) + }, + resize: function (t, i) { + var e = x(this).resizable('instance'), + s = e.options, + o = e.originalSize, + n = e.originalPosition, + r = { + height: e.size.height - o.height || 0, + width: e.size.width - o.width || 0, + top: e.position.top - n.top || 0, + left: e.position.left - n.left || 0, + } + x(s.alsoResize).each(function () { + var t = x(this), + s = x(this).data('ui-resizable-alsoresize'), + o = {}, + e = t.parents(i.originalElement[0]).length + ? ['width', 'height'] + : ['width', 'height', 'top', 'left'] + x.each(e, function (t, e) { + var i = (s[e] || 0) + (r[e] || 0) + i && 0 <= i && (o[e] = i || null) + }), + t.css(o) + }) + }, + stop: function () { + x(this).removeData('ui-resizable-alsoresize') + }, + }), + x.ui.plugin.add('resizable', 'ghost', { + start: function () { + var t = x(this).resizable('instance'), + e = t.size + ;(t.ghost = t.originalElement.clone()), + t.ghost.css({ + opacity: 0.25, + display: 'block', + position: 'relative', + height: e.height, + width: e.width, + margin: 0, + left: 0, + top: 0, + }), + t._addClass(t.ghost, 'ui-resizable-ghost'), + !1 !== x.uiBackCompat && + 'string' == typeof t.options.ghost && + t.ghost.addClass(this.options.ghost), + t.ghost.appendTo(t.helper) + }, + resize: function () { + var t = x(this).resizable('instance') + t.ghost && + t.ghost.css({ + position: 'relative', + height: t.size.height, + width: t.size.width, + }) + }, + stop: function () { + var t = x(this).resizable('instance') + t.ghost && + t.helper && + t.helper.get(0).removeChild(t.ghost.get(0)) + }, + }), + x.ui.plugin.add('resizable', 'grid', { + resize: function () { + var t, + e = x(this).resizable('instance'), + i = e.options, + s = e.size, + o = e.originalSize, + n = e.originalPosition, + r = e.axis, + a = 'number' == typeof i.grid ? [i.grid, i.grid] : i.grid, + h = a[0] || 1, + l = a[1] || 1, + c = Math.round((s.width - o.width) / h) * h, + p = Math.round((s.height - o.height) / l) * l, + u = o.width + c, + d = o.height + p, + f = i.maxWidth && i.maxWidth < u, + g = i.maxHeight && i.maxHeight < d, + m = i.minWidth && i.minWidth > u, + s = i.minHeight && i.minHeight > d + ;(i.grid = a), + m && (u += h), + s && (d += l), + f && (u -= h), + g && (d -= l), + /^(se|s|e)$/.test(r) + ? ((e.size.width = u), (e.size.height = d)) + : /^(ne)$/.test(r) + ? ((e.size.width = u), + (e.size.height = d), + (e.position.top = n.top - p)) + : /^(sw)$/.test(r) + ? ((e.size.width = u), + (e.size.height = d), + (e.position.left = n.left - c)) + : ((d - l <= 0 || u - h <= 0) && + (t = e._getPaddingPlusBorderDimensions(this)), + 0 < d - l + ? ((e.size.height = d), + (e.position.top = n.top - p)) + : ((d = l - t.height), + (e.size.height = d), + (e.position.top = n.top + o.height - d)), + 0 < u - h + ? ((e.size.width = u), + (e.position.left = n.left - c)) + : ((u = h - t.width), + (e.size.width = u), + (e.position.left = n.left + o.width - u))) + }, + }) + x.ui.resizable, + x.widget('ui.sortable', x.ui.mouse, { + version: '1.13.1', + widgetEventPrefix: 'sort', + ready: !1, + options: { + appendTo: 'parent', + axis: !1, + connectWith: !1, + containment: !1, + cursor: 'auto', + cursorAt: !1, + dropOnEmpty: !0, + forcePlaceholderSize: !1, + forceHelperSize: !1, + grid: !1, + handle: !1, + helper: 'original', + items: '> *', + opacity: !1, + placeholder: !1, + revert: !1, + scroll: !0, + scrollSensitivity: 20, + scrollSpeed: 20, + scope: 'default', + tolerance: 'intersect', + zIndex: 1e3, + activate: null, + beforeStop: null, + change: null, + deactivate: null, + out: null, + over: null, + receive: null, + remove: null, + sort: null, + start: null, + stop: null, + update: null, + }, + _isOverAxis: function (t, e, i) { + return e <= t && t < e + i + }, + _isFloating: function (t) { + return ( + /left|right/.test(t.css('float')) || + /inline|table-cell/.test(t.css('display')) + ) + }, + _create: function () { + ;(this.containerCache = {}), + this._addClass('ui-sortable'), + this.refresh(), + (this.offset = this.element.offset()), + this._mouseInit(), + this._setHandleClassName(), + (this.ready = !0) + }, + _setOption: function (t, e) { + this._super(t, e), 'handle' === t && this._setHandleClassName() + }, + _setHandleClassName: function () { + var t = this + this._removeClass( + this.element.find('.ui-sortable-handle'), + 'ui-sortable-handle' + ), + x.each(this.items, function () { + t._addClass( + this.instance.options.handle + ? this.item.find(this.instance.options.handle) + : this.item, + 'ui-sortable-handle' + ) + }) + }, + _destroy: function () { + this._mouseDestroy() + for (var t = this.items.length - 1; 0 <= t; t--) + this.items[t].item.removeData(this.widgetName + '-item') + return this + }, + _mouseCapture: function (t, e) { + var i = null, + s = !1, + o = this + return ( + !this.reverting && + !this.options.disabled && + 'static' !== this.options.type && + (this._refreshItems(t), + x(t.target) + .parents() + .each(function () { + if (x.data(this, o.widgetName + '-item') === o) + return (i = x(this)), !1 + }), + !!(i = + x.data(t.target, o.widgetName + '-item') === o + ? x(t.target) + : i) && + !( + this.options.handle && + !e && + (x(this.options.handle, i) + .find('*') + .addBack() + .each(function () { + this === t.target && (s = !0) + }), + !s) + ) && + ((this.currentItem = i), + this._removeCurrentsFromItems(), + !0)) + ) + }, + _mouseStart: function (t, e, i) { + var s, + o, + n = this.options + if ( + ((this.currentContainer = this).refreshPositions(), + (this.appendTo = x( + 'parent' !== n.appendTo + ? n.appendTo + : this.currentItem.parent() + )), + (this.helper = this._createHelper(t)), + this._cacheHelperProportions(), + this._cacheMargins(), + (this.offset = this.currentItem.offset()), + (this.offset = { + top: this.offset.top - this.margins.top, + left: this.offset.left - this.margins.left, + }), + x.extend(this.offset, { + click: { + left: t.pageX - this.offset.left, + top: t.pageY - this.offset.top, + }, + relative: this._getRelativeOffset(), + }), + this.helper.css('position', 'absolute'), + (this.cssPosition = this.helper.css('position')), + n.cursorAt && this._adjustOffsetFromHelper(n.cursorAt), + (this.domPosition = { + prev: this.currentItem.prev()[0], + parent: this.currentItem.parent()[0], + }), + this.helper[0] !== this.currentItem[0] && + this.currentItem.hide(), + this._createPlaceholder(), + (this.scrollParent = this.placeholder.scrollParent()), + x.extend(this.offset, { parent: this._getParentOffset() }), + n.containment && this._setContainment(), + n.cursor && + 'auto' !== n.cursor && + ((o = this.document.find('body')), + (this.storedCursor = o.css('cursor')), + o.css('cursor', n.cursor), + (this.storedStylesheet = x( + '' + ).appendTo(o))), + n.zIndex && + (this.helper.css('zIndex') && + (this._storedZIndex = this.helper.css('zIndex')), + this.helper.css('zIndex', n.zIndex)), + n.opacity && + (this.helper.css('opacity') && + (this._storedOpacity = this.helper.css('opacity')), + this.helper.css('opacity', n.opacity)), + this.scrollParent[0] !== this.document[0] && + 'HTML' !== this.scrollParent[0].tagName && + (this.overflowOffset = this.scrollParent.offset()), + this._trigger('start', t, this._uiHash()), + this._preserveHelperProportions || + this._cacheHelperProportions(), + !i) + ) + for (s = this.containers.length - 1; 0 <= s; s--) + this.containers[s]._trigger( + 'activate', + t, + this._uiHash(this) + ) + return ( + x.ui.ddmanager && (x.ui.ddmanager.current = this), + x.ui.ddmanager && + !n.dropBehaviour && + x.ui.ddmanager.prepareOffsets(this, t), + (this.dragging = !0), + this._addClass(this.helper, 'ui-sortable-helper'), + this.helper.parent().is(this.appendTo) || + (this.helper.detach().appendTo(this.appendTo), + (this.offset.parent = this._getParentOffset())), + (this.position = this.originalPosition = + this._generatePosition(t)), + (this.originalPageX = t.pageX), + (this.originalPageY = t.pageY), + (this.lastPositionAbs = this.positionAbs = + this._convertPositionTo('absolute')), + this._mouseDrag(t), + !0 + ) + }, + _scroll: function (t) { + var e = this.options, + i = !1 + return ( + this.scrollParent[0] !== this.document[0] && + 'HTML' !== this.scrollParent[0].tagName + ? (this.overflowOffset.top + + this.scrollParent[0].offsetHeight - + t.pageY < + e.scrollSensitivity + ? (this.scrollParent[0].scrollTop = i = + this.scrollParent[0].scrollTop + + e.scrollSpeed) + : t.pageY - this.overflowOffset.top < + e.scrollSensitivity && + (this.scrollParent[0].scrollTop = i = + this.scrollParent[0].scrollTop - + e.scrollSpeed), + this.overflowOffset.left + + this.scrollParent[0].offsetWidth - + t.pageX < + e.scrollSensitivity + ? (this.scrollParent[0].scrollLeft = i = + this.scrollParent[0].scrollLeft + + e.scrollSpeed) + : t.pageX - this.overflowOffset.left < + e.scrollSensitivity && + (this.scrollParent[0].scrollLeft = i = + this.scrollParent[0].scrollLeft - + e.scrollSpeed)) + : (t.pageY - this.document.scrollTop() < + e.scrollSensitivity + ? (i = this.document.scrollTop( + this.document.scrollTop() - e.scrollSpeed + )) + : this.window.height() - + (t.pageY - this.document.scrollTop()) < + e.scrollSensitivity && + (i = this.document.scrollTop( + this.document.scrollTop() + e.scrollSpeed + )), + t.pageX - this.document.scrollLeft() < + e.scrollSensitivity + ? (i = this.document.scrollLeft( + this.document.scrollLeft() - e.scrollSpeed + )) + : this.window.width() - + (t.pageX - this.document.scrollLeft()) < + e.scrollSensitivity && + (i = this.document.scrollLeft( + this.document.scrollLeft() + e.scrollSpeed + ))), + i + ) + }, + _mouseDrag: function (t) { + var e, + i, + s, + o, + n = this.options + for ( + this.position = this._generatePosition(t), + this.positionAbs = this._convertPositionTo('absolute'), + (this.options.axis && 'y' === this.options.axis) || + (this.helper[0].style.left = + this.position.left + 'px'), + (this.options.axis && 'x' === this.options.axis) || + (this.helper[0].style.top = + this.position.top + 'px'), + n.scroll && + !1 !== this._scroll(t) && + (this._refreshItemPositions(!0), + x.ui.ddmanager && + !n.dropBehaviour && + x.ui.ddmanager.prepareOffsets(this, t)), + this.dragDirection = { + vertical: this._getDragVerticalDirection(), + horizontal: this._getDragHorizontalDirection(), + }, + e = this.items.length - 1; + 0 <= e; + e-- + ) + if ( + ((s = (i = this.items[e]).item[0]), + (o = this._intersectsWithPointer(i)) && + i.instance === this.currentContainer && + !( + s === this.currentItem[0] || + this.placeholder[ + 1 === o ? 'next' : 'prev' + ]()[0] === s || + x.contains(this.placeholder[0], s) || + ('semi-dynamic' === this.options.type && + x.contains(this.element[0], s)) + )) + ) { + if ( + ((this.direction = 1 === o ? 'down' : 'up'), + 'pointer' !== this.options.tolerance && + !this._intersectsWithSides(i)) + ) + break + this._rearrange(t, i), + this._trigger('change', t, this._uiHash()) + break + } + return ( + this._contactContainers(t), + x.ui.ddmanager && x.ui.ddmanager.drag(this, t), + this._trigger('sort', t, this._uiHash()), + (this.lastPositionAbs = this.positionAbs), + !1 + ) + }, + _mouseStop: function (t, e) { + var i, s, o, n + if (t) + return ( + x.ui.ddmanager && + !this.options.dropBehaviour && + x.ui.ddmanager.drop(this, t), + this.options.revert + ? ((s = (i = this).placeholder.offset()), + (n = {}), + ((o = this.options.axis) && 'x' !== o) || + (n.left = + s.left - + this.offset.parent.left - + this.margins.left + + (this.offsetParent[0] === + this.document[0].body + ? 0 + : this.offsetParent[0].scrollLeft)), + (o && 'y' !== o) || + (n.top = + s.top - + this.offset.parent.top - + this.margins.top + + (this.offsetParent[0] === + this.document[0].body + ? 0 + : this.offsetParent[0].scrollTop)), + (this.reverting = !0), + x(this.helper).animate( + n, + parseInt(this.options.revert, 10) || 500, + function () { + i._clear(t) + } + )) + : this._clear(t, e), + !1 + ) + }, + cancel: function () { + if (this.dragging) { + this._mouseUp(new x.Event('mouseup', { target: null })), + 'original' === this.options.helper + ? (this.currentItem.css(this._storedCSS), + this._removeClass( + this.currentItem, + 'ui-sortable-helper' + )) + : this.currentItem.show() + for (var t = this.containers.length - 1; 0 <= t; t--) + this.containers[t]._trigger( + 'deactivate', + null, + this._uiHash(this) + ), + this.containers[t].containerCache.over && + (this.containers[t]._trigger( + 'out', + null, + this._uiHash(this) + ), + (this.containers[t].containerCache.over = 0)) + } + return ( + this.placeholder && + (this.placeholder[0].parentNode && + this.placeholder[0].parentNode.removeChild( + this.placeholder[0] + ), + 'original' !== this.options.helper && + this.helper && + this.helper[0].parentNode && + this.helper.remove(), + x.extend(this, { + helper: null, + dragging: !1, + reverting: !1, + _noFinalSort: null, + }), + this.domPosition.prev + ? x(this.domPosition.prev).after(this.currentItem) + : x(this.domPosition.parent).prepend( + this.currentItem + )), + this + ) + }, + serialize: function (e) { + var t = this._getItemsAsjQuery(e && e.connected), + i = [] + return ( + (e = e || {}), + x(t).each(function () { + var t = ( + x(e.item || this).attr(e.attribute || 'id') || '' + ).match(e.expression || /(.+)[\-=_](.+)/) + t && + i.push( + (e.key || t[1] + '[]') + + '=' + + (e.key && e.expression ? t[1] : t[2]) + ) + }), + !i.length && e.key && i.push(e.key + '='), + i.join('&') + ) + }, + toArray: function (t) { + var e = this._getItemsAsjQuery(t && t.connected), + i = [] + return ( + (t = t || {}), + e.each(function () { + i.push( + x(t.item || this).attr(t.attribute || 'id') || '' + ) + }), + i + ) + }, + _intersectsWith: function (t) { + var e = this.positionAbs.left, + i = e + this.helperProportions.width, + s = this.positionAbs.top, + o = s + this.helperProportions.height, + n = t.left, + r = n + t.width, + a = t.top, + h = a + t.height, + l = this.offset.click.top, + c = this.offset.click.left, + l = 'x' === this.options.axis || (a < s + l && s + l < h), + c = 'y' === this.options.axis || (n < e + c && e + c < r) + return 'pointer' === this.options.tolerance || + this.options.forcePointerForContainers || + ('pointer' !== this.options.tolerance && + this.helperProportions[ + this.floating ? 'width' : 'height' + ] > t[this.floating ? 'width' : 'height']) + ? l && c + : n < e + this.helperProportions.width / 2 && + i - this.helperProportions.width / 2 < r && + a < s + this.helperProportions.height / 2 && + o - this.helperProportions.height / 2 < h + }, + _intersectsWithPointer: function (t) { + var e = + 'x' === this.options.axis || + this._isOverAxis( + this.positionAbs.top + this.offset.click.top, + t.top, + t.height + ), + t = + 'y' === this.options.axis || + this._isOverAxis( + this.positionAbs.left + this.offset.click.left, + t.left, + t.width + ) + return ( + !(!e || !t) && + ((e = this.dragDirection.vertical), + (t = this.dragDirection.horizontal), + this.floating + ? 'right' === t || 'down' === e + ? 2 + : 1 + : e && ('down' === e ? 2 : 1)) + ) + }, + _intersectsWithSides: function (t) { + var e = this._isOverAxis( + this.positionAbs.top + this.offset.click.top, + t.top + t.height / 2, + t.height + ), + i = this._isOverAxis( + this.positionAbs.left + this.offset.click.left, + t.left + t.width / 2, + t.width + ), + s = this.dragDirection.vertical, + t = this.dragDirection.horizontal + return this.floating && t + ? ('right' === t && i) || ('left' === t && !i) + : s && (('down' === s && e) || ('up' === s && !e)) + }, + _getDragVerticalDirection: function () { + var t = this.positionAbs.top - this.lastPositionAbs.top + return 0 != t && (0 < t ? 'down' : 'up') + }, + _getDragHorizontalDirection: function () { + var t = this.positionAbs.left - this.lastPositionAbs.left + return 0 != t && (0 < t ? 'right' : 'left') + }, + refresh: function (t) { + return ( + this._refreshItems(t), + this._setHandleClassName(), + this.refreshPositions(), + this + ) + }, + _connectWith: function () { + var t = this.options + return t.connectWith.constructor === String + ? [t.connectWith] + : t.connectWith + }, + _getItemsAsjQuery: function (t) { + var e, + i, + s, + o, + n = [], + r = [], + a = this._connectWith() + if (a && t) + for (e = a.length - 1; 0 <= e; e--) + for ( + i = (s = x(a[e], this.document[0])).length - 1; + 0 <= i; + i-- + ) + (o = x.data(s[i], this.widgetFullName)) && + o !== this && + !o.options.disabled && + r.push([ + 'function' == typeof o.options.items + ? o.options.items.call(o.element) + : x(o.options.items, o.element) + .not('.ui-sortable-helper') + .not('.ui-sortable-placeholder'), + o, + ]) + function h() { + n.push(this) + } + for ( + r.push([ + 'function' == typeof this.options.items + ? this.options.items.call(this.element, null, { + options: this.options, + item: this.currentItem, + }) + : x(this.options.items, this.element) + .not('.ui-sortable-helper') + .not('.ui-sortable-placeholder'), + this, + ]), + e = r.length - 1; + 0 <= e; + e-- + ) + r[e][0].each(h) + return x(n) + }, + _removeCurrentsFromItems: function () { + var i = this.currentItem.find( + ':data(' + this.widgetName + '-item)' + ) + this.items = x.grep(this.items, function (t) { + for (var e = 0; e < i.length; e++) + if (i[e] === t.item[0]) return !1 + return !0 + }) + }, + _refreshItems: function (t) { + ;(this.items = []), (this.containers = [this]) + var e, + i, + s, + o, + n, + r, + a, + h, + l = this.items, + c = [ + [ + 'function' == typeof this.options.items + ? this.options.items.call(this.element[0], t, { + item: this.currentItem, + }) + : x(this.options.items, this.element), + this, + ], + ], + p = this._connectWith() + if (p && this.ready) + for (e = p.length - 1; 0 <= e; e--) + for ( + i = (s = x(p[e], this.document[0])).length - 1; + 0 <= i; + i-- + ) + (o = x.data(s[i], this.widgetFullName)) && + o !== this && + !o.options.disabled && + (c.push([ + 'function' == typeof o.options.items + ? o.options.items.call( + o.element[0], + t, + { item: this.currentItem } + ) + : x(o.options.items, o.element), + o, + ]), + this.containers.push(o)) + for (e = c.length - 1; 0 <= e; e--) + for ( + n = c[e][1], h = (r = c[e][(i = 0)]).length; + i < h; + i++ + ) + (a = x(r[i])).data(this.widgetName + '-item', n), + l.push({ + item: a, + instance: n, + width: 0, + height: 0, + left: 0, + top: 0, + }) + }, + _refreshItemPositions: function (t) { + for (var e, i, s = this.items.length - 1; 0 <= s; s--) + (e = this.items[s]), + (this.currentContainer && + e.instance !== this.currentContainer && + e.item[0] !== this.currentItem[0]) || + ((i = this.options.toleranceElement + ? x(this.options.toleranceElement, e.item) + : e.item), + t || + ((e.width = i.outerWidth()), + (e.height = i.outerHeight())), + (i = i.offset()), + (e.left = i.left), + (e.top = i.top)) + }, + refreshPositions: function (t) { + var e, i + if ( + ((this.floating = + !!this.items.length && + ('x' === this.options.axis || + this._isFloating(this.items[0].item))), + this.offsetParent && + this.helper && + (this.offset.parent = this._getParentOffset()), + this._refreshItemPositions(t), + this.options.custom && + this.options.custom.refreshContainers) + ) + this.options.custom.refreshContainers.call(this) + else + for (e = this.containers.length - 1; 0 <= e; e--) + (i = this.containers[e].element.offset()), + (this.containers[e].containerCache.left = i.left), + (this.containers[e].containerCache.top = i.top), + (this.containers[e].containerCache.width = + this.containers[e].element.outerWidth()), + (this.containers[e].containerCache.height = + this.containers[e].element.outerHeight()) + return this + }, + _createPlaceholder: function (i) { + var s, + o, + n = (i = i || this).options + ;(n.placeholder && n.placeholder.constructor !== String) || + ((s = n.placeholder), + (o = i.currentItem[0].nodeName.toLowerCase()), + (n.placeholder = { + element: function () { + var t = x('<' + o + '>', i.document[0]) + return ( + i + ._addClass( + t, + 'ui-sortable-placeholder', + s || i.currentItem[0].className + ) + ._removeClass(t, 'ui-sortable-helper'), + 'tbody' === o + ? i._createTrPlaceholder( + i.currentItem.find('tr').eq(0), + x('', i.document[0]).appendTo(t) + ) + : 'tr' === o + ? i._createTrPlaceholder(i.currentItem, t) + : 'img' === o && + t.attr('src', i.currentItem.attr('src')), + s || t.css('visibility', 'hidden'), + t + ) + }, + update: function (t, e) { + ;(s && !n.forcePlaceholderSize) || + ((e.height() && + (!n.forcePlaceholderSize || + ('tbody' !== o && 'tr' !== o))) || + e.height( + i.currentItem.innerHeight() - + parseInt( + i.currentItem.css( + 'paddingTop' + ) || 0, + 10 + ) - + parseInt( + i.currentItem.css( + 'paddingBottom' + ) || 0, + 10 + ) + ), + e.width() || + e.width( + i.currentItem.innerWidth() - + parseInt( + i.currentItem.css( + 'paddingLeft' + ) || 0, + 10 + ) - + parseInt( + i.currentItem.css( + 'paddingRight' + ) || 0, + 10 + ) + )) + }, + })), + (i.placeholder = x( + n.placeholder.element.call(i.element, i.currentItem) + )), + i.currentItem.after(i.placeholder), + n.placeholder.update(i, i.placeholder) + }, + _createTrPlaceholder: function (t, e) { + var i = this + t.children().each(function () { + x(' ', i.document[0]) + .attr('colspan', x(this).attr('colspan') || 1) + .appendTo(e) + }) + }, + _contactContainers: function (t) { + for ( + var e, + i, + s, + o, + n, + r, + a, + h, + l, + c = null, + p = null, + u = this.containers.length - 1; + 0 <= u; + u-- + ) + x.contains( + this.currentItem[0], + this.containers[u].element[0] + ) || + (this._intersectsWith(this.containers[u].containerCache) + ? (c && + x.contains( + this.containers[u].element[0], + c.element[0] + )) || + ((c = this.containers[u]), (p = u)) + : this.containers[u].containerCache.over && + (this.containers[u]._trigger( + 'out', + t, + this._uiHash(this) + ), + (this.containers[u].containerCache.over = 0))) + if (c) + if (1 === this.containers.length) + this.containers[p].containerCache.over || + (this.containers[p]._trigger( + 'over', + t, + this._uiHash(this) + ), + (this.containers[p].containerCache.over = 1)) + else { + for ( + i = 1e4, + s = null, + o = (h = + c.floating || + this._isFloating(this.currentItem)) + ? 'left' + : 'top', + n = h ? 'width' : 'height', + l = h ? 'pageX' : 'pageY', + e = this.items.length - 1; + 0 <= e; + e-- + ) + x.contains( + this.containers[p].element[0], + this.items[e].item[0] + ) && + this.items[e].item[0] !== this.currentItem[0] && + ((r = this.items[e].item.offset()[o]), + (a = !1), + t[l] - r > this.items[e][n] / 2 && (a = !0), + Math.abs(t[l] - r) < i && + ((i = Math.abs(t[l] - r)), + (s = this.items[e]), + (this.direction = a ? 'up' : 'down'))) + ;(s || this.options.dropOnEmpty) && + (this.currentContainer !== this.containers[p] + ? (s + ? this._rearrange(t, s, null, !0) + : this._rearrange( + t, + null, + this.containers[p].element, + !0 + ), + this._trigger('change', t, this._uiHash()), + this.containers[p]._trigger( + 'change', + t, + this._uiHash(this) + ), + (this.currentContainer = this.containers[p]), + this.options.placeholder.update( + this.currentContainer, + this.placeholder + ), + (this.scrollParent = + this.placeholder.scrollParent()), + this.scrollParent[0] !== this.document[0] && + 'HTML' !== this.scrollParent[0].tagName && + (this.overflowOffset = + this.scrollParent.offset()), + this.containers[p]._trigger( + 'over', + t, + this._uiHash(this) + ), + (this.containers[p].containerCache.over = 1)) + : this.currentContainer.containerCache.over || + (this.containers[p]._trigger( + 'over', + t, + this._uiHash() + ), + (this.currentContainer.containerCache.over = 1))) + } + }, + _createHelper: function (t) { + var e = this.options, + t = + 'function' == typeof e.helper + ? x( + e.helper.apply(this.element[0], [ + t, + this.currentItem, + ]) + ) + : 'clone' === e.helper + ? this.currentItem.clone() + : this.currentItem + return ( + t.parents('body').length || + this.appendTo[0].appendChild(t[0]), + t[0] === this.currentItem[0] && + (this._storedCSS = { + width: this.currentItem[0].style.width, + height: this.currentItem[0].style.height, + position: this.currentItem.css('position'), + top: this.currentItem.css('top'), + left: this.currentItem.css('left'), + }), + (t[0].style.width && !e.forceHelperSize) || + t.width(this.currentItem.width()), + (t[0].style.height && !e.forceHelperSize) || + t.height(this.currentItem.height()), + t + ) + }, + _adjustOffsetFromHelper: function (t) { + 'string' == typeof t && (t = t.split(' ')), + 'left' in + (t = Array.isArray(t) + ? { left: +t[0], top: +t[1] || 0 } + : t) && + (this.offset.click.left = t.left + this.margins.left), + 'right' in t && + (this.offset.click.left = + this.helperProportions.width - + t.right + + this.margins.left), + 'top' in t && + (this.offset.click.top = t.top + this.margins.top), + 'bottom' in t && + (this.offset.click.top = + this.helperProportions.height - + t.bottom + + this.margins.top) + }, + _getParentOffset: function () { + this.offsetParent = this.helper.offsetParent() + var t = this.offsetParent.offset() + return ( + 'absolute' === this.cssPosition && + this.scrollParent[0] !== this.document[0] && + x.contains( + this.scrollParent[0], + this.offsetParent[0] + ) && + ((t.left += this.scrollParent.scrollLeft()), + (t.top += this.scrollParent.scrollTop())), + { + top: + (t = + this.offsetParent[0] === + this.document[0].body || + (this.offsetParent[0].tagName && + 'html' === + this.offsetParent[0].tagName.toLowerCase() && + x.ui.ie) + ? { top: 0, left: 0 } + : t).top + + (parseInt( + this.offsetParent.css('borderTopWidth'), + 10 + ) || 0), + left: + t.left + + (parseInt( + this.offsetParent.css('borderLeftWidth'), + 10 + ) || 0), + } + ) + }, + _getRelativeOffset: function () { + if ('relative' !== this.cssPosition) return { top: 0, left: 0 } + var t = this.currentItem.position() + return { + top: + t.top - + (parseInt(this.helper.css('top'), 10) || 0) + + this.scrollParent.scrollTop(), + left: + t.left - + (parseInt(this.helper.css('left'), 10) || 0) + + this.scrollParent.scrollLeft(), + } + }, + _cacheMargins: function () { + this.margins = { + left: parseInt(this.currentItem.css('marginLeft'), 10) || 0, + top: parseInt(this.currentItem.css('marginTop'), 10) || 0, + } + }, + _cacheHelperProportions: function () { + this.helperProportions = { + width: this.helper.outerWidth(), + height: this.helper.outerHeight(), + } + }, + _setContainment: function () { + var t, + e, + i = this.options + 'parent' === i.containment && + (i.containment = this.helper[0].parentNode), + ('document' !== i.containment && + 'window' !== i.containment) || + (this.containment = [ + 0 - + this.offset.relative.left - + this.offset.parent.left, + 0 - + this.offset.relative.top - + this.offset.parent.top, + 'document' === i.containment + ? this.document.width() + : this.window.width() - + this.helperProportions.width - + this.margins.left, + ('document' === i.containment + ? this.document.height() || + document.body.parentNode.scrollHeight + : this.window.height() || + this.document[0].body.parentNode + .scrollHeight) - + this.helperProportions.height - + this.margins.top, + ]), + /^(document|window|parent)$/.test(i.containment) || + ((t = x(i.containment)[0]), + (e = x(i.containment).offset()), + (i = 'hidden' !== x(t).css('overflow')), + (this.containment = [ + e.left + + (parseInt(x(t).css('borderLeftWidth'), 10) || + 0) + + (parseInt(x(t).css('paddingLeft'), 10) || 0) - + this.margins.left, + e.top + + (parseInt(x(t).css('borderTopWidth'), 10) || + 0) + + (parseInt(x(t).css('paddingTop'), 10) || 0) - + this.margins.top, + e.left + + (i + ? Math.max(t.scrollWidth, t.offsetWidth) + : t.offsetWidth) - + (parseInt(x(t).css('borderLeftWidth'), 10) || + 0) - + (parseInt(x(t).css('paddingRight'), 10) || 0) - + this.helperProportions.width - + this.margins.left, + e.top + + (i + ? Math.max(t.scrollHeight, t.offsetHeight) + : t.offsetHeight) - + (parseInt(x(t).css('borderTopWidth'), 10) || + 0) - + (parseInt(x(t).css('paddingBottom'), 10) || 0) - + this.helperProportions.height - + this.margins.top, + ])) + }, + _convertPositionTo: function (t, e) { + e = e || this.position + var i = 'absolute' === t ? 1 : -1, + s = + 'absolute' !== this.cssPosition || + (this.scrollParent[0] !== this.document[0] && + x.contains( + this.scrollParent[0], + this.offsetParent[0] + )) + ? this.scrollParent + : this.offsetParent, + t = /(html|body)/i.test(s[0].tagName) + return { + top: + e.top + + this.offset.relative.top * i + + this.offset.parent.top * i - + ('fixed' === this.cssPosition + ? -this.scrollParent.scrollTop() + : t + ? 0 + : s.scrollTop()) * + i, + left: + e.left + + this.offset.relative.left * i + + this.offset.parent.left * i - + ('fixed' === this.cssPosition + ? -this.scrollParent.scrollLeft() + : t + ? 0 + : s.scrollLeft()) * + i, + } + }, + _generatePosition: function (t) { + var e = this.options, + i = t.pageX, + s = t.pageY, + o = + 'absolute' !== this.cssPosition || + (this.scrollParent[0] !== this.document[0] && + x.contains( + this.scrollParent[0], + this.offsetParent[0] + )) + ? this.scrollParent + : this.offsetParent, + n = /(html|body)/i.test(o[0].tagName) + return ( + 'relative' !== this.cssPosition || + (this.scrollParent[0] !== this.document[0] && + this.scrollParent[0] !== this.offsetParent[0]) || + (this.offset.relative = this._getRelativeOffset()), + this.originalPosition && + (this.containment && + (t.pageX - this.offset.click.left < + this.containment[0] && + (i = + this.containment[0] + + this.offset.click.left), + t.pageY - this.offset.click.top < + this.containment[1] && + (s = + this.containment[1] + + this.offset.click.top), + t.pageX - this.offset.click.left > + this.containment[2] && + (i = + this.containment[2] + + this.offset.click.left), + t.pageY - this.offset.click.top > + this.containment[3] && + (s = + this.containment[3] + + this.offset.click.top)), + e.grid && + ((t = + this.originalPageY + + Math.round( + (s - this.originalPageY) / e.grid[1] + ) * + e.grid[1]), + (s = + !this.containment || + (t - this.offset.click.top >= + this.containment[1] && + t - this.offset.click.top <= + this.containment[3]) + ? t + : t - this.offset.click.top >= + this.containment[1] + ? t - e.grid[1] + : t + e.grid[1]), + (t = + this.originalPageX + + Math.round( + (i - this.originalPageX) / e.grid[0] + ) * + e.grid[0]), + (i = + !this.containment || + (t - this.offset.click.left >= + this.containment[0] && + t - this.offset.click.left <= + this.containment[2]) + ? t + : t - this.offset.click.left >= + this.containment[0] + ? t - e.grid[0] + : t + e.grid[0]))), + { + top: + s - + this.offset.click.top - + this.offset.relative.top - + this.offset.parent.top + + ('fixed' === this.cssPosition + ? -this.scrollParent.scrollTop() + : n + ? 0 + : o.scrollTop()), + left: + i - + this.offset.click.left - + this.offset.relative.left - + this.offset.parent.left + + ('fixed' === this.cssPosition + ? -this.scrollParent.scrollLeft() + : n + ? 0 + : o.scrollLeft()), + } + ) + }, + _rearrange: function (t, e, i, s) { + i + ? i[0].appendChild(this.placeholder[0]) + : e.item[0].parentNode.insertBefore( + this.placeholder[0], + 'down' === this.direction + ? e.item[0] + : e.item[0].nextSibling + ), + (this.counter = this.counter ? ++this.counter : 1) + var o = this.counter + this._delay(function () { + o === this.counter && this.refreshPositions(!s) + }) + }, + _clear: function (t, e) { + this.reverting = !1 + var i, + s = [] + if ( + (!this._noFinalSort && + this.currentItem.parent().length && + this.placeholder.before(this.currentItem), + (this._noFinalSort = null), + this.helper[0] === this.currentItem[0]) + ) { + for (i in this._storedCSS) + ('auto' !== this._storedCSS[i] && + 'static' !== this._storedCSS[i]) || + (this._storedCSS[i] = '') + this.currentItem.css(this._storedCSS), + this._removeClass( + this.currentItem, + 'ui-sortable-helper' + ) + } else this.currentItem.show() + function o(e, i, s) { + return function (t) { + s._trigger(e, t, i._uiHash(i)) + } + } + for ( + this.fromOutside && + !e && + s.push(function (t) { + this._trigger( + 'receive', + t, + this._uiHash(this.fromOutside) + ) + }), + (!this.fromOutside && + this.domPosition.prev === + this.currentItem + .prev() + .not('.ui-sortable-helper')[0] && + this.domPosition.parent === + this.currentItem.parent()[0]) || + e || + s.push(function (t) { + this._trigger('update', t, this._uiHash()) + }), + this !== this.currentContainer && + (e || + (s.push(function (t) { + this._trigger('remove', t, this._uiHash()) + }), + s.push( + function (e) { + return function (t) { + e._trigger( + 'receive', + t, + this._uiHash(this) + ) + } + }.call(this, this.currentContainer) + ), + s.push( + function (e) { + return function (t) { + e._trigger( + 'update', + t, + this._uiHash(this) + ) + } + }.call(this, this.currentContainer) + ))), + i = this.containers.length - 1; + 0 <= i; + i-- + ) + e || s.push(o('deactivate', this, this.containers[i])), + this.containers[i].containerCache.over && + (s.push(o('out', this, this.containers[i])), + (this.containers[i].containerCache.over = 0)) + if ( + (this.storedCursor && + (this.document + .find('body') + .css('cursor', this.storedCursor), + this.storedStylesheet.remove()), + this._storedOpacity && + this.helper.css('opacity', this._storedOpacity), + this._storedZIndex && + this.helper.css( + 'zIndex', + 'auto' === this._storedZIndex + ? '' + : this._storedZIndex + ), + (this.dragging = !1), + e || this._trigger('beforeStop', t, this._uiHash()), + this.placeholder[0].parentNode.removeChild( + this.placeholder[0] + ), + this.cancelHelperRemoval || + (this.helper[0] !== this.currentItem[0] && + this.helper.remove(), + (this.helper = null)), + !e) + ) { + for (i = 0; i < s.length; i++) s[i].call(this, t) + this._trigger('stop', t, this._uiHash()) + } + return (this.fromOutside = !1), !this.cancelHelperRemoval + }, + _trigger: function () { + !1 === x.Widget.prototype._trigger.apply(this, arguments) && + this.cancel() + }, + _uiHash: function (t) { + var e = t || this + return { + helper: e.helper, + placeholder: e.placeholder || x([]), + position: e.position, + originalPosition: e.originalPosition, + offset: e.positionAbs, + item: e.currentItem, + sender: t ? t.element : null, + } + }, + }), + x.widget('ui.accordion', { + version: '1.13.1', + options: { + active: 0, + animate: {}, + classes: { + 'ui-accordion-header': 'ui-corner-top', + 'ui-accordion-header-collapsed': 'ui-corner-all', + 'ui-accordion-content': 'ui-corner-bottom', + }, + collapsible: !1, + event: 'click', + header: function (t) { + return t + .find('> li > :first-child') + .add(t.find('> :not(li)').even()) + }, + heightStyle: 'auto', + icons: { + activeHeader: 'ui-icon-triangle-1-s', + header: 'ui-icon-triangle-1-e', + }, + activate: null, + beforeActivate: null, + }, + hideProps: { + borderTopWidth: 'hide', + borderBottomWidth: 'hide', + paddingTop: 'hide', + paddingBottom: 'hide', + height: 'hide', + }, + showProps: { + borderTopWidth: 'show', + borderBottomWidth: 'show', + paddingTop: 'show', + paddingBottom: 'show', + height: 'show', + }, + _create: function () { + var t = this.options + ;(this.prevShow = this.prevHide = x()), + this._addClass('ui-accordion', 'ui-widget ui-helper-reset'), + this.element.attr('role', 'tablist'), + t.collapsible || + (!1 !== t.active && null != t.active) || + (t.active = 0), + this._processPanels(), + t.active < 0 && (t.active += this.headers.length), + this._refresh() + }, + _getCreateEventData: function () { + return { + header: this.active, + panel: this.active.length ? this.active.next() : x(), + } + }, + _createIcons: function () { + var t, + e = this.options.icons + e && + ((t = x('')), + this._addClass( + t, + 'ui-accordion-header-icon', + 'ui-icon ' + e.header + ), + t.prependTo(this.headers), + (t = this.active.children('.ui-accordion-header-icon')), + this._removeClass(t, e.header) + ._addClass(t, null, e.activeHeader) + ._addClass(this.headers, 'ui-accordion-icons')) + }, + _destroyIcons: function () { + this._removeClass(this.headers, 'ui-accordion-icons'), + this.headers.children('.ui-accordion-header-icon').remove() + }, + _destroy: function () { + var t + this.element.removeAttr('role'), + this.headers + .removeAttr( + 'role aria-expanded aria-selected aria-controls tabIndex' + ) + .removeUniqueId(), + this._destroyIcons(), + (t = this.headers + .next() + .css('display', '') + .removeAttr('role aria-hidden aria-labelledby') + .removeUniqueId()), + 'content' !== this.options.heightStyle && + t.css('height', '') + }, + _setOption: function (t, e) { + 'active' !== t + ? ('event' === t && + (this.options.event && + this._off(this.headers, this.options.event), + this._setupEvents(e)), + this._super(t, e), + 'collapsible' !== t || + e || + !1 !== this.options.active || + this._activate(0), + 'icons' === t && + (this._destroyIcons(), e && this._createIcons())) + : this._activate(e) + }, + _setOptionDisabled: function (t) { + this._super(t), + this.element.attr('aria-disabled', t), + this._toggleClass(null, 'ui-state-disabled', !!t), + this._toggleClass( + this.headers.add(this.headers.next()), + null, + 'ui-state-disabled', + !!t + ) + }, + _keydown: function (t) { + if (!t.altKey && !t.ctrlKey) { + var e = x.ui.keyCode, + i = this.headers.length, + s = this.headers.index(t.target), + o = !1 + switch (t.keyCode) { + case e.RIGHT: + case e.DOWN: + o = this.headers[(s + 1) % i] + break + case e.LEFT: + case e.UP: + o = this.headers[(s - 1 + i) % i] + break + case e.SPACE: + case e.ENTER: + this._eventHandler(t) + break + case e.HOME: + o = this.headers[0] + break + case e.END: + o = this.headers[i - 1] + } + o && + (x(t.target).attr('tabIndex', -1), + x(o).attr('tabIndex', 0), + x(o).trigger('focus'), + t.preventDefault()) + } + }, + _panelKeyDown: function (t) { + t.keyCode === x.ui.keyCode.UP && + t.ctrlKey && + x(t.currentTarget).prev().trigger('focus') + }, + refresh: function () { + var t = this.options + this._processPanels(), + (!1 === t.active && !0 === t.collapsible) || + !this.headers.length + ? ((t.active = !1), (this.active = x())) + : !1 === t.active + ? this._activate(0) + : this.active.length && + !x.contains(this.element[0], this.active[0]) + ? this.headers.length === + this.headers.find('.ui-state-disabled').length + ? ((t.active = !1), (this.active = x())) + : this._activate(Math.max(0, t.active - 1)) + : (t.active = this.headers.index(this.active)), + this._destroyIcons(), + this._refresh() + }, + _processPanels: function () { + var t = this.headers, + e = this.panels + 'function' == typeof this.options.header + ? (this.headers = this.options.header(this.element)) + : (this.headers = this.element.find(this.options.header)), + this._addClass( + this.headers, + 'ui-accordion-header ui-accordion-header-collapsed', + 'ui-state-default' + ), + (this.panels = this.headers + .next() + .filter(':not(.ui-accordion-content-active)') + .hide()), + this._addClass( + this.panels, + 'ui-accordion-content', + 'ui-helper-reset ui-widget-content' + ), + e && + (this._off(t.not(this.headers)), + this._off(e.not(this.panels))) + }, + _refresh: function () { + var i, + t = this.options, + e = t.heightStyle, + s = this.element.parent() + ;(this.active = this._findActive(t.active)), + this._addClass( + this.active, + 'ui-accordion-header-active', + 'ui-state-active' + )._removeClass( + this.active, + 'ui-accordion-header-collapsed' + ), + this._addClass( + this.active.next(), + 'ui-accordion-content-active' + ), + this.active.next().show(), + this.headers + .attr('role', 'tab') + .each(function () { + var t = x(this), + e = t.uniqueId().attr('id'), + i = t.next(), + s = i.uniqueId().attr('id') + t.attr('aria-controls', s), + i.attr('aria-labelledby', e) + }) + .next() + .attr('role', 'tabpanel'), + this.headers + .not(this.active) + .attr({ + 'aria-selected': 'false', + 'aria-expanded': 'false', + tabIndex: -1, + }) + .next() + .attr({ 'aria-hidden': 'true' }) + .hide(), + this.active.length + ? this.active + .attr({ + 'aria-selected': 'true', + 'aria-expanded': 'true', + tabIndex: 0, + }) + .next() + .attr({ 'aria-hidden': 'false' }) + : this.headers.eq(0).attr('tabIndex', 0), + this._createIcons(), + this._setupEvents(t.event), + 'fill' === e + ? ((i = s.height()), + this.element.siblings(':visible').each(function () { + var t = x(this), + e = t.css('position') + 'absolute' !== e && + 'fixed' !== e && + (i -= t.outerHeight(!0)) + }), + this.headers.each(function () { + i -= x(this).outerHeight(!0) + }), + this.headers + .next() + .each(function () { + x(this).height( + Math.max( + 0, + i - + x(this).innerHeight() + + x(this).height() + ) + ) + }) + .css('overflow', 'auto')) + : 'auto' === e && + ((i = 0), + this.headers + .next() + .each(function () { + var t = x(this).is(':visible') + t || x(this).show(), + (i = Math.max( + i, + x(this).css('height', '').height() + )), + t || x(this).hide() + }) + .height(i)) + }, + _activate: function (t) { + t = this._findActive(t)[0] + t !== this.active[0] && + ((t = t || this.active[0]), + this._eventHandler({ + target: t, + currentTarget: t, + preventDefault: x.noop, + })) + }, + _findActive: function (t) { + return 'number' == typeof t ? this.headers.eq(t) : x() + }, + _setupEvents: function (t) { + var i = { keydown: '_keydown' } + t && + x.each(t.split(' '), function (t, e) { + i[e] = '_eventHandler' + }), + this._off(this.headers.add(this.headers.next())), + this._on(this.headers, i), + this._on(this.headers.next(), { keydown: '_panelKeyDown' }), + this._hoverable(this.headers), + this._focusable(this.headers) + }, + _eventHandler: function (t) { + var e = this.options, + i = this.active, + s = x(t.currentTarget), + o = s[0] === i[0], + n = o && e.collapsible, + r = n ? x() : s.next(), + a = i.next(), + r = { + oldHeader: i, + oldPanel: a, + newHeader: n ? x() : s, + newPanel: r, + } + t.preventDefault(), + (o && !e.collapsible) || + !1 === this._trigger('beforeActivate', t, r) || + ((e.active = !n && this.headers.index(s)), + (this.active = o ? x() : s), + this._toggle(r), + this._removeClass( + i, + 'ui-accordion-header-active', + 'ui-state-active' + ), + e.icons && + ((i = i.children('.ui-accordion-header-icon')), + this._removeClass( + i, + null, + e.icons.activeHeader + )._addClass(i, null, e.icons.header)), + o || + (this._removeClass( + s, + 'ui-accordion-header-collapsed' + )._addClass( + s, + 'ui-accordion-header-active', + 'ui-state-active' + ), + e.icons && + ((o = s.children('.ui-accordion-header-icon')), + this._removeClass( + o, + null, + e.icons.header + )._addClass(o, null, e.icons.activeHeader)), + this._addClass( + s.next(), + 'ui-accordion-content-active' + ))) + }, + _toggle: function (t) { + var e = t.newPanel, + i = this.prevShow.length ? this.prevShow : t.oldPanel + this.prevShow.add(this.prevHide).stop(!0, !0), + (this.prevShow = e), + (this.prevHide = i), + this.options.animate + ? this._animate(e, i, t) + : (i.hide(), e.show(), this._toggleComplete(t)), + i.attr({ 'aria-hidden': 'true' }), + i.prev().attr({ + 'aria-selected': 'false', + 'aria-expanded': 'false', + }), + e.length && i.length + ? i + .prev() + .attr({ tabIndex: -1, 'aria-expanded': 'false' }) + : e.length && + this.headers + .filter(function () { + return ( + 0 === + parseInt(x(this).attr('tabIndex'), 10) + ) + }) + .attr('tabIndex', -1), + e.attr('aria-hidden', 'false').prev().attr({ + 'aria-selected': 'true', + 'aria-expanded': 'true', + tabIndex: 0, + }) + }, + _animate: function (t, i, e) { + var s, + o, + n, + r = this, + a = 0, + h = t.css('box-sizing'), + l = t.length && (!i.length || t.index() < i.index()), + c = this.options.animate || {}, + p = (l && c.down) || c, + l = function () { + r._toggleComplete(e) + } + return ( + (o = + (o = 'string' == typeof p ? p : o) || + p.easing || + c.easing), + (n = + (n = 'number' == typeof p ? p : n) || + p.duration || + c.duration), + i.length + ? t.length + ? ((s = t.show().outerHeight()), + i.animate(this.hideProps, { + duration: n, + easing: o, + step: function (t, e) { + e.now = Math.round(t) + }, + }), + void t.hide().animate(this.showProps, { + duration: n, + easing: o, + complete: l, + step: function (t, e) { + ;(e.now = Math.round(t)), + 'height' !== e.prop + ? 'content-box' === h && + (a += e.now) + : 'content' !== + r.options.heightStyle && + ((e.now = Math.round( + s - i.outerHeight() - a + )), + (a = 0)) + }, + })) + : i.animate(this.hideProps, n, o, l) + : t.animate(this.showProps, n, o, l) + ) + }, + _toggleComplete: function (t) { + var e = t.oldPanel, + i = e.prev() + this._removeClass(e, 'ui-accordion-content-active'), + this._removeClass( + i, + 'ui-accordion-header-active' + )._addClass(i, 'ui-accordion-header-collapsed'), + e.length && + (e.parent()[0].className = e.parent()[0].className), + this._trigger('activate', null, t) + }, + }) + var g = /ui-corner-([a-z]){2,6}/g + x.widget('ui.controlgroup', { + version: '1.13.1', + defaultElement: '
', + options: { + direction: 'horizontal', + disabled: null, + onlyVisible: !0, + items: { + button: 'input[type=button], input[type=submit], input[type=reset], button, a', + controlgroupLabel: '.ui-controlgroup-label', + checkboxradio: "input[type='checkbox'], input[type='radio']", + selectmenu: 'select', + spinner: '.ui-spinner-input', + }, + }, + _create: function () { + this._enhance() + }, + _enhance: function () { + this.element.attr('role', 'toolbar'), this.refresh() + }, + _destroy: function () { + this._callChildMethod('destroy'), + this.childWidgets.removeData('ui-controlgroup-data'), + this.element.removeAttr('role'), + this.options.items.controlgroupLabel && + this.element + .find(this.options.items.controlgroupLabel) + .find('.ui-controlgroup-label-contents') + .contents() + .unwrap() + }, + _initWidgets: function () { + var n = this, + r = [] + x.each(this.options.items, function (s, t) { + var e, + o = {} + if (t) + return 'controlgroupLabel' === s + ? ((e = n.element.find(t)).each(function () { + var t = x(this) + t.children('.ui-controlgroup-label-contents') + .length || + t + .contents() + .wrapAll( + "" + ) + }), + n._addClass( + e, + null, + 'ui-widget ui-widget-content ui-state-default' + ), + void (r = r.concat(e.get()))) + : void ( + x.fn[s] && + ((o = n['_' + s + 'Options'] + ? n['_' + s + 'Options']('middle') + : { classes: {} }), + n.element.find(t).each(function () { + var t = x(this), + e = t[s]('instance'), + i = x.widget.extend({}, o) + ;('button' === s && + t.parent('.ui-spinner').length) || + ((e = e || t[s]()[s]('instance')) && + (i.classes = n._resolveClassesValues( + i.classes, + e + )), + t[s](i), + (i = t[s]('widget')), + x.data( + i[0], + 'ui-controlgroup-data', + e || t[s]('instance') + ), + r.push(i[0])) + })) + ) + }), + (this.childWidgets = x(x.uniqueSort(r))), + this._addClass(this.childWidgets, 'ui-controlgroup-item') + }, + _callChildMethod: function (e) { + this.childWidgets.each(function () { + var t = x(this).data('ui-controlgroup-data') + t && t[e] && t[e]() + }) + }, + _updateCornerClass: function (t, e) { + e = this._buildSimpleOptions(e, 'label').classes.label + this._removeClass( + t, + null, + 'ui-corner-top ui-corner-bottom ui-corner-left ui-corner-right ui-corner-all' + ), + this._addClass(t, null, e) + }, + _buildSimpleOptions: function (t, e) { + var i = 'vertical' === this.options.direction, + s = { classes: {} } + return ( + (s.classes[e] = { + middle: '', + first: 'ui-corner-' + (i ? 'top' : 'left'), + last: 'ui-corner-' + (i ? 'bottom' : 'right'), + only: 'ui-corner-all', + }[t]), + s + ) + }, + _spinnerOptions: function (t) { + t = this._buildSimpleOptions(t, 'ui-spinner') + return ( + (t.classes['ui-spinner-up'] = ''), + (t.classes['ui-spinner-down'] = ''), + t + ) + }, + _buttonOptions: function (t) { + return this._buildSimpleOptions(t, 'ui-button') + }, + _checkboxradioOptions: function (t) { + return this._buildSimpleOptions(t, 'ui-checkboxradio-label') + }, + _selectmenuOptions: function (t) { + var e = 'vertical' === this.options.direction + return { + width: e && 'auto', + classes: { + middle: { + 'ui-selectmenu-button-open': '', + 'ui-selectmenu-button-closed': '', + }, + first: { + 'ui-selectmenu-button-open': + 'ui-corner-' + (e ? 'top' : 'tl'), + 'ui-selectmenu-button-closed': + 'ui-corner-' + (e ? 'top' : 'left'), + }, + last: { + 'ui-selectmenu-button-open': e ? '' : 'ui-corner-tr', + 'ui-selectmenu-button-closed': + 'ui-corner-' + (e ? 'bottom' : 'right'), + }, + only: { + 'ui-selectmenu-button-open': 'ui-corner-top', + 'ui-selectmenu-button-closed': 'ui-corner-all', + }, + }[t], + } + }, + _resolveClassesValues: function (i, s) { + var o = {} + return ( + x.each(i, function (t) { + var e = s.options.classes[t] || '', + e = String.prototype.trim.call(e.replace(g, '')) + o[t] = (e + ' ' + i[t]).replace(/\s+/g, ' ') + }), + o + ) + }, + _setOption: function (t, e) { + 'direction' === t && + this._removeClass('ui-controlgroup-' + this.options.direction), + this._super(t, e), + 'disabled' !== t + ? this.refresh() + : this._callChildMethod(e ? 'disable' : 'enable') + }, + refresh: function () { + var o, + n = this + this._addClass( + 'ui-controlgroup ui-controlgroup-' + this.options.direction + ), + 'horizontal' === this.options.direction && + this._addClass(null, 'ui-helper-clearfix'), + this._initWidgets(), + (o = this.childWidgets), + (o = this.options.onlyVisible ? o.filter(':visible') : o) + .length && + (x.each(['first', 'last'], function (t, e) { + var i, + s = o[e]().data('ui-controlgroup-data') + s && n['_' + s.widgetName + 'Options'] + ? (((i = n['_' + s.widgetName + 'Options']( + 1 === o.length ? 'only' : e + )).classes = n._resolveClassesValues( + i.classes, + s + )), + s.element[s.widgetName](i)) + : n._updateCornerClass(o[e](), e) + }), + this._callChildMethod('refresh')) + }, + }) + x.widget('ui.checkboxradio', [ + x.ui.formResetMixin, + { + version: '1.13.1', + options: { + disabled: null, + label: null, + icon: !0, + classes: { + 'ui-checkboxradio-label': 'ui-corner-all', + 'ui-checkboxradio-icon': 'ui-corner-all', + }, + }, + _getCreateOptions: function () { + var t, + e = this, + i = this._super() || {} + return ( + this._readType(), + (t = this.element.labels()), + (this.label = x(t[t.length - 1])), + this.label.length || + x.error('No label found for checkboxradio widget'), + (this.originalLabel = ''), + this.label + .contents() + .not(this.element[0]) + .each(function () { + e.originalLabel += + 3 === this.nodeType + ? x(this).text() + : this.outerHTML + }), + this.originalLabel && (i.label = this.originalLabel), + null != (t = this.element[0].disabled) && (i.disabled = t), + i + ) + }, + _create: function () { + var t = this.element[0].checked + this._bindFormResetHandler(), + null == this.options.disabled && + (this.options.disabled = this.element[0].disabled), + this._setOption('disabled', this.options.disabled), + this._addClass( + 'ui-checkboxradio', + 'ui-helper-hidden-accessible' + ), + this._addClass( + this.label, + 'ui-checkboxradio-label', + 'ui-button ui-widget' + ), + 'radio' === this.type && + this._addClass( + this.label, + 'ui-checkboxradio-radio-label' + ), + this.options.label && + this.options.label !== this.originalLabel + ? this._updateLabel() + : this.originalLabel && + (this.options.label = this.originalLabel), + this._enhance(), + t && + this._addClass( + this.label, + 'ui-checkboxradio-checked', + 'ui-state-active' + ), + this._on({ + change: '_toggleClasses', + focus: function () { + this._addClass( + this.label, + null, + 'ui-state-focus ui-visual-focus' + ) + }, + blur: function () { + this._removeClass( + this.label, + null, + 'ui-state-focus ui-visual-focus' + ) + }, + }) + }, + _readType: function () { + var t = this.element[0].nodeName.toLowerCase() + ;(this.type = this.element[0].type), + ('input' === t && /radio|checkbox/.test(this.type)) || + x.error( + "Can't create checkboxradio on element.nodeName=" + + t + + ' and element.type=' + + this.type + ) + }, + _enhance: function () { + this._updateIcon(this.element[0].checked) + }, + widget: function () { + return this.label + }, + _getRadioGroup: function () { + var t = this.element[0].name, + e = "input[name='" + x.escapeSelector(t) + "']" + return t + ? (this.form.length + ? x(this.form[0].elements).filter(e) + : x(e).filter(function () { + return 0 === x(this)._form().length + }) + ).not(this.element) + : x([]) + }, + _toggleClasses: function () { + var t = this.element[0].checked + this._toggleClass( + this.label, + 'ui-checkboxradio-checked', + 'ui-state-active', + t + ), + this.options.icon && + 'checkbox' === this.type && + this._toggleClass( + this.icon, + null, + 'ui-icon-check ui-state-checked', + t + )._toggleClass(this.icon, null, 'ui-icon-blank', !t), + 'radio' === this.type && + this._getRadioGroup().each(function () { + var t = x(this).checkboxradio('instance') + t && + t._removeClass( + t.label, + 'ui-checkboxradio-checked', + 'ui-state-active' + ) + }) + }, + _destroy: function () { + this._unbindFormResetHandler(), + this.icon && (this.icon.remove(), this.iconSpace.remove()) + }, + _setOption: function (t, e) { + if ('label' !== t || e) { + if ((this._super(t, e), 'disabled' === t)) + return ( + this._toggleClass( + this.label, + null, + 'ui-state-disabled', + e + ), + void (this.element[0].disabled = e) + ) + this.refresh() + } + }, + _updateIcon: function (t) { + var e = 'ui-icon ui-icon-background ' + this.options.icon + ? (this.icon || + ((this.icon = x('')), + (this.iconSpace = x(' ')), + this._addClass( + this.iconSpace, + 'ui-checkboxradio-icon-space' + )), + 'checkbox' === this.type + ? ((e += t + ? 'ui-icon-check ui-state-checked' + : 'ui-icon-blank'), + this._removeClass( + this.icon, + null, + t ? 'ui-icon-blank' : 'ui-icon-check' + )) + : (e += 'ui-icon-blank'), + this._addClass(this.icon, 'ui-checkboxradio-icon', e), + t || + this._removeClass( + this.icon, + null, + 'ui-icon-check ui-state-checked' + ), + this.icon.prependTo(this.label).after(this.iconSpace)) + : void 0 !== this.icon && + (this.icon.remove(), + this.iconSpace.remove(), + delete this.icon) + }, + _updateLabel: function () { + var t = this.label.contents().not(this.element[0]) + this.icon && (t = t.not(this.icon[0])), + (t = this.iconSpace + ? t.not(this.iconSpace[0]) + : t).remove(), + this.label.append(this.options.label) + }, + refresh: function () { + var t = this.element[0].checked, + e = this.element[0].disabled + this._updateIcon(t), + this._toggleClass( + this.label, + 'ui-checkboxradio-checked', + 'ui-state-active', + t + ), + null !== this.options.label && this._updateLabel(), + e !== this.options.disabled && + this._setOptions({ disabled: e }) + }, + }, + ]) + var m + x.ui.checkboxradio + x.widget('ui.button', { + version: '1.13.1', + defaultElement: '" + ) + .button({ + label: x('').text(this.options.closeText).html(), + icon: 'ui-icon-closethick', + showLabel: !1, + }) + .appendTo(this.uiDialogTitlebar)), + this._addClass( + this.uiDialogTitlebarClose, + 'ui-dialog-titlebar-close' + ), + this._on(this.uiDialogTitlebarClose, { + click: function (t) { + t.preventDefault(), this.close(t) + }, + }), + (t = x('').uniqueId().prependTo(this.uiDialogTitlebar)), + this._addClass(t, 'ui-dialog-title'), + this._title(t), + this.uiDialogTitlebar.prependTo(this.uiDialog), + this.uiDialog.attr({ 'aria-labelledby': t.attr('id') }) + }, + _title: function (t) { + this.options.title ? t.text(this.options.title) : t.html(' ') + }, + _createButtonPane: function () { + ;(this.uiDialogButtonPane = x('
')), + this._addClass( + this.uiDialogButtonPane, + 'ui-dialog-buttonpane', + 'ui-widget-content ui-helper-clearfix' + ), + (this.uiButtonSet = x('
').appendTo( + this.uiDialogButtonPane + )), + this._addClass(this.uiButtonSet, 'ui-dialog-buttonset'), + this._createButtons() + }, + _createButtons: function () { + var s = this, + t = this.options.buttons + this.uiDialogButtonPane.remove(), + this.uiButtonSet.empty(), + x.isEmptyObject(t) || (Array.isArray(t) && !t.length) + ? this._removeClass(this.uiDialog, 'ui-dialog-buttons') + : (x.each(t, function (t, e) { + var i + ;(e = x.extend( + { type: 'button' }, + (e = + 'function' == typeof e + ? { click: e, text: t } + : e) + )), + (i = e.click), + (t = { + icon: e.icon, + iconPosition: e.iconPosition, + showLabel: e.showLabel, + icons: e.icons, + text: e.text, + }), + delete e.click, + delete e.icon, + delete e.iconPosition, + delete e.showLabel, + delete e.icons, + 'boolean' == typeof e.text && delete e.text, + x('', e) + .button(t) + .appendTo(s.uiButtonSet) + .on('click', function () { + i.apply(s.element[0], arguments) + }) + }), + this._addClass(this.uiDialog, 'ui-dialog-buttons'), + this.uiDialogButtonPane.appendTo(this.uiDialog)) + }, + _makeDraggable: function () { + var o = this, + n = this.options + function r(t) { + return { position: t.position, offset: t.offset } + } + this.uiDialog.draggable({ + cancel: '.ui-dialog-content, .ui-dialog-titlebar-close', + handle: '.ui-dialog-titlebar', + containment: 'document', + start: function (t, e) { + o._addClass(x(this), 'ui-dialog-dragging'), + o._blockFrames(), + o._trigger('dragStart', t, r(e)) + }, + drag: function (t, e) { + o._trigger('drag', t, r(e)) + }, + stop: function (t, e) { + var i = e.offset.left - o.document.scrollLeft(), + s = e.offset.top - o.document.scrollTop() + ;(n.position = { + my: 'left top', + at: + 'left' + + (0 <= i ? '+' : '') + + i + + ' top' + + (0 <= s ? '+' : '') + + s, + of: o.window, + }), + o._removeClass(x(this), 'ui-dialog-dragging'), + o._unblockFrames(), + o._trigger('dragStop', t, r(e)) + }, + }) + }, + _makeResizable: function () { + var o = this, + n = this.options, + t = n.resizable, + e = this.uiDialog.css('position'), + t = 'string' == typeof t ? t : 'n,e,s,w,se,sw,ne,nw' + function r(t) { + return { + originalPosition: t.originalPosition, + originalSize: t.originalSize, + position: t.position, + size: t.size, + } + } + this.uiDialog + .resizable({ + cancel: '.ui-dialog-content', + containment: 'document', + alsoResize: this.element, + maxWidth: n.maxWidth, + maxHeight: n.maxHeight, + minWidth: n.minWidth, + minHeight: this._minHeight(), + handles: t, + start: function (t, e) { + o._addClass(x(this), 'ui-dialog-resizing'), + o._blockFrames(), + o._trigger('resizeStart', t, r(e)) + }, + resize: function (t, e) { + o._trigger('resize', t, r(e)) + }, + stop: function (t, e) { + var i = o.uiDialog.offset(), + s = i.left - o.document.scrollLeft(), + i = i.top - o.document.scrollTop() + ;(n.height = o.uiDialog.height()), + (n.width = o.uiDialog.width()), + (n.position = { + my: 'left top', + at: + 'left' + + (0 <= s ? '+' : '') + + s + + ' top' + + (0 <= i ? '+' : '') + + i, + of: o.window, + }), + o._removeClass(x(this), 'ui-dialog-resizing'), + o._unblockFrames(), + o._trigger('resizeStop', t, r(e)) + }, + }) + .css('position', e) + }, + _trackFocus: function () { + this._on(this.widget(), { + focusin: function (t) { + this._makeFocusTarget(), + (this._focusedElement = x(t.target)) + }, + }) + }, + _makeFocusTarget: function () { + this._untrackInstance(), this._trackingInstances().unshift(this) + }, + _untrackInstance: function () { + var t = this._trackingInstances(), + e = x.inArray(this, t) + ;-1 !== e && t.splice(e, 1) + }, + _trackingInstances: function () { + var t = this.document.data('ui-dialog-instances') + return t || this.document.data('ui-dialog-instances', (t = [])), t + }, + _minHeight: function () { + var t = this.options + return 'auto' === t.height + ? t.minHeight + : Math.min(t.minHeight, t.height) + }, + _position: function () { + var t = this.uiDialog.is(':visible') + t || this.uiDialog.show(), + this.uiDialog.position(this.options.position), + t || this.uiDialog.hide() + }, + _setOptions: function (t) { + var i = this, + s = !1, + o = {} + x.each(t, function (t, e) { + i._setOption(t, e), + t in i.sizeRelatedOptions && (s = !0), + t in i.resizableRelatedOptions && (o[t] = e) + }), + s && (this._size(), this._position()), + this.uiDialog.is(':data(ui-resizable)') && + this.uiDialog.resizable('option', o) + }, + _setOption: function (t, e) { + var i, + s = this.uiDialog + 'disabled' !== t && + (this._super(t, e), + 'appendTo' === t && this.uiDialog.appendTo(this._appendTo()), + 'buttons' === t && this._createButtons(), + 'closeText' === t && + this.uiDialogTitlebarClose.button({ + label: x('') + .text('' + this.options.closeText) + .html(), + }), + 'draggable' === t && + ((i = s.is(':data(ui-draggable)')) && + !e && + s.draggable('destroy'), + !i && e && this._makeDraggable()), + 'position' === t && this._position(), + 'resizable' === t && + ((i = s.is(':data(ui-resizable)')) && + !e && + s.resizable('destroy'), + i && + 'string' == typeof e && + s.resizable('option', 'handles', e), + i || !1 === e || this._makeResizable()), + 'title' === t && + this._title(this.uiDialogTitlebar.find('.ui-dialog-title'))) + }, + _size: function () { + var t, + e, + i, + s = this.options + this.element.show().css({ + width: 'auto', + minHeight: 0, + maxHeight: 'none', + height: 0, + }), + s.minWidth > s.width && (s.width = s.minWidth), + (t = this.uiDialog + .css({ height: 'auto', width: s.width }) + .outerHeight()), + (e = Math.max(0, s.minHeight - t)), + (i = + 'number' == typeof s.maxHeight + ? Math.max(0, s.maxHeight - t) + : 'none'), + 'auto' === s.height + ? this.element.css({ + minHeight: e, + maxHeight: i, + height: 'auto', + }) + : this.element.height(Math.max(0, s.height - t)), + this.uiDialog.is(':data(ui-resizable)') && + this.uiDialog.resizable( + 'option', + 'minHeight', + this._minHeight() + ) + }, + _blockFrames: function () { + this.iframeBlocks = this.document.find('iframe').map(function () { + var t = x(this) + return x('
') + .css({ + position: 'absolute', + width: t.outerWidth(), + height: t.outerHeight(), + }) + .appendTo(t.parent()) + .offset(t.offset())[0] + }) + }, + _unblockFrames: function () { + this.iframeBlocks && + (this.iframeBlocks.remove(), delete this.iframeBlocks) + }, + _allowInteraction: function (t) { + return ( + !!x(t.target).closest('.ui-dialog').length || + !!x(t.target).closest('.ui-datepicker').length + ) + }, + _createOverlay: function () { + var i, s + this.options.modal && + ((i = x.fn.jquery.substring(0, 4)), + (s = !0), + this._delay(function () { + s = !1 + }), + this.document.data('ui-dialog-overlays') || + this.document.on( + 'focusin.ui-dialog', + function (t) { + var e + s || + (e = + this._trackingInstances()[0])._allowInteraction( + t + ) || + (t.preventDefault(), + e._focusTabbable(), + ('3.4.' !== i && '3.5.' !== i) || + e._delay(e._restoreTabbableFocus)) + }.bind(this) + ), + (this.overlay = x('
').appendTo(this._appendTo())), + this._addClass( + this.overlay, + null, + 'ui-widget-overlay ui-front' + ), + this._on(this.overlay, { mousedown: '_keepFocus' }), + this.document.data( + 'ui-dialog-overlays', + (this.document.data('ui-dialog-overlays') || 0) + 1 + )) + }, + _destroyOverlay: function () { + var t + this.options.modal && + this.overlay && + ((t = this.document.data('ui-dialog-overlays') - 1) + ? this.document.data('ui-dialog-overlays', t) + : (this.document.off('focusin.ui-dialog'), + this.document.removeData('ui-dialog-overlays')), + this.overlay.remove(), + (this.overlay = null)) + }, + }), + !1 !== x.uiBackCompat && + x.widget('ui.dialog', x.ui.dialog, { + options: { dialogClass: '' }, + _createWrapper: function () { + this._super(), + this.uiDialog.addClass(this.options.dialogClass) + }, + _setOption: function (t, e) { + 'dialogClass' === t && + this.uiDialog + .removeClass(this.options.dialogClass) + .addClass(e), + this._superApply(arguments) + }, + }) + x.ui.dialog + var v = x, + _ = {}, + b = _.toString, + y = /^([\-+])=\s*(\d+\.?\d*)/, + w = [ + { + re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/, + parse: function (t) { + return [t[1], t[2], t[3], t[4]] + }, + }, + { + re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/, + parse: function (t) { + return [2.55 * t[1], 2.55 * t[2], 2.55 * t[3], t[4]] + }, + }, + { + re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})?/, + parse: function (t) { + return [ + parseInt(t[1], 16), + parseInt(t[2], 16), + parseInt(t[3], 16), + t[4] ? (parseInt(t[4], 16) / 255).toFixed(2) : 1, + ] + }, + }, + { + re: /#([a-f0-9])([a-f0-9])([a-f0-9])([a-f0-9])?/, + parse: function (t) { + return [ + parseInt(t[1] + t[1], 16), + parseInt(t[2] + t[2], 16), + parseInt(t[3] + t[3], 16), + t[4] ? (parseInt(t[4] + t[4], 16) / 255).toFixed(2) : 1, + ] + }, + }, + { + re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/, + space: 'hsla', + parse: function (t) { + return [t[1], t[2] / 100, t[3] / 100, t[4]] + }, + }, + ], + I = (v.Color = function (t, e, i, s) { + return new v.Color.fn.parse(t, e, i, s) + }), + S = { + rgba: { + props: { + red: { idx: 0, type: 'byte' }, + green: { idx: 1, type: 'byte' }, + blue: { idx: 2, type: 'byte' }, + }, + }, + hsla: { + props: { + hue: { idx: 0, type: 'degrees' }, + saturation: { idx: 1, type: 'percent' }, + lightness: { idx: 2, type: 'percent' }, + }, + }, + }, + D = { + byte: { floor: !0, max: 255 }, + percent: { max: 1 }, + degrees: { mod: 360, floor: !0 }, + }, + W = (I.support = {}), + O = v('

')[0], + E = v.each + function L(t) { + return null == t + ? t + '' + : 'object' == typeof t + ? _[b.call(t)] || 'object' + : typeof t + } + function N(t, e, i) { + var s = D[e.type] || {} + return null == t + ? i || !e.def + ? null + : e.def + : ((t = s.floor ? ~~t : parseFloat(t)), + isNaN(t) + ? e.def + : s.mod + ? (t + s.mod) % s.mod + : Math.min(s.max, Math.max(0, t))) + } + function R(s) { + var o = I(), + n = (o._rgba = []) + return ( + (s = s.toLowerCase()), + E(w, function (t, e) { + var i = e.re.exec(s), + i = i && e.parse(i), + e = e.space || 'rgba' + if (i) + return ( + (i = o[e](i)), + (o[S[e].cache] = i[S[e].cache]), + (n = o._rgba = i._rgba), + !1 + ) + }), + n.length + ? ('0,0,0,0' === n.join() && v.extend(n, V.transparent), o) + : V[s] + ) + } + function M(t, e, i) { + return 6 * (i = (i + 1) % 1) < 1 + ? t + (e - t) * i * 6 + : 2 * i < 1 + ? e + : 3 * i < 2 + ? t + (e - t) * (2 / 3 - i) * 6 + : t + } + ;(O.style.cssText = 'background-color:rgba(1,1,1,.5)'), + (W.rgba = -1 < O.style.backgroundColor.indexOf('rgba')), + E(S, function (t, e) { + ;(e.cache = '_' + t), + (e.props.alpha = { idx: 3, type: 'percent', def: 1 }) + }), + v.each( + 'Boolean Number String Function Array Date RegExp Object Error Symbol'.split( + ' ' + ), + function (t, e) { + _['[object ' + e + ']'] = e.toLowerCase() + } + ), + ((I.fn = v.extend(I.prototype, { + parse: function (o, t, e, i) { + if (void 0 === o) + return (this._rgba = [null, null, null, null]), this + ;(o.jquery || o.nodeType) && ((o = v(o).css(t)), (t = void 0)) + var n = this, + s = L(o), + r = (this._rgba = []) + return ( + void 0 !== t && ((o = [o, t, e, i]), (s = 'array')), + 'string' === s + ? this.parse(R(o) || V._default) + : 'array' === s + ? (E(S.rgba.props, function (t, e) { + r[e.idx] = N(o[e.idx], e) + }), + this) + : 'object' === s + ? (E( + S, + o instanceof I + ? function (t, e) { + o[e.cache] && + (n[e.cache] = o[e.cache].slice()) + } + : function (t, i) { + var s = i.cache + E(i.props, function (t, e) { + if (!n[s] && i.to) { + if ( + 'alpha' === t || + null == o[t] + ) + return + n[s] = i.to(n._rgba) + } + n[s][e.idx] = N(o[t], e, !0) + }), + n[s] && + v.inArray( + null, + n[s].slice(0, 3) + ) < 0 && + (null == n[s][3] && + (n[s][3] = 1), + i.from && + (n._rgba = i.from(n[s]))) + } + ), + this) + : void 0 + ) + }, + is: function (t) { + var o = I(t), + n = !0, + r = this + return ( + E(S, function (t, e) { + var i, + s = o[e.cache] + return ( + s && + ((i = + r[e.cache] || + (e.to && e.to(r._rgba)) || + []), + E(e.props, function (t, e) { + if (null != s[e.idx]) + return (n = s[e.idx] === i[e.idx]) + })), + n + ) + }), + n + ) + }, + _space: function () { + var i = [], + s = this + return ( + E(S, function (t, e) { + s[e.cache] && i.push(t) + }), + i.pop() + ) + }, + transition: function (t, r) { + var e = (l = I(t))._space(), + i = S[e], + t = 0 === this.alpha() ? I('transparent') : this, + a = t[i.cache] || i.to(t._rgba), + h = a.slice(), + l = l[i.cache] + return ( + E(i.props, function (t, e) { + var i = e.idx, + s = a[i], + o = l[i], + n = D[e.type] || {} + null !== o && + (null === s + ? (h[i] = o) + : (n.mod && + (o - s > n.mod / 2 + ? (s += n.mod) + : s - o > n.mod / 2 && (s -= n.mod)), + (h[i] = N((o - s) * r + s, e)))) + }), + this[e](h) + ) + }, + blend: function (t) { + if (1 === this._rgba[3]) return this + var e = this._rgba.slice(), + i = e.pop(), + s = I(t)._rgba + return I( + v.map(e, function (t, e) { + return (1 - i) * s[e] + i * t + }) + ) + }, + toRgbaString: function () { + var t = 'rgba(', + e = v.map(this._rgba, function (t, e) { + return null != t ? t : 2 < e ? 1 : 0 + }) + return 1 === e[3] && (e.pop(), (t = 'rgb(')), t + e.join() + ')' + }, + toHslaString: function () { + var t = 'hsla(', + e = v.map(this.hsla(), function (t, e) { + return ( + null == t && (t = 2 < e ? 1 : 0), + (t = e && e < 3 ? Math.round(100 * t) + '%' : t) + ) + }) + return 1 === e[3] && (e.pop(), (t = 'hsl(')), t + e.join() + ')' + }, + toHexString: function (t) { + var e = this._rgba.slice(), + i = e.pop() + return ( + t && e.push(~~(255 * i)), + '#' + + v + .map(e, function (t) { + return 1 === (t = (t || 0).toString(16)).length + ? '0' + t + : t + }) + .join('') + ) + }, + toString: function () { + return 0 === this._rgba[3] ? 'transparent' : this.toRgbaString() + }, + })).parse.prototype = I.fn), + (S.hsla.to = function (t) { + if (null == t[0] || null == t[1] || null == t[2]) + return [null, null, null, t[3]] + var e = t[0] / 255, + i = t[1] / 255, + s = t[2] / 255, + o = t[3], + n = Math.max(e, i, s), + r = Math.min(e, i, s), + a = n - r, + h = n + r, + t = 0.5 * h, + i = + r === n + ? 0 + : e === n + ? (60 * (i - s)) / a + 360 + : i === n + ? (60 * (s - e)) / a + 120 + : (60 * (e - i)) / a + 240, + h = 0 == a ? 0 : t <= 0.5 ? a / h : a / (2 - h) + return [Math.round(i) % 360, h, t, null == o ? 1 : o] + }), + (S.hsla.from = function (t) { + if (null == t[0] || null == t[1] || null == t[2]) + return [null, null, null, t[3]] + var e = t[0] / 360, + i = t[1], + s = t[2], + t = t[3], + i = s <= 0.5 ? s * (1 + i) : s + i - s * i, + s = 2 * s - i + return [ + Math.round(255 * M(s, i, e + 1 / 3)), + Math.round(255 * M(s, i, e)), + Math.round(255 * M(s, i, e - 1 / 3)), + t, + ] + }), + E(S, function (h, t) { + var e = t.props, + n = t.cache, + r = t.to, + a = t.from + ;(I.fn[h] = function (t) { + if ((r && !this[n] && (this[n] = r(this._rgba)), void 0 === t)) + return this[n].slice() + var i = L(t), + s = 'array' === i || 'object' === i ? t : arguments, + o = this[n].slice() + return ( + E(e, function (t, e) { + t = s['object' === i ? t : e.idx] + null == t && (t = o[e.idx]), (o[e.idx] = N(t, e)) + }), + a ? (((t = I(a(o)))[n] = o), t) : I(o) + ) + }), + E(e, function (r, a) { + I.fn[r] || + (I.fn[r] = function (t) { + var e, + i = L(t), + s = + 'alpha' === r + ? this._hsla + ? 'hsla' + : 'rgba' + : h, + o = this[s](), + n = o[a.idx] + return 'undefined' === i + ? n + : ('function' === i && + (i = L((t = t.call(this, n)))), + null == t && a.empty + ? this + : ('string' === i && + (e = y.exec(t)) && + (t = + n + + parseFloat(e[2]) * + ('+' === e[1] ? 1 : -1)), + (o[a.idx] = t), + this[s](o))) + }) + }) + }), + (I.hook = function (t) { + t = t.split(' ') + E(t, function (t, n) { + ;(v.cssHooks[n] = { + set: function (t, e) { + var i, + s, + o = '' + if ( + 'transparent' !== e && + ('string' !== L(e) || (i = R(e))) + ) { + if ( + ((e = I(i || e)), !W.rgba && 1 !== e._rgba[3]) + ) { + for ( + s = + 'backgroundColor' === n + ? t.parentNode + : t; + ('' === o || 'transparent' === o) && + s && + s.style; + + ) + try { + ;(o = v.css(s, 'backgroundColor')), + (s = s.parentNode) + } catch (t) {} + e = e.blend( + o && 'transparent' !== o ? o : '_default' + ) + } + e = e.toRgbaString() + } + try { + t.style[n] = e + } catch (t) {} + }, + }), + (v.fx.step[n] = function (t) { + t.colorInit || + ((t.start = I(t.elem, n)), + (t.end = I(t.end)), + (t.colorInit = !0)), + v.cssHooks[n].set( + t.elem, + t.start.transition(t.end, t.pos) + ) + }) + }) + })( + 'backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor' + ), + (v.cssHooks.borderColor = { + expand: function (i) { + var s = {} + return ( + E(['Top', 'Right', 'Bottom', 'Left'], function (t, e) { + s['border' + e + 'Color'] = i + }), + s + ) + }, + }) + var A, + B, + F, + q, + j, + X, + Y, + U, + K, + $, + V = (v.Color.names = { + aqua: '#00ffff', + black: '#000000', + blue: '#0000ff', + fuchsia: '#ff00ff', + gray: '#808080', + green: '#008000', + lime: '#00ff00', + maroon: '#800000', + navy: '#000080', + olive: '#808000', + purple: '#800080', + red: '#ff0000', + silver: '#c0c0c0', + teal: '#008080', + white: '#ffffff', + yellow: '#ffff00', + transparent: [null, null, null, 0], + _default: '#ffffff', + }), + Q = 'ui-effects-', + G = 'ui-effects-style', + Z = 'ui-effects-animated' + function J(t) { + var e, + i, + s = t.ownerDocument.defaultView + ? t.ownerDocument.defaultView.getComputedStyle(t, null) + : t.currentStyle, + o = {} + if (s && s.length && s[0] && s[s[0]]) + for (i = s.length; i--; ) + 'string' == typeof s[(e = s[i])] && + (o[ + e.replace(/-([\da-z])/gi, function (t, e) { + return e.toUpperCase() + }) + ] = s[e]) + else for (e in s) 'string' == typeof s[e] && (o[e] = s[e]) + return o + } + function tt(t, e, i, s) { + return ( + (t = { effect: (t = x.isPlainObject(t) ? (e = t).effect : t) }), + 'function' == typeof (e = null == e ? {} : e) && + ((s = e), (i = null), (e = {})), + ('number' != typeof e && !x.fx.speeds[e]) || + ((s = i), (i = e), (e = {})), + 'function' == typeof i && ((s = i), (i = null)), + e && x.extend(t, e), + (i = i || e.duration), + (t.duration = x.fx.off + ? 0 + : 'number' == typeof i + ? i + : i in x.fx.speeds + ? x.fx.speeds[i] + : x.fx.speeds._default), + (t.complete = s || e.complete), + t + ) + } + function et(t) { + return ( + !t || + 'number' == typeof t || + x.fx.speeds[t] || + ('string' == typeof t && !x.effects.effect[t]) || + 'function' == typeof t || + ('object' == typeof t && !t.effect) + ) + } + function it(t, e) { + var i = e.outerWidth(), + e = e.outerHeight(), + t = + /^rect\((-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto)\)$/.exec( + t + ) || ['', 0, i, e, 0] + return { + top: parseFloat(t[1]) || 0, + right: 'auto' === t[2] ? i : parseFloat(t[2]), + bottom: 'auto' === t[3] ? e : parseFloat(t[3]), + left: parseFloat(t[4]) || 0, + } + } + ;(x.effects = { effect: {} }), + (q = ['add', 'remove', 'toggle']), + (j = { + border: 1, + borderBottom: 1, + borderColor: 1, + borderLeft: 1, + borderRight: 1, + borderTop: 1, + borderWidth: 1, + margin: 1, + padding: 1, + }), + x.each( + [ + 'borderLeftStyle', + 'borderRightStyle', + 'borderBottomStyle', + 'borderTopStyle', + ], + function (t, e) { + x.fx.step[e] = function (t) { + ;(('none' !== t.end && !t.setAttr) || + (1 === t.pos && !t.setAttr)) && + (v.style(t.elem, e, t.end), (t.setAttr = !0)) + } + } + ), + x.fn.addBack || + (x.fn.addBack = function (t) { + return this.add( + null == t ? this.prevObject : this.prevObject.filter(t) + ) + }), + (x.effects.animateClass = function (o, t, e, i) { + var n = x.speed(t, e, i) + return this.queue(function () { + var i = x(this), + t = i.attr('class') || '', + e = (e = n.children ? i.find('*').addBack() : i).map( + function () { + return { el: x(this), start: J(this) } + } + ), + s = function () { + x.each(q, function (t, e) { + o[e] && i[e + 'Class'](o[e]) + }) + } + s(), + (e = e.map(function () { + return ( + (this.end = J(this.el[0])), + (this.diff = (function (t, e) { + var i, + s, + o = {} + for (i in e) + (s = e[i]), + t[i] !== s && + (j[i] || + (!x.fx.step[i] && + isNaN(parseFloat(s))) || + (o[i] = s)) + return o + })(this.start, this.end)), + this + ) + })), + i.attr('class', t), + (e = e.map(function () { + var t = this, + e = x.Deferred(), + i = x.extend({}, n, { + queue: !1, + complete: function () { + e.resolve(t) + }, + }) + return this.el.animate(this.diff, i), e.promise() + })), + x.when.apply(x, e.get()).done(function () { + s(), + x.each(arguments, function () { + var e = this.el + x.each(this.diff, function (t) { + e.css(t, '') + }) + }), + n.complete.call(i[0]) + }) + }) + }), + x.fn.extend({ + addClass: + ((F = x.fn.addClass), + function (t, e, i, s) { + return e + ? x.effects.animateClass.call(this, { add: t }, e, i, s) + : F.apply(this, arguments) + }), + removeClass: + ((B = x.fn.removeClass), + function (t, e, i, s) { + return 1 < arguments.length + ? x.effects.animateClass.call( + this, + { remove: t }, + e, + i, + s + ) + : B.apply(this, arguments) + }), + toggleClass: + ((A = x.fn.toggleClass), + function (t, e, i, s, o) { + return 'boolean' == typeof e || void 0 === e + ? i + ? x.effects.animateClass.call( + this, + e ? { add: t } : { remove: t }, + i, + s, + o + ) + : A.apply(this, arguments) + : x.effects.animateClass.call( + this, + { toggle: t }, + e, + i, + s + ) + }), + switchClass: function (t, e, i, s, o) { + return x.effects.animateClass.call( + this, + { add: e, remove: t }, + i, + s, + o + ) + }, + }), + x.expr && + x.expr.pseudos && + x.expr.pseudos.animated && + (x.expr.pseudos.animated = + ((X = x.expr.pseudos.animated), + function (t) { + return !!x(t).data(Z) || X(t) + })), + !1 !== x.uiBackCompat && + x.extend(x.effects, { + save: function (t, e) { + for (var i = 0, s = e.length; i < s; i++) + null !== e[i] && t.data(Q + e[i], t[0].style[e[i]]) + }, + restore: function (t, e) { + for (var i, s = 0, o = e.length; s < o; s++) + null !== e[s] && + ((i = t.data(Q + e[s])), t.css(e[s], i)) + }, + setMode: function (t, e) { + return (e = + 'toggle' === e + ? t.is(':hidden') + ? 'show' + : 'hide' + : e) + }, + createWrapper: function (i) { + if (i.parent().is('.ui-effects-wrapper')) return i.parent() + var s = { + width: i.outerWidth(!0), + height: i.outerHeight(!0), + float: i.css('float'), + }, + t = x('

') + .addClass('ui-effects-wrapper') + .css({ + fontSize: '100%', + background: 'transparent', + border: 'none', + margin: 0, + padding: 0, + }), + e = { width: i.width(), height: i.height() }, + o = document.activeElement + try { + o.id + } catch (t) { + o = document.body + } + return ( + i.wrap(t), + (i[0] !== o && !x.contains(i[0], o)) || + x(o).trigger('focus'), + (t = i.parent()), + 'static' === i.css('position') + ? (t.css({ position: 'relative' }), + i.css({ position: 'relative' })) + : (x.extend(s, { + position: i.css('position'), + zIndex: i.css('z-index'), + }), + x.each( + ['top', 'left', 'bottom', 'right'], + function (t, e) { + ;(s[e] = i.css(e)), + isNaN(parseInt(s[e], 10)) && + (s[e] = 'auto') + } + ), + i.css({ + position: 'relative', + top: 0, + left: 0, + right: 'auto', + bottom: 'auto', + })), + i.css(e), + t.css(s).show() + ) + }, + removeWrapper: function (t) { + var e = document.activeElement + return ( + t.parent().is('.ui-effects-wrapper') && + (t.parent().replaceWith(t), + (t[0] !== e && !x.contains(t[0], e)) || + x(e).trigger('focus')), + t + ) + }, + }), + x.extend(x.effects, { + version: '1.13.1', + define: function (t, e, i) { + return ( + i || ((i = e), (e = 'effect')), + (x.effects.effect[t] = i), + (x.effects.effect[t].mode = e), + i + ) + }, + scaledDimensions: function (t, e, i) { + if (0 === e) + return { + height: 0, + width: 0, + outerHeight: 0, + outerWidth: 0, + } + var s = 'horizontal' !== i ? (e || 100) / 100 : 1, + e = 'vertical' !== i ? (e || 100) / 100 : 1 + return { + height: t.height() * e, + width: t.width() * s, + outerHeight: t.outerHeight() * e, + outerWidth: t.outerWidth() * s, + } + }, + clipToBox: function (t) { + return { + width: t.clip.right - t.clip.left, + height: t.clip.bottom - t.clip.top, + left: t.clip.left, + top: t.clip.top, + } + }, + unshift: function (t, e, i) { + var s = t.queue() + 1 < e && s.splice.apply(s, [1, 0].concat(s.splice(e, i))), + t.dequeue() + }, + saveStyle: function (t) { + t.data(G, t[0].style.cssText) + }, + restoreStyle: function (t) { + ;(t[0].style.cssText = t.data(G) || ''), t.removeData(G) + }, + mode: function (t, e) { + t = t.is(':hidden') + return ( + 'toggle' === e && (e = t ? 'show' : 'hide'), + (e = (t ? 'hide' === e : 'show' === e) ? 'none' : e) + ) + }, + getBaseline: function (t, e) { + var i, s + switch (t[0]) { + case 'top': + i = 0 + break + case 'middle': + i = 0.5 + break + case 'bottom': + i = 1 + break + default: + i = t[0] / e.height + } + switch (t[1]) { + case 'left': + s = 0 + break + case 'center': + s = 0.5 + break + case 'right': + s = 1 + break + default: + s = t[1] / e.width + } + return { x: s, y: i } + }, + createPlaceholder: function (t) { + var e, + i = t.css('position'), + s = t.position() + return ( + t + .css({ + marginTop: t.css('marginTop'), + marginBottom: t.css('marginBottom'), + marginLeft: t.css('marginLeft'), + marginRight: t.css('marginRight'), + }) + .outerWidth(t.outerWidth()) + .outerHeight(t.outerHeight()), + /^(static|relative)/.test(i) && + ((i = 'absolute'), + (e = x('<' + t[0].nodeName + '>') + .insertAfter(t) + .css({ + display: /^(inline|ruby)/.test(t.css('display')) + ? 'inline-block' + : 'block', + visibility: 'hidden', + marginTop: t.css('marginTop'), + marginBottom: t.css('marginBottom'), + marginLeft: t.css('marginLeft'), + marginRight: t.css('marginRight'), + float: t.css('float'), + }) + .outerWidth(t.outerWidth()) + .outerHeight(t.outerHeight()) + .addClass('ui-effects-placeholder')), + t.data(Q + 'placeholder', e)), + t.css({ position: i, left: s.left, top: s.top }), + e + ) + }, + removePlaceholder: function (t) { + var e = Q + 'placeholder', + i = t.data(e) + i && (i.remove(), t.removeData(e)) + }, + cleanUp: function (t) { + x.effects.restoreStyle(t), x.effects.removePlaceholder(t) + }, + setTransition: function (s, t, o, n) { + return ( + (n = n || {}), + x.each(t, function (t, e) { + var i = s.cssUnit(e) + 0 < i[0] && (n[e] = i[0] * o + i[1]) + }), + n + ) + }, + }), + x.fn.extend({ + effect: function () { + function t(t) { + var e = x(this), + i = x.effects.mode(e, a) || n + e.data(Z, !0), + h.push(i), + n && + ('show' === i || (i === n && 'hide' === i)) && + e.show(), + (n && 'none' === i) || x.effects.saveStyle(e), + 'function' == typeof t && t() + } + var s = tt.apply(this, arguments), + o = x.effects.effect[s.effect], + n = o.mode, + e = s.queue, + i = e || 'fx', + r = s.complete, + a = s.mode, + h = [] + return x.fx.off || !o + ? a + ? this[a](s.duration, r) + : this.each(function () { + r && r.call(this) + }) + : !1 === e + ? this.each(t).each(l) + : this.queue(i, t).queue(i, l) + function l(t) { + var e = x(this) + function i() { + 'function' == typeof r && r.call(e[0]), + 'function' == typeof t && t() + } + ;(s.mode = h.shift()), + !1 === x.uiBackCompat || n + ? 'none' === s.mode + ? (e[a](), i()) + : o.call(e[0], s, function () { + e.removeData(Z), + x.effects.cleanUp(e), + 'hide' === s.mode && e.hide(), + i() + }) + : (e.is(':hidden') ? 'hide' === a : 'show' === a) + ? (e[a](), i()) + : o.call(e[0], s, i) + } + }, + show: + ((K = x.fn.show), + function (t) { + if (et(t)) return K.apply(this, arguments) + t = tt.apply(this, arguments) + return (t.mode = 'show'), this.effect.call(this, t) + }), + hide: + ((U = x.fn.hide), + function (t) { + if (et(t)) return U.apply(this, arguments) + t = tt.apply(this, arguments) + return (t.mode = 'hide'), this.effect.call(this, t) + }), + toggle: + ((Y = x.fn.toggle), + function (t) { + if (et(t) || 'boolean' == typeof t) + return Y.apply(this, arguments) + t = tt.apply(this, arguments) + return (t.mode = 'toggle'), this.effect.call(this, t) + }), + cssUnit: function (t) { + var i = this.css(t), + s = [] + return ( + x.each(['em', 'px', '%', 'pt'], function (t, e) { + 0 < i.indexOf(e) && (s = [parseFloat(i), e]) + }), + s + ) + }, + cssClip: function (t) { + return t + ? this.css( + 'clip', + 'rect(' + + t.top + + 'px ' + + t.right + + 'px ' + + t.bottom + + 'px ' + + t.left + + 'px)' + ) + : it(this.css('clip'), this) + }, + transfer: function (t, e) { + var i = x(this), + s = x(t.to), + o = 'fixed' === s.css('position'), + n = x('body'), + r = o ? n.scrollTop() : 0, + a = o ? n.scrollLeft() : 0, + n = s.offset(), + n = { + top: n.top - r, + left: n.left - a, + height: s.innerHeight(), + width: s.innerWidth(), + }, + s = i.offset(), + h = x("
") + h.appendTo('body') + .addClass(t.className) + .css({ + top: s.top - r, + left: s.left - a, + height: i.innerHeight(), + width: i.innerWidth(), + position: o ? 'fixed' : 'absolute', + }) + .animate(n, t.duration, t.easing, function () { + h.remove(), 'function' == typeof e && e() + }) + }, + }), + (x.fx.step.clip = function (t) { + t.clipInit || + ((t.start = x(t.elem).cssClip()), + 'string' == typeof t.end && (t.end = it(t.end, t.elem)), + (t.clipInit = !0)), + x(t.elem).cssClip({ + top: t.pos * (t.end.top - t.start.top) + t.start.top, + right: + t.pos * (t.end.right - t.start.right) + t.start.right, + bottom: + t.pos * (t.end.bottom - t.start.bottom) + + t.start.bottom, + left: t.pos * (t.end.left - t.start.left) + t.start.left, + }) + }), + ($ = {}), + x.each(['Quad', 'Cubic', 'Quart', 'Quint', 'Expo'], function (e, t) { + $[t] = function (t) { + return Math.pow(t, e + 2) + } + }), + x.extend($, { + Sine: function (t) { + return 1 - Math.cos((t * Math.PI) / 2) + }, + Circ: function (t) { + return 1 - Math.sqrt(1 - t * t) + }, + Elastic: function (t) { + return 0 === t || 1 === t + ? t + : -Math.pow(2, 8 * (t - 1)) * + Math.sin(((80 * (t - 1) - 7.5) * Math.PI) / 15) + }, + Back: function (t) { + return t * t * (3 * t - 2) + }, + Bounce: function (t) { + for (var e, i = 4; t < ((e = Math.pow(2, --i)) - 1) / 11; ); + return ( + 1 / Math.pow(4, 3 - i) - + 7.5625 * Math.pow((3 * e - 2) / 22 - t, 2) + ) + }, + }), + x.each($, function (t, e) { + ;(x.easing['easeIn' + t] = e), + (x.easing['easeOut' + t] = function (t) { + return 1 - e(1 - t) + }), + (x.easing['easeInOut' + t] = function (t) { + return t < 0.5 ? e(2 * t) / 2 : 1 - e(-2 * t + 2) / 2 + }) + }) + ;(O = x.effects), + x.effects.define('blind', 'hide', function (t, e) { + var i = { + up: ['bottom', 'top'], + vertical: ['bottom', 'top'], + down: ['top', 'bottom'], + left: ['right', 'left'], + horizontal: ['right', 'left'], + right: ['left', 'right'], + }, + s = x(this), + o = t.direction || 'up', + n = s.cssClip(), + r = { clip: x.extend({}, n) }, + a = x.effects.createPlaceholder(s) + ;(r.clip[i[o][0]] = r.clip[i[o][1]]), + 'show' === t.mode && + (s.cssClip(r.clip), + a && a.css(x.effects.clipToBox(r)), + (r.clip = n)), + a && a.animate(x.effects.clipToBox(r), t.duration, t.easing), + s.animate(r, { + queue: !1, + duration: t.duration, + easing: t.easing, + complete: e, + }) + }), + x.effects.define('bounce', function (t, e) { + var i, + s, + o = x(this), + n = t.mode, + r = 'hide' === n, + a = 'show' === n, + h = t.direction || 'up', + l = t.distance, + c = t.times || 5, + n = 2 * c + (a || r ? 1 : 0), + p = t.duration / n, + u = t.easing, + d = 'up' === h || 'down' === h ? 'top' : 'left', + f = 'up' === h || 'left' === h, + g = 0, + t = o.queue().length + for ( + x.effects.createPlaceholder(o), + h = o.css(d), + l = l || o['top' == d ? 'outerHeight' : 'outerWidth']() / 3, + a && + (((s = { opacity: 1 })[d] = h), + o + .css('opacity', 0) + .css(d, f ? 2 * -l : 2 * l) + .animate(s, p, u)), + r && (l /= Math.pow(2, c - 1)), + (s = {})[d] = h; + g < c; + g++ + ) + ((i = {})[d] = (f ? '-=' : '+=') + l), + o.animate(i, p, u).animate(s, p, u), + (l = r ? 2 * l : l / 2) + r && + (((i = { opacity: 0 })[d] = (f ? '-=' : '+=') + l), + o.animate(i, p, u)), + o.queue(e), + x.effects.unshift(o, t, 1 + n) + }), + x.effects.define('clip', 'hide', function (t, e) { + var i = {}, + s = x(this), + o = t.direction || 'vertical', + n = 'both' === o, + r = n || 'horizontal' === o, + n = n || 'vertical' === o, + o = s.cssClip() + ;(i.clip = { + top: n ? (o.bottom - o.top) / 2 : o.top, + right: r ? (o.right - o.left) / 2 : o.right, + bottom: n ? (o.bottom - o.top) / 2 : o.bottom, + left: r ? (o.right - o.left) / 2 : o.left, + }), + x.effects.createPlaceholder(s), + 'show' === t.mode && (s.cssClip(i.clip), (i.clip = o)), + s.animate(i, { + queue: !1, + duration: t.duration, + easing: t.easing, + complete: e, + }) + }), + x.effects.define('drop', 'hide', function (t, e) { + var i = x(this), + s = 'show' === t.mode, + o = t.direction || 'left', + n = 'up' === o || 'down' === o ? 'top' : 'left', + r = 'up' === o || 'left' === o ? '-=' : '+=', + a = '+=' == r ? '-=' : '+=', + h = { opacity: 0 } + x.effects.createPlaceholder(i), + (o = + t.distance || + i['top' == n ? 'outerHeight' : 'outerWidth'](!0) / 2), + (h[n] = r + o), + s && (i.css(h), (h[n] = a + o), (h.opacity = 1)), + i.animate(h, { + queue: !1, + duration: t.duration, + easing: t.easing, + complete: e, + }) + }), + x.effects.define('explode', 'hide', function (t, e) { + var i, + s, + o, + n, + r, + a, + h = t.pieces ? Math.round(Math.sqrt(t.pieces)) : 3, + l = h, + c = x(this), + p = 'show' === t.mode, + u = c.show().css('visibility', 'hidden').offset(), + d = Math.ceil(c.outerWidth() / l), + f = Math.ceil(c.outerHeight() / h), + g = [] + function m() { + g.push(this), + g.length === h * l && + (c.css({ visibility: 'visible' }), x(g).remove(), e()) + } + for (i = 0; i < h; i++) + for (n = u.top + i * f, a = i - (h - 1) / 2, s = 0; s < l; s++) + (o = u.left + s * d), + (r = s - (l - 1) / 2), + c + .clone() + .appendTo('body') + .wrap('
') + .css({ + position: 'absolute', + visibility: 'visible', + left: -s * d, + top: -i * f, + }) + .parent() + .addClass('ui-effects-explode') + .css({ + position: 'absolute', + overflow: 'hidden', + width: d, + height: f, + left: o + (p ? r * d : 0), + top: n + (p ? a * f : 0), + opacity: p ? 0 : 1, + }) + .animate( + { + left: o + (p ? 0 : r * d), + top: n + (p ? 0 : a * f), + opacity: p ? 1 : 0, + }, + t.duration || 500, + t.easing, + m + ) + }), + x.effects.define('fade', 'toggle', function (t, e) { + var i = 'show' === t.mode + x(this) + .css('opacity', i ? 0 : 1) + .animate( + { opacity: i ? 1 : 0 }, + { + queue: !1, + duration: t.duration, + easing: t.easing, + complete: e, + } + ) + }), + x.effects.define('fold', 'hide', function (e, t) { + var i = x(this), + s = e.mode, + o = 'show' === s, + n = 'hide' === s, + r = e.size || 15, + a = /([0-9]+)%/.exec(r), + h = e.horizFirst ? ['right', 'bottom'] : ['bottom', 'right'], + l = e.duration / 2, + c = x.effects.createPlaceholder(i), + p = i.cssClip(), + u = { clip: x.extend({}, p) }, + d = { clip: x.extend({}, p) }, + f = [p[h[0]], p[h[1]]], + s = i.queue().length + a && (r = (parseInt(a[1], 10) / 100) * f[n ? 0 : 1]), + (u.clip[h[0]] = r), + (d.clip[h[0]] = r), + (d.clip[h[1]] = 0), + o && + (i.cssClip(d.clip), + c && c.css(x.effects.clipToBox(d)), + (d.clip = p)), + i + .queue(function (t) { + c && + c + .animate(x.effects.clipToBox(u), l, e.easing) + .animate(x.effects.clipToBox(d), l, e.easing), + t() + }) + .animate(u, l, e.easing) + .animate(d, l, e.easing) + .queue(t), + x.effects.unshift(i, s, 4) + }), + x.effects.define('highlight', 'show', function (t, e) { + var i = x(this), + s = { backgroundColor: i.css('backgroundColor') } + 'hide' === t.mode && (s.opacity = 0), + x.effects.saveStyle(i), + i + .css({ + backgroundImage: 'none', + backgroundColor: t.color || '#ffff99', + }) + .animate(s, { + queue: !1, + duration: t.duration, + easing: t.easing, + complete: e, + }) + }), + x.effects.define('size', function (s, e) { + var o, + i = x(this), + t = ['fontSize'], + n = [ + 'borderTopWidth', + 'borderBottomWidth', + 'paddingTop', + 'paddingBottom', + ], + r = [ + 'borderLeftWidth', + 'borderRightWidth', + 'paddingLeft', + 'paddingRight', + ], + a = s.mode, + h = 'effect' !== a, + l = s.scale || 'both', + c = s.origin || ['middle', 'center'], + p = i.css('position'), + u = i.position(), + d = x.effects.scaledDimensions(i), + f = s.from || d, + g = s.to || x.effects.scaledDimensions(i, 0) + x.effects.createPlaceholder(i), + 'show' === a && ((a = f), (f = g), (g = a)), + (o = { + from: { y: f.height / d.height, x: f.width / d.width }, + to: { y: g.height / d.height, x: g.width / d.width }, + }), + ('box' !== l && 'both' !== l) || + (o.from.y !== o.to.y && + ((f = x.effects.setTransition(i, n, o.from.y, f)), + (g = x.effects.setTransition(i, n, o.to.y, g))), + o.from.x !== o.to.x && + ((f = x.effects.setTransition(i, r, o.from.x, f)), + (g = x.effects.setTransition(i, r, o.to.x, g)))), + ('content' !== l && 'both' !== l) || + (o.from.y !== o.to.y && + ((f = x.effects.setTransition(i, t, o.from.y, f)), + (g = x.effects.setTransition(i, t, o.to.y, g)))), + c && + ((c = x.effects.getBaseline(c, d)), + (f.top = (d.outerHeight - f.outerHeight) * c.y + u.top), + (f.left = (d.outerWidth - f.outerWidth) * c.x + u.left), + (g.top = (d.outerHeight - g.outerHeight) * c.y + u.top), + (g.left = (d.outerWidth - g.outerWidth) * c.x + u.left)), + delete f.outerHeight, + delete f.outerWidth, + i.css(f), + ('content' !== l && 'both' !== l) || + ((n = n.concat(['marginTop', 'marginBottom']).concat(t)), + (r = r.concat(['marginLeft', 'marginRight'])), + i.find('*[width]').each(function () { + var t = x(this), + e = x.effects.scaledDimensions(t), + i = { + height: e.height * o.from.y, + width: e.width * o.from.x, + outerHeight: e.outerHeight * o.from.y, + outerWidth: e.outerWidth * o.from.x, + }, + e = { + height: e.height * o.to.y, + width: e.width * o.to.x, + outerHeight: e.height * o.to.y, + outerWidth: e.width * o.to.x, + } + o.from.y !== o.to.y && + ((i = x.effects.setTransition(t, n, o.from.y, i)), + (e = x.effects.setTransition(t, n, o.to.y, e))), + o.from.x !== o.to.x && + ((i = x.effects.setTransition( + t, + r, + o.from.x, + i + )), + (e = x.effects.setTransition(t, r, o.to.x, e))), + h && x.effects.saveStyle(t), + t.css(i), + t.animate(e, s.duration, s.easing, function () { + h && x.effects.restoreStyle(t) + }) + })), + i.animate(g, { + queue: !1, + duration: s.duration, + easing: s.easing, + complete: function () { + var t = i.offset() + 0 === g.opacity && i.css('opacity', f.opacity), + h || + (i + .css( + 'position', + 'static' === p ? 'relative' : p + ) + .offset(t), + x.effects.saveStyle(i)), + e() + }, + }) + }), + x.effects.define('scale', function (t, e) { + var i = x(this), + s = t.mode, + s = + parseInt(t.percent, 10) || + (0 === parseInt(t.percent, 10) || 'effect' !== s ? 0 : 100), + s = x.extend( + !0, + { + from: x.effects.scaledDimensions(i), + to: x.effects.scaledDimensions( + i, + s, + t.direction || 'both' + ), + origin: t.origin || ['middle', 'center'], + }, + t + ) + t.fade && ((s.from.opacity = 1), (s.to.opacity = 0)), + x.effects.effect.size.call(this, s, e) + }), + x.effects.define('puff', 'hide', function (t, e) { + t = x.extend(!0, {}, t, { + fade: !0, + percent: parseInt(t.percent, 10) || 150, + }) + x.effects.effect.scale.call(this, t, e) + }), + x.effects.define('pulsate', 'show', function (t, e) { + var i = x(this), + s = t.mode, + o = 'show' === s, + n = 2 * (t.times || 5) + (o || 'hide' === s ? 1 : 0), + r = t.duration / n, + a = 0, + h = 1, + s = i.queue().length + for ( + (!o && i.is(':visible')) || + (i.css('opacity', 0).show(), (a = 1)); + h < n; + h++ + ) + i.animate({ opacity: a }, r, t.easing), (a = 1 - a) + i.animate({ opacity: a }, r, t.easing), + i.queue(e), + x.effects.unshift(i, s, 1 + n) + }), + x.effects.define('shake', function (t, e) { + var i = 1, + s = x(this), + o = t.direction || 'left', + n = t.distance || 20, + r = t.times || 3, + a = 2 * r + 1, + h = Math.round(t.duration / a), + l = 'up' === o || 'down' === o ? 'top' : 'left', + c = 'up' === o || 'left' === o, + p = {}, + u = {}, + d = {}, + o = s.queue().length + for ( + x.effects.createPlaceholder(s), + p[l] = (c ? '-=' : '+=') + n, + u[l] = (c ? '+=' : '-=') + 2 * n, + d[l] = (c ? '-=' : '+=') + 2 * n, + s.animate(p, h, t.easing); + i < r; + i++ + ) + s.animate(u, h, t.easing).animate(d, h, t.easing) + s + .animate(u, h, t.easing) + .animate(p, h / 2, t.easing) + .queue(e), + x.effects.unshift(s, o, 1 + a) + }), + x.effects.define('slide', 'show', function (t, e) { + var i, + s, + o = x(this), + n = { + up: ['bottom', 'top'], + down: ['top', 'bottom'], + left: ['right', 'left'], + right: ['left', 'right'], + }, + r = t.mode, + a = t.direction || 'left', + h = 'up' === a || 'down' === a ? 'top' : 'left', + l = 'up' === a || 'left' === a, + c = + t.distance || + o['top' == h ? 'outerHeight' : 'outerWidth'](!0), + p = {} + x.effects.createPlaceholder(o), + (i = o.cssClip()), + (s = o.position()[h]), + (p[h] = (l ? -1 : 1) * c + s), + (p.clip = o.cssClip()), + (p.clip[n[a][1]] = p.clip[n[a][0]]), + 'show' === r && + (o.cssClip(p.clip), + o.css(h, p[h]), + (p.clip = i), + (p[h] = s)), + o.animate(p, { + queue: !1, + duration: t.duration, + easing: t.easing, + complete: e, + }) + }), + !1 !== x.uiBackCompat && + x.effects.define('transfer', function (t, e) { + x(this).transfer(t, e) + }) +}) diff --git a/v0/src/simulator/vendor/table2csv.js b/v0/src/simulator/vendor/table2csv.js new file mode 100644 index 00000000..ab734062 --- /dev/null +++ b/v0/src/simulator/vendor/table2csv.js @@ -0,0 +1,113 @@ +jQuery.fn.table2CSV = function (options) { + var options = jQuery.extend( + { + separator: ',', + header: [], + headerSelector: 'none', + columnSelector: 'td, th', + delivery: 'value', // popup, value, download + // filename: 'test.csv', // filename to download + transform_gt_lt: true, // make > and < to > and < + }, + options + ) + + var csvData = [] + var headerArr = [] + var el = this + + //header + var numCols = options.header.length + var tmpRow = [] // construct header avalible array + + if (numCols > 0) { + for (var i = 0; i < numCols; i++) { + tmpRow[tmpRow.length] = formatData(options.header[i]) + } + } else { + $(el) + .filter(':visible') + .find(options.headerSelector) + .each(function () { + if ($(this).css('display') != 'none') + tmpRow[tmpRow.length] = formatData($(this).html()) + }) + } + + row2CSV(tmpRow) + + // actual data + $(el) + .find('tr') + .each(function () { + var tmpRow = [] + $(this) + .filter(':visible') + .find(options.columnSelector) + .each(function () { + if ($(this).css('display') != 'none') + tmpRow[tmpRow.length] = formatData($(this).html()) + }) + row2CSV(tmpRow) + }) + if (options.delivery == 'popup') { + var mydata = csvData.join('\n') + if (options.transform_gt_lt) { + mydata = sinri_recover_gt_and_lt(mydata) + } + return popup(mydata) + } else if (options.delivery == 'download') { + var mydata = csvData.join('\n') + if (options.transform_gt_lt) { + mydata = sinri_recover_gt_and_lt(mydata) + } + var url = 'data:text/csv;charset=utf8,' + encodeURIComponent(mydata) + window.open(url) + return true + } else { + var mydata = csvData.join('\n') + if (options.transform_gt_lt) { + mydata = sinri_recover_gt_and_lt(mydata) + } + return mydata + } + + function sinri_recover_gt_and_lt(input) { + var regexp = new RegExp(/>/g) + var input = input.replace(regexp, '>') + var regexp = new RegExp(/</g) + var input = input.replace(regexp, '<') + return input + } + + function row2CSV(tmpRow) { + var tmp = tmpRow.join('') // to remove any blank rows + // alert(tmp); + if (tmpRow.length > 0 && tmp != '') { + var mystr = tmpRow.join(options.separator) + csvData[csvData.length] = mystr + } + } + function formatData(input) { + // double " according to rfc4180 + var regexp = new RegExp(/["]/g) + var output = input.replace(regexp, '""') + //HTML + var regexp = new RegExp(/\<[^\<]+\>/g) + var output = output.replace(regexp, '') + output = output.replace(/ /gi, ' ') //replace   + if (output == '') return '' + return '"' + output.trim() + '"' + } + function popup(data) { + var generator = window.open('', 'csv', 'height=400,width=600') + generator.document.write('CSV') + generator.document.write('') + generator.document.write('') + generator.document.write('') + generator.document.close() + return true + } +} diff --git a/v0/src/store/SimulatorStore/SimulatorStore.ts b/v0/src/store/SimulatorStore/SimulatorStore.ts new file mode 100644 index 00000000..69aa9cb3 --- /dev/null +++ b/v0/src/store/SimulatorStore/SimulatorStore.ts @@ -0,0 +1,13 @@ +import { extractStore } from '../extractStore' +import { defineStore } from 'pinia' +import { useActions } from './actions' +import { useGetters } from './getters' +import { useState } from './state' + +export const SimulatorStore = defineStore('simulatorStore', () => { + return { + ...extractStore(useState()), + ...extractStore(useGetters()), + ...extractStore(useActions()), + } +}) diff --git a/v0/src/store/SimulatorStore/actions.ts b/v0/src/store/SimulatorStore/actions.ts new file mode 100644 index 00000000..e127bc5f --- /dev/null +++ b/v0/src/store/SimulatorStore/actions.ts @@ -0,0 +1,16 @@ +import { defineStore } from 'pinia' +import { useState } from './state' + +export const useActions = defineStore('simulatorStore.actions', () => { + const state = useState() + + function showTitle(): void { + console.log(state.title) + } + + // Note you are free to define as many internal functions as you want. + // You only expose the functions that are returned. + return { + showTitle, + } +}) diff --git a/v0/src/store/SimulatorStore/getters.ts b/v0/src/store/SimulatorStore/getters.ts new file mode 100644 index 00000000..6d409942 --- /dev/null +++ b/v0/src/store/SimulatorStore/getters.ts @@ -0,0 +1,15 @@ +import { defineStore } from 'pinia' +import { computed } from 'vue' +import { useState } from './state' + +export const useGetters = defineStore('simulatorStore.getters', () => { + const state = useState() + + const getTitle = computed((): string => { + return `${state.title} !!!` + }) + + return { + getTitle, + } +}) diff --git a/v0/src/store/SimulatorStore/state.ts b/v0/src/store/SimulatorStore/state.ts new file mode 100644 index 00000000..1bad89ad --- /dev/null +++ b/v0/src/store/SimulatorStore/state.ts @@ -0,0 +1,66 @@ +import { defineStore } from 'pinia' + +// use camel case variable names +export interface State { + title: string + activeCircuit: + | Object + | { + id: number | string + name: string + } + circuit_list: Array + dialogBox: { + // create_circuit: boolean + // delete_circuit: boolean + combinationalanalysis_dialog: boolean + hex_bin_dec_converter_dialog: boolean + saveimage_dialog: boolean + theme_dialog: boolean + customshortcut_dialog: boolean + insertsubcircuit_dialog: boolean + exportverilog_dialog: boolean + save_project_dialog: boolean + open_project_dialog: boolean + export_project_dialog: boolean + import_project_dialog: boolean + } + // createCircuit: Object | { circuitName: string } + combinationalAnalysis: Object +} + +export const useState = defineStore({ + id: 'simulatorStore.state', + + state: (): State => { + return { + title: 'Welcome to CircuitVerse Simulator', + activeCircuit: {}, + circuit_list: [], + dialogBox: { + // create_circuit: false, + // delete_circuit: false, + combinationalanalysis_dialog: false, + hex_bin_dec_converter_dialog: false, + saveimage_dialog: false, + theme_dialog: false, + customshortcut_dialog: false, + insertsubcircuit_dialog: false, + exportverilog_dialog: false, + save_project_dialog: false, + open_project_dialog: false, + export_project_dialog: false, + import_project_dialog: false, + }, + // createCircuit: { + // circuitName: 'Untitled Circuit', + // }, + combinationalAnalysis: { + inputNameList: 'eg. In A, In B', + outputNameList: 'eg. Out X, Out Y', + booleanExpression: 'Example: (AB)', + decimalColumnBox: false, + }, + } + }, +}) diff --git a/v0/src/store/authStore.ts b/v0/src/store/authStore.ts new file mode 100644 index 00000000..12dd433c --- /dev/null +++ b/v0/src/store/authStore.ts @@ -0,0 +1,57 @@ +import { defineStore } from 'pinia' + +interface AuthStoreType { + isLoggedIn: boolean + userId: string | number + username: string + locale: string + isAdmin: boolean +} + +interface UserInfo { + isLoggedIn: boolean + id: string + attributes: { + name: string + locale: string + admin: boolean + } +} +export const useAuthStore = defineStore({ + id: 'authStore', + state: (): AuthStoreType => ({ + isLoggedIn: false, + userId: '', + username: '', + locale: 'en', + isAdmin: false, + }), + actions: { + setUserInfo(userInfo: UserInfo): void { + this.isLoggedIn = true + this.userId = userInfo.id ?? '' + this.username = userInfo.attributes.name ?? '' + this.locale = userInfo.attributes.locale ?? 'en' + this.isAdmin = userInfo.attributes.admin + }, + }, + getters: { + getIsLoggedIn(): boolean { + return this.isLoggedIn + }, + getUserId(): string | number { + return this.userId + }, + getUsername(): string { + return this.username + }, + getLocale(): string { + return this.locale + }, + getIsAdmin(): boolean { + return this.isAdmin + }, + }, +}) + +// TODO: extract store verify and check better ways to impliment diff --git a/v0/src/store/extractStore.ts b/v0/src/store/extractStore.ts new file mode 100644 index 00000000..f7b48129 --- /dev/null +++ b/v0/src/store/extractStore.ts @@ -0,0 +1,40 @@ +import type { + PiniaCustomStateProperties, + StoreActions, + StoreGeneric, + StoreGetters, + StoreState, +} from 'pinia' +import type { ToRefs } from 'vue' +import { isReactive, isRef, toRaw, toRef } from 'vue' + +type Extracted = ToRefs< + StoreState & + StoreGetters & + PiniaCustomStateProperties> +> & + StoreActions + +/** + * Creates an object of references with all the state, getters, actions + * and plugin-added state properties of the store. + * + * @param store - store to extract the refs from + */ +export function extractStore( + store: SS +): Extracted { + const rawStore = toRaw(store) + const refs: Record = {} + + for (const [key, value] of Object.entries(rawStore)) { + if (isRef(value) || isReactive(value)) { + refs[key] = toRef(store, key) + } else if (typeof value === 'function') { + refs[key] = value + } + } + + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + return refs as Extracted +} diff --git a/v0/src/store/projectStore.ts b/v0/src/store/projectStore.ts new file mode 100644 index 00000000..3a09d0c8 --- /dev/null +++ b/v0/src/store/projectStore.ts @@ -0,0 +1,37 @@ +import { defineStore } from 'pinia' + +interface projectStoreType { + project: { + // id: number //use later if needed + name: string + nameDefined: boolean + } +} + +export const useProjectStore = defineStore({ + id: 'projectStore', + state: (): projectStoreType => ({ + project: { + // id: 0, //use later if needed + name: 'Untitled', + nameDefined: false, + }, + }), + actions: { + setProjectName(projectName: string): void { + this.project.name = projectName + this.project.nameDefined = true + }, + setProjectNameDefined(defined: boolean = true): void { + this.project.nameDefined = defined + }, + }, + getters: { + getProjectName(): string { + return this.project.name + }, + getProjectNameDefined(): boolean { + return this.project.nameDefined + }, + }, +}) diff --git a/v0/src/store/promptStore.ts b/v0/src/store/promptStore.ts new file mode 100644 index 00000000..612c35b3 --- /dev/null +++ b/v0/src/store/promptStore.ts @@ -0,0 +1,113 @@ +import { HTMLContent } from '@tiptap/core' +import { defineStore } from 'pinia' + +interface promptStoreType { + resolvePromise: Function + // resolvePromise: (value?: string | undefined) => void + prompt: { + activate: boolean + messageText: string + isPersistent: boolean + buttonList: Array<{ + text: string + emitOption: string + }> + inputList: Array<{ + text: string + val: string + placeholder: string + id: string + class: string + style: string + type: string + }> + } + confirm: { + activate: boolean + messageText: string + isPersistent: boolean + buttonList: Array<{ + text: string + emitOption: string | boolean + }> + } + DeleteCircuit: { + activate: boolean + messageText: string + isPersistent: boolean + buttonList: Array<{ + text: string + emitOption: string + }> + circuitItem: object + } + UpdateProjectDetail: { + activate: boolean + projectId: number + projectName: string + projectTags: string + projectType: Readonly | string + projectDescription: HTMLContent + } +} + +export const usePromptStore = defineStore({ + id: 'promptStore', + state: (): promptStoreType => ({ + resolvePromise: (): any => {}, + prompt: { + activate: false, + messageText: '', + isPersistent: false, + buttonList: [], + inputList: [], + }, + confirm: { + activate: false, + messageText: '', + isPersistent: false, + buttonList: [], + }, + DeleteCircuit: { + activate: false, + messageText: '', + isPersistent: false, + buttonList: [], + circuitItem: {}, + }, + UpdateProjectDetail: { + activate: false, + projectId: 0, + projectName: '', + projectTags: '', + projectType: 'Public', + projectDescription: '', + }, + }), + actions: { + // resolvePromise(): any {}, + setProjectName(projectName: string): void { + this.UpdateProjectDetail.projectName = projectName + }, + setProjectId(projectId: number): void { + this.UpdateProjectDetail.projectId = projectId + }, + }, + getters: { + getProjectName(): string { + return this.UpdateProjectDetail.projectName + }, + getProjectId(): number { + return this.UpdateProjectDetail.projectId + }, + getProjectTags(): string { + return this.UpdateProjectDetail.projectTags + }, + getProjectType(): Readonly | string { + return this.UpdateProjectDetail.projectType + }, + getProjectDescription(): HTMLContent { + return this.UpdateProjectDetail.projectDescription + }, + }, +}) diff --git a/v0/src/styles/color_theme.scss b/v0/src/styles/color_theme.scss new file mode 100644 index 00000000..5bdc9fff --- /dev/null +++ b/v0/src/styles/color_theme.scss @@ -0,0 +1,551 @@ +:root { + --primary: #454545; + + --bg-navbar: #454545; + --text-navbar--alt: #000; + --qp-br-tl: #333; + --qp-br-rd: #535353; + + --br-circuit: #454545; + --br-circuit-cur: #fff; + --bg-tabs: #8b8b8b; + --bg-circuit: #bbb; + + --text-lite: #fff; + --text-dark: #000; + --text-panel: #fff; + --text-circuit: #000; + + --context-text: #fff; + --context-text-hov: #000; + + --cus-radio_label: #656565; + + --br-secondary: #7d7d7d; + + --br-primary: #fff; + --bg-primary-moz: hsla(0, 0%, 27%, 0.902); + --bg-primary-chr: hsla(0, 0%, 27%, 0.702); + --bg-icons: #7d7d7d; + --bg-text: #cacaca; + --bg-secondary: #bbb; + --canvas-stroke:#eee; + --canvas-fill: #fff; + --bg-toggle-btn-primary: #42b983; + --primary-btn-hov: #3ca877; + --btn-danger: #dc5656; + --btn-danger-darken: #b03662; + --disable: #6c8b93; + --qp-box-shadow-1: #3b3b3b; + --qp-box-shadow-2: #4f4f4f; + --cus-btn-hov--bg: #ddd; + --cus-btn-hov-text: #000; + --node: green; + --stroke: black; + --fill: white; + --hover-and-sel: rgba(255, 255, 32, 0.8); + --wire-draw: #000; + --wire-cnt: green; + --wire-pow: Lime; + --wire-sel: blue; + --wire-lose: red; + --mini-map: green; + --mini-map-stroke: darkgreen; + --input-text: green; + --secondary-stroke: red; + --text: black; + --wire-norm: black; + --node-norm: green; + --splitter: black; + --output-rect: blue; +} + +::-moz-selection { + color: var(--text-lite); + background: var(--bg-icons); +} + +::selection { + color: var(--text-lite); + background: var(--bg-icons); +} + +.navbar-menu > li > a { + color: #fff; +} + +.projectName { + color: #fff; +} + +.header { + background: var(--bg-navbar); +} + +.dropdown > ul { + background-color: var(--bg-primary-moz); + border: 0.5px solid var(--bg-tabs); +} + +@supports (backdrop-filter: blur()) { + .dropdown > ul { + background-color: var(--bg-primary-chr); + } +} + +.dropdown > ul::before { + border-top: 1px solid var(--bg-tabs); + border-right: 1px solid var(--bg-tabs); +} + +.dropdown > ul::after { + border-top: 1.5px solid var(--primary); +} + +.dropdown-menu > li > a { + color: #fff !important; +} + +.signIn-btn { + color: #fff !important; +} + +.dropdown-menu > li > a:hover { + color: var(--text-navbar--alt) !important; + background: var(--bg-text); +} + +#contextMenu { + border: 0.5px solid var(--bg-tabs); + background-color: var(--bg-primary-moz); + color: var(--context-text); +} + +#contextMenu ul li:hover { + color: var(--context-text-hov); + background: var(--bg-text); +} + +@supports (backdrop-filter: blur()) { + #contextMenu { + background-color: var(--bg-primary-chr); + } +} + +.draggable-panel { + background: var(--primary); + border: 2px solid var(--br-primary); + box-shadow: 0px 0px 10px #4545457f; +} + +.panel-header { + color: var(--text-panel); + background: var(--primary); +} + +.panel-body { + color: var(--text-panel); + border-top: 1px solid var(--br-secondary); +} + +.panel-header::before { + border-top: 2px solid var(--text-panel); +} + +.search-input { + color: var(--text-panel); + border: 1px solid var(--br-secondary); +} + +.timing-diagram-toolbar input { + color: var(--text-panel); + border: 1px solid var(--br-secondary); +} + +.search-results, +.search-close { + color: var(--text-panel); +} + +#exitViewBtn { + border: 1px solid var(--br-primary); +} +.ce-hidden, +.prop-hidden, +#exitViewBtn { + color: var(--text-panel); + background: var(--primary); +} + +.ui-accordion-header { + color: var(--text-panel) !important; +} + +.panelHeader:hover { + background-color: var(--bg-icons); +} + +.panelHeader:after, +.panelHeader:before { + background-color: var(--br-primary); + border: 1px solid var(--br-primary); +} + +.ui-accordion .ui-accordion-content { + background-color: white; +} + +.icon { + background-color: white; +} + +.custom-tooltip-styling { + background-color: var(--bg-icons) !important; + color: var(--text-panel) !important; + border: 1px solid var(--br-primary); +} + +.icon:hover { + background-color: var(--bg-icons); +} + +.search-results::-webkit-scrollbar-thumb { + background-color: #585858; +} + +.search-results::-webkit-scrollbar-thumb:hover { + background-color: var(--primary); +} + +.timing-diagram-toolbar { + background-color: var(--bg-tabs); +} + +#tabsBar { + background-color: var(--bg-tabs); + border-top: 1px solid var(--br-primary); + border-bottom: 1px solid var(--br-primary); +} + +#tabsBar .circuits { + border: 1px solid var(--br-circuit); +} + +#tabsBar .circuits { + color: var(--text-circuit); + background-color: var(--bg-tabs); +} + +#tabsBar .current { + color: var(--text-circuit); + background-color: var(--bg-circuit); + border: 1px solid var(--br-circuit-cur); +} + +#tabsBar .current > span { +} + +#tabsBar button { + color: var(--text-panel); + background-color: var(--primary); + border: 1px solid var(--br-circuit-cur); +} + +#tabsBar button:hover { + color: var(--text-panel); + + border: 1px solid var(--br-circuit-cur); +} + +.moduleProperty input, +.moduleProperty textarea { + color: var(--text-panel); +} + +.moduleProperty { + background: var(--primary); + border: 2px solid var(--br-primary); + box-shadow: 0px 0px 10px #4545457f; + color: var(--text-panel); +} + +#moduleProperty-title { + color: var(--text-panel); +} + +.moduleProperty input, +.moduleProperty select, +.moduleProperty textarea { + border: 1px solid var(--br-secondary) !important; + color: var(--text-panel); +} + +.moduleProperty input:focus, +.moduleProperty select:focus, +.moduleProperty textarea:focus { + color: var(--text-panel); + border-color: var(--br-primary) !important; +} + +.input-group div button { + color: var(--text-lite); +} + +.input-group-prepend button:hover { + background: rgba(202, 202, 202, 0.5); +} + +.input-group-append button:hover { + background: rgba(202, 202, 202, 0.5); +} + +.slider { + background-color: #ccc; + box-shadow: inset 0px 0px 5px rgba(69, 69, 69, 0.255); +} + +.slider:before { + background-color: white; + box-shadow: 0px 0px 7px rgba(69, 69, 69, 0.8); +} + +input:checked + .slider { + background-color: var(--bg-toggle-btn-primary); +} + +.custom-btn--primary { + background-color: var(--bg-toggle-btn-primary); + color: var(--text-lite); +} +.custom-btn--primary:hover { + background-color: var(--primary-btn-hov); + color: var(--text-lite); +} +.custom-btn--secondary { + border: 1px solid white; + color: var(--text-lite); +} +.custom-btn--secondary:hover { + background-color: #ddd; + color: var(--cus-btn-hov-text); +} + +.custom-btn--secondary:active, +.custom-btn--secondary:focus { + border: 1px solid white; +} + +.custom-btn--tertiary { + background-color: var(--btn-danger); + color: var(--text-lite); +} + +.custom-btn--tertiary:hover { + background-color: var(--btn-danger-darken); + color: var(--text-lite); +} + +#HelpButton { + border: 2px solid var(--br-primary); + color: var(--text-panel); +} + +select { + background: var(--bg-secondary); + background-color: var(--primary); + color: var(--text-lite); +} + +#layoutDialog { + border: 2px solid var(--br-primary); + box-shadow: 0px 0px 10px #4545457f; + background-color: var(--primary); + color: var(--text-panel); +} + +#layoutDialog > div span:before { + color: var(--text-panel); +} + +.panel-heading { + color: var(--text-panel); +} + +.ui-dialog { + border: 0.5px solid var(--br-primary) !important; + background: var(--bg-primary-moz) !important; +} + +@supports (backdrop-filter: blur()) { + .ui-dialog { + background-color: var(--bg-primary-chr) !important; + } +} + +.ui-widget-header { + color: var(--text-lite) !important; + border-bottom: 0.5px solid var(--br-primary); +} + +.option { + background-color: white; + color: var(--cus-radio_label); +} + +.custom-radio span { + border: 3px solid var(--cus-radio_label); +} + +.custom-radio span:after { + background: var(--cus-radio_label); +} + +#saveImageDialog { + border: 1px solid var(--br-secondary); +} +.download-dialog-section-2 { + color: var(--text-lite); +} + +.download-dialog-section-2 .active-btn { + background: var(--bg-toggle-btn-primary); + color: var(--text-lite); +} + +.download-dialog-section-2 .inactive-btn { + color: var(--text-lite); +} + +.download-dialog-section-3 { + border: 1px solid var(--br-secondary); +} + +.download-dialog-section-3 > span { + color: var(--text-lite); +} + +.ui-dialog-titlebar-close::before:hover { + background-color: var(--primary); +} + +.ui-dialog .ui-dialog-buttonpane button:hover { + color: var(--cus-btn-hov-text) !important; + background: var(--cus-btn-hov--bg); + border: 1px solid transparent; +} + +.render-btn { + color: var(--text-lite); + border: 1px solid white; +} + +.render-btn:active { + border: 1px solid var(--br-primary); +} + +.render-btn:hover { + background: #3ba374; + color: var(--text-lite); + border: 1px solid transparent; +} + +#combinationalAnalysis { + border: 1px solid var(--br-secondary); + color: var(--text-lite); +} + +#combinationalAnalysis p input { + border-bottom: 1px solid white !important; + color: var(--text-lite); +} + +.content-table tr th { + background-color: var(--primary); + color: var(--text-lite); +} + +.content-table th, +.content-table td { + background-color: #f3f3f3; +} + +#openProjectDialog { + color: var(--text-lite); +} + +#openProjectDialog > label { + border: 1px solid var(--br-primary); + color: var(--text-lite); +} + +#openProjectDialog > label > span { + border: 3px solid white; +} + +#openProjectDialog > label > span:after { + background: var(--text-lite); +} + +#insertSubcircuitDialog { + color: var(--text-lite); +} + +.disable::after { + background: var(--disable); +} + +.radio-green { + background: #42b983; +} + +.btn-group-toggle { + border: 1px solid var(--br-secondary) !important; +} + +.set { + border: 2px solid cyan !important; +} + +.theme { + color: var(--text-panel) !important; + background: var(--bg-icons) !important; + border-radius: 1.5px; +} + +.input-group-prepend button:hover { + background: var(--bg-secondary) !important; + color: var(--text-lite) !important; +} + +.input-group-append button:hover { + background: var(--bg-secondary) !important; + color: var(--text-lite) !important; +} + +.input-group-prepend button { + color: var(--text-panel) !important; +} + +.input-group-append button { + color: var(--text-panel) !important; +} + +#Rectangle_1072 { + stroke: var(--text-panel); +} + +#Path_36 { + fill: var(--text-panel); +} + +.layout--btn-group { + display: block; + margin-right: 25px; + margin-top: -10px; +} + +#clockProperty { + background: var(--primary); + border: 1px solid var(--br-primary); + color: var(--text-panel); +} diff --git a/v0/src/styles/css/0-helpers/_color-config.scss b/v0/src/styles/css/0-helpers/_color-config.scss new file mode 100644 index 00000000..dfe909f0 --- /dev/null +++ b/v0/src/styles/css/0-helpers/_color-config.scss @@ -0,0 +1,22 @@ +// +// Color configuration +// + +$primary: $charcoal; //base color theme +$input: $silver; //input border (mainly bottom) +$text-primary: $white; //global font color +$text-secondary: $silver; //placeholder text +$sim-primary: $white; //simulator bg +$sim-secondary: $very-light-grey; //simulator gridlines +$dialog-primary: $primary; //dialog boxes bg color +$dialog-secondary: $grey; //dialog boxes box border color +$button-primary: $medium-sea-green; //btn primary +$button-secondary: $roman; //delete button +$error-primary: $carousel-pink; //error text color +$box-shadow: $grey-shadow; //box shadow +$border-primary: $white; //border color +$border-secondary: $suva-grey; //dropdown border color +$border-tertiary: $silver; //input box border +$bg-primary--nav: $suva-grey; //nav-bar bg color +$bg-secondary--nav: $primary; //active nav-bar menu bg color +$bg-tertiary--nav: $gainsboro; //inactive nav-bar menu bg color diff --git a/v0/src/styles/css/0-helpers/_functions.scss b/v0/src/styles/css/0-helpers/_functions.scss new file mode 100644 index 00000000..e69de29b diff --git a/v0/src/styles/css/0-helpers/_mixins.scss b/v0/src/styles/css/0-helpers/_mixins.scss new file mode 100644 index 00000000..e69de29b diff --git a/v0/src/styles/css/0-helpers/_variables.scss b/v0/src/styles/css/0-helpers/_variables.scss new file mode 100644 index 00000000..93711734 --- /dev/null +++ b/v0/src/styles/css/0-helpers/_variables.scss @@ -0,0 +1,16 @@ +// +// Color Variables +// + +$charcoal: #454545; +$silver: #bbbbbb; +$gainsboro: #dddddd; +$white: #ffffff; +$grey: #7d7d7d; +$very-light-grey: #cacaca; +$suva-grey: #8b8b8b; +$medium-sea-green: #42b983; +$roman: #dc5656; +$grey-shadow: #4545457f; +$fire-brick: #ac3522; +$carousel-pink: #f8d7da; diff --git a/v0/src/styles/css/2-basics/_buttons.scss b/v0/src/styles/css/2-basics/_buttons.scss new file mode 100644 index 00000000..e69de29b diff --git a/v0/src/styles/css/2-basics/_close.scss b/v0/src/styles/css/2-basics/_close.scss new file mode 100644 index 00000000..e69de29b diff --git a/v0/src/styles/css/2-basics/_global.scss b/v0/src/styles/css/2-basics/_global.scss new file mode 100644 index 00000000..e69de29b diff --git a/v0/src/styles/css/2-basics/_links.scss b/v0/src/styles/css/2-basics/_links.scss new file mode 100644 index 00000000..e69de29b diff --git a/v0/src/styles/css/2-basics/_toggle.scss b/v0/src/styles/css/2-basics/_toggle.scss new file mode 100644 index 00000000..e69de29b diff --git a/v0/src/styles/css/2-basics/_typography.scss b/v0/src/styles/css/2-basics/_typography.scss new file mode 100644 index 00000000..e69de29b diff --git a/v0/src/styles/css/2-basics/base.scss b/v0/src/styles/css/2-basics/base.scss new file mode 100644 index 00000000..f7f722b9 --- /dev/null +++ b/v0/src/styles/css/2-basics/base.scss @@ -0,0 +1,11 @@ +/* base/default rule set here + no class or ID selectors +*/ + +html { + box-sizing: border-box; +} + +* *:before *:after { + box-sizing: inherit; +} diff --git a/v0/src/styles/css/2-basics/reset.scss b/v0/src/styles/css/2-basics/reset.scss new file mode 100644 index 00000000..ba934af9 --- /dev/null +++ b/v0/src/styles/css/2-basics/reset.scss @@ -0,0 +1,64 @@ +/* http://meyerweb.com/eric/tools/css/reset/ + v2.0 | 20110126 + License: none (public domain) +*/ + +/* File modified - uncomment below */ +/* +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +*/ +/* HTML5 display-role reset for older browsers */ +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +menu, +nav, +section { + display: block; +} +body { + line-height: 1; +} +ol, +ul { + list-style: none; +} +blockquote, +q { + quotes: none; +} +blockquote:before, +blockquote:after, +q:before, +q:after { + content: ''; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} diff --git a/v0/src/styles/css/3-sub-components/_navigation.scss b/v0/src/styles/css/3-sub-components/_navigation.scss new file mode 100644 index 00000000..e69de29b diff --git a/v0/src/styles/css/3-sub-components/_searchbar.scss b/v0/src/styles/css/3-sub-components/_searchbar.scss new file mode 100644 index 00000000..e69de29b diff --git a/v0/src/styles/css/3-sub-components/_shortcut-menu.scss b/v0/src/styles/css/3-sub-components/_shortcut-menu.scss new file mode 100644 index 00000000..e69de29b diff --git a/v0/src/styles/css/3-sub-components/_zoom-slider.scss b/v0/src/styles/css/3-sub-components/_zoom-slider.scss new file mode 100644 index 00000000..e69de29b diff --git a/v0/src/styles/css/4-components/_bool-logic-dialog.scss b/v0/src/styles/css/4-components/_bool-logic-dialog.scss new file mode 100644 index 00000000..e69de29b diff --git a/v0/src/styles/css/4-components/_canvas.scss b/v0/src/styles/css/4-components/_canvas.scss new file mode 100644 index 00000000..e69de29b diff --git a/v0/src/styles/css/4-components/_circuitelements.scss b/v0/src/styles/css/4-components/_circuitelements.scss new file mode 100644 index 00000000..e69de29b diff --git a/v0/src/styles/css/4-components/_context-menu.scss b/v0/src/styles/css/4-components/_context-menu.scss new file mode 100644 index 00000000..e69de29b diff --git a/v0/src/styles/css/4-components/_dropdown.scss b/v0/src/styles/css/4-components/_dropdown.scss new file mode 100644 index 00000000..e69de29b diff --git a/v0/src/styles/css/4-components/_header.scss b/v0/src/styles/css/4-components/_header.scss new file mode 100644 index 00000000..e69de29b diff --git a/v0/src/styles/css/4-components/_layout-dialog.scss b/v0/src/styles/css/4-components/_layout-dialog.scss new file mode 100644 index 00000000..e69de29b diff --git a/v0/src/styles/css/4-components/_navbar.scss b/v0/src/styles/css/4-components/_navbar.scss new file mode 100644 index 00000000..e69de29b diff --git a/v0/src/styles/css/4-components/_new-circuit-dialog.scss b/v0/src/styles/css/4-components/_new-circuit-dialog.scss new file mode 100644 index 00000000..e69de29b diff --git a/v0/src/styles/css/4-components/_properties.scss b/v0/src/styles/css/4-components/_properties.scss new file mode 100644 index 00000000..e69de29b diff --git a/v0/src/styles/css/4-components/_save-img-dialog.scss b/v0/src/styles/css/4-components/_save-img-dialog.scss new file mode 100644 index 00000000..e69de29b diff --git a/v0/src/styles/css/4-components/_shortcut-dialog.scss b/v0/src/styles/css/4-components/_shortcut-dialog.scss new file mode 100644 index 00000000..e69de29b diff --git a/v0/src/styles/css/4-components/_tabs-bar.scss b/v0/src/styles/css/4-components/_tabs-bar.scss new file mode 100644 index 00000000..e69de29b diff --git a/v0/src/styles/css/5-layout/simulator.scss b/v0/src/styles/css/5-layout/simulator.scss new file mode 100644 index 00000000..89b84d4d --- /dev/null +++ b/v0/src/styles/css/5-layout/simulator.scss @@ -0,0 +1,321 @@ +/* old ui ruleset starts here */ +.deleteOfflineProject { + float: right; + cursor: pointer; + padding: 2px; +} + +.pointerCursor { + cursor: pointer; +} + +.defaultCursor { + cursor: default; +} + +#container { + display: table; + width: 100%; + height: 100%; +} + +#container > div { + display: table-row; + height: 0; +} + +#container > div.fill { + height: auto; +} + +/* END OF MODULES */ + +#restrictedDiv { + position: absolute; + top: 10px; + margin-left: 10px; + width: 560px; + z-index: 100; +} + +#restrictedElementsDiv { + position: absolute; + top: 90px; + right: 10px; + z-index: 100; + width: 200px; +} + +#MessageDiv { + position: absolute; + left: 30px; + bottom: 30px; + z-index: 110; +} + +.errorMessage { + height: auto; + width: 100%; + padding: 2px; + margin: 2px; + border: 1px solid red; + border-radius: 3px; + background-color: #fee; + font-size: 15px; +} + +.normalMessage { + height: auto; + width: 100%; + padding: 2px; + margin: 2px; + border: 1px solid green; + border-radius: 3px; + background-color: #99ff33; + font-size: 15px; +} + +#canvasArea { + display: block; + position: relative; + width: 100%; + background-color: red; +} + +.simulation { + position: relative; + width: auto; + height: 100%; + overflow: hidden; + background-color: 'white'; +} + +.left { + float: left; +} + +.right { + float: right; +} + +.objectPropertyAttributeChecked.btn { + width: 100%; + margin-bottom: 5px; +} + +/* For loading screen - pace.js */ + +.pace { + -webkit-pointer-events: none; + pointer-events: none; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + position: fixed; + width: 100vw; + height: 100vh; + background-color: #fff; + z-index: 100000; +} + +.pace-inactive { + display: none; +} + +#Help { + visibility: hidden; + /* Hidden by default. Visible on click */ + min-width: 250px; + /* Set a default minimum width */ + margin-left: -125px; + /* Divide value of min-width by 2 */ + background-color: #333; + /* Black background color */ + color: #fff; + /* White text color */ + text-align: center; + /* Centered text */ + border-radius: 2px; + /* Rounded borders */ + padding: 16px; + /* Padding */ + position: fixed; + /* Sit on top of the screen */ + z-index: 1; + /* Add a z-index if needed */ + right: 50px; + /* Center the snackbar */ + bottom: 50px; + /* 30px from the bottom */ + opacity: 0; +} + +#Help.show { + visibility: visible; + /* Show the snackbar */ + opacity: 1; + -webkit-transition-delay: 0.5s; + /* Safari */ + transition-delay: 0.5s; + -webkit-transition-duration: 0.3s; + /* Safari */ + transition-duration: 0.3s; +} + +@-webkit-keyframes fadein { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes fadein { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@-webkit-keyframes fadeout { + from { + opacity: 1; + } + to { + opacity: 0; + } +} + +@keyframes fadeout { + from { + bottom: 0px; + opacity: 1; + } + to { + bottom: 0; + opacity: 0; + } +} + +.pace .pace-progress { + background: #29d; + position: fixed; + z-index: 2000; + top: 0; + right: 100%; + width: 50%; + height: 5px; +} + +/* LOADING ICON CSS STARTS*/ + +.sk-folding-cube { + margin: 20px auto; + width: 40px; + height: 40px; + position: relative; + -webkit-transform: rotateZ(45deg); + transform: rotateZ(45deg); +} + +.sk-folding-cube .sk-cube { + float: left; + width: 50%; + height: 50%; + position: relative; + -webkit-transform: scale(1.1); + -ms-transform: scale(1.1); + transform: scale(1.1); +} + +.sk-folding-cube .sk-cube:before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: #09f; + -webkit-animation: sk-foldCubeAngle 2.4s infinite linear both; + animation: sk-foldCubeAngle 2.4s infinite linear both; + -webkit-transform-origin: 100% 100%; + -ms-transform-origin: 100% 100%; + transform-origin: 100% 100%; +} + +.sk-folding-cube .sk-cube2 { + -webkit-transform: scale(1.1) rotateZ(90deg); + transform: scale(1.1) rotateZ(90deg); +} + +.sk-folding-cube .sk-cube3 { + -webkit-transform: scale(1.1) rotateZ(180deg); + transform: scale(1.1) rotateZ(180deg); +} + +.sk-folding-cube .sk-cube4 { + -webkit-transform: scale(1.1) rotateZ(270deg); + transform: scale(1.1) rotateZ(270deg); +} + +.sk-folding-cube .sk-cube2:before { + -webkit-animation-delay: 0.3s; + animation-delay: 0.3s; +} + +.sk-folding-cube .sk-cube3:before { + -webkit-animation-delay: 0.6s; + animation-delay: 0.6s; +} + +.sk-folding-cube .sk-cube4:before { + -webkit-animation-delay: 0.9s; + animation-delay: 0.9s; +} + +@-webkit-keyframes sk-foldCubeAngle { + 0%, + 10% { + -webkit-transform: perspective(140px) rotateX(-180deg); + transform: perspective(140px) rotateX(-180deg); + opacity: 0; + } + 25%, + 75% { + -webkit-transform: perspective(140px) rotateX(0deg); + transform: perspective(140px) rotateX(0deg); + opacity: 1; + } + 90%, + 100% { + -webkit-transform: perspective(140px) rotateY(180deg); + transform: perspective(140px) rotateY(180deg); + opacity: 0; + } +} + +@keyframes sk-foldCubeAngle { + 0%, + 10% { + -webkit-transform: perspective(140px) rotateX(-180deg); + transform: perspective(140px) rotateX(-180deg); + opacity: 0; + } + 25%, + 75% { + -webkit-transform: perspective(140px) rotateX(0deg); + transform: perspective(140px) rotateX(0deg); + opacity: 1; + } + 90%, + 100% { + -webkit-transform: perspective(140px) rotateY(180deg); + transform: perspective(140px) rotateY(180deg); + opacity: 0; + } +} + +/* LOADING ICON CSS ENDS*/ diff --git a/v0/src/styles/css/UX.css b/v0/src/styles/css/UX.css new file mode 100644 index 00000000..e5011d8a --- /dev/null +++ b/v0/src/styles/css/UX.css @@ -0,0 +1,969 @@ +.deleteOfflineProject { + float: right; + cursor: pointer; + padding: 2px; +} + +#contextMenu { + width: 150px; + visibility: hidden; + box-shadow: 0px 2px 7px rgba(0, 0, 0, 0.2); + border: 1px solid rgba(0, 0, 0, 0.2); + position: fixed; + z-index: 100; + background: #fff; + opacity: 0; + top: 100; + left: 100; + cursor: pointer; + color: #000; + padding-bottom: 4px; + padding-top: 4px; + transition: opacity 0.2s ease-in-out; + user-select: none; +} + +#contextMenu ul { + margin: 0; + padding: 0; + font: 16px sans-serif; +} + +#contextMenu ul li { + list-style: none; + padding: 8px; + padding-left: 20px; +} + +#contextMenu ul li a { + text-decoration: none; + color: #000 !important; +} + +#contextMenu ul li:hover { + background: rgba(0, 0, 0, 0.1); +} + +button:focus { + outline: 0; +} + +.side { + height: 100%; + background-color: #333; + padding: 3px; + color: #fff; + border-side: 1px solid #0099ff; + border-bottom: 40px solid #0099ff; + padding: 0.5em; +} + +.option { + display: block; + background-color: black; + border: 1px solid #005cb3; + color: #0099ff; + padding: 5px; + width: 200px; + margin: 3px; + word-wrap: break-word; + overflow-x: hidden; +} + +.pannel-heading { + background-color: #f5f5f5; +} + +#layoutDialog { + position: absolute; + right: 100px; + top: 100px; + z-index: 101; + width: 200px; + height: 230px; + border: 1px solid grey; + border-radius: 2px; + background-color: white; + overflow-x: hidden; +} + +.projectName { + /*margin:3px;*/ + color: #0099ff; + margin: 0 auto; + text-align: center; + font-size: 1.4em; + position: static; + left: 50%; + display: block; + width: 500px; + text-align: center; + margin-left: -250px; +} + +.inline { + width: auto; + padding-right: 20px; + display: inline-block; +} + +.option:hover { + border-color: #0099ff; +} + +input[type='radio']:checked ~ label { + color: #0dff92; +} + +.option input[type='radio'] { + margin-right: 5px; + /*position: absolute;*/ + visibility: hidden; +} + +.option input[type='radio']:checked { + /*position: absolute;*/ + visibility: visible; +} + +.zoomButton:focus { + outline: 0; +} + +.zoomButton { + padding: 5px; + opacity: 0.3; +} + +.zoomButton:hover { + /*height:20px; + width:20px;*/ + opacity: 0.8; + transition: opacity 0.2s; +} + +.ui-accordion-header-icon.ui-icon { + /*background-image: url("./ui-icons_white_256x240.png");*/ +} + +.noSelect { + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + -o-user-select: none; +} + +.pointerCursor { + cursor: pointer; +} + +.defaultCursor { + cursor: default; +} + +#container { + display: table; + width: 100%; + height: 100%; +} + +#container > div { + display: table-row; + height: 0; +} + +#container > div.fill { + height: auto; +} + +#modules-header { + margin-bottom: 0.5em; + font-size: 1.3em; + text-transform: uppercase; + font-family: Arial, Helvetica, sans-serif; + color: #0099ff; + text-align: center; + padding-top: 0.3em; +} + +.panel { + padding: 0em; + background-color: #333; + margin: 0; + border-radius: 0; + margin-bottom: 0em; + border: 1px solid #0099ff; +} + +.ui-accordion-header { + background-color: #333; + color: #fff; + border: 1px solid #0099ff; + border-radius: 0; + margin: 0em; + padding: 0em; + outline: none; +} + +.ui-accordion-header.ui-accordion-header-active.ui-state-active { + background-color: #0099ff; + outline: none; + margin-bottom: 0; +} + +.ui-accordion-header.ui-state-hover { + background-color: #0066cc; + outline: none; + /*margin-bottom: 0;*/ +} + +/* MODULES */ + +.moduleProperty { + display: none; + background-color: #333; + color: #fff; + /*padding-bottom: 2em;*/ + margin-top: 1em; +} + +#moduleProperty-inner { + border: 1px solid #0099ff; + padding: 1em; + /*margin-bottom: 1em;*/ +} + +#moduleProperty-toolTip { + padding: 10px; + /*font-size: 1.1em;*/ + color: #0099ff; +} + +#moduleProperty-title { + text-transform: uppercase; + font-size: 1.3em; + color: #0099ff; + margin-bottom: 0.55em; + text-align: center; +} + +#moduleProperty-header { + font-size: 1.1em; + text-transform: uppercase; + margin-bottom: 0.5em; +} + +#moduleProperty-inner > p { + margin: 0; + margin-top: 0.2em; +} + +input, +select { + padding: 0.25rem; +} + +.moduleProperty input, +.moduleProperty select { + background-color: #333; + border: none; + border-bottom: 2px solid #ccc; +} + +.moduleProperty input:active, +.moduleProperty input:focus, +.moduleProperty select:active, +.moduleProperty select:focus { + border-bottom: 2px solid #0099ff; +} + +.navbar.navbar-default { + margin: 0px; + border-radius: 0px; + border: 0; + padding: 0px; + min-height: 0px; + border-bottom: 1px solid #0099ff; +} + +.navbar-brand { + padding: 7px 15px; + height: auto; +} + +/* END OF MODULES */ + +#tabsBar { + width: 100%; + /*height: 3em;*/ + margin-left: 20px; + background-color: #000; +} + +#tabsBar div { + display: inline-block; + /*padding-left: 0.5em;*/ + margin: 0.1em; + color: #fff; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + position: relative; +} + +#tabsBar .circuits { + color: #fff; + text-align: center; + background-color: #00284d; + padding-left: 0.5em; + padding-right: 1.5em; + border: 1px solid #005cb3; + border-radius: 0.1em; +} + +#tabsBar .circuits:hover { + background-color: #00ace6; + /*border: 1px solid #0099ff;*/ + transition-duration: 100ms; +} + +#tabsBar .current { + /*background-color: #0086b3;*/ + background-color: #004280; + border: 1px solid #0099ff; +} + +.tabsCloseButton:hover { + color: #fff; + font-family: 'Gill Sans', sans-serif; + margin-left: 1em; + opacity: 0.5; +} +.tabsCloseButton { + color: #111; + font-family: 'Gill Sans', sans-serif; + margin-left: 1em; + opacity: 1; + position: absolute; + top: 5px; + right: 3; +} + +th, +td { + padding-left: 15px; + padding-right: 15px; + text-align: left; + border: 1px solid #0099ff; + color: white; +} + +#booleanTable { + width: 200px; +} + +table { + border-collapse: collapse; + -webkit-user-select: none; + /* Chrome/Safari */ + -moz-user-select: none; + /* Firefox */ + -ms-user-select: none; + /* IE10+ */ + /* Rules below not implemented in browsers yet */ + -o-user-select: none; + user-select: none; +} + +body, +html { + width: 100%; + height: 100%; + margin: 0; + padding: 0; + overflow: hidden; +} + +#restrictedDiv { + position: absolute; + top: 10px; + margin-left: 10px; + width: 560px; + z-index: 100; +} + +#restrictedElementsDiv { + position: absolute; + top: 90px; + right: 10px; + z-index: 100; + width: 200px; +} + +#MessageDiv { + position: absolute; + margin-left: 30px; + bottom: 100px; + /*height:auto;*/ + /*width:60%;*/ + /*padding: 2px;*/ + /*border: 3px solid red;*/ + /*border-radius: 6px;*/ + /*background-color: #fcc;*/ + z-index: 10; +} + +.errorMessage { + /*position: absolute;*/ + /*margin-left: 30%;*/ + /*bottom: 1px;*/ + height: auto; + width: 100%; + /*margin-bottom: 10px;*/ + padding: 2px; + margin: 2px; + border: 1px solid red; + border-radius: 3px; + background-color: #fee; + font-size: 15px; + /*z-index: 10;*/ +} + +.normalMessage { + /*position: absolute;*/ + /*margin-left: 30%;*/ + /*bottom: 150px;*/ + height: auto; + width: 100%; + /*margin-bottom: 10px;*/ + padding: 2px; + margin: 2px; + border: 1px solid green; + border-radius: 3px; + background-color: #99ff33; + font-size: 15px; + /*z-index: 10;*/ +} + +#canvasArea { + display: block; + position: relative; + /*height: 100%;*/ + width: 100%; + background-color: red; +} + +.simulation { + position: relative; + width: auto; + height: 100%; + overflow: hidden; + background-color: 'white'; +} + +.switch { + position: relative; + display: inline-block; + width: 43px; + height: 17px; + margin-bottom: 0px; +} + +.switch input { + display: none; +} + +.slider { + position: absolute; + cursor: pointer; + top: 2; + left: 0; + right: 0; + bottom: -2; + background-color: #ccc; + -webkit-transition: 0.4s; + transition: 0.4s; +} + +.slider:before { + position: absolute; + content: ''; + height: 17px; + width: 17px; + left: 0px; + bottom: 0px; + background-color: white; + -webkit-transition: 0.4s; + transition: 0.4s; +} + +input:checked + .slider { + background-color: #2196f3; +} + +input:focus + .slider { + box-shadow: 0 0 1px #2196f3; +} + +input:checked + .slider:before { + -webkit-transform: translateX(26px); + -ms-transform: translateX(26px); + transform: translateX(26px); +} + +/* Rounded sliders */ + +.slider.round { + border-radius: 34px; +} + +.slider.round:before { + border-radius: 50%; +} + +/* Slider for white background */ +.slider2 { + position: absolute; + cursor: pointer; + top: 2; + left: 0; + right: 0; + bottom: -2; + /* border: 1px solid black; */ + background-color: #ccc; + -webkit-transition: 0.4s; + transition: 0.4s; +} + +.slider2:before { + position: absolute; + content: ''; + height: 17px; + width: 17px; + left: 0px; + bottom: 0px; + background-color: #aaa; + -webkit-transition: 0.4s; + transition: 0.4s; +} + +input:checked + .slider2 { + background-color: #2196f3; +} + +input:focus + .slider2 { + box-shadow: 0 0 1px #2196f3; +} + +input:checked + .slider2:before { + -webkit-transform: translateX(26px); + -ms-transform: translateX(26px); + transform: translateX(26px); +} + +/* Rounded sliders */ + +.slider2.round { + border-radius: 34px; +} + +.slider2.round:before { + border-radius: 50%; +} + +#miniMap { + position: fixed; + z-index: 2; + bottom: 20px; + right: 40px; + /*height:150px; + width: 25%;*/ + overflow-y: scroll; + background-color: black; + /*border:1px solid #aaa;*/ + opacity: 0.97; + box-shadow: 0px 0px 15px #888888; + overflow: hidden; + /*transition: opacity .25s ease-in-out;*/ +} + +#plot { + position: fixed; + z-index: 1; + bottom: 0; + right: 0; + /*display: block;*/ + /*height: 0px;*/ + /*width: 100%;*/ + overflow-y: scroll; + background-color: #eee; + /*background-blend-mode: color;*/ +} + +.left { + float: left; +} + +.right { + float: right; +} + +.icon { + position: relative; + height: 70px; + width: 70px; + /*margin: 1px;*/ + margin-bottom: 5px; + margin-left: 3px; + display: inline-block; + background-color: white; + border-radius: 4px; + /*border-color: #0099ff;*/ + border: 2px solid #0099ff; + text-align: center; + font-size: 8px; + padding: 5px; +} + +img { + display: none; +} + +div.icon img { + -webkit-user-drag: none; + -khtml-user-drag: none; + -moz-user-drag: none; + -o-user-drag: none; + user-drag: none; + /*margin: auto;*/ + width: 100%; + /*height:100%;*/ + display: inline-block; +} + +.img__description { + position: absolute; + /*top: 0;*/ + bottom: -16; + text-align: center; + left: 0; + right: 0; + background-color: #0099ff; + color: white; + font-size: 8px; + /*background: rgba(29, 106, 154, 0.72); + color: #fff;*/ + visibility: hidden; + border-bottom-left-radius: 2px; + border-bottom-right-radius: 2px; + opacity: 0; + /* transition effect. not necessary */ + transition: opacity 0.2s, visibility 0.2s; +} + +.icon:hover .img__description { + visibility: visible; + opacity: 1; +} + +.icon:hover { + /*background-color: #cce5ff;*/ + /*border-color: blue;*/ + margin-bottom: 1px; + height: 74px; + background-color: #f5f5f5; + transition: height 0.2s margin 0.2s; +} + +.objectPropertyAttributeChecked.btn { + width: 100%; + margin-bottom: 5px; +} + +/* For loading screen - pace.js */ + +.pace { + -webkit-pointer-events: none; + pointer-events: none; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + width: 100%; + height: 100%; + background-color: #fff; +} + +.pace-inactive { + display: none; +} + +#Help { + visibility: hidden; + /* Hidden by default. Visible on click */ + min-width: 250px; + /* Set a default minimum width */ + margin-left: -125px; + /* Divide value of min-width by 2 */ + background-color: #333; + /* Black background color */ + color: #fff; + /* White text color */ + text-align: center; + /* Centered text */ + border-radius: 2px; + /* Rounded borders */ + padding: 16px; + /* Padding */ + position: fixed; + /* Sit on top of the screen */ + z-index: 1; + /* Add a z-index if needed */ + right: 50px; + /* Center the snackbar */ + bottom: 50px; + /* 30px from the bottom */ + opacity: 0; +} + +#Help.show { + visibility: visible; + /* Show the snackbar */ + opacity: 1; + -webkit-transition-delay: 0.5s; + /* Safari */ + transition-delay: 0.5s; + -webkit-transition-duration: 0.3s; + /* Safari */ + transition-duration: 0.3s; +} + +/* Animations to fade the snackbar in and out */ + +@-webkit-keyframes fadein { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes fadein { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@-webkit-keyframes fadeout { + from { + opacity: 1; + } + to { + opacity: 0; + } +} + +@keyframes fadeout { + from { + bottom: 0px; + opacity: 1; + } + to { + bottom: 0; + opacity: 0; + } +} + +.pace .pace-progress { + background: #29d; + position: fixed; + z-index: 2000; + top: 0; + right: 100%; + width: 50%; + height: 5px; +} + +/* dropdown-menu styles */ + +.dropdown-menu { + background-color: black; + border: 1px solid #09f; + border-top: none; +} + +.navbar-nav > li > a { + padding: 7px 15px; +} + +.dropdown-menu > li > a { + color: #939393 !important; + padding: 3px 14px; +} + +.dropdown-menu > li > a:hover { + color: #4db8ff !important; + background-color: black; +} + +.ui-dialog { + background: #222; +} + +.ui-dialog p { + color: #9d9d9d; +} + +.ui-widget-header { + border: 1px solid #0099ff; + color: #0099ff; +} + +.ui-dialog-buttonpane { + background-color: black; +} + +.ui-dialog-titlebar { + background-color: black; +} + +.ui-dialog-titlebar-close { + background-image: url('../img/cross.png'); + position: absolute; + right: 0.3em; + top: 50%; + width: 21px; + margin: -10px 0 0 0; + padding: 1px; + height: 20px; +} + +.ui-icon-close { + background-position: -80px -128px; +} + +.ui-dialog .ui-dialog-buttonpane button { + background-color: #004280; + border: 1px solid #09f; + color: white; +} + +.navbar .nav.pull-right { + float: right; + margin-right: 10px; + /*set margin this way in your custom stylesheet*/ +} + +/* LOADING ICON CSS STARTS*/ + +.sk-folding-cube { + margin: 20px auto; + width: 40px; + height: 40px; + position: relative; + -webkit-transform: rotateZ(45deg); + transform: rotateZ(45deg); +} + +.sk-folding-cube .sk-cube { + float: left; + width: 50%; + height: 50%; + position: relative; + -webkit-transform: scale(1.1); + -ms-transform: scale(1.1); + transform: scale(1.1); +} + +.sk-folding-cube .sk-cube:before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: #09f; + -webkit-animation: sk-foldCubeAngle 2.4s infinite linear both; + animation: sk-foldCubeAngle 2.4s infinite linear both; + -webkit-transform-origin: 100% 100%; + -ms-transform-origin: 100% 100%; + transform-origin: 100% 100%; +} + +.sk-folding-cube .sk-cube2 { + -webkit-transform: scale(1.1) rotateZ(90deg); + transform: scale(1.1) rotateZ(90deg); +} + +.sk-folding-cube .sk-cube3 { + -webkit-transform: scale(1.1) rotateZ(180deg); + transform: scale(1.1) rotateZ(180deg); +} + +.sk-folding-cube .sk-cube4 { + -webkit-transform: scale(1.1) rotateZ(270deg); + transform: scale(1.1) rotateZ(270deg); +} + +.sk-folding-cube .sk-cube2:before { + -webkit-animation-delay: 0.3s; + animation-delay: 0.3s; +} + +.sk-folding-cube .sk-cube3:before { + -webkit-animation-delay: 0.6s; + animation-delay: 0.6s; +} + +.sk-folding-cube .sk-cube4:before { + -webkit-animation-delay: 0.9s; + animation-delay: 0.9s; +} + +@-webkit-keyframes sk-foldCubeAngle { + 0%, + 10% { + -webkit-transform: perspective(140px) rotateX(-180deg); + transform: perspective(140px) rotateX(-180deg); + opacity: 0; + } + 25%, + 75% { + -webkit-transform: perspective(140px) rotateX(0deg); + transform: perspective(140px) rotateX(0deg); + opacity: 1; + } + 90%, + 100% { + -webkit-transform: perspective(140px) rotateY(180deg); + transform: perspective(140px) rotateY(180deg); + opacity: 0; + } +} + +@keyframes sk-foldCubeAngle { + 0%, + 10% { + -webkit-transform: perspective(140px) rotateX(-180deg); + transform: perspective(140px) rotateX(-180deg); + opacity: 0; + } + 25%, + 75% { + -webkit-transform: perspective(140px) rotateX(0deg); + transform: perspective(140px) rotateX(0deg); + opacity: 1; + } + 90%, + 100% { + -webkit-transform: perspective(140px) rotateY(180deg); + transform: perspective(140px) rotateY(180deg); + opacity: 0; + } +} + +/* LOADING ICON CSS ENDS*/ diff --git a/v0/src/styles/css/assets/layout-panel/down.svg b/v0/src/styles/css/assets/layout-panel/down.svg new file mode 100644 index 00000000..d8e8d105 --- /dev/null +++ b/v0/src/styles/css/assets/layout-panel/down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/styles/css/assets/layout-panel/left.svg b/v0/src/styles/css/assets/layout-panel/left.svg new file mode 100644 index 00000000..f73a40ff --- /dev/null +++ b/v0/src/styles/css/assets/layout-panel/left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/styles/css/assets/layout-panel/minus.svg b/v0/src/styles/css/assets/layout-panel/minus.svg new file mode 100644 index 00000000..b26d17f4 --- /dev/null +++ b/v0/src/styles/css/assets/layout-panel/minus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/styles/css/assets/layout-panel/plus.svg b/v0/src/styles/css/assets/layout-panel/plus.svg new file mode 100644 index 00000000..364e696d --- /dev/null +++ b/v0/src/styles/css/assets/layout-panel/plus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/styles/css/assets/layout-panel/right.svg b/v0/src/styles/css/assets/layout-panel/right.svg new file mode 100644 index 00000000..bef89def --- /dev/null +++ b/v0/src/styles/css/assets/layout-panel/right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/styles/css/assets/layout-panel/up.svg b/v0/src/styles/css/assets/layout-panel/up.svg new file mode 100644 index 00000000..7c014709 --- /dev/null +++ b/v0/src/styles/css/assets/layout-panel/up.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/styles/css/assets/logo.svg b/v0/src/styles/css/assets/logo.svg new file mode 100644 index 00000000..ffae4558 --- /dev/null +++ b/v0/src/styles/css/assets/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/styles/css/assets/shorcuts/delete.svg b/v0/src/styles/css/assets/shorcuts/delete.svg new file mode 100644 index 00000000..3611bbac --- /dev/null +++ b/v0/src/styles/css/assets/shorcuts/delete.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/styles/css/assets/shorcuts/download.svg b/v0/src/styles/css/assets/shorcuts/download.svg new file mode 100644 index 00000000..9dc67f1e --- /dev/null +++ b/v0/src/styles/css/assets/shorcuts/download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/styles/css/assets/shorcuts/dragDots.svg b/v0/src/styles/css/assets/shorcuts/dragDots.svg new file mode 100644 index 00000000..dacde439 --- /dev/null +++ b/v0/src/styles/css/assets/shorcuts/dragDots.svg @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/v0/src/styles/css/assets/shorcuts/fit.svg b/v0/src/styles/css/assets/shorcuts/fit.svg new file mode 100644 index 00000000..4cc765e2 --- /dev/null +++ b/v0/src/styles/css/assets/shorcuts/fit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/styles/css/assets/shorcuts/new.svg b/v0/src/styles/css/assets/shorcuts/new.svg new file mode 100644 index 00000000..bc1bb3bd --- /dev/null +++ b/v0/src/styles/css/assets/shorcuts/new.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/styles/css/assets/shorcuts/redo.svg b/v0/src/styles/css/assets/shorcuts/redo.svg new file mode 100644 index 00000000..30e00a4c --- /dev/null +++ b/v0/src/styles/css/assets/shorcuts/redo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/styles/css/assets/shorcuts/save-online.svg b/v0/src/styles/css/assets/shorcuts/save-online.svg new file mode 100644 index 00000000..bb414b9a --- /dev/null +++ b/v0/src/styles/css/assets/shorcuts/save-online.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/styles/css/assets/shorcuts/save.svg b/v0/src/styles/css/assets/shorcuts/save.svg new file mode 100644 index 00000000..9e3c8da5 --- /dev/null +++ b/v0/src/styles/css/assets/shorcuts/save.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/styles/css/assets/shorcuts/undo.svg b/v0/src/styles/css/assets/shorcuts/undo.svg new file mode 100644 index 00000000..c82d8b64 --- /dev/null +++ b/v0/src/styles/css/assets/shorcuts/undo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/styles/css/assets/small-components/chevron-down.svg b/v0/src/styles/css/assets/small-components/chevron-down.svg new file mode 100644 index 00000000..e4c8404d --- /dev/null +++ b/v0/src/styles/css/assets/small-components/chevron-down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/styles/css/assets/small-components/close.svg b/v0/src/styles/css/assets/small-components/close.svg new file mode 100644 index 00000000..8dc79fa4 --- /dev/null +++ b/v0/src/styles/css/assets/small-components/close.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v0/src/styles/css/custom_mailer.css b/v0/src/styles/css/custom_mailer.css new file mode 100644 index 00000000..3284a610 --- /dev/null +++ b/v0/src/styles/css/custom_mailer.css @@ -0,0 +1,6 @@ +.navbar, +.navbar-search-active, +.footer-empty-div, +.footer-container-fluid { + display: none; +} diff --git a/v0/src/styles/css/embed.css b/v0/src/styles/css/embed.css new file mode 100644 index 00000000..ed09eb73 --- /dev/null +++ b/v0/src/styles/css/embed.css @@ -0,0 +1,261 @@ +.switch { + position: relative; + display: inline-block; + width: 43px; + height: 17px; + margin-bottom: 0px; +} + +.switch input { + display: none; +} + +.slider { + position: absolute; + cursor: pointer; + top: 2; + left: 0; + right: 0; + bottom: -2; + background-color: #ccc; + -webkit-transition: 0.4s; + transition: 0.4s; +} + +.slider:before { + position: absolute; + content: ''; + height: 17px; + width: 17px; + left: 0px; + bottom: 0px; + background-color: white; + -webkit-transition: 0.4s; + transition: 0.4s; +} + +#clockProperty { + padding: 10px; +} + +#clockPropertyHeader { + border-radius: 3px; +} +#clockProperty { + padding: 10px; + border-radius: 6px; + opacity: 0.1; + transition: 0.4s; + height: 109px; + display: flex; + flex-direction: column; + justify-content: space-between; +} +#clockProperty:hover { + opacity: 1; + -webkit-transition: 0.4s; + transition: 0.4s; +} +input:checked + .slider { + /* background-color: #2196F3; */ +} + +input:focus + .slider { + box-shadow: 0 0 1px #2196f3; +} + +input:checked + .slider:before { + -webkit-transform: translateX(26px); + -ms-transform: translateX(26px); + transform: translateX(26px); +} + +#tabsBar .circuits { + color: #000; + text-align: center; + padding-left: 0.5em; + padding-right: 0.5em; + border: 1px solid #111; + display: inline-block; +} +button:focus { + outline: 0; +} +#tabsBar .circuits:hover { + background-color: lightgray; + /*border: 1px solid #0099ff;*/ + transition-duration: 100ms; +} +#tabsBar .current { + transition-duration: 100ms; +} + +.noSelect { + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + -o-user-select: none; +} +.pointerCursor { + cursor: pointer; +} +.defaultCursor { + cursor: default; +} +.simulation { + position: relative; + width: auto; + height: 100%; + overflow: hidden; + background-color: 'white'; +} + +#elementName { + position: absolute; + left: 6px; + bottom: 6px; + background-color: white; + z-index: 101; + color: black; + padding: 1px; + border: 0.5px solid black; + display: none; +} + +/* LOADING ICON CSS STARTS*/ +.sk-folding-cube { + margin: 20px auto; + width: 40px; + height: 40px; + position: relative; + -webkit-transform: rotateZ(45deg); + transform: rotateZ(45deg); +} + +.sk-folding-cube .sk-cube { + float: left; + width: 50%; + height: 50%; + position: relative; + -webkit-transform: scale(1.1); + -ms-transform: scale(1.1); + transform: scale(1.1); +} +.sk-folding-cube .sk-cube:before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: #09f; + -webkit-animation: sk-foldCubeAngle 2.4s infinite linear both; + animation: sk-foldCubeAngle 2.4s infinite linear both; + -webkit-transform-origin: 100% 100%; + -ms-transform-origin: 100% 100%; + transform-origin: 100% 100%; +} +.sk-folding-cube .sk-cube2 { + -webkit-transform: scale(1.1) rotateZ(90deg); + transform: scale(1.1) rotateZ(90deg); +} +.sk-folding-cube .sk-cube3 { + -webkit-transform: scale(1.1) rotateZ(180deg); + transform: scale(1.1) rotateZ(180deg); +} +.sk-folding-cube .sk-cube4 { + -webkit-transform: scale(1.1) rotateZ(270deg); + transform: scale(1.1) rotateZ(270deg); +} +.sk-folding-cube .sk-cube2:before { + -webkit-animation-delay: 0.3s; + animation-delay: 0.3s; +} +.sk-folding-cube .sk-cube3:before { + -webkit-animation-delay: 0.6s; + animation-delay: 0.6s; +} +.sk-folding-cube .sk-cube4:before { + -webkit-animation-delay: 0.9s; + animation-delay: 0.9s; +} +@-webkit-keyframes sk-foldCubeAngle { + 0%, + 10% { + -webkit-transform: perspective(140px) rotateX(-180deg); + transform: perspective(140px) rotateX(-180deg); + opacity: 0; + } + 25%, + 75% { + -webkit-transform: perspective(140px) rotateX(0deg); + transform: perspective(140px) rotateX(0deg); + opacity: 1; + } + 90%, + 100% { + -webkit-transform: perspective(140px) rotateY(180deg); + transform: perspective(140px) rotateY(180deg); + opacity: 0; + } +} + +@keyframes sk-foldCubeAngle { + 0%, + 10% { + -webkit-transform: perspective(140px) rotateX(-180deg); + transform: perspective(140px) rotateX(-180deg); + opacity: 0; + } + 25%, + 75% { + -webkit-transform: perspective(140px) rotateX(0deg); + transform: perspective(140px) rotateX(0deg); + opacity: 1; + } + 90%, + 100% { + -webkit-transform: perspective(140px) rotateY(180deg); + transform: perspective(140px) rotateY(180deg); + opacity: 0; + } +} +/* LOADING ICON CSS ENDS*/ + +#restrictedElementsDiv { + position: absolute; + bottom: 10; + right: 2; + z-index: 100; + width: 215px; + font-size: 14px; + background-color: gainsboro; + border: 1px solid #0d3349; + opacity: 0.1; + padding: 3px 5px; +} + +#restrictedElementsDiv:hover { + opacity: 1; + transition: 0.4s; +} + +.zoom-wrapper { + position: absolute; + bottom: 1px; + right: 0px; + font-size: 10px; + z-index: 100; +} + +.zoom-wrapper button { + opacity: 0.3; +} +.zoom-wrapper button:hover { + opacity: 1; +} + +.embed-fullscreen-btn { + border-radius: 20px; +} diff --git a/v0/src/styles/css/error.css b/v0/src/styles/css/error.css new file mode 100644 index 00000000..cf03badb --- /dev/null +++ b/v0/src/styles/css/error.css @@ -0,0 +1,24 @@ +.error-code { + color: #42b983; + font-family: 'CircuitBoredNF'; + font-size: 142px; +} + +.help-text-main { + color: #1c1c1c; + font-family: 'Segoe UI'; + font-size: 22px; + font-weight: 400; + margin: 0; +} + +.return { + color: #42b983; + display: inline-block; + font-family: 'Segoe UI'; + font-weight: 700; +} + +.return:hover { + color: #42b983; +} diff --git a/v0/src/styles/css/main.stylesheet.css b/v0/src/styles/css/main.stylesheet.css new file mode 100644 index 00000000..b487179b --- /dev/null +++ b/v0/src/styles/css/main.stylesheet.css @@ -0,0 +1,1956 @@ +/* +************ +* This stylesheet is to be made modular later +************ +*/ + +@import './5-layout/simulator.scss'; +@import './2-basics/base.scss'; +@import './2-basics/reset.scss'; +@import './shortcut.panel.css'; +@import './embed.css'; +@import './plugin-stylesheets/checkBo.min.css'; +/** new UI ruleset starts here */ +/*! Adding color variables to root, required later for hokey binding */ + +/* typography */ +@font-face { + font-family: Raleway; + src: url('https://fonts.gstatic.com/s/raleway/v18/1Ptxg8zYS_SKggPN4iEgvnHyvveLxVvaorCIPrcVIT9d0c8.woff'); +} + +/* typography */ +@font-face { + font-family: 'Nunito', sans-serif; + src: url('https://fonts.googleapis.com/css2?family=Nunito:ital,wght@0,200;1,200&display=swap'); +} + +/** Global Styles starts here */ + +body { + line-height: 1 !important; +} + +body, +html { + font-family: 'Nunito', sans-serif; + font-weight: 200; + width: 100%; + height: 100%; + margin: 0; + padding: 0; + overflow: hidden; + position: fixed; +} + +button:focus { + outline: 0; +} + +a { + color: var(--text-primary); +} + +a:hover { + color: white; + text-decoration: none; +} + +select > option { + color: black; + padding: 2px 4px; + margin-right: 5px; +} + +select:focus, +select > option:focus { + border: none; + outline: none; +} + +input[type='number']:focus { + background-color: transparent; + outline: none; + border: none; + color: white; +} + +table { + border-collapse: collapse; + -webkit-user-select: none; + /* Chrome/Safari */ + -moz-user-select: none; + /* Firefox */ + -ms-user-select: none; + /* IE10+ */ + /* Rules below not implemented in browsers yet */ + -o-user-select: none; + user-select: none; +} + +button { + background: none; + color: inherit; + border: none; + padding: 0; + font: inherit; + cursor: pointer; + outline: inherit; + box-shadow: none; +} + +button:not('.quick-btn button'):active { + background: transparent !important; +} + +button:active, +button:focus { + box-shadow: none !important; + border: none; + outline: none; + /* border-color: white !important; */ +} + +button:focus { + box-shadow: none; +} + +input[type='text']:focus { + background: transparent; + /* color: white; */ +} + +/*! Global styles ends here */ + +.noSelect { + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + -o-user-select: none; +} + +.navbar-menu { + position: relative; + transition: all 0.2s ease-in-out; +} + +.navbar-menu > li > a { + border: 1px solid transparent; + border-radius: 1px; + padding: 2px 8px; + transition: all 0.2s ease-in-out; + margin-right: 10px; +} + +.navbar-menu > li > a span, +.acc-caret { + content: ''; + background: url(./assets/small-components/chevron-down.svg) center/cover + no-repeat; + display: inline-block; + height: 5px; + width: 5px; + position: absolute; + right: 0; + top: 50%; + transform: translateY(-66%); + padding: 4px; + margin: 0 5px; + transition: all 0.2s ease-in-out; +} + +.acc-caret { + right: -17px; +} +.navbar-menu > li > a:hover { + border-bottom: 1px solid white; + text-decoration: none; +} + +.navbar-menu > li > a:hover span, +.acc-drop:hover .acc-caret { + background: none; +} + +.projectName { + position: relative; + left: 0.5rem; + font-size: 1em; + text-align: center; + display: inline-block; + width: 35vw; + overflow: hidden; + text-overflow: ellipsis; +} + +@media (max-width: 991px) { + .projectName { + visibility: hidden; + } +} + +.account-btn { + position: absolute; + right: 13px; + padding: 4px 10px; + border: 1px solid transparent; + border-radius: 1px; + transition: all 0.2s ease-in-out; +} + +.account-btn:hover { + border-bottom: 1px solid white; + text-decoration: none; +} + +.user-field { + display: inline-block; + max-width: 11rem; + white-space: nowrap; + text-overflow: ellipsis; + text-align: right; +} + +@media (max-width: 991px) { + .user-field { + visibility: hidden; + } +} + +.signIn-btn { + color: var(--text-primary); +} + +.cur-user, +.signIn-btn { + color: #fff; +} + +.signIn-btn:hover { + color: var(--text-primary); + text-decoration: none; +} + +.logo { + background: url(./assets/logo.svg) center/cover; + height: 30px; + width: 105px; + display: inline-block; + margin-right: 36px; +} + +/* dropdown-menu styles */ + +.dropdown > ul { + border-radius: 5px; + text-align: center; + position: absolute; + left: 50%; + transform: translate(-50%, 13px); +} + +.draggable-panel-css { + border-radius: 5px; + z-index: 70; + transition: background 0.5s ease-out; + position: fixed; +} + +@supports (backdrop-filter: blur()) { + .dropdown > ul { + backdrop-filter: blur(5px); + } +} + +.mw-override { + min-width: 110px; +} + +.dropdown > ul::before { + background-color: transparent; + content: ''; + width: 10px; + display: inline-block; + height: 10px; + position: absolute; + transform: translate(-50%, -13px) rotate(-45deg); +} + +.dropdown > ul::after { + content: ''; + width: 11.5px; + display: inline-block; + height: 10px; + position: absolute; + transform: translate(-50%, -15.5px); + top: 14.5px; +} + +.dropdown-menu > li > a { + padding: 7px 0; + width: 90%; + margin: auto; + transition: all 0.2s ease-in-out; + text-align: left; + padding-left: 10px; +} + +.dropdown-menu > li > a:hover { + border-radius: 7px; + opacity: 1; +} + +@media (max-width: 991px) { + .navbar-nav .dropdown-menu { + position: absolute !important; + float: none; + } +} + +#contextMenu { + width: 150px; + visibility: hidden; + position: fixed; + z-index: 1000; + opacity: 0; + top: 100; + left: 100; + cursor: pointer; + padding-bottom: 7px; + padding-top: 7px; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + border-radius: 5px; +} + +#contextMenu ul { + margin: 0; + padding: 0; +} + +#contextMenu ul li { + list-style: none; + padding: 8px; + padding-left: 20px; + width: 90%; + margin: auto; +} + +#contextMenu ul li:hover { + border-radius: 7px; + opacity: 1; +} + +@supports (backdrop-filter: blur()) { + #contextMenu { + backdrop-filter: blur(5px); + } + #contextMenu ul li:hover { + backdrop-filter: blur(50px); + -webkit-backdrop-filter: blur(50px); + } +} + +/** ce-panel styling starts */ + +.ce-panel { + font: inherit; + width: 240px; + top: 90px; + left: 10px; +} + +.accordion > :last-child { + margin-bottom: 15px; +} + +.draggable-panel-css .panel-header { + border-radius: 5px; + border-top-right-radius: 5px; + padding-top: 15px; + padding: 10px; + padding-top: 15px; + padding-left: 17px; + font-weight: bold; + font-size: 16px; + text-transform: uppercase; + text-align: left; + cursor: move; +} + +.draggable-panel-css .panel-header::before { + content: ''; + width: 34px; + border-radius: 2px; + position: absolute; + left: 50%; + transform: translateX(-50%); + top: 6px; +} + +.draggable-panel-css .panel-body { + padding-top: 10px; +} + +.draggable-panel-css .panel { + padding: 0em; + margin: 0; + border-radius: 0; + margin-bottom: 0em; + display: grid; + grid-template-columns: 1fr 1fr 1fr; + max-height: 185px; + border-radius: 2px; + overflow: auto; +} + +.ui-accordion-header { + background-color: transparent; + margin: 0em; + padding: 0em; + outline: none; +} + +.ui-accordion-header.ui-accordion-header-active.ui-state-active { + outline: none; +} + +.ui-accordion-header.ui-state-hover { + outline: none; +} + +.ui-accordion-header-icon.ui-icon { + display: none; +} + +.accordion { + width: 90%; + margin: auto; + position: relative; +} + +/* expansion panel styles - start */ +.v-expansion-panel { + background-color: transparent; + color: var(--text-panel); +} +.v-expansion-panel--active > .v-expansion-panel-title { + min-height: 48px; +} +.v-expansion-panel-title__overlay { + border-radius: 3px; + outline: none; + background-color: #ededed; + font-weight: 400; +} + +.v-expansion-panel__shadow { + box-shadow: none; +} + +.v-expansion-panel-title { + cursor: pointer; + position: relative; + margin: 2px 0 0; + padding: 0 0.5em 0 0.7em; + font-size: 100%; + font-weight: 400; + line-height: 1.3; +} + +.v-expansion-panel-text { + background-color: var(--text-panel); + color: black; +} +.v-expansion-panel-text__wrapper { + padding: 0; +} +/* expansion panel styles - ends */ + +.panelHeader { + border: none; + border-radius: 0; + transition: all 0.2s ease-in-out; +} + +.panelHeader:hover { + border-radius: 3px; +} + +.panelHeader:after, +.panelHeader:before { + content: ''; + height: 8px; + display: inline-block; + right: 15px; + position: absolute; + border-radius: 5px; + top: 50%; + transform: translateY(-50%) rotate(132deg); + transition: 0.2s ease-out; + background-color: white; +} + +.panelHeader:after { + transform: translate(260%, -50%) rotate(226deg); +} + +.ui-accordion-header-active:before { + transition: 0.2s ease-out; + transform-origin: left; + transform: translate(29%, -40%) rotate(50deg); + top: 46%; +} + +.ui-accordion-header-active:after { + transform-origin: bottom; + transform: translate(420%, -50%) rotate(310deg); + transition: 0.2s ease-out; + top: 46%; +} + +.ui-accordion-header-active:hover { + background-color: transparent; +} + +.ui-accordion .ui-accordion-content { + border: none; + padding: 0; +} + +.icon { + position: relative; + width: 50px; + margin: 5px; + display: inline-block; + text-align: center; + font-size: 8px; +} + +img { + display: none; +} + +div.icon img { + -webkit-user-drag: none; + -khtml-user-drag: none; + -moz-user-drag: none; + -o-user-drag: none; + width: 100%; + display: inline-block; +} + +.custom-tooltip-styling { + box-shadow: none; + border-radius: 3px; + font: inherit; + font-size: 14px; + font-weight: 100; +} + +.icon:hover { + border-radius: 3px; +} + +/*! ce-panel styling ends */ + +/** custom scroll styling starts here */ + +.search-results { + scrollbar-width: thin; /* for firefox */ +} + +.search-results::-webkit-scrollbar { + margin-right: 3px; + width: 6px; +} + +/*! custom scroll styling starts ends here */ + +/*! ce-panel styling ends */ + +/** tab bar styling starts */ + +#tabsBar { + width: 100%; + /* height: 23.5px; */ + display: block; + align-items: center; + z-index: 99; + /* position: absolute; + top: 47px; */ +} + +.embed-tabs { + background-color: transparent !important; +} + +#tabsBar .placeholder { + justify-content: space-between; + padding: 1px; + display: inline-block; + margin: 2px; + text-align: center; + /* min-width: 110px; */ + font-size: 14px; + transition: all 0.2s ease-in-out; +} + +.placeholder::before { + display: inline-block; + padding: 2px 5px; + content: '|'; +} + +#tabsBar .circuits { + justify-content: space-between; + border-radius: 3px; + padding: 1px; + display: inline-flex; + align-items: center; + margin: 2px; + text-align: center; + /* min-width: 110px; */ + font-size: 14px; + transition: all 0.2s ease-in-out; +} + +#tabsBar .circuits > span { + display: inline-block; + padding: 2px 5px; +} + +.circuitName { + cursor: pointer; +} + +#tabsBar button { + order: 99; /* could have better solution */ + width: 20px; + align-items: center; + display: inline; + font-size: 20px; + text-align: center; + padding-bottom: 5px; + text-decoration: none; + outline: none; + border-radius: 1px; + transition: all 0.1s ease-in-out; + border-radius: 4px; + margin-left: 1px; +} + +#tabsBar button:focus { + outline: none !important; + box-shadow: none !important; +} +#tabsBar button:active { + outline: none !important; + box-shadow: none !important; +} + +/*! tab bar styling ends */ +/** Module property styling starts here */ + +.moduleProperty { + font: inherit; + width: 250px; + top: 90px; + right: 10px; +} + +.layoutElementPanel { + width: 220px; + font: inherit; + display: none; + top: 90px; + left: 10px; +} + +.timing-diagram-panel { + border-radius: 5px; + z-index: 70; + transition: background 0.5s ease-out; + position: fixed; + cursor: pointer; + left: 300px; + top: 90px; +} + +.timing-diagram-panel .panel-header { + border-radius: 5px; + border-top-right-radius: 5px; + padding: 3px; + font-weight: bold; + font-size: 16px; + text-transform: uppercase; + text-align: left; + cursor: move; +} + +/* Testbench UI Styling begin */ + +.testbench-manual-panel { + border-radius: 5px; + z-index: 100; + transition: background 0.5s ease-out; + position: fixed; + cursor: pointer; + left: 10px; + top: 470px; +} + +.testbench-manual-panel .panel-header { + border-radius: 5px; + border-top-right-radius: 5px; + padding: 3px; + font-weight: bold; + font-size: 16px; + text-transform: uppercase; + text-align: left; + cursor: move; +} + +.tb-case-arrow { + border: solid var(--text-panel); + border-width: 0 3px 3px 0; + display: inline-block; + padding: 3px; +} + +.tb-case-arrow-right { + transform: rotate(-45deg); + -webkit-transform: rotate(-45deg); +} + +.tb-case-arrow-left { + transform: rotate(135deg); + -webkit-transform: rotate(135deg); +} + +.testbench-manual-panel .panel-body { + width: 700px; +} + +.testbench-manual-panel b { + font-weight: bold; +} + +.tb-manual-test-data { + /*text-align: center;*/ + margin-top: 10px; + border-bottom: 1px solid var(--br-secondary); + padding-left: 8px; + padding-right: 8px; +} + +.tb-manual-test-data .tb-data { + margin-right: 10px; +} + +.tb-data span { + vertical-align: middle; + display: inline-block; + max-width: 200px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +.tb-data#data-title { + float: left; +} + +.tb-data#data-type { + float: right; +} + +.tb-manual-table { + position: relative; + display: inline-block; + margin-top: 10px; + color: var(--text-panel); + max-width: 650px; + overflow-x: auto; + white-space: nowrap; +} + +.tb-manual-table td, +.tb-manual-table th { + padding-left: 15px; + padding-right: 15px; + padding-top: 12px; + padding-bottom: 12px; + text-align: center; + min-width: 80px; +} + +.tb-manual-table th { + background: var(--table-head-dark); + height: 50px; +} + +.testbench-manual-panel-buttons { + position: relative; + display: table-cell; + flex-wrap: wrap; + right: 0px; + text-align: left; + width: 200px; +} + +.testbench-runall-label { + display: none; +} + +.tb-dialog-button { + display: inline; + margin: 8px; + border-radius: 5px !important; + padding-left: 8px !important; + padding-right: 8px !important; + padding-top: 4px !important; + padding-bottom: 4px !important; +} + +.tb-manual-test-buttons { + display: flex; + margin-top: 20px; + margin-left: 30px; + margin-right: 30px; + height: 25px; + overflow: auto; +} + +.tb-manual-test-buttons .tb-case-button-left { + border-bottom-left-radius: 5px; + border-top-left-radius: 5px; + width: 24px; +} + +.tb-manual-test-buttons .tb-case-button-right { + border-bottom-right-radius: 5px; + border-top-right-radius: 5px; + width: 24px; +} + +.tb-manual-test-buttons .tb-test-label { + position: relative; + top: 0px; + line-height: 25px; + height: 25px; + margin: 0px; + padding-left: 2px; + padding-right: 2px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + background: #c4c4c4; + color: black; +} + +.tb-manual-test-buttons .tb-test-label.group-label { + text-align: center; + width: 100px; +} + +.tb-manual-test-buttons .tb-test-label.case-label { + text-align: center; + width: 40px; +} + +.tb-group-buttons { + float: left; +} + +.tb-case-buttons { + float: right; +} + +.tb-test-null { + width: 350px !important; +} + +.validation-ui-table td, +.validation-ui-table th { + padding-left: 15px; + padding-right: 15px; + padding-top: 12px; + padding-bottom: 12px; + text-align: center; + min-width: 80px; + color: white; +} + +/* Testbench UI styling end */ + +#plotArea { + padding: 3px; + width: 100%; +} + +#verilogEditorPanel { + width: 220px; + font: inherit; + display: none; + top: 90px; + right: 300px; +} + +#moduleProperty-toolTip { + padding: 10px; +} + +#moduleProperty-inner { + width: 85%; + margin: auto; +} + +#moduleProperty-header { + font-size: 1.1em; + text-transform: uppercase; + margin-bottom: 20px; + text-align: left; +} + +#moduleProperty-inner > p span { + display: inline-block; + font-weight: bold; +} + +#moduleProperty-inner > div span { + display: inline-block; + font-weight: bold; +} + +#moduleProperty-inner > p button { + border-radius: 2px; + margin: 3px; +} + +#moduleProperty-inner > div button { + border-radius: 2px; + margin: 3px; +} + +#moduleProperty-inner:last-child { + margin-bottom: 15px; +} + +.moduleProperty select { + background-color: transparent; + border: none; + margin-left: 2px; + outline: none; +} + +.moduleProperty input, +.moduleProperty textarea { + background-color: transparent; + margin-top: 7px; + outline: none; + padding: 5px 5px; + width: 100%; +} + +.moduleProperty textarea { + text-align: left; +} + +.moduleProperty select, +.moduleProperty input, +.moduleProperty textarea { + border-radius: 7px !important; +} + +.moduleProperty input:focus, +.moduleProperty select:focus, +.moduleProperty textarea { + outline-width: 0; + outline: none; + box-shadow: none; +} + +.input-group-prepend button { + margin-right: 5px; +} +.input-group-append button { + margin-left: 5px; +} + +.input-group-prepend button:hover { + border-radius: 3px !important; +} +.input-group-append button:hover { + border-radius: 3px !important; +} + +/* toogle */ + +.switch { + position: relative; + width: 43px; + height: 17px; + margin-bottom: 0px; + float: right; +} + +.switch input { + display: none; +} + +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + -webkit-transition: 0.2s all ease; + transition: 0.2s all ease; + border-radius: 25px; + width: 35px; +} + +.slider:before { + position: absolute; + content: ''; + height: 20px; + width: 20px; + left: -3px; + top: 50%; + transform: translateY(-51%); + -webkit-transition: 0.2s all ease-in-out; + transition: 0.2s all ease-in-out; + border-radius: 50%; +} + +input:checked + .slider:before { + transform: translate(21px, -51%); +} + +/** custom button styling */ + +.custom-btn--primary { + border-radius: 1px; +} + +.custom-btn--secondary { + background-color: transparent; + border-radius: 1px; + width: 90%; + display: inline-block; + line-height: inherit; +} + +.custom-btn--secondary:hover { + /* color: white; */ + transition: all 0.3s ease; +} + +.custom-btn--tertiary { + border-radius: 1px; +} +/* Used to force auto width on secondary button */ +.custom-btn--basic { + border-radius: 1px; + border: solid 1px; + background-color: transparent; + display: inline-block; + background: var(--table-head-dark); +} + +.custom-btn--basic:focus { + border: solid 1px; +} + +#HelpButton { + background-color: transparent; + border: 2px solid white; + width: 90%; + margin-bottom: 15px; + margin-top: 15px; + font-weight: bold; +} + +.btn-parent { + width: 100%; + display: flex; + justify-content: center; + margin: 0; +} + +/* custom spin button */ + +/*! Module property styling starts here */ + +/** selects styling starts here */ + +.moduleProperty select { + text-align: center; + width: 81px; + height: 20px; + float: right; + font-size: 17px; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +/*! selects styling end here */ + +/** layout dialog styling starts here */ + +.layout-body { + text-align: center; + padding: 10px; + padding-bottom: 17px; + font-weight: bold; +} + +#layoutDialog { + /* display: none; */ + right: 10px; + top: 90px; + width: 220px; +} + +.layout-title span { + display: block; + font-weight: bold; + margin: 8px; +} + +.layout-title--enable { + display: flex; + justify-content: space-between; + margin: 15px 0; + padding: 0 8px; +} + +.Layout-btn { + width: 48%; + height: 30px; + line-height: inherit; +} + +.zoomButton-up { + /* background: url(./assets/layout-panel/up.svg) center/cover no-repeat; */ + display: inline-block; + height: 35px; + width: 35px; +} +.zoomButton-down { + /* background: url(./assets/layout-panel/down.svg) center/cover no-repeat; */ + display: inline-block; + height: 35px; + width: 35px; +} +.zoomButton-left { + /* background: url(./assets/layout-panel/left.svg) center/cover no-repeat; */ + display: inline-block; + height: 35px; + width: 35px; +} +.zoomButton-right { + /* background: url(./assets/layout-panel/right.svg) center/cover no-repeat; */ + display: inline-block; + height: 35px; + width: 35px; +} + +/*! layout dialog styling ends here */ + +/** download dialog styling starts here */ + +.ui-dialog { + /*this also affects all dialog created using jquery UI, needs to be more universe */ + font: inherit; + border-radius: 5px; + width: 600px; + height: 320px; + padding: 10px 17px; + padding-bottom: 0; + /* border: none !important; */ +} + +@supports (backdrop-filter: blur()) { + .ui-dialog { + backdrop-filter: blur(5px); + } +} + +.ui-widget-header { + background: transparent; + border: none; + border-radius: 0; +} + +.option { + display: inline-flex; + border-radius: 7px; + align-items: center; + justify-content: space-around; + padding: 0 7px; + position: relative; + cursor: pointer; + margin: 2px 3px; +} + +.option input[type='radio'] { + visibility: hidden; +} + +.download-dialog-section-2 .btn { + color: var(--text-lite); +} +.download-dialog-section-2 .btn:hover { + color: var(--text-lite); +} + +.download-dialog-section-2 .option { + background: transparent; +} + +.custom-radio span { + height: 20px; + width: 20px; + border-radius: 50%; + display: block; + position: absolute; + left: 2px; + top: 50%; + transform: translateY(-50%); +} + +.custom-radio span:after { + content: ''; + height: 8px; + width: 8px; + border-radius: 50%; + display: block; + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%) scale(0); + transition: 300ms ease-in-out 0s; +} + +.custom-radio input[type='radio']:checked ~ span:after { + transform: translate(-50%, -50%) scale(1); +} + +#saveImageDialog { + border-radius: 2px; + padding: 13px; + margin: 0; + margin-top: 15px; + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; + min-height: 188px !important; +} + +.download-dialog-section-2 .option { + padding: 0; +} + +.download-dialog-section-1 > label { + height: 30px; + width: 85px; +} + +.download-dialog-section-2 { + background: transparent; + width: 100%; + display: inline-flex; + justify-content: space-around; +} + +.btn-group-toggle { + background-color: transparent; + overflow: hidden; +} +.download-dialog-section-2 .active-btn { + box-shadow: none; +} + +.download-dialog-section-2 .btn input[type='radio']:disabled { + background: red !important; + color: red !important; +} + +.download-dialog-section-2_2 { + display: flex; + align-items: center; + justify-content: center; +} + +.download-dialog-section-3 { + border-radius: 2px; + display: flex; + justify-content: space-between; + align-items: center; + padding: 6px 10px; + width: 320px; + position: inherit; +} + +.download-dialog-section-3 > label { + width: 60px; + height: 25px; + margin-bottom: 0; +} + +.ui-dialog-buttonpane { + background: transparent; +} + +.ui-dialog .ui-dialog-titlebar { + padding: 0; + padding-bottom: 8px; + font: inherit; + line-height: inherit; + font-weight: bold; +} + +.ui-dialog-titlebar-close { + border: none; + color: white; + position: absolute; + top: 15px; + right: 15px; + visibility: hidden; +} + +.ui-dialog-titlebar-close::after { + content: ''; + display: block; + position: absolute; + background: url(./assets/small-components/close.svg) center/cover no-repeat; + height: 15px; + width: 15px; + visibility: visible; + right: 0; + top: 0; +} + +.ui-dialog-titlebar-close::hover { + border: none; +} + +.ui-dialog .ui-dialog-buttonpane { + border: none; + padding: 0; + margin: 0; + display: flex; + justify-content: center; + align-items: center; + margin: 12px; +} + +.ui-dialog .ui-dialog-buttonpane button { + background: transparent; + color: white; + line-height: inherit; + border-radius: 1px; + font: inherit; +} + +.ui-dialog .ui-dialog-buttonpane button:hover { + transition: all 0.3s ease; + border: 1px solid transparent; +} + +.render-btn { + height: 35px; + border-radius: 1px; +} +.navbar { + transition: background 0.5s ease-out; +} + +.navbar .nav.pull-right { + float: right; + margin-right: 10px; + min-width: 85px; +} + +@media (max-width: 991px) { + .navbar .nav.pull-right { + display: none; + } +} + +@media (max-width: 991px) { + .nav-dropdown { + text-align: center; + padding-top: 20px; + } +} + +/*! download dialog styling end here */ + +/** combinationalAnalysis dialog styling starts here */ + +.ui-dialog[aria-describedby='combinationalAnalysis'] { + width: 460px; + min-height: 210px; + border: none; +} + +#combinationalAnalysis { + margin-top: 10px; +} + +#combinationalAnalysis p input { + border: 1px solid white; + background: transparent; + font: inherit; + text-align: center; +} + +.ui-dialog input::placeholder { + /* Chrome, Firefox, Opera, Safari 10.1+ */ + color: white; + opacity: 0.7; /* Firefox */ +} + +#combinationalAnalysis table { + width: 460px; +} + +#booleanTable { + width: 200px; +} + +.content-table { + border-collapse: collapse; + font-size: 0.9em; + min-width: 400px; +} + +.content-table tr th { + font-weight: bold; +} + +.content-table th, +.content-table td { + padding: 5px 15px; + margin: 0 3px; + width: 20%; + border-radius: 2px; +} + +.content-table tbody tr { + text-align: center; + display: flex; + margin-bottom: 4px; +} + +.content-table tbody { + display: table-row-group; + overflow: auto !important; + margin-left: 52px; +} + +.output { + cursor: pointer; +} + +/*! combinationalAnalysis dialog styling end here */ + +#setTestbenchData input { + border: 1px solid white; + background: transparent; + text-align: center; + font: inherit; + color: white; +} + +#setTestbenchData p { + font: inherit; + color: white; +} + +/** openProjectDialog styling starts here */ + +#openProjectDialog { + display: grid; + /* grid-template-columns: 1fr 1fr 1fr; */ + /* grid-gap: 0 10px; */ + align-items: center; +} + +#openProjectDialog > label { + margin: 4px; + padding: 10px; + background: transparent; + border-radius: 1px; + width: 100%; +} + +/*! openProjectDialog styling ends here */ + +#insertSubcircuitDialog { + display: block; + padding-bottom: 0; + overflow: visible; +} + +#insertSubcircuitDialog > p { + margin-bottom: 0; +} + +#insertSubcircuitDialog > label { + height: 30px; + border-radius: 3px; + margin: 0 5px; + margin-bottom: 4px; + justify-content: center; + padding-left: 10px; +} + +#miniMap { + position: fixed; + z-index: 3; + bottom: 20px; + right: 40px; + overflow-y: scroll; + opacity: 0.5; + overflow: hidden; + border: none; +} + +.disable::after { + content: ''; + position: absolute; + height: 100%; + width: 100%; + cursor: not-allowed; + left: 0; +} + +.ui-dialog .ui-dialog-buttonpane button { + margin-left: 0.4em; +} + +.ui-dialog-titlebar-close:hover { + border: none; +} + +.radio-green { + background: #42b983; +} + +.search-input { + margin: 0 10px; + padding: 3px 10px; + width: 90%; + border-radius: 13px; + margin-bottom: 10px; + background: transparent !important; +} + +.search-input:focus { + outline: none !important; +} + +.search-close { + position: absolute; + right: 19px; + top: 6px; + cursor: pointer; +} + +.search-results { + padding: 15px; + transition: all 0.5s ease; + max-height: 340px; + overflow-y: scroll; + padding-right: 0; +} + +.search-results div { + border-radius: 3px; +} + +.draggable-panel-css .minimize { + position: absolute; + right: 15px; + cursor: pointer; +} + +.panel-button-icon { + cursor: pointer; +} + +.panel-button { + cursor: pointer; + padding: 2px; +} + +.draggable-panel-css .maximize { + position: absolute; + right: 15px; + cursor: pointer; + display: none; +} + +.ce-hidden, +.prop-hidden { + font-weight: bold; + padding: 10px; + font-size: 16px; + text-transform: uppercase; + border-radius: 5px; +} + +.largeButton.btn { + width: 100%; + margin-bottom: 5px; + margin-left: 0 !important; +} + +.objectPropertyAttributeChecked { + margin-left: 0 !important; +} + +#exitViewBtn { + position: fixed; + z-index: 1000000000; + right: 2%; + top: 3%; + box-shadow: 0px 0px 10Xpx #4545457f; + padding: 10px 15px; + border-radius: 5px; +} + +.ce-hidden, +.prop-hidden { + cursor: move; +} + +#canvasArea, +#backgroundArea, +#simulationArea, +canvas { + /* cursor: wait !important; */ +} + +/** Color them dialog styles starts here*/ + +.ui-dialog[aria-describedby='colorThemesDialog'] { + min-width: 760px; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} + +.colorThemesDialog { + height: 390px !important; + display: grid; + grid-template-columns: 1fr 1fr 1fr; + overflow-y: auto; + margin-top: 10px; + border: 1px solid white !important; +} + +.colorThemesDialog input { + margin: 15px; +} + +.colorThemesDialog label { + margin-bottom: 0; +} + +.theme { + color: white; + width: 202.5px; + line-height: 30px; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + margin: 15px; + border-radius: 1.5px; + transition: all 0.1s ease-out; + position: relative; + overflow-x: hidden; + height: 154px; +} + +.themeNameBox { + display: block; + width: 100%; + cursor: pointer; +} + +.themeSel { + background: transparent; + display: block; + width: 100%; + height: 100%; + position: absolute; +} + +/*! Color them dialog styles ends here*/ + +/*! Custom Color theme dialog styles starts here*/ + +.ui-dialog[aria-describedby='CustomColorThemesDialog'] { + min-width: 760px; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} + +#CustomColorThemesDialog { + height: 400px !important; + background: none; + overflow: auto; +} + +#CustomColorThemesDialog label { + color: var(--text-panel); + width: 60%; + height: 30px; +} + +#CustomColorThemesDialog input { + cursor: pointer; + width: 30%; + height: 30px; +} + +/*! Custom Color theme dialog styles ends here*/ + +.code-window .CodeMirror { + height: calc(100vh - 78px); + overflow: scroll; +} + +.code-window-embed .CodeMirror { + height: 100%; + overflow: scroll; +} + +.code-window-embed { + position: absolute; + top: 28px; + height: 100%; + width: 100%; + overflow: scroll; + z-index: 3; + display: none; +} + +.code-window { + display: none; +} + +#verilogOutput { + font-size: 12px; +} + +.embed-fullscreen-btn { + border-radius: 3px; + width: auto; +} + +#plot { + width: 800px; +} + +.timing-diagram-toolbar { + padding-left: 4px; + padding: 2px; + cursor: default; +} + +.timing-diagram-toolbar input { + width: 80px; + background: transparent !important; +} + +#timing-diagram-log { + font-size: 12.5px; + padding: 3px; + margin-left: 5px; + /* margin-bottom: 5px; */ + border-radius: 3px; +} + +/* CustomColorInput Styles Starts Here */ +.customColorInput { + cursor: pointer; + width: 30%; + height: 30px; + overflow: visible; + position: relative; + top: 8px; + appearance: auto; + background-color: buttonface; + color: buttontext; + border-width: 1px; + border-style: solid; + border-color: buttonborder; + border-image: initial; + padding: 1px 2px; +} +.customColorLabel { + width: 60%; + height: 30px; +} + +/* Vue Dialog Box Styles STARTS */ + +.inputField { + width: 100%; + padding: 10px 10px; + margin: 8px 0; + box-sizing: border-box; + border-radius: 5px; + border: 1px solid #c5c5c5; + color: white; + outline: none; +} + +.cAinput { + width: 30%; + padding: 0 5px; + margin: 8px 0; + box-sizing: border-box; + border-radius: 5px; + border: 1px solid #c5c5c5; + color: white; + outline: none; +} + +.combinationalAnalysisInput:first-child { + padding-top: 20px; +} + +.combinationalAnalysisInput { + display: flex; + flex-direction: row; + justify-content: space-evenly; + align-items: baseline; +} + +.inputField:focus { + border: 2px solid #c5c5c5; +} + +.v-card-actions { + width: fit-content; + display: flex; + flex-direction: row; + justify-content: center; + margin: auto; +} + +.v-input__details { + margin-bottom: 0.5rem; +} + +/* +.ProseMirror ul { + list-style-type: disc; +} + +.ProseMirror ol { + list-style-type: decimal; +} */ + +.ProseMirror { + height: 12rem; + overflow-y: auto; + padding-left: 0.5em; + padding-right: 0.5em; + outline: none; +} + +.fullscreen .ProseMirror { + height: 75vh; +} + +/* .ProseMirror ul, +.ProseMirror ol, +.ProseMirror li { + margin: 0; + padding: 0; + list-style: inherit; +} */ + +.messageBoxContent { + height: auto; + width: 760px; + justify-content: center; + margin: auto; + backdrop-filter: blur(5px); + border-radius: 5px; + border: 0.5px solid var(--br-primary) !important; + background: var(--bg-primary-moz) !important; + background-color: var(--bg-primary-chr) !important; + color: white; +} + +/* media query for .messageBoxContent */ +@media screen and (max-width: 991px) { + .messageBoxContent { + width: 100%; + } +} + +.tabsbarInput { + align-items: center; +} + +.messageBtn { + width: fit-content; + min-width: 50px; + border: 1px solid #c5c5c5; + padding: 5px 5px; +} + +.messageBtn:hover { + background: #c5c5c5; + color: black; +} + +.dialogHeader { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + margin-bottom: 10px; +} +.dialogClose { + position: absolute; + top: 5px; + right: 5px; + color: white; + background: none; + font-size: x-small; + box-shadow: none; +} +.dialogClose:hover { + font-weight: bold; + background: white; + opacity: 0.5; + color: black; +} + +.dialogHeader { + font-weight: bold; + margin-bottom: 25px; +} + +.downloadCheckbox { + width: 5px; + height: 5px; +} +/* STYLE ENDS */ diff --git a/v0/src/styles/css/plugin-stylesheets/checkBo.min.css b/v0/src/styles/css/plugin-stylesheets/checkBo.min.css new file mode 100644 index 00000000..199f3c9b --- /dev/null +++ b/v0/src/styles/css/plugin-stylesheets/checkBo.min.css @@ -0,0 +1,386 @@ +/* + * checkBo lightweight jQuery plugin v0.1.4 by @ElmahdiMahmoud + * Licensed under the MIT license - https://github.com/elmahdim/checkbo/blob/master/LICENSE + * + * Custom checkbox and radio + * Author URL: elmahdim.com + */ + +.cb-checkbox .cb-inner, +.cb-checkbox i { + width: 18px; + height: 18px; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; +} +.cb-checkbox.cb-sm .cb-inner, +.cb-checkbox.cb-sm i { + width: 14px; + height: 14px; +} +.cb-checkbox.cb-md .cb-inner, +.cb-checkbox.cb-md i { + width: 24px; + height: 24px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; +} +.cb-checkbox.cb-lg .cb-inner, +.cb-checkbox.cb-lg i { + width: 30px; + height: 30px; + -moz-border-radius: 6px; + -webkit-border-radius: 6px; + border-radius: 6px; +} +.cb-radio .cb-inner { + width: 18px; + height: 18px; +} +.cb-radio.cb-sm .cb-inner { + width: 14px; + height: 14px; +} +.cb-radio.cb-md .cb-inner { + width: 24px; + height: 24px; +} +.cb-radio.cb-lg .cb-inner { + width: 30px; + height: 30px; +} +.cb-checkbox, +.cb-radio { + padding: 3px 0; + color: inherit; + cursor: pointer; + overflow: hidden; + font-size: inherit; + font-weight: 400; + display: inline-block; + line-height: 18px; +} +.cb-checkbox input[type='checkbox'], +.cb-radio input[type='radio'], +.cb-switcher input[type='checkbox'], +.cb-switcher input[type='radio'] { + /* display: none; */ +} +.cb-checkbox.disabled, +.cb-checkbox.disabled *, +.cb-radio.disabled, +.cb-radio.disabled *, +.cb-switcher.disabled, +.cb-switcher.disabled * { + cursor: default; +} +.cb-checkbox.disabled, +.cb-checkbox.disabled .cb-inner { + color: #ddd; +} +.cb-checkbox.disabled:hover .cb-inner { + border-color: #ddd; +} +.cb-checkbox.disabled.checked .cb-inner { + background-color: #ddd; + border-color: #ddd; +} +.cb-radio.disabled { + color: #ddd; +} +.cb-radio.disabled .cb-inner { + border-color: #ddd; +} +.cb-radio.disabled i { + background-color: transparent; +} +.cb-radio.disabled.checked .cb-inner { + border-color: #ddd; +} +.cb-radio.disabled.checked .cb-inner i { + background-color: #ddd; +} +.cb-radio.disabled:hover .cb-inner { + border-color: #ddd; +} +.cb-checkbox .cb-inner { + float: left; + overflow: hidden; + margin: 0 5px 0 0; + position: relative; + background: transparent; + display: inline-block; + border: 1px solid #d6d6d6; + /* -moz-transition: all 0.5s ease; + -o-transition: all 0.5s ease; + -webkit-transition: all 0.5s ease; + transition: all 0.5s ease; */ +} +.cb-checkbox i { + top: 1px; + left: 2px; + display: block; + position: absolute; +} +.cb-checkbox i:after, +.cb-checkbox i:before { + height: 0; + width: 2px; + content: ''; + display: block; + position: absolute; + background-color: #fff; + /* -moz-transition: all 0.2s ease; + -o-transition: all 0.2s ease; + -webkit-transition: all 0.2s ease; + transition: all 0.2s ease; */ +} +.cb-checkbox i:before { + top: 0; + left: 0; + -moz-transform: rotate(-45deg); + -ms-transform: rotate(-45deg); + -webkit-transform: rotate(-45deg); + transform: rotate(-45deg); +} +.cb-checkbox i:after { + left: 7px; + bottom: 5px; + /* -moz-transition-delay: 0.3s; + -o-transition-delay: 0.3s; + -webkit-transition-delay: 0.3s; + transition-delay: 0.3s; */ + -moz-transform: rotate(30deg); + -ms-transform: rotate(30deg); + -webkit-transform: rotate(30deg); + transform: rotate(30deg); +} +.cb-radio .cb-inner { + float: left; + overflow: hidden; + margin: 0 5px 0 0; + position: relative; + display: inline-block; + border: 1px solid #d7d7d7; + background-color: transparent; + -moz-border-radius: 100%; + -webkit-border-radius: 100%; + border-radius: 100%; + -moz-transition: all 0.1s ease; + -o-transition: all 0.1s ease; + -webkit-transition: all 0.1s ease; + transition: all 0.1s ease; +} +.cb-radio i { + top: 50%; + left: 50%; + width: 6px; + height: 6px; + margin-top: -3px; + margin-left: -3px; + position: absolute; + background-color: transparent; + -moz-border-radius: 100%; + -webkit-border-radius: 100%; + border-radius: 100%; + -moz-transform: scale(0.05, 5); + -ms-transform: scale(0.05, 5); + -webkit-transform: scale(0.05, 5); + transform: scale(0.05, 5); + -moz-transition: all 0.2s ease; + -o-transition: all 0.2s ease; + -webkit-transition: all 0.2s ease; + transition: all 0.2s ease; +} +.cb-checkbox.cb-sm, +.cb-radio.cb-sm { + line-height: 14px; +} +.cb-checkbox.cb-md, +.cb-radio.cb-md { + line-height: 24px; +} +.cb-checkbox.cb-lg, +.cb-radio.cb-lg { + line-height: 30px; +} +.cb-checkbox.cb-sm i:before { + top: 4px; + left: 1px; +} +.cb-checkbox.cb-sm i:after { + left: 5px; +} +.cb-checkbox.cb-md i:before { + top: 10px; + left: 5px; +} +.cb-checkbox.cb-md i:after { + bottom: 6px; + left: 11px; +} +.cb-checkbox.checked .cb-inner { + border-color: transparent; + background-color: transparent; +} +.cb-checkbox.checked.cb-sm i:before { + top: 4px; + left: 1px; +} +.cb-checkbox.checked.cb-sm i:after { + height: 9px; +} +.cb-checkbox.checked.cb-md i:before { + top: 10px; + left: 4px; + height: 8px; +} +.cb-checkbox.checked.cb-md i:after { + bottom: 6px; + left: 11px; + height: 16px; +} +.cb-checkbox.checked.cb-lg i:before { + top: 11px; + left: 6px; + height: 12px; +} +.cb-checkbox.checked.cb-lg i:after { + left: 14px; + bottom: 7px; + height: 20px; +} +.cb-checkbox.checked i:before { + top: 6px; + left: 2px; + height: 6px; +} +.cb-checkbox.checked i:after { + height: 12px; +} +.cb-radio.checked .cb-inner { + background: #fff; + box-shadow: 0 0 3px #efefef; +} +.cb-radio.checked i { + -moz-transform: scale(1.1, 1.1); + -ms-transform: scale(1.1, 1.1); + -webkit-transform: scale(1.1, 1.1); + transform: scale(1.1, 1.1); + background-color: transparent; +} +.cb-checkbox:hover .cb-inner, +.cb-radio:hover .cb-inner { + border-color: white; +} +.cb-switcher { + display: inline-block; + border: 1px solid #eee; + background-color: #fff; + width: 95px; + height: 35px; + position: relative; + -moz-border-radius: 20px; + -webkit-border-radius: 20px; + border-radius: 20px; + -moz-transition: background 0.4s ease; + -o-transition: background 0.4s ease; + -webkit-transition: background 0.4s ease; + transition: background 0.4s ease; +} +.cb-switcher, +.cb-switcher * { + cursor: pointer; +} +.cb-switcher ::-moz-selection { + background-color: transparent; +} +.cb-switcher ::selection { + background-color: transparent; +} +.cb-switcher .cb-state { + z-index: 1; + text-align: center; + font-size: 12px; +} +.cb-switcher .cb-state, +.cb-switcher:before { + width: 34px; + height: 34px; + line-height: 34px; + position: absolute; + left: 0; + top: -1px; + -moz-border-radius: 100%; + -webkit-border-radius: 100%; + border-radius: 100%; + -moz-transition: all 0.4s ease; + -o-transition: all 0.4s ease; + -webkit-transition: all 0.4s ease; + transition: all 0.4s ease; +} +.cb-switcher:before { + content: ''; + background-color: #eee; + -moz-box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1); + -webkit-box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1); + box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1); +} +.cb-switcher.checked { + background-color: transparent; +} +.cb-switcher.checked .cb-state, +.cb-switcher.checked:before { + left: 60px; + color: transparent; +} +.cb-switcher.checked:before { + background-color: #fff; + -moz-box-shadow: -1px 1px 1px rgba(0, 0, 0, 0.1); + -webkit-box-shadow: -1px 1px 1px rgba(0, 0, 0, 0.1); + box-shadow: -1px 1px 1px rgba(0, 0, 0, 0.1); +} +.cb-switcher.checked .inner-switcher:before { + border-top-color: transparent; +} +.cb-switcher.checked .inner-switcher:after { + border-bottom-color: transparent; +} +.cb-switcher .inner-switcher:after, +.cb-switcher .inner-switcher:before { + content: ''; + position: absolute; + left: 50%; + width: 0; + height: 0; + z-index: 2; + margin-left: -20px; + border-left: 20px solid transparent; + border-right: 20px solid transparent; + -moz-transition: border 0.4s ease; + -o-transition: border 0.4s ease; + -webkit-transition: border 0.4s ease; + transition: border 0.4s ease; +} +.cb-switcher .inner-switcher:before { + border-top: 17px solid #fff; + top: 0; +} +.cb-switcher .inner-switcher:after { + border-bottom: 17px solid #fff; + bottom: 0; +} +.cb-state { + color: #ccc; + display: inline-block; +} +.cb-switcher-group .cb-state { + position: relative; + top: 7px; +} +.is-hidden { + display: none !important; + visibility: hidden !important; +} diff --git a/v0/src/styles/css/restrictedElements.css b/v0/src/styles/css/restrictedElements.css new file mode 100644 index 00000000..68c21493 --- /dev/null +++ b/v0/src/styles/css/restrictedElements.css @@ -0,0 +1,20 @@ +.display--none { + display: none; +} + +.circuit-element-category { + border-bottom: 1px solid #026e57; + font-weight: 500; + margin: 20px 0 5px; + padding-bottom: 5px; +} + +.restricted-elements-list { + margin: 10px 0 25px; +} + +.form-check-label { + font-size: 16px; + margin-bottom: 6px; + margin-top: 6px; +} diff --git a/v0/src/styles/css/shortcut.panel.css b/v0/src/styles/css/shortcut.panel.css new file mode 100644 index 00000000..a2be4f72 --- /dev/null +++ b/v0/src/styles/css/shortcut.panel.css @@ -0,0 +1,146 @@ +.ui-dialog[aria-describedby='customShortcutDialog'] { + min-width: 680px; +} + +#edit { + display: none; + position: absolute; + width: 490px; + height: 150px; + background: #2d302e; + border-radius: 5px; + z-index: 10000; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + animation: none; +} + +#edit > span { + margin-top: 10px; + position: absolute; + text-align: center; + width: 100%; +} + +#pressedKeys { + text-align: center; + position: absolute; + top: 50%; + width: 100%; +} + +#warning { + position: absolute; + bottom: 5px; + width: 100%; + text-align: center; + font-size: 14px; + color: #dc5656; +} + +#customShortcutDialog { + align-items: center; + color: white; + flex-direction: column; + max-height: 430px !important; + overflow: hidden !important; +} + +#closeDialog { + font-size: 25px; + color: white; + transform: rotate(45deg); + position: absolute; + top: 0%; + right: 2%; + cursor: pointer; + user-select: none; + -moz-user-select: none; +} + +#dialogTitle { + position: absolute; + top: 2%; + left: 3%; + user-select: none; + -moz-user-select: none; +} + +#heading { + display: flex; + justify-content: space-between; + align-items: center; + font-weight: bold; + width: 100%; + height: 35px; + user-select: none; + padding-bottom: 10px; +} + +#heading > span { + padding: 0 20px; +} + +#preference { + max-width: 600px; + height: 350px; + overflow-y: auto; +} + +#preference div { + display: inline-flex; + justify-content: space-between; + align-items: center; + height: 35px; + cursor: pointer; + user-select: none; + -moz-user-select: none; + width: 100%; + padding-left: 5px; + padding-right: 7px; +} + +/* #preference div:not('#edit-icon') > span { + padding: 0 30px; +} */ + +#preference > div:hover { + background-color: #7474743f; +} + +#preference > div:hover span { + visibility: visible; +} + +#edit-icon { + background: url(../../assets/img/edit_icon.png) no-repeat; + background-size: 15px 15px; + display: inline-block; + visibility: hidden; + width: 15px; + height: 15px; +} + +@keyframes shake { + 10%, + 90% { + transform: translate(-50.5%, -50%); + } + + 20%, + 80% { + transform: translate(-49.5%, -50%); + } + + 30%, + 50%, + 70% { + transform: translate(-50.5%, -50%); + } + + 40%, + 60% { + transform: translate(-49.5%, -50%); + } +} diff --git a/v0/src/styles/css/testCreator.css b/v0/src/styles/css/testCreator.css new file mode 100644 index 00000000..47877b3c --- /dev/null +++ b/v0/src/styles/css/testCreator.css @@ -0,0 +1,141 @@ +.tb-test-title { + text-align: center; + margin-top: 50px; +} + +.lower-button { + height: 40px; + width: auto; + min-width: 40px; + background-color: #ffffff; + border: 2px solid black; + color: black; + /*padding: 20px;*/ + text-align: center; + text-decoration: none; + display: inline-block; + /*font-size: 16px;*/ + margin: 4px 2px; + border-radius: 4px; +} + +.table-button { + height: 20px; + width: 20px; + background-color: #ffffff; + border: 2px solid black; + color: black; + text-decoration: none; + display: inline-block; + margin: 4px 4px; + padding: 0px; + border-radius: 5px; +} + +.plus-button { + font-size: 25px; +} + +.tb-minus { + color: red; +} + +.save-buton { + background-color: #42b983; + color: white; + border: 1px solid gray; + min-width: 70px; +} + +.latest-button { + float: left; +} + +.buttons-alignment { + display: flex; + flex-direction: row; + align-items: flex-start; +} + +.tablink { + background-color: #555; + color: white; + float: left; + border: 1px solid white; + border-radius: 5px; + outline: none; + cursor: pointer; + padding: 14px 16px; + font-size: 17px; + width: 50%; +} + +/* Change background color of buttons on hover */ +.tablink:hover.tablink-no-override { + background-color: #a5dfc5; +} + +.tablink-hover-override { +} + +.tablink.tab-selected { + background-color: #42b983; + color: #fff; + outline: none; +} + +.data-group { + margin-top: 2%; +} + +.tb-table { + table-layout: fixed; + width: 100%; + height: 20px; + border-spacing: 5px; +} + +.tb-table th, +td { + border: 2px solid black; + border-collapse: collapse; + padding: 15px; + text-align: center; + transition: transform 0.2s; +} + +.tb-table th { + text-align: center; +} + +.tb-table tr th:first-child, +tr td:first-child { + width: 250px; +} + +.label-table { + margin-top: 100px; +} + +.test-title { + display: flex; + width: 100%; + font-size: 25px; + margin-top: 20px; + margin-bottom: 10px; +} + +.test-title #test-title-label { + width: 80%; + border: 1px solid; + border-radius: 5px; +} + +.tb-handle { + padding: 0px !important; + border: 0px !important; +} + +body { + overflow: scroll; +} diff --git a/v0/src/styles/css/typeahead.min.css b/v0/src/styles/css/typeahead.min.css new file mode 100644 index 00000000..01891f6d --- /dev/null +++ b/v0/src/styles/css/typeahead.min.css @@ -0,0 +1,186 @@ +.has-warning .twitter-typeahead .tt-hint, +.has-warning .twitter-typeahead .tt-input { + border-color: #8a6d3b; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.has-warning .twitter-typeahead .tt-hint:focus, +.has-warning .twitter-typeahead .tt-input:focus { + border-color: #66512c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b; +} +.has-error .twitter-typeahead .tt-hint, +.has-error .twitter-typeahead .tt-input { + border-color: #a94442; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.has-error .twitter-typeahead .tt-hint:focus, +.has-error .twitter-typeahead .tt-input:focus { + border-color: #843534; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; +} +.has-success .twitter-typeahead .tt-hint, +.has-success .twitter-typeahead .tt-input { + border-color: #3c763d; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.has-success .twitter-typeahead .tt-hint:focus, +.has-success .twitter-typeahead .tt-input:focus { + border-color: #2b542c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168; +} +.input-group .twitter-typeahead:first-child .tt-hint, +.input-group .twitter-typeahead:first-child .tt-input { + border-bottom-left-radius: 4px; + border-top-left-radius: 4px; + width: 100%; +} +.input-group .twitter-typeahead:last-child .tt-hint, +.input-group .twitter-typeahead:last-child .tt-input { + border-bottom-right-radius: 4px; + border-top-right-radius: 4px; + width: 100%; +} +.input-group.input-group-sm .twitter-typeahead .tt-hint, +.input-group.input-group-sm .twitter-typeahead .tt-input { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-group.input-group-sm .twitter-typeahead .tt-hint, +select.input-group.input-group-sm .twitter-typeahead .tt-input { + height: 30px; + line-height: 30px; +} +select[multiple].input-group.input-group-sm .twitter-typeahead .tt-hint, +select[multiple].input-group.input-group-sm .twitter-typeahead .tt-input, +textarea.input-group.input-group-sm .twitter-typeahead .tt-hint, +textarea.input-group.input-group-sm .twitter-typeahead .tt-input { + height: auto; +} +.input-group.input-group-sm + .twitter-typeahead:not(:first-child):not(:last-child) + .tt-hint, +.input-group.input-group-sm + .twitter-typeahead:not(:first-child):not(:last-child) + .tt-input { + border-radius: 0; +} +.input-group.input-group-sm .twitter-typeahead:first-child .tt-hint, +.input-group.input-group-sm .twitter-typeahead:first-child .tt-input { + border-radius: 3px 0 0 3px; +} +.input-group.input-group-sm .twitter-typeahead:last-child .tt-hint, +.input-group.input-group-sm .twitter-typeahead:last-child .tt-input { + border-radius: 0 3px 3px 0; +} +.input-group.input-group-lg .twitter-typeahead .tt-hint, +.input-group.input-group-lg .twitter-typeahead .tt-input { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} +select.input-group.input-group-lg .twitter-typeahead .tt-hint, +select.input-group.input-group-lg .twitter-typeahead .tt-input { + height: 46px; + line-height: 46px; +} +select[multiple].input-group.input-group-lg .twitter-typeahead .tt-hint, +select[multiple].input-group.input-group-lg .twitter-typeahead .tt-input, +textarea.input-group.input-group-lg .twitter-typeahead .tt-hint, +textarea.input-group.input-group-lg .twitter-typeahead .tt-input { + height: auto; +} +.input-group.input-group-lg + .twitter-typeahead:not(:first-child):not(:last-child) + .tt-hint, +.input-group.input-group-lg + .twitter-typeahead:not(:first-child):not(:last-child) + .tt-input { + border-radius: 0; +} +.input-group.input-group-lg .twitter-typeahead:first-child .tt-hint, +.input-group.input-group-lg .twitter-typeahead:first-child .tt-input { + border-radius: 6px 0 0 6px; +} +.input-group.input-group-lg .twitter-typeahead:last-child .tt-hint, +.input-group.input-group-lg .twitter-typeahead:last-child .tt-input { + border-radius: 0 6px 6px 0; +} +.twitter-typeahead { + width: 100%; + float: left; +} +.input-group .twitter-typeahead { + display: table-cell !important; +} +.twitter-typeahead .tt-hint { + color: #999; +} +.twitter-typeahead .tt-input { + z-index: 2; +} +.twitter-typeahead .tt-input[disabled], +.twitter-typeahead .tt-input[readonly], +fieldset[disabled] .twitter-typeahead .tt-input { + cursor: not-allowed; + background-color: #eee !important; +} +.tt-dropdown-menu, +.tt-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + min-width: 160px; + width: 100%; + padding: 5px 0; + margin: 2px 0 0; + list-style: none; + font-size: 14px; + background-color: #fff; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 4px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + background-clip: padding-box; +} +.tt-dropdown-menu .tt-suggestion, +.tt-menu .tt-suggestion { + display: block; + padding: 3px 20px; + clear: both; + font-weight: 400; + line-height: 1.42857143; + color: #333; +} +.tt-dropdown-menu .tt-suggestion.tt-cursor, +.tt-dropdown-menu .tt-suggestion:hover, +.tt-menu .tt-suggestion.tt-cursor, +.tt-menu .tt-suggestion:hover { + cursor: pointer; + text-decoration: none; + outline: 0; + background-color: #f5f5f5; + color: #262626; +} +.tt-dropdown-menu .tt-suggestion.tt-cursor a, +.tt-dropdown-menu .tt-suggestion:hover a, +.tt-menu .tt-suggestion.tt-cursor a, +.tt-menu .tt-suggestion:hover a { + color: #262626; +} +.tt-dropdown-menu .tt-suggestion p, +.tt-menu .tt-suggestion p { + margin: 0; +} diff --git a/v0/src/styles/simulator.scss b/v0/src/styles/simulator.scss new file mode 100644 index 00000000..f6460422 --- /dev/null +++ b/v0/src/styles/simulator.scss @@ -0,0 +1,39 @@ +$fa-font-path: '../../../node_modules/@fortawesome/fontawesome-free/webfonts'; +@import '../../../node_modules/@fortawesome/fontawesome-free/scss/fontawesome.scss'; +@import '../../../node_modules/@fortawesome/fontawesome-free/scss/solid.scss'; + +.subcircuitdialog { + display: none; + overflow-x: hidden; + overflow-y: auto; +} + +.side { + width: 330px; + max-width: 500px; + min-width: 200px; + overflow-x: hidden; + overflow-y: scroll; +} + +.report-sidebar a { + color: #fff; + font-size: 13px; + padding: 10px; + position: fixed; + right: -119px; + text-decoration: none; + bottom: 30px; + transition: 0.3s; + width: 160px; + z-index: 999; +} + +.report-sidebar span { + font-size: 1.2em; + padding-right: 20px; +} + +.report-sidebar a:hover { + right: 0; +} diff --git a/v0/src/styles/tutorials.scss b/v0/src/styles/tutorials.scss new file mode 100644 index 00000000..59c4db7b --- /dev/null +++ b/v0/src/styles/tutorials.scss @@ -0,0 +1,52 @@ +@import '/node_modules/driver.js/dist/driver.min.css'; + +#driver-highlighted-element-stage { + background-color: transparent !important; +} + +.driver-disabled { + border: 1.5px solid #ddd !important; +} + +.driver-btn-group button { + font-family: 'Nunito', sans-serif !important; + font-size: 14px !important; + border: 1.5px solid #42b983 !important; + text-shadow: none !important; + border-radius: 0px !important; +} + +.driver-btn-group button:hover { + font-family: 'Nunito', sans-serif !important; + background: #42b983 !important; + color: white !important; +} + +.driver-close-btn { + font-family: 'Nunito', sans-serif !important; + font-size: 14px !important; + border: none !important; + border-radius: 0px !important; + text-shadow: none !important; + background: #dc5656 !important; + color: white !important; + margin: auto 3px !important; +} + +.driver-popover-description, +.driver-popover-title { + font-family: 'Nunito', sans-serif !important; +} + +.bug-guide .right { + top: 114px !important; +} + +.tourHelpStep.driver-popover-title { + display: none !important; +} + +.driver-next-btn { + color: white !important; + background-color: #42b983 !important; +} diff --git a/v1/src/App.vue b/v1/src/App.vue new file mode 100644 index 00000000..39422761 --- /dev/null +++ b/v1/src/App.vue @@ -0,0 +1,21 @@ + + + diff --git a/v1/src/assets/constants/Navbar/NAVBAR_DATA.json b/v1/src/assets/constants/Navbar/NAVBAR_DATA.json new file mode 100644 index 00000000..98d70a94 --- /dev/null +++ b/v1/src/assets/constants/Navbar/NAVBAR_DATA.json @@ -0,0 +1,236 @@ +[ + { + "id": "1", + "text": "project", + "spanClass": "caret", + "dropdownItems": [ + { + "id": "1", + "item": "new_project", + "itemid": "newProject", + "attributes": [] + }, + { + "id": "2", + "item": "save_online", + "itemid": "save", + "attributes": [] + }, + { + "id": "3", + "item": "save_offline", + "itemid": "saveOffline", + "attributes": [] + }, + { + "id": "4", + "item": "open_offline", + "itemid": "createOpenLocalPrompt", + "attributes": [] + }, + { + "id": "5", + "item": "export_as_file", + "itemid": "ExportProject", + "attributes": [] + }, + { + "id": "6", + "item": "import_project", + "itemid": "ImportProject", + "attributes": [] + }, + { + "id": "7", + "item": "clear_project", + "itemid": "clearProject", + "attributes": [] + }, + { + "id": "8", + "item": "recover_project", + "itemid": "recoverProject", + "attributes": [] + }, + { + "id": "9", + "item": "preview_circuit", + "itemid": "fullViewOption", + "attributes": [] + }, + { + "id": "10", + "item": "view_previous_ui", + "itemid": "", + "attributes": [ + { + "name": "onclick", + "value": "old_ui_redirect()" + } + ] + } + ] + }, + { + "id": "2", + "text": "circuit", + "spanClass": "caret", + "dropdownItems": [ + { + "id": "1", + "item": "new_circuit", + "itemid": "createNewCircuitScope", + "attributes": [] + }, + { + "id": "2", + "item": "new_verilog_module_html", + "itemid": "newVerilogModule", + "attributes": [] + }, + { + "id": "3", + "item": "insert_subcircuit", + "itemid": "createSubCircuitPrompt", + "attributes": [] + } + ] + }, + { + "id": "3", + "text": "tools", + "spanClass": "caret", + "dropdownItems": [ + { + "id": "1", + "item": "combinational_analysis_html", + "itemid": "createCombinationalAnalysisPrompt", + "attributes": [] + }, + { + "id": "2", + "item": "hex_bin_dec_converter_html", + "itemid": "bitconverter", + "attributes": [] + }, + { + "id": "3", + "item": "download_image", + "itemid": "createSaveAsImgPrompt", + "attributes": [] + }, + { + "id": "4", + "item": "themes", + "itemid": "colorThemes", + "attributes": [] + }, + { + "id": "5", + "item": "custom_shortcut", + "itemid": "customShortcut", + "attributes": [] + }, + { + "id": "6", + "item": "export_verilog", + "itemid": "generateVerilog", + "attributes": [] + } + ] + }, + { + "id": "4", + "text": "help", + "spanClass": "caret", + "dropdownItems": [ + { + "id": "1", + "item": "tutorial_guide", + "itemid": "showTourGuide", + "attributes": [] + }, + { + "id": "2", + "item": "user_manual", + "itemid": "", + "attributes": [ + { + "name": "href", + "value": "https://docs.circuitverse.org" + }, + { + "name": "target", + "value": "_blank" + }, + { + "name": "role", + "value": "button" + }, + { + "name": "aria-haspopup", + "value": "true" + }, + { + "name": "aria-expanded", + "value": "false" + } + ] + }, + { + "id": "3", + "item": "learn_digital_logic", + "itemid": "", + "attributes": [ + { + "name": "href", + "value": "https://learn.circuitverse.org" + }, + { + "name": "target", + "value": "_blank" + }, + { + "name": "role", + "value": "button" + }, + { + "name": "aria-haspopup", + "value": "true" + }, + { + "name": "aria-expanded", + "value": "false" + } + ] + }, + { + "id": "4", + "item": "discussion_forum", + "itemid": "", + "attributes": [ + { + "name": "href", + "value": "https://circuitverse.org/forum" + }, + { + "name": "target", + "value": "_blank" + }, + { + "name": "role", + "value": "button" + }, + { + "name": "aria-haspopup", + "value": "true" + }, + { + "name": "aria-expanded", + "value": "false" + } + ] + } + ] + } +] diff --git a/v1/src/assets/constants/Navbar/USER_DATA.json b/v1/src/assets/constants/Navbar/USER_DATA.json new file mode 100644 index 00000000..4d317d1e --- /dev/null +++ b/v1/src/assets/constants/Navbar/USER_DATA.json @@ -0,0 +1,71 @@ +[ + { + "id": "1", + "item": "dashboard", + "itemid": "", + "attributes": [ + { + "name": "href", + "value": "/users/${:id}" + }, + { + "name": "role", + "value": "button" + }, + { + "name": "aria-haspopup", + "value": "true" + }, + { + "name": "aria-expanded", + "value": "false" + } + ] + }, + { + "id": "2", + "item": "my_groups", + "itemid": "", + "attributes": [ + { + "name": "href", + "value": "/users/${:id}/groups" + }, + { + "name": "role", + "value": "button" + }, + { + "name": "aria-haspopup", + "value": "true" + }, + { + "name": "aria-expanded", + "value": "false" + } + ] + }, + { + "id": "3", + "item": "notifications", + "itemid": "", + "attributes": [ + { + "name": "href", + "value": "/users/${:id}/notifications" + }, + { + "name": "role", + "value": "button" + }, + { + "name": "aria-haspopup", + "value": "true" + }, + { + "name": "aria-expanded", + "value": "false" + } + ] + } +] diff --git a/v1/src/assets/constants/Panels/TimingDiagramPanel/buttons.json b/v1/src/assets/constants/Panels/TimingDiagramPanel/buttons.json new file mode 100644 index 00000000..357a00fa --- /dev/null +++ b/v1/src/assets/constants/Panels/TimingDiagramPanel/buttons.json @@ -0,0 +1,79 @@ +[ + { + "title": "Decrease Size", + "icon": "fa-chevron-left", + "class": "timing-diagram-smaller", + "type": "primary", + "click": "smaller" + }, + { + "title": "Increase Size", + "icon": "fa-chevron-right", + "class": "timing-diagram-larger", + "type": "primary", + "click": "larger" + }, + { + "title": "Decrease Height", + "icon": "fa-chevron-up", + "class": "timing-diagram-small-height", + "type": "primary", + "click": "smallHeight" + }, + { + "title": "Increase Height", + "icon": "fa-chevron-down", + "class": "timing-diagram-large-height", + "type": "primary", + "click": "largeHeight" + }, + { + "title": "Download As Image", + "icon": "fa-download", + "class": "timing-diagram-download", + "type": "primary", + "click": "download" + }, + { + "title": "Reset Timing Diagram", + "icon": "fa-undo", + "class": "timing-diagram-reset", + "type": "tertiary", + "click": "reset" + }, + { + "title": "Autocalibrate Cycle Units", + "icon": "fa-magic", + "class": "timing-diagram-calibrate", + "type": "tertiary", + "click": "calibrate" + }, + { + "title": "Zoom In", + "icon": "fa-search-plus", + "class": "timing-diagram-zoom-in", + "type": "primary", + "click": "zoomIn" + }, + { + "title": "Zoom Out", + "icon": "fa-search-minus", + "class": "timing-diagram-zoom-out", + "type": "primary", + "click": "zoomOut" + }, + { + "title": "Resume auto-scroll", + "icon": "fa-play", + "class": "timing-diagram-resume", + "type": "primary", + "click": "resume" + }, + { + "title": "Pause auto-scroll", + "icon": "fa-pause", + "class": "timing-diagram-pause", + "type": "primary", + "click": "pause" + } +] diff --git a/v1/src/assets/constants/Panels/VerilogEditorPanel/THEMES.json b/v1/src/assets/constants/Panels/VerilogEditorPanel/THEMES.json new file mode 100644 index 00000000..4319f822 --- /dev/null +++ b/v1/src/assets/constants/Panels/VerilogEditorPanel/THEMES.json @@ -0,0 +1,24 @@ +[ + { + "label": "Light Themes", + "options": [ + { "value": "default" }, + { "value": "solarized" }, + { "value": "elegant" }, + { "value": "neat" }, + { "value": "idea" }, + { "value": "neo" } + ] + }, + { + "label": "Dark Themes", + "options": [ + { "value": "blackboard" }, + { "value": "cobalt" }, + { "value": "night" }, + { "value": "the-matrix" }, + { "value": "midnight" }, + { "value": "monokai" } + ] + } +] diff --git a/v1/src/assets/img/ALU.png b/v1/src/assets/img/ALU.png new file mode 100644 index 00000000..fa809425 Binary files /dev/null and b/v1/src/assets/img/ALU.png differ diff --git a/v1/src/assets/img/ALU.svg b/v1/src/assets/img/ALU.svg new file mode 100644 index 00000000..0db7c17a --- /dev/null +++ b/v1/src/assets/img/ALU.svg @@ -0,0 +1 @@ +BACTRCarryAnsALU \ No newline at end of file diff --git a/v1/src/assets/img/Adder.svg b/v1/src/assets/img/Adder.svg new file mode 100644 index 00000000..9249cff0 --- /dev/null +++ b/v1/src/assets/img/Adder.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/AndGate.svg b/v1/src/assets/img/AndGate.svg new file mode 100644 index 00000000..6273b83b --- /dev/null +++ b/v1/src/assets/img/AndGate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/Arrow.svg b/v1/src/assets/img/Arrow.svg new file mode 100644 index 00000000..c25f20a1 --- /dev/null +++ b/v1/src/assets/img/Arrow.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/AsyncCounter.jpeg b/v1/src/assets/img/AsyncCounter.jpeg new file mode 100644 index 00000000..88a40590 Binary files /dev/null and b/v1/src/assets/img/AsyncCounter.jpeg differ diff --git a/v1/src/assets/img/BitSelector.svg b/v1/src/assets/img/BitSelector.svg new file mode 100644 index 00000000..b0436573 --- /dev/null +++ b/v1/src/assets/img/BitSelector.svg @@ -0,0 +1 @@ +x \ No newline at end of file diff --git a/v1/src/assets/img/Buffer.svg b/v1/src/assets/img/Buffer.svg new file mode 100644 index 00000000..bf867043 --- /dev/null +++ b/v1/src/assets/img/Buffer.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/Button.svg b/v1/src/assets/img/Button.svg new file mode 100644 index 00000000..6c16a24e --- /dev/null +++ b/v1/src/assets/img/Button.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/Clock.svg b/v1/src/assets/img/Clock.svg new file mode 100644 index 00000000..653d29e0 --- /dev/null +++ b/v1/src/assets/img/Clock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/ConstantVal.svg b/v1/src/assets/img/ConstantVal.svg new file mode 100644 index 00000000..dcc41214 --- /dev/null +++ b/v1/src/assets/img/ConstantVal.svg @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/v1/src/assets/img/Control Sequencer.png b/v1/src/assets/img/Control Sequencer.png new file mode 100644 index 00000000..2189f0a7 Binary files /dev/null and b/v1/src/assets/img/Control Sequencer.png differ diff --git a/v1/src/assets/img/ControlledInverter.svg b/v1/src/assets/img/ControlledInverter.svg new file mode 100644 index 00000000..927dc8c8 --- /dev/null +++ b/v1/src/assets/img/ControlledInverter.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/Counter.svg b/v1/src/assets/img/Counter.svg new file mode 100644 index 00000000..ee2aa77b --- /dev/null +++ b/v1/src/assets/img/Counter.svg @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/v1/src/assets/img/Decoder.svg b/v1/src/assets/img/Decoder.svg new file mode 100644 index 00000000..9a48c394 --- /dev/null +++ b/v1/src/assets/img/Decoder.svg @@ -0,0 +1 @@ +01 \ No newline at end of file diff --git a/v1/src/assets/img/Demultiplexer.svg b/v1/src/assets/img/Demultiplexer.svg new file mode 100644 index 00000000..61cad635 --- /dev/null +++ b/v1/src/assets/img/Demultiplexer.svg @@ -0,0 +1 @@ +01 \ No newline at end of file diff --git a/v1/src/assets/img/DflipFlop.svg b/v1/src/assets/img/DflipFlop.svg new file mode 100644 index 00000000..49f4ab5f --- /dev/null +++ b/v1/src/assets/img/DflipFlop.svg @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/v1/src/assets/img/DigitalLed.svg b/v1/src/assets/img/DigitalLed.svg new file mode 100644 index 00000000..a9259c2f --- /dev/null +++ b/v1/src/assets/img/DigitalLed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/Dlatch.svg b/v1/src/assets/img/Dlatch.svg new file mode 100644 index 00000000..8aef7671 --- /dev/null +++ b/v1/src/assets/img/Dlatch.svg @@ -0,0 +1 @@ +3 \ No newline at end of file diff --git a/v1/src/assets/img/EEPROM.svg b/v1/src/assets/img/EEPROM.svg new file mode 100644 index 00000000..504f61b0 --- /dev/null +++ b/v1/src/assets/img/EEPROM.svg @@ -0,0 +1 @@ +EPROMADIWDO \ No newline at end of file diff --git a/v1/src/assets/img/Flag.svg b/v1/src/assets/img/Flag.svg new file mode 100644 index 00000000..87ef27f4 --- /dev/null +++ b/v1/src/assets/img/Flag.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/FlipFlop.jpeg b/v1/src/assets/img/FlipFlop.jpeg new file mode 100644 index 00000000..5c3d87ce Binary files /dev/null and b/v1/src/assets/img/FlipFlop.jpeg differ diff --git a/v1/src/assets/img/ForceGate.svg b/v1/src/assets/img/ForceGate.svg new file mode 100644 index 00000000..28b50f3f --- /dev/null +++ b/v1/src/assets/img/ForceGate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/Ground.svg b/v1/src/assets/img/Ground.svg new file mode 100644 index 00000000..70f453f6 --- /dev/null +++ b/v1/src/assets/img/Ground.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/HexDisplay.svg b/v1/src/assets/img/HexDisplay.svg new file mode 100644 index 00000000..10c89e13 --- /dev/null +++ b/v1/src/assets/img/HexDisplay.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/ImageAnnotation.svg b/v1/src/assets/img/ImageAnnotation.svg new file mode 100644 index 00000000..8fbd9f75 --- /dev/null +++ b/v1/src/assets/img/ImageAnnotation.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/Input.svg b/v1/src/assets/img/Input.svg new file mode 100644 index 00000000..42a626ff --- /dev/null +++ b/v1/src/assets/img/Input.svg @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/v1/src/assets/img/JKflipFlop.svg b/v1/src/assets/img/JKflipFlop.svg new file mode 100644 index 00000000..9e723197 --- /dev/null +++ b/v1/src/assets/img/JKflipFlop.svg @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/v1/src/assets/img/Keyboard.jpeg b/v1/src/assets/img/Keyboard.jpeg new file mode 100644 index 00000000..9922daff Binary files /dev/null and b/v1/src/assets/img/Keyboard.jpeg differ diff --git a/v1/src/assets/img/Keyboard.svg b/v1/src/assets/img/Keyboard.svg new file mode 100644 index 00000000..6e215f6f --- /dev/null +++ b/v1/src/assets/img/Keyboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/LSB.svg b/v1/src/assets/img/LSB.svg new file mode 100644 index 00000000..31148b47 --- /dev/null +++ b/v1/src/assets/img/LSB.svg @@ -0,0 +1 @@ +LSBEN \ No newline at end of file diff --git a/v1/src/assets/img/MSB.svg b/v1/src/assets/img/MSB.svg new file mode 100644 index 00000000..21997b94 --- /dev/null +++ b/v1/src/assets/img/MSB.svg @@ -0,0 +1 @@ +MSBEN \ No newline at end of file diff --git a/v1/src/assets/img/Main.png b/v1/src/assets/img/Main.png new file mode 100644 index 00000000..3bb25dac Binary files /dev/null and b/v1/src/assets/img/Main.png differ diff --git a/v1/src/assets/img/Multiplexer.svg b/v1/src/assets/img/Multiplexer.svg new file mode 100644 index 00000000..b1b8472d --- /dev/null +++ b/v1/src/assets/img/Multiplexer.svg @@ -0,0 +1 @@ +01 \ No newline at end of file diff --git a/v1/src/assets/img/NandGate.svg b/v1/src/assets/img/NandGate.svg new file mode 100644 index 00000000..04cddce2 --- /dev/null +++ b/v1/src/assets/img/NandGate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/NorGate.svg b/v1/src/assets/img/NorGate.svg new file mode 100644 index 00000000..55e9abc2 --- /dev/null +++ b/v1/src/assets/img/NorGate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/NotGate.svg b/v1/src/assets/img/NotGate.svg new file mode 100644 index 00000000..a0d40ad4 --- /dev/null +++ b/v1/src/assets/img/NotGate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/OrGate.svg b/v1/src/assets/img/OrGate.svg new file mode 100644 index 00000000..741ba9fe --- /dev/null +++ b/v1/src/assets/img/OrGate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/Output.svg b/v1/src/assets/img/Output.svg new file mode 100644 index 00000000..7d4298f6 --- /dev/null +++ b/v1/src/assets/img/Output.svg @@ -0,0 +1 @@ +x \ No newline at end of file diff --git a/v1/src/assets/img/Power.svg b/v1/src/assets/img/Power.svg new file mode 100644 index 00000000..a4619920 --- /dev/null +++ b/v1/src/assets/img/Power.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/PriorityEncoder.svg b/v1/src/assets/img/PriorityEncoder.svg new file mode 100644 index 00000000..417325c6 --- /dev/null +++ b/v1/src/assets/img/PriorityEncoder.svg @@ -0,0 +1 @@ +010EN \ No newline at end of file diff --git a/v1/src/assets/img/RAM.svg b/v1/src/assets/img/RAM.svg new file mode 100644 index 00000000..92fd8293 --- /dev/null +++ b/v1/src/assets/img/RAM.svg @@ -0,0 +1 @@ +RAMADIWDO \ No newline at end of file diff --git a/v1/src/assets/img/RGBLed.svg b/v1/src/assets/img/RGBLed.svg new file mode 100644 index 00000000..299657c8 --- /dev/null +++ b/v1/src/assets/img/RGBLed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/RGBLedMatrix.svg b/v1/src/assets/img/RGBLedMatrix.svg new file mode 100644 index 00000000..cbb52e83 --- /dev/null +++ b/v1/src/assets/img/RGBLedMatrix.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/Random.svg b/v1/src/assets/img/Random.svg new file mode 100644 index 00000000..4da0fa99 --- /dev/null +++ b/v1/src/assets/img/Random.svg @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/v1/src/assets/img/Rectangle.svg b/v1/src/assets/img/Rectangle.svg new file mode 100644 index 00000000..f2ac9773 --- /dev/null +++ b/v1/src/assets/img/Rectangle.svg @@ -0,0 +1 @@ +Rectangle \ No newline at end of file diff --git a/v1/src/assets/img/RippleCarry.jpeg b/v1/src/assets/img/RippleCarry.jpeg new file mode 100644 index 00000000..c1fbf61f Binary files /dev/null and b/v1/src/assets/img/RippleCarry.jpeg differ diff --git a/v1/src/assets/img/Rom.svg b/v1/src/assets/img/Rom.svg new file mode 100644 index 00000000..b72c1c26 --- /dev/null +++ b/v1/src/assets/img/Rom.svg @@ -0,0 +1 @@ +ADEn000000000000000000000000000000000004080c \ No newline at end of file diff --git a/v1/src/assets/img/SAP.jpeg b/v1/src/assets/img/SAP.jpeg new file mode 100644 index 00000000..79b76da0 Binary files /dev/null and b/v1/src/assets/img/SAP.jpeg differ diff --git a/v1/src/assets/img/SRflipFlop.svg b/v1/src/assets/img/SRflipFlop.svg new file mode 100644 index 00000000..c41ff746 --- /dev/null +++ b/v1/src/assets/img/SRflipFlop.svg @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/v1/src/assets/img/SevenSegDisplay.svg b/v1/src/assets/img/SevenSegDisplay.svg new file mode 100644 index 00000000..bb3d2ae8 --- /dev/null +++ b/v1/src/assets/img/SevenSegDisplay.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/SixteenSegDisplay.svg b/v1/src/assets/img/SixteenSegDisplay.svg new file mode 100644 index 00000000..ad05274b --- /dev/null +++ b/v1/src/assets/img/SixteenSegDisplay.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/Splitter.svg b/v1/src/assets/img/Splitter.svg new file mode 100644 index 00000000..fa4a969d --- /dev/null +++ b/v1/src/assets/img/Splitter.svg @@ -0,0 +1 @@ +0:11:2 \ No newline at end of file diff --git a/v1/src/assets/img/SquareRGBLed.svg b/v1/src/assets/img/SquareRGBLed.svg new file mode 100644 index 00000000..7900e614 --- /dev/null +++ b/v1/src/assets/img/SquareRGBLed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/Stepper.svg b/v1/src/assets/img/Stepper.svg new file mode 100644 index 00000000..f18aa4fa --- /dev/null +++ b/v1/src/assets/img/Stepper.svg @@ -0,0 +1 @@ +5f \ No newline at end of file diff --git a/v1/src/assets/img/T-clock.png b/v1/src/assets/img/T-clock.png new file mode 100644 index 00000000..66442d9e Binary files /dev/null and b/v1/src/assets/img/T-clock.png differ diff --git a/v1/src/assets/img/TB_Input.svg b/v1/src/assets/img/TB_Input.svg new file mode 100644 index 00000000..c1c42ead --- /dev/null +++ b/v1/src/assets/img/TB_Input.svg @@ -0,0 +1 @@ +Test1 [INPUT] Case:0 \ No newline at end of file diff --git a/v1/src/assets/img/TB_Output.svg b/v1/src/assets/img/TB_Output.svg new file mode 100644 index 00000000..b62e1993 --- /dev/null +++ b/v1/src/assets/img/TB_Output.svg @@ -0,0 +1 @@ +Test1 [OUTPUT] Paired \ No newline at end of file diff --git a/v1/src/assets/img/TTY.svg b/v1/src/assets/img/TTY.svg new file mode 100644 index 00000000..208e42b9 --- /dev/null +++ b/v1/src/assets/img/TTY.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/Text.svg b/v1/src/assets/img/Text.svg new file mode 100644 index 00000000..db86087a --- /dev/null +++ b/v1/src/assets/img/Text.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/TflipFlop.svg b/v1/src/assets/img/TflipFlop.svg new file mode 100644 index 00000000..50925f80 --- /dev/null +++ b/v1/src/assets/img/TflipFlop.svg @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/v1/src/assets/img/TriState.svg b/v1/src/assets/img/TriState.svg new file mode 100644 index 00000000..d251882c --- /dev/null +++ b/v1/src/assets/img/TriState.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/Tunnel.svg b/v1/src/assets/img/Tunnel.svg new file mode 100644 index 00000000..ff5b95bb --- /dev/null +++ b/v1/src/assets/img/Tunnel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/TwoComplement.svg b/v1/src/assets/img/TwoComplement.svg new file mode 100644 index 00000000..6baa7056 --- /dev/null +++ b/v1/src/assets/img/TwoComplement.svg @@ -0,0 +1 @@ +2' \ No newline at end of file diff --git a/v1/src/assets/img/VariableLed.svg b/v1/src/assets/img/VariableLed.svg new file mode 100644 index 00000000..e2829407 --- /dev/null +++ b/v1/src/assets/img/VariableLed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/XnorGate.svg b/v1/src/assets/img/XnorGate.svg new file mode 100644 index 00000000..4a8bae40 --- /dev/null +++ b/v1/src/assets/img/XnorGate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/XorGate.svg b/v1/src/assets/img/XorGate.svg new file mode 100644 index 00000000..639b4c0b --- /dev/null +++ b/v1/src/assets/img/XorGate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/assignment.png b/v1/src/assets/img/assignment.png new file mode 100644 index 00000000..c27df5bc Binary files /dev/null and b/v1/src/assets/img/assignment.png differ diff --git a/v1/src/assets/img/bus.png b/v1/src/assets/img/bus.png new file mode 100644 index 00000000..d37b30aa Binary files /dev/null and b/v1/src/assets/img/bus.png differ diff --git a/v1/src/assets/img/caret.jpg b/v1/src/assets/img/caret.jpg new file mode 100644 index 00000000..a382f8da Binary files /dev/null and b/v1/src/assets/img/caret.jpg differ diff --git a/v1/src/assets/img/circuitverse2.svg b/v1/src/assets/img/circuitverse2.svg new file mode 100644 index 00000000..23dd6118 --- /dev/null +++ b/v1/src/assets/img/circuitverse2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/circuitverse_black.svg b/v1/src/assets/img/circuitverse_black.svg new file mode 100644 index 00000000..eba77c2a --- /dev/null +++ b/v1/src/assets/img/circuitverse_black.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/circuitverse_logo.svg b/v1/src/assets/img/circuitverse_logo.svg new file mode 100644 index 00000000..27202ecb --- /dev/null +++ b/v1/src/assets/img/circuitverse_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/cross.png b/v1/src/assets/img/cross.png new file mode 100644 index 00000000..bb7126e2 Binary files /dev/null and b/v1/src/assets/img/cross.png differ diff --git a/v1/src/assets/img/cvlogo.svg b/v1/src/assets/img/cvlogo.svg new file mode 100644 index 00000000..ce4f7e59 --- /dev/null +++ b/v1/src/assets/img/cvlogo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/assets/img/default.png b/v1/src/assets/img/default.png new file mode 100644 index 00000000..8e183a8a Binary files /dev/null and b/v1/src/assets/img/default.png differ diff --git a/v1/src/assets/img/drag.mp4 b/v1/src/assets/img/drag.mp4 new file mode 100644 index 00000000..b211439c Binary files /dev/null and b/v1/src/assets/img/drag.mp4 differ diff --git a/v1/src/assets/img/edit_icon.png b/v1/src/assets/img/edit_icon.png new file mode 100644 index 00000000..32f99fb1 Binary files /dev/null and b/v1/src/assets/img/edit_icon.png differ diff --git a/v1/src/assets/img/embed.png b/v1/src/assets/img/embed.png new file mode 100644 index 00000000..520cee0b Binary files /dev/null and b/v1/src/assets/img/embed.png differ diff --git a/v1/src/assets/img/facebook.png b/v1/src/assets/img/facebook.png new file mode 100644 index 00000000..fc961175 Binary files /dev/null and b/v1/src/assets/img/facebook.png differ diff --git a/v1/src/assets/img/facebook_signin.png b/v1/src/assets/img/facebook_signin.png new file mode 100644 index 00000000..e78185f0 Binary files /dev/null and b/v1/src/assets/img/facebook_signin.png differ diff --git a/v1/src/assets/img/fullAdder.png b/v1/src/assets/img/fullAdder.png new file mode 100644 index 00000000..1150d9c6 Binary files /dev/null and b/v1/src/assets/img/fullAdder.png differ diff --git a/v1/src/assets/img/google.png b/v1/src/assets/img/google.png new file mode 100644 index 00000000..2accb4e6 Binary files /dev/null and b/v1/src/assets/img/google.png differ diff --git a/v1/src/assets/img/google_signin.png b/v1/src/assets/img/google_signin.png new file mode 100644 index 00000000..18bbd0b7 Binary files /dev/null and b/v1/src/assets/img/google_signin.png differ diff --git a/v1/src/assets/img/grading.png b/v1/src/assets/img/grading.png new file mode 100644 index 00000000..166ee57e Binary files /dev/null and b/v1/src/assets/img/grading.png differ diff --git a/v1/src/assets/img/groups.png b/v1/src/assets/img/groups.png new file mode 100644 index 00000000..e3619597 Binary files /dev/null and b/v1/src/assets/img/groups.png differ diff --git a/v1/src/assets/img/halfAdder.png b/v1/src/assets/img/halfAdder.png new file mode 100644 index 00000000..7c9f3f0c Binary files /dev/null and b/v1/src/assets/img/halfAdder.png differ diff --git a/v1/src/assets/img/help.png b/v1/src/assets/img/help.png new file mode 100644 index 00000000..5c72dbb7 Binary files /dev/null and b/v1/src/assets/img/help.png differ diff --git a/v1/src/assets/img/iDecoder.png b/v1/src/assets/img/iDecoder.png new file mode 100644 index 00000000..d55e9bfa Binary files /dev/null and b/v1/src/assets/img/iDecoder.png differ diff --git a/v1/src/assets/img/iiitb.png b/v1/src/assets/img/iiitb.png new file mode 100644 index 00000000..5a1c2fe3 Binary files /dev/null and b/v1/src/assets/img/iiitb.png differ diff --git a/v1/src/assets/img/implemented.png b/v1/src/assets/img/implemented.png new file mode 100644 index 00000000..6766aaf6 Binary files /dev/null and b/v1/src/assets/img/implemented.png differ diff --git a/v1/src/assets/img/logix.png b/v1/src/assets/img/logix.png new file mode 100644 index 00000000..2f6daac5 Binary files /dev/null and b/v1/src/assets/img/logix.png differ diff --git a/v1/src/assets/img/logixBanner.png b/v1/src/assets/img/logixBanner.png new file mode 100644 index 00000000..b9dad5d5 Binary files /dev/null and b/v1/src/assets/img/logixBanner.png differ diff --git a/v1/src/assets/img/logixBanner2.png b/v1/src/assets/img/logixBanner2.png new file mode 100644 index 00000000..9e7b044a Binary files /dev/null and b/v1/src/assets/img/logixBanner2.png differ diff --git a/v1/src/assets/img/logix_banner_new.png b/v1/src/assets/img/logix_banner_new.png new file mode 100644 index 00000000..13d7fc63 Binary files /dev/null and b/v1/src/assets/img/logix_banner_new.png differ diff --git a/v1/src/assets/img/multiselectionDrag.mp4 b/v1/src/assets/img/multiselectionDrag.mp4 new file mode 100644 index 00000000..6a193d8e Binary files /dev/null and b/v1/src/assets/img/multiselectionDrag.mp4 differ diff --git a/v1/src/assets/img/properties.mp4 b/v1/src/assets/img/properties.mp4 new file mode 100644 index 00000000..754f6c58 Binary files /dev/null and b/v1/src/assets/img/properties.mp4 differ diff --git a/v1/src/assets/img/properties.png b/v1/src/assets/img/properties.png new file mode 100644 index 00000000..167810f5 Binary files /dev/null and b/v1/src/assets/img/properties.png differ diff --git a/v1/src/assets/img/stats.png b/v1/src/assets/img/stats.png new file mode 100644 index 00000000..700bd7fb Binary files /dev/null and b/v1/src/assets/img/stats.png differ diff --git a/v1/src/assets/img/students.png b/v1/src/assets/img/students.png new file mode 100644 index 00000000..0be22e0e Binary files /dev/null and b/v1/src/assets/img/students.png differ diff --git a/v1/src/assets/img/super.png b/v1/src/assets/img/super.png new file mode 100644 index 00000000..4e3a0996 Binary files /dev/null and b/v1/src/assets/img/super.png differ diff --git a/v1/src/assets/img/wire.mp4 b/v1/src/assets/img/wire.mp4 new file mode 100644 index 00000000..8b246df9 Binary files /dev/null and b/v1/src/assets/img/wire.mp4 differ diff --git a/v1/src/assets/themes/ColorBlind.svg b/v1/src/assets/themes/ColorBlind.svg new file mode 100644 index 00000000..93bdf57a --- /dev/null +++ b/v1/src/assets/themes/ColorBlind.svg @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/v1/src/assets/themes/DefaultTheme.svg b/v1/src/assets/themes/DefaultTheme.svg new file mode 100644 index 00000000..f7979326 --- /dev/null +++ b/v1/src/assets/themes/DefaultTheme.svg @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/v1/src/assets/themes/GnW.svg b/v1/src/assets/themes/GnW.svg new file mode 100644 index 00000000..9d095029 --- /dev/null +++ b/v1/src/assets/themes/GnW.svg @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/v1/src/assets/themes/HighContrast.svg b/v1/src/assets/themes/HighContrast.svg new file mode 100644 index 00000000..d025b140 --- /dev/null +++ b/v1/src/assets/themes/HighContrast.svg @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/v1/src/assets/themes/LitebornSpring.svg b/v1/src/assets/themes/LitebornSpring.svg new file mode 100644 index 00000000..81b2bce4 --- /dev/null +++ b/v1/src/assets/themes/LitebornSpring.svg @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/v1/src/assets/themes/NightSky.svg b/v1/src/assets/themes/NightSky.svg new file mode 100644 index 00000000..718859b5 --- /dev/null +++ b/v1/src/assets/themes/NightSky.svg @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/v1/src/components/ContextMenu/ContextMenu.css b/v1/src/components/ContextMenu/ContextMenu.css new file mode 100644 index 00000000..e69de29b diff --git a/v1/src/components/ContextMenu/ContextMenu.vue b/v1/src/components/ContextMenu/ContextMenu.vue new file mode 100644 index 00000000..d060d4f6 --- /dev/null +++ b/v1/src/components/ContextMenu/ContextMenu.vue @@ -0,0 +1,82 @@ + + + diff --git a/v1/src/components/DialogBox/BooleanTable.vue b/v1/src/components/DialogBox/BooleanTable.vue new file mode 100644 index 00000000..528a6cd5 --- /dev/null +++ b/v1/src/components/DialogBox/BooleanTable.vue @@ -0,0 +1,23 @@ + + + diff --git a/v1/src/components/DialogBox/CombinationalAnalysis.vue b/v1/src/components/DialogBox/CombinationalAnalysis.vue new file mode 100644 index 00000000..3b4092a8 --- /dev/null +++ b/v1/src/components/DialogBox/CombinationalAnalysis.vue @@ -0,0 +1,708 @@ + + + + + + + diff --git a/v1/src/components/DialogBox/CustomShortcut.vue b/v1/src/components/DialogBox/CustomShortcut.vue new file mode 100644 index 00000000..ae67a842 --- /dev/null +++ b/v1/src/components/DialogBox/CustomShortcut.vue @@ -0,0 +1,275 @@ + + + + + + + diff --git a/v1/src/components/DialogBox/ExportProject.vue b/v1/src/components/DialogBox/ExportProject.vue new file mode 100644 index 00000000..0da58f88 --- /dev/null +++ b/v1/src/components/DialogBox/ExportProject.vue @@ -0,0 +1,117 @@ + + + + + + + + + diff --git a/v1/src/components/DialogBox/ExportVerilog.vue b/v1/src/components/DialogBox/ExportVerilog.vue new file mode 100644 index 00000000..d826ad35 --- /dev/null +++ b/v1/src/components/DialogBox/ExportVerilog.vue @@ -0,0 +1,98 @@ + + + diff --git a/v1/src/components/DialogBox/HexBinDec.vue b/v1/src/components/DialogBox/HexBinDec.vue new file mode 100644 index 00000000..e65ebf73 --- /dev/null +++ b/v1/src/components/DialogBox/HexBinDec.vue @@ -0,0 +1,253 @@ + + + + + diff --git a/v1/src/components/DialogBox/ImportProject.vue b/v1/src/components/DialogBox/ImportProject.vue new file mode 100644 index 00000000..caa1df60 --- /dev/null +++ b/v1/src/components/DialogBox/ImportProject.vue @@ -0,0 +1,250 @@ + + + + + + + + + + + diff --git a/v1/src/components/DialogBox/InsertSubcircuit.vue b/v1/src/components/DialogBox/InsertSubcircuit.vue new file mode 100644 index 00000000..f2c1b1a6 --- /dev/null +++ b/v1/src/components/DialogBox/InsertSubcircuit.vue @@ -0,0 +1,113 @@ + + + + + diff --git a/v1/src/components/DialogBox/OpenOffline.vue b/v1/src/components/DialogBox/OpenOffline.vue new file mode 100644 index 00000000..7bd3b94f --- /dev/null +++ b/v1/src/components/DialogBox/OpenOffline.vue @@ -0,0 +1,99 @@ + + + diff --git a/v1/src/components/DialogBox/SaveImage.vue b/v1/src/components/DialogBox/SaveImage.vue new file mode 100644 index 00000000..9cfaba08 --- /dev/null +++ b/v1/src/components/DialogBox/SaveImage.vue @@ -0,0 +1,157 @@ + + + diff --git a/v1/src/components/DialogBox/Themes/ApplyThemes.vue b/v1/src/components/DialogBox/Themes/ApplyThemes.vue new file mode 100644 index 00000000..7d564509 --- /dev/null +++ b/v1/src/components/DialogBox/Themes/ApplyThemes.vue @@ -0,0 +1,288 @@ + + + diff --git a/v1/src/components/Dropdown/DropDown.css b/v1/src/components/Dropdown/DropDown.css new file mode 100644 index 00000000..e69de29b diff --git a/v1/src/components/Dropdown/DropDown.vue b/v1/src/components/Dropdown/DropDown.vue new file mode 100644 index 00000000..89dc2b20 --- /dev/null +++ b/v1/src/components/Dropdown/DropDown.vue @@ -0,0 +1,58 @@ + + + diff --git a/v1/src/components/Extra.vue b/v1/src/components/Extra.vue new file mode 100644 index 00000000..e61d1597 --- /dev/null +++ b/v1/src/components/Extra.vue @@ -0,0 +1,323 @@ + + + \ No newline at end of file diff --git a/v1/src/components/Logo/Logo.css b/v1/src/components/Logo/Logo.css new file mode 100644 index 00000000..e69de29b diff --git a/v1/src/components/Logo/Logo.vue b/v1/src/components/Logo/Logo.vue new file mode 100644 index 00000000..56130b8c --- /dev/null +++ b/v1/src/components/Logo/Logo.vue @@ -0,0 +1,24 @@ + + + + + diff --git a/v1/src/components/MessageBox/messageBox.vue b/v1/src/components/MessageBox/messageBox.vue new file mode 100644 index 00000000..f008946f --- /dev/null +++ b/v1/src/components/MessageBox/messageBox.vue @@ -0,0 +1,82 @@ + + + + + diff --git a/v1/src/components/Navbar/Hamburger/Hamburger.css b/v1/src/components/Navbar/Hamburger/Hamburger.css new file mode 100644 index 00000000..e69de29b diff --git a/v1/src/components/Navbar/Hamburger/Hamburger.vue b/v1/src/components/Navbar/Hamburger/Hamburger.vue new file mode 100644 index 00000000..8c362ef8 --- /dev/null +++ b/v1/src/components/Navbar/Hamburger/Hamburger.vue @@ -0,0 +1,18 @@ + + + + + diff --git a/v1/src/components/Navbar/Navbar.css b/v1/src/components/Navbar/Navbar.css new file mode 100644 index 00000000..acdaef1a --- /dev/null +++ b/v1/src/components/Navbar/Navbar.css @@ -0,0 +1,306 @@ +@import url('/src/styles/color_theme.scss'); + +.navbar { + background-color: var(--white); + position: relative; + top: 0; + width: 100%; + z-index: 100; +} +.header { + background: var(--bg-navbar); +} + +.logo { + display: block; + max-width: 100%; + margin-bottom: 0px; +} +.logo { + background: url(/src/assets/logo.svg) center/cover; + height: 30px; + width: 105px; + display: inline-block; + margin-right: 36px; +} + +.navbar-nav > li > a { + padding: 7px 15px; +} + +.navbar-logo { + height: 70px; + padding-top: 5px; +} + +.navbar-search-icon-container { + height: 40px; + position: relative; + width: 40px; +} +.navbar-search-icon-container.search-icon { + cursor: pointer; +} + +.navbar-search-icon-oncollapse { + display: none; + margin-right: 0; + padding-right: 0; + padding-top: 5px; + text-align: right; +} + +.fa-search { + color: var(--navbar-dark-grey); + font-size: 20px; + margin-right: 12px; + vertical-align: middle; +} +.fa-search:hover { + color: var(--primary-green); + cursor: pointer; +} + +.fa-search.active { + color: var(--secondary-green); +} + +.fa-search:focus { + outline: none; +} + +.navbar-simulator-text { + border: 2px solid var(--primary-green); + color: var(--navbar-dark-grey); + font-weight: 500; + transition: all 0.3s ease-in; +} + +.navbar-simulator-text:hover { + background: var(--primary-green); + color: var(--white); +} + +.navbar-dropdown-toggle-hidden.dropdown-toggle::after { + display: none; +} + +.dropdown-toggle.navbar-user-dropdown { + cursor: pointer; +} +.dropdown-toggle.navbar-user-dropdown::after { + margin-left: 0; + padding-left: 0; + vertical-align: 0.5em; +} + +.dropdown-item:hover { + background-color: var(--primary-green); + color: var(--white); +} + +.navbar-text { + color: var(--navbar-dark-grey); + font-weight: 500; +} + +.navbar-text:hover { + color: var(--primary-green); +} + +.navbar-text.active { + color: var(--secondary-green); +} + +.navbar-text:focus { + outline: none; + text-decoration: underline; +} + +.navbar-search-active { + background-color: var(--card-green); + display: none; + margin-top: 0; + padding: 20px; + position: fixed; + top: 96px; + width: 100%; + z-index: 90; +} + +#navbar-dropdown-1, +#navbar-dropdown-2 :hover { + color: var(--primary-green); +} + +#navbar-dropdown-1, +#navbar-dropdown-2 [aria-expanded='true'] { + color: var(--secondary-green); +} + +#navbar-dropdown-1, +#navbar-dropdown-2 :focus { + outline: none; + text-decoration: underline; +} + +.navbar-username-truncate { + display: inline-block; + max-width: 70px; + overflow: hidden; + text-overflow: ellipsis; +} + +.affix { + box-shadow: 0 8px 6px -6px var(--shadow-grey); +} + +.navbar-search-bar-form { + display: flex; + margin-left: auto; + margin-right: auto; + width: 70%; +} + +.navbar-search-bar-input { + border: 1px solid var(--primary-green); + margin: 5px; + min-width: 150px; + padding: 5px; + width: 100%; +} + +.navbar-search-bar-select { + -moz-appearance: none; + -webkit-appearance: none; + appearance: none; + background-color: var(--card-green); + background-image: url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%23000000%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E'), + linear-gradient(to bottom, var(--white) 0%, var(--white) 100%); + background-position: right 0.3em top 50%, 0 0; + background-repeat: no-repeat, repeat; + background-size: 0.45em auto, 100%; + border: 1px solid var(--primary-green); + cursor: pointer; + font-size: 18px; + height: 40px; + margin-right: 6px; + margin-top: 5px; + max-width: 100px; + min-width: 100px; + padding-left: 5px; +} + +@media (max-width: 991px) { + .navbar-search-icon-oncollapse { + display: block; + } + + .navbar-search-icon-onexpand { + display: none; + } +} + +@media (max-width: 768px) { + .navbar-search-bar-form { + width: 90%; + } + + .navbar-search-bar-button { + padding-left: 10px; + padding-right: 10px; + } + + .navbar-search-bar-select { + width: 50px; + } +} + +@media (max-width: 400px) { + .navbar-search-bar-form { + width: 100%; + } + + .navbar-search-container { + padding-left: 0; + padding-right: 0; + } + + .navbar-search-active { + padding-left: 10px; + padding-right: 10px; + } + + .navbar-logo { + height: 60px; + padding-top: 10px; + } +} + +/* dropdown-menu styles */ + +.dropdown > ul { + border-radius: 5px; + text-align: center; + position: absolute; + left: 50%; + transform: translate(-50%, 13px); +} + +.draggable-panel-css { + border-radius: 5px; + z-index: 70; + transition: background 0.5s ease-out; + position: fixed; +} + +@supports (backdrop-filter: blur()) { + .dropdown > ul { + backdrop-filter: blur(5px); + } +} + +.mw-override { + min-width: 110px; +} + +.dropdown > ul::before { + background-color: transparent; + content: ''; + width: 10px; + display: inline-block; + height: 10px; + position: absolute; + transform: translate(-50%, -13px) rotate(-45deg); +} + +.dropdown > ul::after { + content: ''; + width: 11.5px; + display: inline-block; + height: 10px; + position: absolute; + transform: translate(-50%, -15.5px); + top: 14.5px; +} + +.dropdown-menu > li > a { + padding: 7px 0; + width: 90%; + margin: auto; + transition: all 0.2s ease-in-out; + text-align: left; + padding-left: 10px; +} + +.dropdown-menu > li > a:hover { + border-radius: 7px; + opacity: 1; +} + +@media (max-width: 991px) { + .navbar-nav .dropdown-menu { + position: absolute; + float: none; + } +} diff --git a/v1/src/components/Navbar/Navbar.vue b/v1/src/components/Navbar/Navbar.vue new file mode 100644 index 00000000..caeec961 --- /dev/null +++ b/v1/src/components/Navbar/Navbar.vue @@ -0,0 +1,51 @@ + + + + + diff --git a/v1/src/components/Navbar/NavbarLinks/NavbarLink/NavbarLink.css b/v1/src/components/Navbar/NavbarLinks/NavbarLink/NavbarLink.css new file mode 100644 index 00000000..e69de29b diff --git a/v1/src/components/Navbar/NavbarLinks/NavbarLink/NavbarLink.vue b/v1/src/components/Navbar/NavbarLinks/NavbarLink/NavbarLink.vue new file mode 100644 index 00000000..7f61cd7e --- /dev/null +++ b/v1/src/components/Navbar/NavbarLinks/NavbarLink/NavbarLink.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/v1/src/components/Navbar/NavbarLinks/NavbarLinks.css b/v1/src/components/Navbar/NavbarLinks/NavbarLinks.css new file mode 100644 index 00000000..e69de29b diff --git a/v1/src/components/Navbar/NavbarLinks/NavbarLinks.vue b/v1/src/components/Navbar/NavbarLinks/NavbarLinks.vue new file mode 100644 index 00000000..3c01a4dc --- /dev/null +++ b/v1/src/components/Navbar/NavbarLinks/NavbarLinks.vue @@ -0,0 +1,22 @@ + + + + + diff --git a/v1/src/components/Navbar/QuickButton/QuickButton.css b/v1/src/components/Navbar/QuickButton/QuickButton.css new file mode 100644 index 00000000..e69de29b diff --git a/v1/src/components/Navbar/QuickButton/QuickButton.vue b/v1/src/components/Navbar/QuickButton/QuickButton.vue new file mode 100644 index 00000000..aad07b93 --- /dev/null +++ b/v1/src/components/Navbar/QuickButton/QuickButton.vue @@ -0,0 +1,296 @@ + + + + + diff --git a/v1/src/components/Navbar/User/User.css b/v1/src/components/Navbar/User/User.css new file mode 100644 index 00000000..e69de29b diff --git a/v1/src/components/Navbar/User/User.vue b/v1/src/components/Navbar/User/User.vue new file mode 100644 index 00000000..839dae5e --- /dev/null +++ b/v1/src/components/Navbar/User/User.vue @@ -0,0 +1,55 @@ + + + + + diff --git a/v1/src/components/Panels/ElementsPanel/ElementsPanel.vue b/v1/src/components/Panels/ElementsPanel/ElementsPanel.vue new file mode 100644 index 00000000..e5c52896 --- /dev/null +++ b/v1/src/components/Panels/ElementsPanel/ElementsPanel.vue @@ -0,0 +1,240 @@ + + + + + diff --git a/v1/src/components/Panels/PropertiesPanel/LayoutProperty/LayoutProperty.vue b/v1/src/components/Panels/PropertiesPanel/LayoutProperty/LayoutProperty.vue new file mode 100644 index 00000000..49c7689f --- /dev/null +++ b/v1/src/components/Panels/PropertiesPanel/LayoutProperty/LayoutProperty.vue @@ -0,0 +1,151 @@ + + + + + diff --git a/v1/src/components/Panels/PropertiesPanel/ModuleProperty/ElementProperty/ElementProperty.vue b/v1/src/components/Panels/PropertiesPanel/ModuleProperty/ElementProperty/ElementProperty.vue new file mode 100644 index 00000000..fec2c571 --- /dev/null +++ b/v1/src/components/Panels/PropertiesPanel/ModuleProperty/ElementProperty/ElementProperty.vue @@ -0,0 +1,121 @@ + + + diff --git a/v1/src/components/Panels/PropertiesPanel/ModuleProperty/ModuleProperty.vue b/v1/src/components/Panels/PropertiesPanel/ModuleProperty/ModuleProperty.vue new file mode 100644 index 00000000..92d0c9aa --- /dev/null +++ b/v1/src/components/Panels/PropertiesPanel/ModuleProperty/ModuleProperty.vue @@ -0,0 +1,43 @@ + + + diff --git a/v1/src/components/Panels/PropertiesPanel/ModuleProperty/ProjectProperty/ProjectProperty.vue b/v1/src/components/Panels/PropertiesPanel/ModuleProperty/ProjectProperty/ProjectProperty.vue new file mode 100644 index 00000000..1fc88e1f --- /dev/null +++ b/v1/src/components/Panels/PropertiesPanel/ModuleProperty/ProjectProperty/ProjectProperty.vue @@ -0,0 +1,241 @@ + + + + + + + diff --git a/v1/src/components/Panels/PropertiesPanel/ModuleProperty/SubcircuitProperty/SubcircuitProperty.vue b/v1/src/components/Panels/PropertiesPanel/ModuleProperty/SubcircuitProperty/SubcircuitProperty.vue new file mode 100644 index 00000000..6ac03ece --- /dev/null +++ b/v1/src/components/Panels/PropertiesPanel/ModuleProperty/SubcircuitProperty/SubcircuitProperty.vue @@ -0,0 +1,62 @@ + + + diff --git a/v1/src/components/Panels/PropertiesPanel/PropertiesPanel.vue b/v1/src/components/Panels/PropertiesPanel/PropertiesPanel.vue new file mode 100644 index 00000000..368c5b79 --- /dev/null +++ b/v1/src/components/Panels/PropertiesPanel/PropertiesPanel.vue @@ -0,0 +1,75 @@ + + + diff --git a/v1/src/components/Panels/Shared/DropdownSelect.vue b/v1/src/components/Panels/Shared/DropdownSelect.vue new file mode 100644 index 00000000..f1983987 --- /dev/null +++ b/v1/src/components/Panels/Shared/DropdownSelect.vue @@ -0,0 +1,29 @@ + + + diff --git a/v1/src/components/Panels/Shared/HelpButton.vue b/v1/src/components/Panels/Shared/HelpButton.vue new file mode 100644 index 00000000..9956010b --- /dev/null +++ b/v1/src/components/Panels/Shared/HelpButton.vue @@ -0,0 +1,21 @@ + + + \ No newline at end of file diff --git a/v1/src/components/Panels/Shared/InputGroups.vue b/v1/src/components/Panels/Shared/InputGroups.vue new file mode 100644 index 00000000..628da797 --- /dev/null +++ b/v1/src/components/Panels/Shared/InputGroups.vue @@ -0,0 +1,81 @@ + + + diff --git a/v1/src/components/Panels/Shared/PanelHeader.vue b/v1/src/components/Panels/Shared/PanelHeader.vue new file mode 100644 index 00000000..212d5eab --- /dev/null +++ b/v1/src/components/Panels/Shared/PanelHeader.vue @@ -0,0 +1,22 @@ + + + + + diff --git a/v1/src/components/Panels/TimingDiagramPanel/TimingDiagramButtons.vue b/v1/src/components/Panels/TimingDiagramPanel/TimingDiagramButtons.vue new file mode 100644 index 00000000..e5b16ad4 --- /dev/null +++ b/v1/src/components/Panels/TimingDiagramPanel/TimingDiagramButtons.vue @@ -0,0 +1,26 @@ + + + diff --git a/v1/src/components/Panels/TimingDiagramPanel/TimingDiagramPanel.vue b/v1/src/components/Panels/TimingDiagramPanel/TimingDiagramPanel.vue new file mode 100644 index 00000000..70cbc244 --- /dev/null +++ b/v1/src/components/Panels/TimingDiagramPanel/TimingDiagramPanel.vue @@ -0,0 +1,107 @@ + + + + + + + diff --git a/v1/src/components/Panels/VerilogEditorPanel/VerilogEditorPanel.vue b/v1/src/components/Panels/VerilogEditorPanel/VerilogEditorPanel.vue new file mode 100644 index 00000000..5a2011fa --- /dev/null +++ b/v1/src/components/Panels/VerilogEditorPanel/VerilogEditorPanel.vue @@ -0,0 +1,103 @@ + + + + + diff --git a/v1/src/components/ReportIssue/ReportIssue.vue b/v1/src/components/ReportIssue/ReportIssue.vue new file mode 100644 index 00000000..14a9c288 --- /dev/null +++ b/v1/src/components/ReportIssue/ReportIssue.vue @@ -0,0 +1,274 @@ + + + + + diff --git a/v1/src/components/ReportIssue/ReportIssueButton.vue b/v1/src/components/ReportIssue/ReportIssueButton.vue new file mode 100644 index 00000000..128b6288 --- /dev/null +++ b/v1/src/components/ReportIssue/ReportIssueButton.vue @@ -0,0 +1,24 @@ + + + diff --git a/v1/src/components/TabsBar/TabsBar.vue b/v1/src/components/TabsBar/TabsBar.vue new file mode 100644 index 00000000..68c22aa4 --- /dev/null +++ b/v1/src/components/TabsBar/TabsBar.vue @@ -0,0 +1,368 @@ + + + + + + + diff --git a/v1/src/components/helpers/Helper.vue b/v1/src/components/helpers/Helper.vue new file mode 100644 index 00000000..14934a66 --- /dev/null +++ b/v1/src/components/helpers/Helper.vue @@ -0,0 +1,28 @@ + + + diff --git a/v1/src/components/helpers/confirmComponent/ConfirmComponent.vue b/v1/src/components/helpers/confirmComponent/ConfirmComponent.vue new file mode 100644 index 00000000..f10fe442 --- /dev/null +++ b/v1/src/components/helpers/confirmComponent/ConfirmComponent.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/v1/src/components/helpers/createNewProject/TextEditor.vue b/v1/src/components/helpers/createNewProject/TextEditor.vue new file mode 100644 index 00000000..62811a4c --- /dev/null +++ b/v1/src/components/helpers/createNewProject/TextEditor.vue @@ -0,0 +1,445 @@ + + + + + diff --git a/v1/src/components/helpers/createNewProject/UpdateProjectDetail.vue b/v1/src/components/helpers/createNewProject/UpdateProjectDetail.vue new file mode 100644 index 00000000..94327f0b --- /dev/null +++ b/v1/src/components/helpers/createNewProject/UpdateProjectDetail.vue @@ -0,0 +1,264 @@ + + + + + + + diff --git a/v1/src/components/helpers/deleteCircuit/DeleteCircuit.vue b/v1/src/components/helpers/deleteCircuit/DeleteCircuit.vue new file mode 100644 index 00000000..04b7d478 --- /dev/null +++ b/v1/src/components/helpers/deleteCircuit/DeleteCircuit.vue @@ -0,0 +1,151 @@ + + + + + diff --git a/v1/src/components/helpers/promptComponent/PromptComponent.vue b/v1/src/components/helpers/promptComponent/PromptComponent.vue new file mode 100644 index 00000000..fcedab76 --- /dev/null +++ b/v1/src/components/helpers/promptComponent/PromptComponent.vue @@ -0,0 +1,151 @@ + + + + + diff --git a/v1/src/env.d.ts b/v1/src/env.d.ts new file mode 100644 index 00000000..76a98121 --- /dev/null +++ b/v1/src/env.d.ts @@ -0,0 +1,8 @@ +/// + +declare module '*.vue' { + import type { DefineComponent } from 'vue' + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types + const component: DefineComponent<{}, {}, any> + export default component +} diff --git a/v1/src/globalVariables.ts b/v1/src/globalVariables.ts new file mode 100644 index 00000000..34a7a546 --- /dev/null +++ b/v1/src/globalVariables.ts @@ -0,0 +1,27 @@ +/*global ...*/ +/*eslint no-undef: "error"*/ + +declare const window: any + +import jQuery from 'jquery' +window.$ = window.jQuery = jQuery + +import Array from './simulator/src/arrayHelpers.js' +window.Array = Array +window.isUserLoggedIn = false +window.logixProjectId = undefined + +window.restrictedElements = [] +window.globalScope = undefined +window.lightMode = false // To be deprecated +window.projectId = undefined +window.id = undefined +window.loading = false // Flag - all assets are loaded + +window.embed = false + +window.width = undefined +window.height = undefined +window.DPR = window.devicePixelRatio || 1 // devicePixelRatio, 2 for retina displays, 1 for low resolution displays + +window.elementHierarchy = [] diff --git a/v1/src/locales/en.json b/v1/src/locales/en.json new file mode 100644 index 00000000..6681330a --- /dev/null +++ b/v1/src/locales/en.json @@ -0,0 +1,162 @@ +{ + "simulator": { + "save_online": "Save Online", + "save_offline": "Save Offline", + "preview_circuit": "Preview Circuit", + "export_verilog": "Export Verilog", + "insert_subcircuit": "Insert SubCircuit", + "undo": "Undo", + "report_issue": "Report an issue", + "restricted_elements_used": "Restricted elements used:", + "made_with_circuitverse": "Made With CircuitVerse", + "nav": { + "untitled_project": "Untitled", + "sign_out": "Sign Out", + "sign_in": "Sign In", + "user_dropdown": { + "dashboard": "Dashboard", + "my_groups": "My Groups", + "notifications": "Notifications" + }, + "project": { + "heading": "Project", + "project_page": "Project Page", + "new_project": "New Project", + "open_offline": "Open Offline", + "export_as_file": "Export as File", + "import_project": "Import Project", + "save_online": "Save Online", + "save_offline": "Save Offline", + "preview_circuit": "Preview Circuit", + "clear_project": "Clear Project", + "recover_project": "Recover Project", + "view_previous_ui": "View Previous UI" + }, + "circuit": { + "heading": "Circuit", + "new_circuit": "New Circuit +", + "insert_subcircuit": "Insert SubCircuit", + "new_verilog_module_html": "New Verilog\nModule" + }, + "tools": { + "heading": "Tools", + "combinational_analysis_html": "Combinational\nAnalysis", + "hex_bin_dec_converter_html": "Hex-Bin-Dec\nConverter", + "download_image": "Download Image", + "themes": "Themes", + "export_verilog": "Export Verilog", + "custom_shortcut": "Custom Shortcut" + }, + "help": { + "heading": "Help", + "tutorial_guide": "Tutorial Guide", + "user_manual": "User Manual", + "learn_digital_logic": "Learn Digital Logic", + "discussion_forum": "Discussion Forum" + } + }, + "panel_header": { + "circuit_elements": "Circuit Elements", + "layout_elements": "Layout Elements", + "timing_diagram": "Timing Diagram", + "verilog_module": "Verilog Module", + "properties": "Properties", + "layout": "Layout", + "keybinding_preference": "Keybinding Preference", + "render_image": "Render Image", + "select_theme": "Select Theme", + "boolean_logic_table": "BooleanLogicTable", + "open_project": "Open Project", + "bit_converter": "Dec-Bin-Hex-Converter" + }, + "panel_body": { + "circuit_elements": { + "search": "Search...", + "search_result": "No elements found...", + "expansion_panel_title": { + "Input": "Input", + "Output": "Output", + "Gates": "Gates", + "Decoders & Plexers": "Decoders & Plexers", + "Sequential Elements": "Sequential Elements", + "Annotation": "Annotation", + "Misc": "Misc" + } + }, + "timing_diagram": { + "one_cycle": "1 Cycle =", + "units": "Units" + }, + "verilog_module": { + "reset_code": "Reset Code", + "save_code": "Save Code", + "module_in_experiment_notice": "This is an experimental module. The code is not saved unless the \"Save Code\" button is clicked.", + "apply_themes": "Apply Themes", + "select_theme": "Select a theme:" + }, + "layout": { + "width": "Width", + "height": "Height", + "reset_all_nodes": "Reset all nodes:", + "title": "Title", + "title_enabled": "Title Enabled:", + "save": "Save", + "cancel": "Cancel" + }, + "render_image": { + "full_circuit_view": "Full Circuit View", + "current_view": "Current View", + "transparent_background": "Transparent Background", + "resolution": "Resolution:" + }, + "context_menu": { + "paste": "Paste", + "copy": "Copy", + "cut": "Cut", + "delete": "Delete", + "new_circuit": "New Circuit", + "center_focus": "Center Focus" + }, + "bit_converter": { + "decimal_value": "Decimal Value", + "binary_value": "Binary value", + "octal_value": "Octal value", + "hexadecimal_value": "Hexadecimal value" + }, + "custom_shortcut": { + "esc_cancel": "Press Desire Key Combination & press Enter", + "command": "Command", + "keymapping": "Keymapping", + "reset_to_default": "Reset to Default", + "save": "Save" + }, + "report_issue": { + "describe_issue": "Describe your issue:", + "email": "Email", + "optional": " [Optional]", + "report_btn": "Report", + "cancel_btn": "Cancel", + "close_btn": "Close" + } + }, + "tooltip": { + "delete_selected": "Delete Selected", + "download_as_image": "Download as Image", + "fit_to_screen": "Fit to Screen", + "redo": "Redo", + "decrease_size": "Decrease Size", + "increase_size": "Increase Size", + "decrease_height": "Decrease Height", + "increase_height": "Increase Height", + "reset_timing_diagram": "Reset Timing Diagram", + "autocalibrate_cycle_units": "Autocalibrate Cycle Units", + "zoom_in": "Zoom In", + "zoom_out": "Zoom Out", + "resume_timing_diagram": "Resume Timing Diagram", + "pause_timing_diagram": "Pause Timing Diagram", + "decrease_width": "Decrease Width", + "increase_width": "Increase Width", + "reset": "Reset" + } + } +} diff --git a/v1/src/locales/hi.json b/v1/src/locales/hi.json new file mode 100644 index 00000000..39086a16 --- /dev/null +++ b/v1/src/locales/hi.json @@ -0,0 +1,162 @@ +{ + "simulator": { + "save_online": "ऑनलाइन सेव करें", + "save_offline": "ऑफलाइन सेव करें", + "preview_circuit": "सर्किट प्रीव्यू करें", + "export_verilog": "वेरिलोग एक्सपोर्ट करें", + "insert_subcircuit": "सब-सर्किट इन्सर्ट करें", + "undo": "पूर्ववत् करें", + "report_issue": "मामले की रिपोर्ट करें", + "restricted_elements_used": "प्रतिबंधित एलिमेंट्स जिनका इस्तेमाल किया गया:", + "made_with_circuitverse": "सर्किटवर्स में बनाया गया", + "nav": { + "untitled_project": "शीर्षकहीन", + "sign_out": "साइन आउट", + "sign_in": "साइन इन करें", + "user_dropdown": { + "dashboard": "डैशबोर्ड", + "my_groups": "मेरे समूह", + "notifications": "सूचनाएं" + }, + "project": { + "heading": "परियोजना", + "project_page": "परियोजना का पेज", + "new_project": "नयी परियोजना", + "open_offline": "ऑफ़लाइन खोलें", + "save_online": "ऑनलाइन सहेजें", + "save_offline": "ऑफ़लाइन सहेजें", + "export_as_file": "फ़ाइल में निर्यात करें", + "import_project": "फ़ाइल से आयात करें", + "preview_circuit": "पूर्वावलोकन सर्किट", + "clear_project": "परियोजना क्लियर करें", + "recover_project": "परियोजना पुनर्प्राप्त करें", + "view_previous_ui": "पिछला UI देखें" + }, + "circuit": { + "heading": "सर्किट", + "new_circuit": "नया सर्किट +", + "insert_subcircuit": "सब-सर्किट डालें", + "new_verilog_module_html": "नया वेरिलोग\nमॉड्यूल" + }, + "tools": { + "heading": "उपकरण", + "combinational_analysis_html": "कॉम्बिनेशनल\nएनालिसिस", + "hex_bin_dec_converter_html": "Hex-Bin-Dec\nकनवर्टर", + "download_image": "छवि डाउनलोड करें", + "themes": "थीम", + "export_verilog": "निर्यात Verilog", + "custom_shortcut": "कस्टम शॉर्टकट" + }, + "help": { + "heading": "सहायता", + "tutorial_guide": "ट्यूटोरियल गाइड", + "user_manual": "उपयोगकर्ताओं के लिए मैन्युअल", + "learn_digital_logic": "डिजिटल लॉजिक सीखें", + "discussion_forum": "चर्चा के लिए मंच" + } + }, + "panel_header": { + "circuit_elements": "सर्किट के एलिमेंट्स", + "layout_elements": "लेआउट के एलिमेंट्स", + "timing_diagram": "टाइमिंग डायग्राम", + "verilog_module": "वेरिलोग मॉड्यूल", + "properties": "प्रॉपर्टीज", + "layout": "लेआउट", + "keybinding_preference": "कीबाइंडिंग परेफरेंस", + "render_image": "छवि प्रस्तुत करें", + "select_theme": "थीम चुनें", + "boolean_logic_table": "बूलियन लॉजिक टेबल", + "open_project": "परियोजना खोलें", + "bit_converter": "Dec-Bin-Hex-कनवर्टर" + }, + "panel_body": { + "circuit_elements": { + "search": "खोजें...", + "search_result": "कोई तत्व नहीं मिला...", + "expansion_panel_title": { + "Input": "इनपुट", + "Output": "उत्पादन", + "Gates": "गेट्स", + "Decoders & Plexers": "डिकोडर्स और प्लेक्सर्स", + "Sequential Elements": "अनुक्रमिक तत्व", + "Annotation": "टिप्पणी", + "Misc": "विविध" + } + }, + "timing_diagram": { + "one_cycle": "1 साइकिल =", + "units": "यूनिट्स" + }, + "verilog_module": { + "reset_code": "कोड रिसेट करें", + "save_code": "कोड सेव करें", + "module_in_experiment_notice": "यह एक प्रायोगिक मॉड्यूल है। कोड तब तक सेव नहीं होगा जब तक \"कोड सेव करें\" बटन क्लिक नहीं किया जाता।", + "apply_themes": "थीम लागू करें", + "select_theme": "थीम चुनें:" + }, + "layout": { + "width": "चौड़ाई", + "height": "ऊंचाई", + "reset_all_nodes": "सभी नोड्स रीसेट करें:", + "title": "शीर्षक", + "title_enabled": "शीर्षक इनेबल किया गया:", + "save": "सेव करें", + "cancel": "रद्द करें" + }, + "render_image": { + "full_circuit_view": "सर्किट का पूर्ण दृश्य देखें", + "current_view": "वर्तमान का दृश्य", + "transparent_background": "बैकग्राउंड ट्रांसपेरेंट करें", + "resolution": "रेजोलुएशन" + }, + "context_menu": { + "paste": "पेस्ट करें", + "copy": "कॉपी करें", + "cut": "कट करें", + "delete": "हटाएं", + "new_circuit": "नया सर्किट", + "center_focus": "सेंटर फोकस" + }, + "bit_converter": { + "decimal_value": "डेसीमल वैल्यू", + "binary_value": "बाइनरी वैल्यू", + "octal_value": "ऑक्टल वैल्यू", + "hexadecimal_value": "हेक्साडेसिमल वैल्यू" + }, + "custom_shortcut": { + "esc_cancel": "वांछित कुंजी संयोजन दबाएं और स्टोर करने के लिए enter दबाएं", + "command": "कमांड", + "keymapping": "कीमैपिंग", + "reset_to_default": "डिफ़ॉल्ट पर रीसेट करें", + "save": "सेव करें" + }, + "report_issue": { + "describe_issue": "अपनी समस्या का वर्णन करें:", + "email": "ईमेल", + "optional": " [वैकल्पिक]", + "report_btn": "रिपोर्ट करें", + "cancel_btn": "रद्द करें", + "close_btn": "बंद करें" + } + }, + "tooltip": { + "delete_selected": "चयनित हटाए", + "download_as_image": "छवि के रूप में डाउनलोड करें", + "fit_to_screen": "स्क्रीन में फिट", + "redo": "फिर से करें", + "decrease_size": "आकार घटाएं", + "increase_size": "आकार बढ़ाएँ", + "decrease_height": "ऊंचाई घटाएं", + "increase_height": "ऊँचाई बढ़ाएँ", + "reset_timing_diagram": "टाइमिंग डायग्राम रिसेट करें", + "autocalibrate_cycle_units": "साइक्ल यूनिट्स को ऑटोकैलिब्रेट करें", + "zoom_in": "ज़ूम इन", + "zoom_out": "ज़ूम आउट", + "resume_timing_diagram": "टाइमिंग डायग्राम को रिज्यूम करें", + "pause_timing_diagram": "टाइमिंग डायग्राम को पॉज करें", + "decrease_width": "चौड़ाई घटाएं", + "increase_width": "चौड़ाई बढ़ाएँ", + "reset": "रिसेट करें" + } + } +} diff --git a/v1/src/locales/i18n.ts b/v1/src/locales/i18n.ts new file mode 100644 index 00000000..d82d9d3f --- /dev/null +++ b/v1/src/locales/i18n.ts @@ -0,0 +1,16 @@ +import { createI18n } from 'vue-i18n' +import en from './en.json' +import hi from './hi.json' + +const i18n = createI18n({ + legacy: false, + locale: 'en', + globalInjection: true, + // messages + messages: { + en, + hi, + }, +}) + +export default i18n diff --git a/v1/src/main.ts b/v1/src/main.ts new file mode 100644 index 00000000..616edaa2 --- /dev/null +++ b/v1/src/main.ts @@ -0,0 +1,29 @@ +import { useActions } from './store/SimulatorStore/actions' +import { createApp } from 'vue' +import App from './App.vue' +import vuetify from './plugins/vuetify' +import router from './router/index' +import { createPinia } from 'pinia' +import { loadFonts } from './plugins/webfontloader' +import i18n from './locales/i18n' + +import 'bootstrap' + +import './globalVariables' + +import './styles/css/main.stylesheet.css' +import '../../node_modules/bootstrap/scss/bootstrap.scss' +import './styles/color_theme.scss' +import './styles/simulator.scss' +import './styles/tutorials.scss' +import '@fortawesome/fontawesome-free/css/all.css' + +loadFonts() + +const app = createApp(App) + +app.use(createPinia()) +app.use(vuetify) +app.use(router) +app.use(i18n) +app.mount('#app') diff --git a/v1/src/pages/embed.vue b/v1/src/pages/embed.vue new file mode 100644 index 00000000..18a68a24 --- /dev/null +++ b/v1/src/pages/embed.vue @@ -0,0 +1,317 @@ + + + + + diff --git a/v1/src/pages/simulator.vue b/v1/src/pages/simulator.vue new file mode 100644 index 00000000..98c3e17e --- /dev/null +++ b/v1/src/pages/simulator.vue @@ -0,0 +1,27 @@ + + + diff --git a/v1/src/pages/simulatorHandler.vue b/v1/src/pages/simulatorHandler.vue new file mode 100644 index 00000000..d9e935ed --- /dev/null +++ b/v1/src/pages/simulatorHandler.vue @@ -0,0 +1,96 @@ + + + + + diff --git a/v1/src/plugins/vuetify.ts b/v1/src/plugins/vuetify.ts new file mode 100644 index 00000000..d58e9e51 --- /dev/null +++ b/v1/src/plugins/vuetify.ts @@ -0,0 +1,9 @@ +// Styles +import '@mdi/font/css/materialdesignicons.css' +import 'vuetify/styles' + +// Vuetify +import { createVuetify } from 'vuetify' + +export default createVuetify() +// https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides diff --git a/v1/src/plugins/webfontloader.ts b/v1/src/plugins/webfontloader.ts new file mode 100644 index 00000000..f3287331 --- /dev/null +++ b/v1/src/plugins/webfontloader.ts @@ -0,0 +1,17 @@ +/** + * plugins/webfontloader.js + * + * webfontloader documentation: https://github.com/typekit/webfontloader + */ + +export async function loadFonts() { + const webFontLoader = await import( + /* webpackChunkName: "webfontloader" */ 'webfontloader' + ) + + webFontLoader.load({ + google: { + families: ['Roboto:100,300,400,500,700,900&display=swap'], + }, + }) +} diff --git a/v1/src/router/index.ts b/v1/src/router/index.ts new file mode 100644 index 00000000..730aea09 --- /dev/null +++ b/v1/src/router/index.ts @@ -0,0 +1,41 @@ +import { createRouter, createWebHistory } from 'vue-router' +import simulatorHandler from '../pages/simulatorHandler.vue' +import Embed from '../pages/embed.vue' + +const routes = [ + { + path: '/', + redirect: '/simulatorvue', // @TODO: update later back to /simulator + }, + { + path: '/simulatorvue', // @TODO: update later back to /simulator + name: 'simulator', + component: simulatorHandler, + children: [ + { + path: 'edit/:projectId', + name: 'simulator-edit', + component: simulatorHandler, + props: true, + }, + ], + }, + { + path: '/simulatorvue/:projectId', + name: 'simulator-view', + component: Embed, + props: true, + }, + { + path: '/simulatorvue/embed/:projectId', + name: 'simulator-embed', + component: Embed, + props: true, + }, +] +const router = createRouter({ + history: createWebHistory(), + routes, +}) + +export default router diff --git a/v1/src/shims-vuetify.d.ts b/v1/src/shims-vuetify.d.ts new file mode 100644 index 00000000..e4a5d5c5 --- /dev/null +++ b/v1/src/shims-vuetify.d.ts @@ -0,0 +1,3 @@ +declare module 'vuetify' +declare module 'vuetify/lib/components' +declare module 'vuetify/lib/directives' diff --git a/v1/src/simulator/spec/circuits/Decoders-plexers-circuitdata.json b/v1/src/simulator/spec/circuits/Decoders-plexers-circuitdata.json new file mode 100644 index 00000000..bf601345 --- /dev/null +++ b/v1/src/simulator/spec/circuits/Decoders-plexers-circuitdata.json @@ -0,0 +1,1109 @@ +{ + "name": "Decoders and Plexers", + "timePeriod": 500, + "clockEnabled": true, + "projectId": "RVvp1Qq4hf3eVcfUO7sE", + "focussedCircuit": 11597572508, + "orderedTabs": ["11597572508"], + "scopes": [ + { + "layout": { + "width": 100, + "height": 280, + "title_x": 50, + "title_y": 13, + "titleEnabled": true + }, + "verilogMetadata": { + "isVerilogCircuit": false, + "isMainCircuit": false, + "code": "// Write Some Verilog Code Here!", + "subCircuitScopeIds": [] + }, + "allNodes": [ + { + "x": -10, + "y": -10, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [14] + }, + { + "x": -10, + "y": 10, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [5] + }, + { + "x": 10, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [6] + }, + { + "x": 0, + "y": 20, + "type": 0, + "bitWidth": 1, + "label": "Control Signal", + "connections": [24] + }, + { + "x": 10, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [14] + }, + { + "x": 10, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [1] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [2] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [15] + }, + { + "x": -10, + "y": -10, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [12] + }, + { + "x": -10, + "y": 10, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [13] + }, + { + "x": 0, + "y": 20, + "type": 0, + "bitWidth": 1, + "label": "Control Signal", + "connections": [11] + }, + { + "x": 80, + "y": 510, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [10, 25] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [8] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [9] + }, + { + "x": -30, + "y": 130, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [0, 4, 15] + }, + { + "x": -30, + "y": 340, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [7, 14] + }, + { + "x": -20, + "y": 0, + "type": 0, + "bitWidth": 2, + "label": "Input", + "connections": [20] + }, + { + "x": 20, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "Output", + "connections": [19] + }, + { + "x": 0, + "y": 20, + "type": 0, + "bitWidth": 1, + "label": "Bit Selector", + "connections": [26] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [17] + }, + { + "x": 20, + "y": 0, + "type": 1, + "bitWidth": 2, + "label": "", + "connections": [16] + }, + { + "x": 10, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [22] + }, + { + "x": -180, + "y": 160, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [21, 23] + }, + { + "x": -180, + "y": 220, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [22, 24, 25] + }, + { + "x": 90, + "y": 220, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [3, 23] + }, + { + "x": -180, + "y": 510, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [11, 23, 27] + }, + { + "x": 70, + "y": 700, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [18, 27] + }, + { + "x": -180, + "y": 700, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [25, 26] + }, + { + "x": -10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [33] + }, + { + "x": 20, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [32] + }, + { + "x": 20, + "y": 20, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [31] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [30] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [29] + }, + { + "x": 10, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [28] + }, + { + "x": -10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [39] + }, + { + "x": 20, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [37] + }, + { + "x": 20, + "y": 20, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [38] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [35] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [36] + }, + { + "x": 10, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [34] + }, + { + "x": -10, + "y": -10, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [46] + }, + { + "x": -10, + "y": 10, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [47] + }, + { + "x": 30, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [45] + }, + { + "x": 10, + "y": 30, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [44] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [43] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [42] + }, + { + "x": 10, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [40] + }, + { + "x": 10, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [41] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [53] + }, + { + "x": -10, + "y": -10, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [51] + }, + { + "x": -10, + "y": 10, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [52] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [49] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [50] + }, + { + "x": 10, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [48] + } + ], + "id": 11597572508, + "name": "Main", + "Input": [ + { + "x": -70, + "y": 130, + "objectType": "Input", + "label": "inp1", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 0, + "customData": { + "nodes": { + "output1": 4 + }, + "values": { + "state": 1 + }, + "constructorParamaters": [ + "RIGHT", + 1, + { + "x": 0, + "y": 60, + "id": "cZW4OLLsTA1aBoRSmHxv" + } + ] + } + }, + { + "x": -70, + "y": 150, + "objectType": "Input", + "label": "inp2", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 0, + "customData": { + "nodes": { + "output1": 5 + }, + "values": { + "state": 1 + }, + "constructorParamaters": [ + "RIGHT", + 1, + { + "x": 0, + "y": 20, + "id": "OhBFKFzir02JVzBVOV44" + } + ] + } + }, + { + "x": -30, + "y": 630, + "objectType": "Input", + "label": "", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 0, + "customData": { + "nodes": { + "output1": 20 + }, + "values": { + "state": 3 + }, + "constructorParamaters": [ + "RIGHT", + 2, + { + "x": 0, + "y": 120, + "id": "Tci49l79hiLHSfxqSPhk" + } + ] + } + }, + { + "x": -360, + "y": 160, + "objectType": "Input", + "label": "s", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 0, + "customData": { + "nodes": { + "output1": 21 + }, + "values": { + "state": 0 + }, + "constructorParamaters": [ + "RIGHT", + 1, + { + "x": 0, + "y": 40, + "id": "2jYcmNNFpmoq6jcELh8X" + } + ] + } + }, + { + "x": -10, + "y": 840, + "objectType": "Input", + "label": "", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 0, + "customData": { + "nodes": { + "output1": 33 + }, + "values": { + "state": 0 + }, + "constructorParamaters": [ + "RIGHT", + 1, + { + "x": 0, + "y": 80, + "id": "wUbTZgbmrEStf17qxgOw" + } + ] + } + }, + { + "x": 0, + "y": 1030, + "objectType": "Input", + "label": "", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 0, + "customData": { + "nodes": { + "output1": 39 + }, + "values": { + "state": 1 + }, + "constructorParamaters": [ + "RIGHT", + 1, + { + "x": 0, + "y": 100, + "id": "hiFGtGajuArxucFOfydA" + } + ] + } + }, + { + "x": -550, + "y": 310, + "objectType": "Input", + "label": "", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 0, + "customData": { + "nodes": { + "output1": 46 + }, + "values": { + "state": 1 + }, + "constructorParamaters": [ + "RIGHT", + 1, + { + "x": 0, + "y": 140, + "id": "6br60gi40FN03qZRFG9W" + } + ] + } + }, + { + "x": -550, + "y": 330, + "objectType": "Input", + "label": "", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 0, + "customData": { + "nodes": { + "output1": 47 + }, + "values": { + "state": 1 + }, + "constructorParamaters": [ + "RIGHT", + 1, + { + "x": 0, + "y": 160, + "id": "EcNRmJVcoyFZdMUmGwQw" + } + ] + } + }, + { + "x": -600, + "y": 680, + "objectType": "Input", + "label": "", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 0, + "customData": { + "nodes": { + "output1": 53 + }, + "values": { + "state": 1 + }, + "constructorParamaters": [ + "RIGHT", + 1, + { + "x": 0, + "y": 180, + "id": "eAluIwGbrt2vmFD37xUb" + } + ] + } + } + ], + "Output": [ + { + "x": 250, + "y": 140, + "objectType": "Output", + "label": "out1", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 6 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 40, + "id": "PQtMRkU1V36zoiUZaKnC" + } + ] + } + }, + { + "x": 340, + "y": 330, + "objectType": "Output", + "label": "out3", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 12 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 20, + "id": "rl7YenrdGRoBr9BXA2Ee" + } + ] + } + }, + { + "x": 340, + "y": 350, + "objectType": "Output", + "label": "out4", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 13 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 60, + "id": "gPrg5glyUDxsQmGldTow" + } + ] + } + }, + { + "x": 260, + "y": 630, + "objectType": "Output", + "label": "", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 19 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 80, + "id": "qqnDrlsm0T8y1dyyiGlL" + } + ] + } + }, + { + "x": 270, + "y": 860, + "objectType": "Output", + "label": "", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 31 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 120, + "id": "2smYfkmptbhEZ2gFBMmf" + } + ] + } + }, + { + "x": 270, + "y": 840, + "objectType": "Output", + "label": "", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 32 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 100, + "id": "45NWNA9b3l54VZE5cFYS" + } + ] + } + }, + { + "x": 220, + "y": 1030, + "objectType": "Output", + "label": "", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 37 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 140, + "id": "COIaU2CIfnmcZ8h0Nxpq" + } + ] + } + }, + { + "x": 220, + "y": 1050, + "objectType": "Output", + "label": "", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 38 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 160, + "id": "bJ7CLsYt9Johbanua2d4" + } + ] + } + }, + { + "x": -460, + "y": 400, + "objectType": "Output", + "label": "", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 44 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 180, + "id": "Of7qPmfBvyW9mhiLkfpN" + } + ] + } + }, + { + "x": -290, + "y": 320, + "objectType": "Output", + "label": "", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 45 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 200, + "id": "LLwXovDSOe6rplHPIDhv" + } + ] + } + }, + { + "x": -310, + "y": 670, + "objectType": "Output", + "label": "", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 51 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 220, + "id": "Yo4Zo5P6gClWQJZWt5DF" + } + ] + } + }, + { + "x": -310, + "y": 690, + "objectType": "Output", + "label": "", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 52 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 240, + "id": "T86CxN5DtKbZtDaZXSMx" + } + ] + } + } + ], + "Multiplexer": [ + { + "x": 90, + "y": 140, + "objectType": "Multiplexer", + "label": "multiplexer", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 10, + "customData": { + "constructorParamaters": ["RIGHT", 1, 1], + "nodes": { + "inp": [0, 1], + "output1": 2, + "controlSignalInput": 3 + } + } + } + ], + "BitSelector": [ + { + "x": 70, + "y": 630, + "objectType": "BitSelector", + "label": "", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 10, + "customData": { + "nodes": { + "inp1": 16, + "output1": 17, + "bitSelectorInp": 18 + }, + "constructorParamaters": ["RIGHT", 2, 1] + } + } + ], + "Demultiplexer": [ + { + "x": 80, + "y": 340, + "objectType": "Demultiplexer", + "label": "demultiplexer", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 10, + "customData": { + "constructorParamaters": ["LEFT", 1, 1], + "nodes": { + "output1": [8, 9], + "input": 7, + "controlSignalInput": 10 + } + } + } + ], + "MSB": [ + { + "x": 70, + "y": 840, + "objectType": "MSB", + "label": "", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 10, + "customData": { + "nodes": { + "inp1": 28, + "output1": 29, + "enable": 30 + }, + "constructorParamaters": ["RIGHT", 1] + } + } + ], + "LSB": [ + { + "x": 70, + "y": 1030, + "objectType": "LSB", + "label": "", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 10, + "customData": { + "nodes": { + "inp1": 34, + "output1": 35, + "enable": 36 + }, + "constructorParamaters": ["RIGHT", 1] + } + } + ], + "PriorityEncoder": [ + { + "x": -480, + "y": 320, + "objectType": "PriorityEncoder", + "label": "", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 10, + "customData": { + "nodes": { + "inp1": [40, 41], + "output1": [42], + "enable": 43 + }, + "constructorParamaters": ["RIGHT", 1] + } + } + ], + "Decoder": [ + { + "x": -480, + "y": 680, + "objectType": "Decoder", + "label": "", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 10, + "customData": { + "constructorParamaters": ["LEFT", 1], + "nodes": { + "output1": [49, 50], + "input": 48 + } + } + } + ], + "restrictedCircuitElementsUsed": [], + "nodes": [11, 14, 15, 22, 23, 24, 25, 26, 27] + } + ] +} diff --git a/v1/src/simulator/spec/circuits/gates-circuitdata.json b/v1/src/simulator/spec/circuits/gates-circuitdata.json new file mode 100644 index 00000000..a5c96609 --- /dev/null +++ b/v1/src/simulator/spec/circuits/gates-circuitdata.json @@ -0,0 +1,710 @@ +{ + "name": "gates-circuitdata", + "timePeriod": 500, + "clockEnabled": true, + "projectId": "hCqg1Ns4JVckHsnyKQDi", + "focussedCircuit": 11597572508, + "orderedTabs": ["11597572508"], + "scopes": [ + { + "layout": { + "width": 100, + "height": 280, + "title_x": 50, + "title_y": 13, + "titleEnabled": true + }, + "verilogMetadata": { + "isVerilogCircuit": false, + "isMainCircuit": false, + "code": "// Write Some Verilog Code Here!", + "subCircuitScopeIds": [] + }, + "allNodes": [ + { + "x": -10, + "y": -10, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [29] + }, + { + "x": -10, + "y": 10, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [31] + }, + { + "x": 20, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [22] + }, + { + "x": -10, + "y": -10, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [30] + }, + { + "x": -10, + "y": 10, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [32] + }, + { + "x": 20, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [23] + }, + { + "x": -10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [35] + }, + { + "x": 20, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [25] + }, + { + "x": -20, + "y": -10, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [38] + }, + { + "x": -20, + "y": 10, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [40] + }, + { + "x": 20, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [27] + }, + { + "x": -10, + "y": -10, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [33] + }, + { + "x": -10, + "y": 10, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [34] + }, + { + "x": 30, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [24] + }, + { + "x": -10, + "y": -10, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [37] + }, + { + "x": -10, + "y": 10, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [39] + }, + { + "x": 30, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [28] + }, + { + "x": -20, + "y": -10, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [36] + }, + { + "x": -20, + "y": 10, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [41] + }, + { + "x": 30, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [26] + }, + { + "x": 10, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [29] + }, + { + "x": 10, + "y": 0, + "type": 1, + "bitWidth": 1, + "label": "", + "connections": [31] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [2] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [5] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [13] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [7] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [19] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [10] + }, + { + "x": 10, + "y": 0, + "type": 0, + "bitWidth": 1, + "label": "", + "connections": [16] + }, + { + "x": 260, + "y": -10, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [0, 20, 30] + }, + { + "x": 260, + "y": 130, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [29, 3, 33] + }, + { + "x": 210, + "y": 10, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [1, 21, 32] + }, + { + "x": 210, + "y": 150, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [31, 4, 34] + }, + { + "x": 260, + "y": 300, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [30, 11, 35] + }, + { + "x": 210, + "y": 320, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [32, 12, 41] + }, + { + "x": 260, + "y": 460, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [33, 6, 36] + }, + { + "x": 260, + "y": 600, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [35, 17, 38] + }, + { + "x": 260, + "y": 820, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [38, 14] + }, + { + "x": 260, + "y": 740, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [36, 37, 8] + }, + { + "x": 210, + "y": 840, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [15, 40] + }, + { + "x": 210, + "y": 760, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [39, 9, 41] + }, + { + "x": 210, + "y": 620, + "type": 2, + "bitWidth": 1, + "label": "", + "connections": [34, 40, 18] + } + ], + "id": 11597572508, + "name": "Main", + "Input": [ + { + "x": -30, + "y": -10, + "objectType": "Input", + "label": "inp1", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 0, + "customData": { + "nodes": { + "output1": 20 + }, + "values": { + "state": 1 + }, + "constructorParamaters": [ + "RIGHT", + 1, + { + "x": 0, + "y": 20, + "id": "55mKFwVFnZeU6ucfO8am" + } + ] + } + }, + { + "x": -30, + "y": 10, + "objectType": "Input", + "label": "inp2", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 0, + "customData": { + "nodes": { + "output1": 21 + }, + "values": { + "state": 1 + }, + "constructorParamaters": [ + "RIGHT", + 1, + { + "x": 0, + "y": 40, + "id": "2O2YfZk7yuqLrtiRb429" + } + ] + } + } + ], + "Output": [ + { + "x": 500, + "y": 0, + "objectType": "Output", + "label": "out1", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 22 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 20, + "id": "RarwxCcQgRygZftUFlMr" + } + ] + } + }, + { + "x": 510, + "y": 140, + "objectType": "Output", + "label": "out2", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 23 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 40, + "id": "N0agpzN04aqy9nLexP98" + } + ] + } + }, + { + "x": 520, + "y": 310, + "objectType": "Output", + "label": "out3", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 24 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 60, + "id": "2yO9fxrqrjPk1urH47Fc" + } + ] + } + }, + { + "x": 520, + "y": 460, + "objectType": "Output", + "label": "out4", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 25 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 80, + "id": "IN7sNsKhkByzBHRShVdu" + } + ] + } + }, + { + "x": 520, + "y": 610, + "objectType": "Output", + "label": "out5", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 26 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 100, + "id": "Dfr4VEfv27q3AOwkLqpx" + } + ] + } + }, + { + "x": 520, + "y": 750, + "objectType": "Output", + "label": "out6", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 27 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 120, + "id": "FGeZ7ip5nJo9MUlEyJ35" + } + ] + } + }, + { + "x": 530, + "y": 830, + "objectType": "Output", + "label": "out7", + "direction": "LEFT", + "labelDirection": "RIGHT", + "propagationDelay": 0, + "customData": { + "nodes": { + "inp1": 28 + }, + "constructorParamaters": [ + "LEFT", + 1, + { + "x": 100, + "y": 140, + "id": "oYVAxzNmrQgKjC7I3xOg" + } + ] + } + } + ], + "NotGate": [ + { + "x": 340, + "y": 460, + "objectType": "NotGate", + "label": "NOT GATE", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 10, + "customData": { + "constructorParamaters": ["RIGHT", 1], + "nodes": { + "output1": 7, + "inp1": 6 + } + } + } + ], + "OrGate": [ + { + "x": 330, + "y": 140, + "objectType": "OrGate", + "label": "OR GATE", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 10, + "customData": { + "constructorParamaters": ["RIGHT", 2, 1], + "nodes": { + "inp": [3, 4], + "output1": 5 + } + } + } + ], + "AndGate": [ + { + "x": 320, + "y": 0, + "objectType": "AndGate", + "label": "AND GATE", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 10, + "customData": { + "constructorParamaters": ["RIGHT", 2, 1], + "nodes": { + "inp": [0, 1], + "output1": 2 + } + } + } + ], + "NorGate": [ + { + "x": 330, + "y": 830, + "objectType": "NorGate", + "label": "NOR", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 10, + "customData": { + "constructorParamaters": ["RIGHT", 2, 1], + "nodes": { + "inp": [14, 15], + "output1": 16 + } + } + } + ], + "NandGate": [ + { + "x": 340, + "y": 310, + "objectType": "NandGate", + "label": "NAND GATE", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 10, + "customData": { + "constructorParamaters": ["RIGHT", 2, 1], + "nodes": { + "inp": [11, 12], + "output1": 13 + } + } + } + ], + "XorGate": [ + { + "x": 330, + "y": 750, + "objectType": "XorGate", + "label": "XOR", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 10, + "customData": { + "constructorParamaters": ["RIGHT", 2, 1], + "nodes": { + "inp": [8, 9], + "output1": 10 + } + } + } + ], + "XnorGate": [ + { + "x": 330, + "y": 610, + "objectType": "XnorGate", + "label": "XNOR GATE", + "direction": "RIGHT", + "labelDirection": "LEFT", + "propagationDelay": 10, + "customData": { + "constructorParamaters": ["RIGHT", 2, 1], + "nodes": { + "inp": [17, 18], + "output1": 19 + } + } + } + ], + "restrictedCircuitElementsUsed": [], + "nodes": [29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41] + } + ] +} diff --git a/v1/src/simulator/spec/data.spec.js b/v1/src/simulator/spec/data.spec.js new file mode 100644 index 00000000..f91a08c8 --- /dev/null +++ b/v1/src/simulator/spec/data.spec.js @@ -0,0 +1,121 @@ +/** + * @jest-environment jsdom + */ + +import CodeMirror from 'codemirror' +import { setup } from '../src/setup' +import load from '../src/data/load' +import gatesCircuitData from './circuits/gates-circuitdata.json' +import decoderCircuitData from './circuits/Decoders-plexers-circuitdata.json' +import { checkIfBackup, scheduleBackup } from '../src/data/backupCircuit' +import undo from '../src/data/undo' +import redo from '../src/data/redo' +import save from '../src/data/save' +import { + clearProject, + newProject, + recoverProject, + saveOffline, + openOffline, +} from '../src/data/project' +import createSaveAsImgPrompt from '../src/data/saveImage' + +jest.mock('codemirror') + +describe('data dir working', () => { + CodeMirror.fromTextArea.mockReturnValueOnce({ setValue: () => {} }) + window.confirm = jest.fn(() => true) + setup() + + test('load gates_circuitData without throwing error', () => { + expect(() => load(gatesCircuitData)).not.toThrow() + }) + + test('should load another circuit data decoder_circuitData', () => { + expect(() => load(decoderCircuitData)).not.toThrow() + }) + + test('schedule backup working', () => { + // toggle states of inputs a dn then run schedule backup + globalScope.Input.forEach((input) => { + input.state = input.state === 1 ? 0 : 1 + expect(() => scheduleBackup()).not.toThrow() + }) + }) + + test('check if backup performed', () => { + expect(() => checkIfBackup(globalScope)).toBeTruthy() + }) + + test('undo working', () => { + const beforeUndo = { + backups: globalScope.backups.length, + history: globalScope.history.length, + } + for (let i = 1; i < beforeUndo.backups; i++) { + undo() + const afterUndo = { + backups: globalScope.backups.length + i, + history: globalScope.history.length - i, + } + expect(afterUndo).toEqual(beforeUndo) + } + }) + + test('redo working', () => { + const beforeRedo = { + backups: globalScope.backups.length, + history: globalScope.history.length, + } + for (let i = 1; i < beforeRedo.history; i++) { + redo() + const afterRedo = { + backups: globalScope.backups.length - i, + history: globalScope.history.length + i, + } + expect(afterRedo).toEqual(beforeRedo) + } + }) + + test('save updated circuit_data', () => { + // save project + window.logixProjectId = decoderCircuitData.projectId + expect(() => save()).not.toThrow() + }) + + test('project working', () => { + // create new project + expect(() => newProject(true)).not.toThrow() + }) + + test('clear Project working', () => { + // clear project + expect(() => clearProject()).not.toThrow() + }) + + test('recover Project working', () => { + // recover project from localstorage + localStorage.setItem('recover', JSON.stringify(gatesCircuitData)) + expect(() => recoverProject()).not.toThrow() + }) + + test('SaveOffline working', () => { + // save offline gate project + expect(() => saveOffline()).not.toThrow() + }) + + test('OpenOffline working', () => { + // open dialog + openOffline() + // click on first input + $('#openProjectDialog input')[0].click() + // click on open button + $('#Open_offline_btn')[0].click() + // it should load the offline saved project + expect(globalScope.id).toBe(11597572508) + }) + + test('saveImage working', () => { + expect(() => createSaveAsImgPrompt()).not.toThrow() + }) +}) diff --git a/v1/src/simulator/spec/gates.spec.js b/v1/src/simulator/spec/gates.spec.js new file mode 100644 index 00000000..90c7b1bd --- /dev/null +++ b/v1/src/simulator/spec/gates.spec.js @@ -0,0 +1,57 @@ +/** + * @jest-environment jsdom + */ + +import CodeMirror from 'codemirror' +import { setup } from '../src/setup' + +import load from '../src/data/load' +import circuitData from './circuits/gates-circuitdata.json' +import testData from './testData/gates-testdata.json' +import { runAll } from '../src/testbench' + +jest.mock('codemirror') + +describe('Simulator Gates Testing', () => { + CodeMirror.fromTextArea.mockReturnValueOnce({ setValue: (text) => {} }) + setup() + + test('load circuitData', () => { + expect(() => load(circuitData)).not.toThrow() + }) + + test('AND gate testing', () => { + const result = runAll(testData.AndGate) + expect(result.summary.passed).toBe(4) + }) + + test('NAND gate testing', () => { + const result = runAll(testData.nandGate) + expect(result.summary.passed).toBe(4) + }) + + test('NOR gate testing', () => { + const result = runAll(testData.norGate) + expect(result.summary.passed).toBe(4) + }) + + test('NOT gate testing', () => { + const result = runAll(testData.notGate) + expect(result.summary.passed).toBe(2) + }) + + test('OR gate testing', () => { + const result = runAll(testData.OrGate) + expect(result.summary.passed).toBe(4) + }) + + test('XNOR gate testing', () => { + const result = runAll(testData.xnorGate) + expect(result.summary.passed).toBe(4) + }) + + test('XOR gate testing', () => { + const result = runAll(testData.xorGate) + expect(result.summary.passed).toBe(4) + }) +}) diff --git a/v1/src/simulator/spec/testData/gates-testdata.json b/v1/src/simulator/spec/testData/gates-testdata.json new file mode 100644 index 00000000..3152253b --- /dev/null +++ b/v1/src/simulator/spec/testData/gates-testdata.json @@ -0,0 +1,200 @@ +{ + "AndGate": { + "type": "comb", + "title": "AND Gate", + "groups": [ + { + "label": "Group 1", + "inputs": [ + { + "label": "inp1", + "bitWidth": 1, + "values": ["0", "0", "1", "1"] + }, + { + "label": "inp2", + "bitWidth": 1, + "values": ["0", "1", "0", "1"] + } + ], + "outputs": [ + { + "label": "out1", + "bitWidth": 1, + "values": ["0", "0", "0", "1"] + } + ], + "n": 4 + } + ] + }, + "OrGate": { + "type": "comb", + "title": "OR Gate", + "groups": [ + { + "label": "Group 1", + "inputs": [ + { + "label": "inp1", + "bitWidth": 1, + "values": ["0", "0", "1", "1"] + }, + { + "label": "inp2", + "bitWidth": 1, + "values": ["0", "1", "0", "1"] + } + ], + "outputs": [ + { + "label": "out2", + "bitWidth": 1, + "values": ["0", "1", "1", "1"] + } + ], + "n": 4 + } + ] + }, + "nandGate": { + "type": "comb", + "title": "NAND Gate", + "groups": [ + { + "label": "Group 1", + "inputs": [ + { + "label": "inp1", + "bitWidth": 1, + "values": ["0", "0", "1", "1"] + }, + { + "label": "inp2", + "bitWidth": 1, + "values": ["0", "1", "0", "1"] + } + ], + "outputs": [ + { + "label": "out3", + "bitWidth": 1, + "values": ["1", "1", "1", "0"] + } + ], + "n": 4 + } + ] + }, + "xorGate": { + "type": "comb", + "title": "XOR Gate", + "groups": [ + { + "label": "Group 1", + "inputs": [ + { + "label": "inp1", + "bitWidth": 1, + "values": ["0", "0", "1", "1"] + }, + { + "label": "inp2", + "bitWidth": 1, + "values": ["0", "1", "0", "1"] + } + ], + "outputs": [ + { + "label": "out6", + "bitWidth": 1, + "values": ["0", "1", "1", "0"] + } + ], + "n": 4 + } + ] + }, + "norGate": { + "type": "comb", + "title": "NOR Gate", + "groups": [ + { + "label": "Group 1", + "inputs": [ + { + "label": "inp1", + "bitWidth": 1, + "values": ["0", "0", "1", "1"] + }, + { + "label": "inp2", + "bitWidth": 1, + "values": ["0", "1", "0", "1"] + } + ], + "outputs": [ + { + "label": "out7", + "bitWidth": 1, + "values": ["1", "0", "0", "0"] + } + ], + "n": 4 + } + ] + }, + "notGate": { + "type": "comb", + "title": "NOT GAte", + "groups": [ + { + "label": "Group 1", + "inputs": [ + { + "label": "inp1", + "bitWidth": 1, + "values": ["0", "1"] + } + ], + "outputs": [ + { + "label": "out4", + "bitWidth": 1, + "values": ["1", "0"] + } + ], + "n": 2 + } + ] + }, + "xnorGate": { + "type": "comb", + "title": "XNOR GAte", + "groups": [ + { + "label": "Group 1", + "inputs": [ + { + "label": "inp1", + "bitWidth": 1, + "values": ["0", "0", "1", "1"] + }, + { + "label": "inp2", + "bitWidth": 1, + "values": ["0", "1", "0", "1"] + } + ], + "outputs": [ + { + "label": "out5", + "bitWidth": 1, + "values": ["1", "0", "0", "1"] + } + ], + "n": 4 + } + ] + } +} diff --git a/v1/src/simulator/src/Verilog2CV.js b/v1/src/simulator/src/Verilog2CV.js new file mode 100644 index 00000000..83e7641e --- /dev/null +++ b/v1/src/simulator/src/Verilog2CV.js @@ -0,0 +1,264 @@ +import { + createNewCircuitScope, + switchCircuit, + changeCircuitName, +} from './circuit' +import SubCircuit from './subcircuit' +import simulationArea from './simulationArea' +import CodeMirror from 'codemirror/lib/codemirror.js' +import 'codemirror/lib/codemirror.css' + +// Importing CodeMirror themes +import 'codemirror/theme/3024-day.css' +import 'codemirror/theme/solarized.css' +import 'codemirror/theme/elegant.css' +import 'codemirror/theme/neat.css' +import 'codemirror/theme/idea.css' +import 'codemirror/theme/neo.css' +import 'codemirror/theme/3024-night.css' +import 'codemirror/theme/blackboard.css' +import 'codemirror/theme/cobalt.css' +import 'codemirror/theme/the-matrix.css' +import 'codemirror/theme/night.css' +import 'codemirror/theme/monokai.css' +import 'codemirror/theme/midnight.css' + +import 'codemirror/addon/hint/show-hint.css' +import 'codemirror/mode/verilog/verilog.js' +import 'codemirror/addon/edit/closebrackets.js' +import 'codemirror/addon/hint/anyword-hint.js' +import 'codemirror/addon/hint/show-hint.js' +import 'codemirror/addon/display/autorefresh.js' +import { showError, showMessage } from './utils' +import { showProperties } from './ux' + +var editor +var verilogMode = false + +export async function createVerilogCircuit() { + const returned = await createNewCircuitScope( + undefined, + undefined, + true, + true + ) + if (returned) verilogModeSet(true) +} + +export function saveVerilogCode() { + var code = editor.getValue() + globalScope.verilogMetadata.code = code + generateVerilogCircuit(code) +} + +export function applyVerilogTheme(theme) { + localStorage.setItem('verilog-theme', theme) + editor.setOption('theme', theme) +} + +export function resetVerilogCode() { + editor.setValue(globalScope.verilogMetadata.code) +} + +export function hasVerilogCodeChanges() { + return editor.getValue() != globalScope.verilogMetadata.code +} + +export function verilogModeGet() { + return verilogMode +} + +export function verilogModeSet(mode) { + if (mode == verilogMode) return + verilogMode = mode + if (mode) { + document.getElementById('code-window').style.display = 'block' + document.querySelector('.elementPanel').style.display = 'none' + document.querySelector('.timing-diagram-panel').style.display = 'none' + document.querySelector('.quick-btn').style.display = 'none' + document.getElementById('verilogEditorPanel').style.display = 'block' + if (!embed) { + simulationArea.lastSelected = globalScope.root + showProperties(undefined) + showProperties(simulationArea.lastSelected) + } + resetVerilogCode() + } else { + document.getElementById('code-window').style.display = 'none' + document.querySelector('.elementPanel').style.display = '' + document.querySelector('.timing-diagram-panel').style.display = '' + document.querySelector('.quick-btn').style.display = '' + document.getElementById('verilogEditorPanel').style.display = 'none' + } +} + +import yosysTypeMap from './VerilogClasses' + +class verilogSubCircuit { + constructor(circuit) { + this.circuit = circuit + } + + getPort(portName) { + var numInputs = this.circuit.inputNodes.length + var numOutputs = this.circuit.outputNodes.length + + for (var i = 0; i < numInputs; i++) { + if (this.circuit.data.Input[i].label == portName) { + return this.circuit.inputNodes[i] + } + } + + for (var i = 0; i < numOutputs; i++) { + if (this.circuit.data.Output[i].label == portName) { + return this.circuit.outputNodes[i] + } + } + } +} + +export function YosysJSON2CV( + JSON, + parentScope = globalScope, + name = 'verilogCircuit', + subCircuitScope = {}, + root = false +) { + var parentID = parentScope.id + var subScope + if (root) { + subScope = parentScope + } else { + subScope = newCircuit(name, undefined, true, false) + } + var circuitDevices = {} + + for (var subCircuitName in JSON.subcircuits) { + var scope = YosysJSON2CV( + JSON.subcircuits[subCircuitName], + subScope, + subCircuitName, + subCircuitScope + ) + subCircuitScope[subCircuitName] = scope.id + } + + for (var device in JSON.devices) { + var deviceType = JSON.devices[device].type + if (deviceType == 'Subcircuit') { + var subCircuitName = JSON.devices[device].celltype + circuitDevices[device] = new verilogSubCircuit( + new SubCircuit( + 500, + 500, + undefined, + subCircuitScope[subCircuitName] + ) + ) + } else { + circuitDevices[device] = new yosysTypeMap[deviceType]( + JSON.devices[device] + ) + } + } + + for (var connection in JSON.connectors) { + var fromId = JSON.connectors[connection]['from']['id'] + var fromPort = JSON.connectors[connection]['from']['port'] + var toId = JSON.connectors[connection]['to']['id'] + var toPort = JSON.connectors[connection]['to']['port'] + + var fromObj = circuitDevices[fromId] + var toObj = circuitDevices[toId] + + var fromPortNode = fromObj.getPort(fromPort) + var toPortNode = toObj.getPort(toPort) + + fromPortNode.connect(toPortNode) + } + + if (!root) { + switchCircuit(parentID) + return subScope + } +} + +export default function generateVerilogCircuit( + verilogCode, + scope = globalScope +) { + var params = { code: verilogCode } + fetch('/api/v1/simulator/verilogcv', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(params), + }) + .then((response) => { + if (!response.ok) { + throw response + } + return response.json() + }) + .then((circuitData) => { + scope.initialize() + for (var id in scope.verilogMetadata.subCircuitScopeIds) + delete scopeList[id] + scope.verilogMetadata.subCircuitScopeIds = [] + scope.verilogMetadata.code = verilogCode + var subCircuitScope = {} + YosysJSON2CV( + circuitData, + globalScope, + 'verilogCircuit', + subCircuitScope, + true + ) + changeCircuitName(circuitData.name) + showMessage('Verilog Circuit Successfully Created') + document.getElementById('verilogOutput').innerHTML = '' + }) + .catch((error) => { + if (error.status == 500) { + showError('Could not connect to Yosys') + } else { + showError('There is some issue with the code') + error.json().then((errorMessage) => { + document.getElementById('verilogOutput').innerHTML = + errorMessage.message + }) + } + }) +} + +export function setupCodeMirrorEnvironment() { + var myTextarea = document.getElementById('codeTextArea') + + CodeMirror.commands.autocomplete = function (cm) { + cm.showHint({ hint: CodeMirror.hint.anyword }) + } + + editor = CodeMirror.fromTextArea(myTextarea, { + mode: 'verilog', + autoRefresh: true, + styleActiveLine: true, + lineNumbers: true, + autoCloseBrackets: true, + smartIndent: true, + indentWithTabs: true, + extraKeys: { 'Ctrl-Space': 'autocomplete' }, + }) + + if (!localStorage.getItem('verilog-theme')) { + localStorage.setItem('verilog-theme', 'default') + } else { + const prevtheme = localStorage.getItem('verilog-theme') + editor.setOption('theme', prevtheme) + } + + editor.setValue('// Write Some Verilog Code Here!') + setTimeout(function () { + editor.refresh() + }, 1) +} diff --git a/v1/src/simulator/src/VerilogClasses.js b/v1/src/simulator/src/VerilogClasses.js new file mode 100644 index 00000000..253748af --- /dev/null +++ b/v1/src/simulator/src/VerilogClasses.js @@ -0,0 +1,1507 @@ +import AndGate from './modules/AndGate' +import NandGate from './modules/NandGate' +import Multiplexer from './modules/Multiplexer' +import XorGate from './modules/XorGate' +import XnorGate from './modules/XnorGate' +import SevenSegDisplay from './modules/SevenSegDisplay' +import SixteenSegDisplay from './modules/SixteenSegDisplay' +import HexDisplay from './modules/HexDisplay' +import OrGate from './modules/OrGate' +import Stepper from './modules/Stepper' +import NotGate from './modules/NotGate' +import Text from './modules/Text' +import TriState from './modules/TriState' +import Buffer from './modules/Buffer' +import ControlledInverter from './modules/ControlledInverter' +import Adder from './modules/Adder' +import verilogMultiplier from './modules/verilogMultiplier' +import verilogDivider from './modules/verilogDivider' +import verilogPower from './modules/verilogPower' +import verilogShiftLeft from './modules/verilogShiftLeft' +import verilogShiftRight from './modules/verilogShiftRight' +import TwoComplement from './modules/TwoComplement' +import Splitter from './modules/Splitter' +import Ground from './modules/Ground' +import Power from './modules/Power' +import Input from './modules/Input' +import Output from './modules/Output' +import BitSelector from './modules/BitSelector' +import ConstantVal from './modules/ConstantVal' +import NorGate from './modules/NorGate' +import DigitalLed from './modules/DigitalLed' +import VariableLed from './modules/VariableLed' +import Button from './modules/Button' +import RGBLed from './modules/RGBLed' +import SquareRGBLed from './modules/SquareRGBLed' +import Demultiplexer from './modules/Demultiplexer' +import Decoder from './modules/Decoder' +import Flag from './modules/Flag' +import MSB from './modules/MSB' +import LSB from './modules/LSB' +import PriorityEncoder from './modules/PriorityEncoder' +import Tunnel from './modules/Tunnel' +import ALU from './modules/ALU' +import Rectangle from './modules/Rectangle' +import Arrow from './modules/Arrow' +import Counter from './modules/Counter' +import Random from './modules/Random' +import RGBLedMatrix from './modules/RGBLedMatrix' +import simulationArea from './simulationArea' +import TflipFlop from './sequential/TflipFlop' +import DflipFlop from './sequential/DflipFlop' +import Dlatch from './sequential/Dlatch' +import SRflipFlop from './sequential/SRflipFlop' +import JKflipFlop from './sequential/JKflipFlop' +import TTY from './sequential/TTY' +import Keyboard from './sequential/Keyboard' +import Clock from './sequential/Clock' +import RAM from './sequential/RAM' +import verilogRAM from './sequential/verilogRAM' +import EEPROM from './sequential/EEPROM' +import Rom from './sequential/Rom' +import TB_Input from './testbench/testbenchInput' +import TB_Output from './testbench/testbenchOutput' +import ForceGate from './testbench/ForceGate' +import { newCircuit, switchCircuit, changeCircuitName } from './circuit' +import SubCircuit from './subcircuit' + +function getBitWidth(bitsJSON) { + if (Number.isInteger(bitsJSON)) { + return bitsJSON + } else { + var ans = 1 + for (var i in bitsJSON) { + ans = Math.max(ans, bitsJSON[i]) + } + return ans + } +} + +class verilogUnaryGate { + constructor(deviceJSON) { + this.bitWidth = 1 + if (deviceJSON['bits']) { + this.bitWidth = getBitWidth(deviceJSON['bits']) + } + } + + getPort(portName) { + if (portName == 'in') { + return this.input + } + if (portName == 'out') { + return this.output + } + } +} + +class verilogInput extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + if (deviceJSON['net'] == 'clk' || deviceJSON['net'] == 'clock') { + this.element = new Clock(0, 0) + } else { + this.element = new Input(0, 0, undefined, undefined, this.bitWidth) + } + this.output = this.element.output1 + this.element.label = deviceJSON['net'] + } +} + +class verilogOutput extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + this.element = new Output(0, 0, undefined, undefined, this.bitWidth) + this.input = this.element.inp1 + this.element.label = deviceJSON['net'] + } +} + +class verilogClock extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + this.element = new Clock(0, 0) + this.output = this.element.output1 + } +} + +class verilogButton extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + this.element = new Button(0, 0) + this.output = this.element.output1 + } +} + +class verilogLamp extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + this.element = new DigitalLed(0, 0) + this.input = this.element.inp1 + } +} + +class verilogNotGate extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + this.element = new NotGate(0, 0, undefined, undefined, this.bitWidth) + this.input = this.element.inp1 + this.output = this.element.output1 + } +} + +class verilogRepeaterGate extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + this.element = new Buffer(0, 0, undefined, undefined, this.bitWidth) + this.input = this.element.inp1 + this.output = this.element.output1 + } +} + +class verilogConstantVal extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + this.bitWidth = deviceJSON['constant'].length + this.state = deviceJSON['constant'] + if (this.state[0] == 'x') { + this.state = undefined + } + this.element = new ConstantVal( + 0, + 0, + undefined, + undefined, + this.bitWidth, + this.state + ) + this.input = this.element.inp1 + this.output = this.element.output1 + } +} + +class verilogReduceAndGate extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + + this.bitWidthSplit = [] + for (var i = 0; i < this.bitWidth; i++) { + this.bitWidthSplit.push(1) + } + + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + this.bitWidthSplit + ) + this.andGate = new AndGate(0, 0, undefined, undefined, this.bitWidth, 1) + + for (var i = 0; i < this.bitWidth; i++) { + this.splitter.outputs[i].connect(this.andGate.inp[i]) + } + + this.input = this.splitter.inp1 + this.output = this.andGate.output1 + } +} + +class verilogReduceNandGate extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + + this.bitWidthSplit = [] + for (var i = 0; i < this.bitWidth; i++) { + this.bitWidthSplit.push(1) + } + + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + this.bitWidthSplit + ) + this.nandGate = new NandGate( + 0, + 0, + undefined, + undefined, + this.bitWidth, + 1 + ) + + for (var i = 0; i < this.bitWidth; i++) { + this.splitter.outputs[i].connect(this.nandGate.inp[i]) + } + + this.input = this.splitter.inp1 + this.output = this.nandGate.output1 + } +} + +class verilogReduceOrGate extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + + this.bitWidthSplit = [] + for (var i = 0; i < this.bitWidth; i++) { + this.bitWidthSplit.push(1) + } + + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + this.bitWidthSplit + ) + this.orGate = new OrGate(0, 0, undefined, undefined, this.bitWidth, 1) + + for (var i = 0; i < this.bitWidth; i++) { + this.splitter.outputs[i].connect(this.orGate.inp[i]) + } + + this.input = this.splitter.inp1 + this.output = this.orGate.output1 + } +} + +class verilogReduceNorGate extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + + this.bitWidthSplit = [] + for (var i = 0; i < this.bitWidth; i++) { + this.bitWidthSplit.push(1) + } + + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + this.bitWidthSplit + ) + this.norGate = new NorGate(0, 0, undefined, undefined, this.bitWidth, 1) + + for (var i = 0; i < this.bitWidth; i++) { + this.splitter.outputs[i].connect(this.norGate.inp[i]) + } + + this.input = this.splitter.inp1 + this.output = this.norGate.output1 + } +} + +class verilogReduceXorGate extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + + this.bitWidthSplit = [] + for (var i = 0; i < this.bitWidth; i++) { + this.bitWidthSplit.push(1) + } + + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + this.bitWidthSplit + ) + this.xorGate = new XorGate(0, 0, undefined, undefined, this.bitWidth, 1) + + for (var i = 0; i < this.bitWidth; i++) { + this.splitter.outputs[i].connect(this.xorGate.inp[i]) + } + + this.input = this.splitter.inp1 + this.output = this.xorGate.output1 + } +} + +class verilogReduceXnorGate extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + + this.bitWidthSplit = [] + for (var i = 0; i < this.bitWidth; i++) { + this.bitWidthSplit.push(1) + } + + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + this.bitWidthSplit + ) + this.xnorGate = new XnorGate( + 0, + 0, + undefined, + undefined, + this.bitWidth, + 1 + ) + + for (var i = 0; i < this.bitWidth; i++) { + this.splitter.outputs[i].connect(this.xnorGate.inp[i]) + } + + this.input = this.splitter.inp1 + this.output = this.xnorGate.output1 + } +} + +class verilogBusSlice extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + this.bitWidth = deviceJSON['slice']['total'] + + this.start = deviceJSON['slice']['first'] + this.count = deviceJSON['slice']['count'] + if (this.start == 0) { + if (this.count == this.bitWidth) { + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + [this.bitWidth] + ) + } else { + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + [this.count, this.bitWidth - this.count] + ) + } + + this.input = this.splitter.inp1 + this.output = this.splitter.outputs[0] + } else { + if (this.start + this.count == this.bitWidth) { + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + [this.start, this.count] + ) + } else { + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + [ + this.start, + this.count, + this.bitWidth - this.start - this.count, + ] + ) + } + this.input = this.splitter.inp1 + this.output = this.splitter.outputs[1] + } + } +} + +class verilogZeroExtend extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + + this.inputBitWidth = deviceJSON['extend']['input'] + this.outputBitWidth = deviceJSON['extend']['output'] + + var extraBits = this.outputBitWidth - this.inputBitWidth + + var zeroState = '' + for (var i = 0; i < extraBits; i++) { + zeroState += '0' + } + + this.zeroConstant = new ConstantVal( + 0, + 0, + undefined, + undefined, + extraBits, + zeroState + ) + + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.outputBitWidth, + [this.inputBitWidth, extraBits] + ) + + this.zeroConstant.output1.connect(this.splitter.outputs[1]) + this.input = this.splitter.outputs[0] + this.output = this.splitter.inp1 + } +} + +class verilogNegationGate extends verilogUnaryGate { + constructor(deviceJSON) { + super(deviceJSON) + this.inputBitWidth = deviceJSON['bits']['in'] + + this.notGate = new NotGate(400, 0, undefined, undefined, this.bitWidth) + this.adder = new Adder(300, 0, undefined, undefined, this.bitWidth) + + if (this.inputBitWidth != this.bitWidth) { + var extraBits = this.bitWidth - this.inputBitWidth + this.splitter = new Splitter( + 600, + 600, + undefined, + undefined, + this.bitWidth, + [this.inputBitWidth, extraBits] + ) + + var zeroState = '' + for (var i = 0; i < extraBits; i++) { + zeroState += '0' + } + + this.zeroConstant = new ConstantVal( + 550, + 550, + undefined, + undefined, + extraBits, + zeroState + ) + + this.zeroConstant.output1.connect(this.splitter.outputs[1]) + this.splitter.inp1.connect(this.notGate.inp1) + + this.input = this.splitter.outputs[0] + } else { + this.input = this.notGate.inp1 + } + + var oneVal = '' + for (var i = 0; i < this.bitWidth - 1; i++) { + oneVal += '0' + } + oneVal += '1' + + this.oneConstant = new ConstantVal( + 0, + 0, + undefined, + undefined, + this.bitWidth, + oneVal + ) + + this.notGate.output1.connect(this.adder.inpA) + this.oneConstant.output1.connect(this.adder.inpB) + + this.output = this.adder.sum + } +} + +class verilogBinaryGate { + constructor(deviceJSON) { + this.bitWidth = 1 + if (deviceJSON['bits']) { + this.bitWidth = getBitWidth(deviceJSON['bits']) + } + } + + getPort(portName) { + if (portName == 'in1') { + return this.input[0] + } else if (portName == 'in2') { + return this.input[1] + } else if (portName == 'out') { + return this.output + } + } +} + +class verilogAndGate extends verilogBinaryGate { + constructor(deviceJSON) { + super(deviceJSON) + this.element = new AndGate( + 0, + 0, + undefined, + undefined, + undefined, + this.bitWidth + ) + this.input = [this.element.inp[0], this.element.inp[1]] + this.output = this.element.output1 + } +} + +class verilogNandGate extends verilogBinaryGate { + constructor(deviceJSON) { + super(deviceJSON) + this.element = new NandGate( + 0, + 0, + undefined, + undefined, + undefined, + this.bitWidth + ) + this.input = [this.element.inp[0], this.element.inp[1]] + this.output = this.element.output1 + } +} + +class verilogOrGate extends verilogBinaryGate { + constructor(deviceJSON) { + super(deviceJSON) + this.element = new OrGate( + 0, + 0, + undefined, + undefined, + undefined, + this.bitWidth + ) + this.input = [this.element.inp[0], this.element.inp[1]] + this.output = this.element.output1 + } +} + +class verilogNorGate extends verilogBinaryGate { + constructor(deviceJSON) { + super(deviceJSON) + this.element = new NorGate( + 0, + 0, + undefined, + undefined, + undefined, + this.bitWidth + ) + this.input = [this.element.inp[0], this.element.inp[1]] + this.output = this.element.output1 + } +} + +class verilogXorGate extends verilogBinaryGate { + constructor(deviceJSON) { + super(deviceJSON) + this.element = new XorGate( + 0, + 0, + undefined, + undefined, + undefined, + this.bitWidth + ) + this.input = [this.element.inp[0], this.element.inp[1]] + this.output = this.element.output1 + } +} + +class verilogXnorGate extends verilogBinaryGate { + constructor(deviceJSON) { + super(deviceJSON) + this.element = new XnorGate( + 0, + 0, + undefined, + undefined, + undefined, + this.bitWidth + ) + this.input = [this.element.inp[0], this.element.inp[1]] + this.output = this.element.output1 + } +} + +class verilogMathGate extends verilogBinaryGate { + constructor(deviceJSON, includeOutBitWidth) { + super(deviceJSON) + + this.bitWidth = Math.max( + deviceJSON['bits']['in1'], + deviceJSON['bits']['in2'] + ) + + if (includeOutBitWidth) { + this.bitWidth = Math.max(deviceJSON['bits']['out'], this.bitWidth) + } + + if (!Number.isInteger(deviceJSON['bits'])) { + this.in1BitWidth = deviceJSON['bits']['in1'] + this.in2BitWidth = deviceJSON['bits']['in2'] + } + + this.input = [] + + var extraBits = this.bitWidth - this.in1BitWidth + + if (extraBits != 0) { + this.in1Splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + [this.in1BitWidth, extraBits] + ) + + var zeroState = '' + for (var i = 0; i < extraBits; i++) { + zeroState += '0' + } + this.in1ZeroConstant = new ConstantVal( + 0, + 0, + undefined, + undefined, + extraBits, + zeroState + ) + this.in1ZeroConstant.output1.connect(this.in1Splitter.outputs[1]) + } else { + this.in1Splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + [this.bitWidth] + ) + } + + var extraBits = this.bitWidth - this.in2BitWidth + if (extraBits != 0) { + this.in2Splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + [this.in2BitWidth, extraBits] + ) + var zeroState = '' + for (var i = 0; i < extraBits; i++) { + zeroState += '0' + } + + this.in2ZeroConstant = new ConstantVal( + 0, + 0, + undefined, + undefined, + extraBits, + zeroState + ) + this.in2ZeroConstant.output1.connect(this.in2Splitter.outputs[1]) + } else { + this.in2Splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + [this.bitWidth] + ) + } + + this.input = [this.in1Splitter.outputs[0], this.in2Splitter.outputs[0]] + } +} + +class verilogEqGate extends verilogMathGate { + constructor(deviceJSON) { + super(deviceJSON, false) + + var bitWidthSplit = [] + + for (var i = 0; i < this.bitWidth; i++) { + bitWidthSplit.push(1) + } + + this.xnorGate = new XnorGate( + 0, + 0, + undefined, + undefined, + undefined, + this.bitWidth + ) + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + bitWidthSplit + ) + this.andGate = new AndGate(0, 0, undefined, undefined, this.bitWidth) + this.in1Splitter.inp1.connect(this.xnorGate.inp[0]) + this.in2Splitter.inp1.connect(this.xnorGate.inp[1]) + + this.xnorGate.output1.connect(this.splitter.inp1) + for (var i = 0; i < this.bitWidth; i++) { + this.splitter.outputs[i].connect(this.andGate.inp[i]) + } + + this.output = this.andGate.output1 + } +} + +class verilogNeGate extends verilogMathGate { + constructor(deviceJSON) { + super(deviceJSON, false) + + var bitWidthSplit = [] + + for (var i = 0; i < this.bitWidth; i++) { + bitWidthSplit.push(1) + } + + this.xnorGate = new XnorGate( + 0, + 0, + undefined, + undefined, + undefined, + this.bitWidth + ) + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + bitWidthSplit + ) + this.nandGate = new NandGate(0, 0, undefined, undefined, this.bitWidth) + + this.in1Splitter.inp1.connect(this.xnorGate.inp[0]) + this.in2Splitter.inp1.connect(this.xnorGate.inp[1]) + + this.xnorGate.output1.connect(this.splitter.inp1) + for (var i = 0; i < this.bitWidth; i++) { + this.splitter.outputs[i].connect(this.nandGate.inp[i]) + } + + this.output = this.nandGate.output1 + } +} + +class verilogLtGate extends verilogMathGate { + constructor(deviceJSON) { + super(deviceJSON, false) + this.constant7 = new ConstantVal(0, 0, undefined, undefined, 3, '111') + this.alu = new ALU(0, 0, undefined, undefined, this.bitWidth) + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + [1] + ) + + this.in1Splitter.inp1.connect(this.alu.inp1) + this.in2Splitter.inp1.connect(this.alu.inp2) + + this.constant7.output1.connect(this.alu.controlSignalInput) + this.alu.output.connect(this.splitter.inp1) + + this.output = this.splitter.outputs[0] + } +} + +class verilogGtGate extends verilogMathGate { + constructor(deviceJSON) { + super(deviceJSON, false) + this.constant7 = new ConstantVal(0, 0, undefined, undefined, 3, '111') + this.alu = new ALU(0, 0, undefined, undefined, this.bitWidth) + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + [1] + ) + + this.in1Splitter.inp1.connect(this.alu.inp1) + this.in2Splitter.inp1.connect(this.alu.inp2) + + this.constant7.output1.connect(this.alu.controlSignalInput) + this.alu.output.connect(this.splitter.inp1) + + this.output = this.splitter.outputs[0] + } +} + +class verilogGeGate extends verilogMathGate { + constructor(deviceJSON) { + super(deviceJSON, false) + this.constant7 = new ConstantVal(0, 0, undefined, undefined, 3, '111') + this.alu = new ALU(0, 0, undefined, undefined, this.bitWidth) + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + [1] + ) + this.notGate = new NotGate(0, 0) + + this.in1Splitter.inp1.connect(this.alu.inp1) + this.in2Splitter.inp1.connect(this.alu.inp2) + + this.constant7.output1.connect(this.alu.controlSignalInput) + this.alu.output.connect(this.splitter.inp1) + this.splitter.outputs[0].connect(this.notGate.inp1) + + this.output = this.notGate.output1 + } +} + +class verilogLeGate extends verilogMathGate { + constructor(deviceJSON) { + super(deviceJSON, false) + this.constant7 = new ConstantVal(0, 0, undefined, undefined, 3, '111') + this.alu = new ALU(0, 0, undefined, undefined, this.bitWidth) + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + [1] + ) + this.notGate = new NotGate(0, 0) + + this.in1Splitter.inp1.connect(this.alu.inp1) + this.in2Splitter.inp1.connect(this.alu.inp2) + + this.constant7.output1.connect(this.alu.controlSignalInput) + this.alu.output.connect(this.splitter.inp1) + this.splitter.outputs[0].connect(this.notGate.inp1) + + this.output = this.notGate.output1 + } +} + +class verilogAdditionGate extends verilogMathGate { + constructor(deviceJSON) { + super(deviceJSON, false) + + this.outBitWidth = deviceJSON['bits']['out'] + + this.adder = new Adder(0, 0, undefined, undefined, this.bitWidth) + + this.in1Splitter.inp1.connect(this.adder.inpA) + this.in2Splitter.inp1.connect(this.adder.inpB) + + if (this.outBitWidth == this.bitWidth) { + this.output = this.adder.sum + } else if (this.outBitWidth == this.bitWidth + 1) { + this.outputSplitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.outBitWidth, + [this.bitWidth, 1] + ) + this.adder.sum.connect(this.outputSplitter.outputs[0]) + this.adder.carryOut.connect(this.outputSplitter.outputs[1]) + this.output = this.outputSplitter.inp1 + } + } +} + +class verilogMultiplicationGate extends verilogMathGate { + constructor(deviceJSON) { + super(deviceJSON) + + this.outBitWidth = deviceJSON['bits']['out'] + + this.verilogMultiplier = new verilogMultiplier( + 300, + 300, + undefined, + undefined, + this.bitWidth, + this.outBitWidth + ) + + this.in1Splitter.inp1.connect(this.verilogMultiplier.inpA) + this.in2Splitter.inp1.connect(this.verilogMultiplier.inpB) + + this.output = this.verilogMultiplier.product + } +} + +class verilogDivisionGate extends verilogMathGate { + constructor(deviceJSON) { + super(deviceJSON) + + this.outBitWidth = deviceJSON['bits']['out'] + + this.verilogDivider = new verilogDivider( + 300, + 300, + undefined, + undefined, + this.bitWidth, + this.outBitWidth + ) + + this.in1Splitter.inp1.connect(this.verilogDivider.inpA) + this.in2Splitter.inp1.connect(this.verilogDivider.inpB) + + this.output = this.verilogDivider.quotient + } +} + +class verilogPowerGate extends verilogMathGate { + constructor(deviceJSON) { + super(deviceJSON) + + this.outBitWidth = deviceJSON['bits']['out'] + + this.verilogPower = new verilogPower( + 300, + 300, + undefined, + undefined, + this.bitWidth, + this.outBitWidth + ) + + this.in1Splitter.inp1.connect(this.verilogPower.inpA) + this.in2Splitter.inp1.connect(this.verilogPower.inpB) + + this.output = this.verilogPower.answer + } +} + +class verilogModuloGate extends verilogMathGate { + constructor(deviceJSON) { + super(deviceJSON) + + this.outBitWidth = deviceJSON['bits']['out'] + + this.verilogDivider = new verilogDivider( + 300, + 300, + undefined, + undefined, + this.bitWidth, + this.outBitWidth + ) + + this.in1Splitter.inp1.connect(this.verilogDivider.inpA) + this.in2Splitter.inp1.connect(this.verilogDivider.inpB) + + this.output = this.verilogDivider.remainder + } +} + +class verilogShiftLeftGate extends verilogMathGate { + constructor(deviceJSON) { + super(deviceJSON) + + this.outBitWidth = deviceJSON['bits']['out'] + + this.verilogShiftLeft = new verilogShiftLeft( + 300, + 300, + undefined, + undefined, + this.bitWidth, + this.outBitWidth + ) + + this.in1Splitter.inp1.connect(this.verilogShiftLeft.inp1) + this.in2Splitter.inp1.connect(this.verilogShiftLeft.shiftInp) + + this.output = this.verilogShiftLeft.output1 + } +} + +class verilogShiftRightGate extends verilogMathGate { + constructor(deviceJSON) { + super(deviceJSON) + + this.outBitWidth = deviceJSON['bits']['out'] + + this.verilogShiftRight = new verilogShiftRight( + 300, + 300, + undefined, + undefined, + this.bitWidth, + this.outBitWidth + ) + + this.in1Splitter.inp1.connect(this.verilogShiftRight.inp1) + this.in2Splitter.inp1.connect(this.verilogShiftRight.shiftInp) + + this.output = this.verilogShiftRight.output1 + } +} + +class verilogSubtractionGate extends verilogMathGate { + constructor(deviceJSON) { + super(deviceJSON, true) + + this.alu = new ALU(0, 0, undefined, undefined, this.bitWidth) + + this.controlConstant = new ConstantVal( + 0, + 0, + undefined, + undefined, + 3, + '110' + ) + this.alu.controlSignalInput.connect(this.controlConstant.output1) + + this.in1Splitter.inp1.connect(this.alu.inp1) + this.in2Splitter.inp1.connect(this.alu.inp2) + + this.output = this.alu.output + } +} + +class verilogDff { + constructor(deviceJSON) { + this.bitWidth = 1 + if (deviceJSON['bits']) { + this.bitWidth = getBitWidth(deviceJSON['bits']) + } + + this.dff = new DflipFlop(0, 0, undefined, undefined, this.bitWidth) + this.clockInput = this.dff.clockInp + this.arstInput = this.dff.reset + this.enableInput = this.dff.en + + this.clockPolarity = true + this.arstPolarity = true + this.enablePolarity = true + + if (deviceJSON['polarity']['clock'] != undefined) { + this.clockPolarity = deviceJSON['polarity']['clock'] + } + if (this.clockPolarity == false) { + this.notGateClock = new NotGate(0, 0) + this.notGateClock.output1.connect(this.dff.clockInp) + this.clockInput = this.notGateClock.inp1 + } + + if (deviceJSON['polarity']['enable'] != undefined) { + this.enablePolarity = deviceJSON['polarity']['enable'] + } + if (this.enablePolarity == false) { + this.notGateEnable = new NotGate(0, 0) + this.notGateEnable.output1.connect(this.dff.en) + this.enableInput = this.notGateEnable.inp1 + } + + if (deviceJSON['polarity']['arst'] != undefined) { + this.arstPolarity = deviceJSON['polarity']['arst'] + } + if (this.arstPolarity == false) { + this.notGateArst = new NotGate(0, 0) + this.notGateArst.output1.connect(this.dff.reset) + this.arstInput = this.notGateArst.inp1 + } + if (deviceJSON['arst_value'] != undefined) { + this.arst_value_constant = new ConstantVal( + 0, + 0, + undefined, + undefined, + this.bitWidth, + deviceJSON['arst_value'] + ) + this.arst_value_constant.output1.connect(this.dff.preset) + } + + this.dInput = this.dff.dInp + this.qOutput = this.dff.qOutput + } + + getPort(portName) { + if (portName == 'clk') { + return this.clockInput + } else if (portName == 'in') { + return this.dInput + } else if (portName == 'arst') { + return this.arstInput + } else if (portName == 'en') { + return this.enableInput + } else if (portName == 'out') { + return this.qOutput + } + } +} + +class verilogMultiplexer { + constructor(deviceJSON) { + this.bitWidth = 1 + this.selectBitWidth = undefined + if (deviceJSON['bits']['in'] != undefined) { + this.bitWidth = deviceJSON['bits']['in'] + } + + if (deviceJSON['bits']['sel'] != undefined) { + this.selectBitWidth = deviceJSON['bits']['sel'] + } + + this.multiplexer = new Multiplexer( + 0, + 0, + undefined, + undefined, + this.bitWidth, + this.selectBitWidth + ) + + this.input = this.multiplexer.inp + this.selectInput = this.multiplexer.controlSignalInput + this.output = this.multiplexer.output1 + } + + getPort(portName) { + if (portName == 'sel') { + return this.selectInput + } else if (portName == 'out') { + return this.output + } else { + var len = portName.length + var index = parseInt(portName.substring(2, len)) + + return this.input[index] + } + } +} + +class verilogMultiplexer1Hot { + constructor(deviceJSON) { + this.bitWidth = 1 + this.selectBitWidth = undefined + if (deviceJSON['bits']['in'] != undefined) { + this.bitWidth = deviceJSON['bits']['in'] + } + + if (deviceJSON['bits']['sel'] != undefined) { + this.selectBitWidth = deviceJSON['bits']['sel'] + } + + this.multiplexer = new Multiplexer( + 0, + 0, + undefined, + undefined, + this.bitWidth, + this.selectBitWidth + ) + this.lsb = new LSB(0, 0, undefined, undefined, this.selectBitWidth) + this.adder = new Adder(0, 0, undefined, undefined, this.selectBitWidth) + + var zeroState = '' + for (var i = 0; i < this.selectBitWidth - 1; i++) { + zeroState += '0' + } + this.zeroPadEnable = new ConstantVal( + 0, + 0, + undefined, + undefined, + this.selectBitWidth - 1, + zeroState + ) + + this.enbaleSplitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.selectBitWidth, + [1, this.selectBitWidth - 1] + ) + + this.lsb.enable.connect(this.enbaleSplitter.outputs[0]) + this.zeroPadEnable.output1.connect(this.enbaleSplitter.outputs[1]) + + this.adder.inpA.connect(this.lsb.output1) + this.adder.inpB.connect(this.enbaleSplitter.inp1) + + this.adder.sum.connect(this.multiplexer.controlSignalInput) + this.input = this.multiplexer.inp + this.selectInput = this.lsb.inp1 + this.output = this.multiplexer.output1 + } + + getPort(portName) { + if (portName == 'sel') { + return this.selectInput + } else if (portName == 'out') { + return this.output + } else { + var len = portName.length + var index = parseInt(portName.substring(2, len)) + + return this.input[index] + } + } +} + +class verilogBusGroup { + constructor(deviceJSON) { + this.bitWidth = 0 + this.bitWidthSplit = deviceJSON['groups'] + + for (var i = 0; i < this.bitWidthSplit.length; i++) { + this.bitWidth += this.bitWidthSplit[i] + } + + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + this.bitWidthSplit + ) + + this.input = this.splitter.outputs + this.output = this.splitter.inp1 + } + + getPort(portName) { + if (portName == 'out') { + return this.output + } else { + var len = portName.length + var index = parseInt(portName.substring(2, len)) + + return this.input[index] + } + } +} + +class verilogBusUngroup { + constructor(deviceJSON) { + this.bitWidth = 0 + this.bitWidthSplit = deviceJSON['groups'] + + for (var i = 0; i < this.bitWidthSplit.length; i++) { + this.bitWidth += this.bitWidthSplit[i] + } + + this.splitter = new Splitter( + 0, + 0, + undefined, + undefined, + this.bitWidth, + this.bitWidthSplit + ) + + this.input = this.splitter.inp1 + this.output = this.splitter.outputs + } + + getPort(portName) { + if (portName == 'in') { + return this.input + } else { + var len = portName.length + var index = parseInt(portName.substring(3, len)) + + return this.output[index] + } + } +} + +class verilogMemory { + constructor(deviceJSON) { + this.memData = deviceJSON['memdata'] + this.dataBitWidth = deviceJSON['bits'] + this.addressBitWidth = deviceJSON['abits'] + this.words = deviceJSON['words'] + + this.numRead = deviceJSON['rdports'].length + this.numWrite = deviceJSON['wrports'].length + + this.verilogRAM = new verilogRAM( + 0, + 0, + undefined, + undefined, + this.dataBitWidth, + this.addressBitWidth, + this.memData, + this.words, + this.numRead, + this.numWrite, + deviceJSON['rdports'], + deviceJSON['wrports'] + ) + + this.writeAddressInput = this.verilogRAM.writeAddress + this.readAddressInput = this.verilogRAM.readAddress + this.writeDataInput = this.verilogRAM.writeDataIn + this.writeEnableInput = this.verilogRAM.writeEnable + this.readDataOutput = this.verilogRAM.dataOut + this.readDffOut = this.verilogRAM.readDff + + for (var i = 0; i < this.numWrite; i++) { + var writeEnInput = new Input( + 0, + 0, + undefined, + undefined, + 1, + undefined + ) + writeEnInput.label = 'en' + i.toString() + writeEnInput.output1.connect(this.verilogRAM.writeEnable[i]) + } + } + + getPort(portName) { + var len = portName.length + var isPortAddr = portName.slice(len - 4, len) == 'addr' + var isPortData = portName.slice(len - 4, len) == 'data' + var isPortClk = portName.slice(len - 3, len) == 'clk' + var isPortEn = portName.slice(len - 2, len) == 'en' + if (portName.startsWith('rd')) { + if (isPortAddr) { + var portNum = portName.slice(2, len - 4) + portNum = parseInt(portNum) + + return this.readAddressInput[portNum] + } + if (isPortData) { + var portNum = portName.slice(2, len - 4) + portNum = parseInt(portNum) + return this.verilogRAM.readDffQOutput[portNum] + } + if (isPortClk) { + var portNum = portName.slice(2, len - 3) + portNum = parseInt(portNum) + + return this.verilogRAM.readDffClock[portNum] + } + if (isPortEn) { + var portNum = portName.slice(2, len - 2) + portNum = parseInt(portNum) + + return this.verilogRAM.readDffEn[portNum] + } + } else { + if (isPortAddr) { + var portNum = portName.slice(2, len - 4) + portNum = parseInt(portNum) + return this.writeAddressInput[portNum] + } + if (isPortData) { + var portNum = portName.slice(2, len - 4) + portNum = parseInt(portNum) + return this.writeDataInput[portNum] + } + if (isPortClk) { + var portNum = portName.slice(2, len - 3) + portNum = parseInt(portNum) + + return this.verilogRAM.writeDffClock[portNum] + } + if (isPortEn) { + var portNum = portName.slice(2, len - 2) + portNum = parseInt(portNum) + + return this.verilogRAM.writeDffEn[portNum] + } + } + } +} + +let yosysTypeMap = {} + +yosysTypeMap['Not'] = verilogNotGate +yosysTypeMap['Repeater'] = verilogRepeaterGate +yosysTypeMap['And'] = verilogAndGate +yosysTypeMap['Nand'] = verilogNandGate +yosysTypeMap['Or'] = verilogOrGate +yosysTypeMap['Nor'] = verilogNorGate +yosysTypeMap['Xor'] = verilogXorGate +yosysTypeMap['Xnor'] = verilogXnorGate +yosysTypeMap['Constant'] = verilogConstantVal +yosysTypeMap['Input'] = verilogInput +yosysTypeMap['Output'] = verilogOutput +yosysTypeMap['AndReduce'] = verilogReduceAndGate +yosysTypeMap['NandReduce'] = verilogReduceNandGate +yosysTypeMap['OrReduce'] = verilogReduceOrGate +yosysTypeMap['NorReduce'] = verilogReduceNorGate +yosysTypeMap['XorReduce'] = verilogReduceXorGate +yosysTypeMap['XnorReduce'] = verilogReduceXnorGate + +yosysTypeMap['Eq'] = verilogEqGate +yosysTypeMap['Ne'] = verilogNeGate + +yosysTypeMap['Lt'] = verilogLtGate +yosysTypeMap['Le'] = verilogLeGate +yosysTypeMap['Ge'] = verilogGeGate +yosysTypeMap['Gt'] = verilogGtGate + +yosysTypeMap['ZeroExtend'] = verilogZeroExtend +yosysTypeMap['Negation'] = verilogNegationGate + +yosysTypeMap['Dff'] = verilogDff +yosysTypeMap['Mux'] = verilogMultiplexer +yosysTypeMap['Mux1Hot'] = verilogMultiplexer1Hot +yosysTypeMap['BusSlice'] = verilogBusSlice +yosysTypeMap['BusGroup'] = verilogBusGroup +yosysTypeMap['BusUngroup'] = verilogBusUngroup + +yosysTypeMap['Addition'] = verilogAdditionGate +yosysTypeMap['Subtraction'] = verilogSubtractionGate +yosysTypeMap['Multiplication'] = verilogMultiplicationGate +yosysTypeMap['Division'] = verilogDivisionGate +yosysTypeMap['Modulo'] = verilogModuloGate +yosysTypeMap['Power'] = verilogPowerGate +yosysTypeMap['ShiftLeft'] = verilogShiftLeftGate +yosysTypeMap['ShiftRight'] = verilogShiftRightGate + +yosysTypeMap['Clock'] = verilogClock +yosysTypeMap['Lamp'] = verilogLamp +yosysTypeMap['Button'] = verilogButton + +yosysTypeMap['Memory'] = verilogMemory + +export default yosysTypeMap diff --git a/v1/src/simulator/src/app.js b/v1/src/simulator/src/app.js new file mode 100644 index 00000000..00b64e60 --- /dev/null +++ b/v1/src/simulator/src/app.js @@ -0,0 +1,213 @@ +import { setup } from './setup' +import Array from './arrayHelpers' + +document.addEventListener('DOMContentLoaded', () => { + setup() + var js = { + devices: { + dev0: { + type: 'Input', + net: 'clk', + order: 0, + bits: 1, + }, + dev1: { + type: 'Input', + net: 'addr', + order: 1, + bits: 4, + }, + dev2: { + type: 'Output', + net: 'data', + order: 2, + bits: 5, + }, + dev3: { + type: 'Input', + net: 'addr2', + order: 3, + bits: 4, + }, + dev4: { + type: 'Output', + net: 'data2', + order: 4, + bits: 5, + }, + dev5: { + type: 'Input', + net: 'wraddr', + order: 5, + bits: 4, + }, + dev6: { + type: 'Input', + net: 'wrdata', + order: 6, + bits: 5, + }, + dev7: { + type: 'Input', + net: 'wraddr2', + order: 7, + bits: 4, + }, + dev8: { + type: 'Input', + net: 'wrdata2', + order: 8, + bits: 5, + }, + dev9: { + label: 'mem', + type: 'Memory', + bits: 5, + abits: 4, + words: 16, + offset: 0, + rdports: [ + {}, + { + clock_polarity: true, + }, + ], + wrports: [ + { + clock_polarity: true, + }, + { + clock_polarity: true, + }, + ], + memdata: [13, '00001', 3, '11111'], + }, + }, + connectors: [ + { + to: { + id: 'dev9', + port: 'rd1clk', + }, + from: { + id: 'dev0', + port: 'out', + }, + name: 'clk', + }, + { + to: { + id: 'dev9', + port: 'wr0clk', + }, + from: { + id: 'dev0', + port: 'out', + }, + name: 'clk', + }, + { + to: { + id: 'dev9', + port: 'wr1clk', + }, + from: { + id: 'dev0', + port: 'out', + }, + name: 'clk', + }, + { + to: { + id: 'dev9', + port: 'rd0addr', + }, + from: { + id: 'dev1', + port: 'out', + }, + name: 'addr', + }, + { + to: { + id: 'dev2', + port: 'in', + }, + from: { + id: 'dev9', + port: 'rd0data', + }, + name: 'data', + }, + { + to: { + id: 'dev9', + port: 'rd1addr', + }, + from: { + id: 'dev3', + port: 'out', + }, + name: 'addr2', + }, + { + to: { + id: 'dev4', + port: 'in', + }, + from: { + id: 'dev9', + port: 'rd1data', + }, + name: 'data2', + }, + { + to: { + id: 'dev9', + port: 'wr0addr', + }, + from: { + id: 'dev5', + port: 'out', + }, + name: 'wraddr', + }, + { + to: { + id: 'dev9', + port: 'wr0data', + }, + from: { + id: 'dev6', + port: 'out', + }, + name: 'wrdata', + }, + { + to: { + id: 'dev9', + port: 'wr1addr', + }, + from: { + id: 'dev7', + port: 'out', + }, + name: 'wraddr2', + }, + { + to: { + id: 'dev9', + port: 'wr1data', + }, + from: { + id: 'dev8', + port: 'out', + }, + name: 'wrdata2', + }, + ], + subcircuits: {}, + } +}) + +window.Array = Array diff --git a/v1/src/simulator/src/arrayHelpers.js b/v1/src/simulator/src/arrayHelpers.js new file mode 100644 index 00000000..8d10917e --- /dev/null +++ b/v1/src/simulator/src/arrayHelpers.js @@ -0,0 +1,34 @@ +/* eslint-disable func-names */ +/* eslint-disable no-global-assign */ +/* eslint-disable no-extend-native */ +export default Array = window.Array + +Object.defineProperty(Array.prototype, 'clean', { + value: function (deleteValue) { + for (var i = 0; i < this.length; i++) { + if (this[i] === deleteValue) { + this.splice(i, 1) + i-- + } + } + return this + }, + enumerable: false, +}) + +Object.defineProperty(Array.prototype, 'extend', { + value: function (otherArray) { + /* you should include a test to check whether other_array really is an array */ + otherArray.forEach(function (v) { + this.push(v) + }, this) + }, + enumerable: false, +}) + +Object.defineProperty(Array.prototype, 'contains', { + value: function (value) { + return this.indexOf(value) > -1 + }, + enumerable: false, +}) diff --git a/v1/src/simulator/src/backgroundArea.js b/v1/src/simulator/src/backgroundArea.js new file mode 100644 index 00000000..49da21aa --- /dev/null +++ b/v1/src/simulator/src/backgroundArea.js @@ -0,0 +1,17 @@ +import { dots } from './canvasApi' + +var backgroundArea +export default backgroundArea = { + canvas: document.getElementById('backgroundArea'), + setup() { + this.canvas = document.getElementById('backgroundArea') + this.canvas.width = width + this.canvas.height = height + this.context = this.canvas.getContext('2d') + dots(true, false) + }, + clear() { + if (!this.context) return + this.context.clearRect(0, 0, this.canvas.width, this.canvas.height) + }, +} diff --git a/v1/src/simulator/src/canvas2svg.js b/v1/src/simulator/src/canvas2svg.js new file mode 100644 index 00000000..d6f5dc64 --- /dev/null +++ b/v1/src/simulator/src/canvas2svg.js @@ -0,0 +1,1433 @@ +/*!! + * Canvas 2 Svg v1.0.19 + * A low level canvas to SVG converter. Uses a mock canvas context to build an SVG document. + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/mit-license.php + * + * Author: + * Kerry Liu + * + * Copyright (c) 2014 Gliffy Inc. + */ + +/** + * CircuitVerse - Edited + * Latest npm package is 1.0.16 but we need 1.0.19 + */ + +'use strict' + +var STYLES, ctx, CanvasGradient, CanvasPattern, namedEntities + +//helper function to format a string +function format(str, args) { + var keys = Object.keys(args), + i + for (i = 0; i < keys.length; i++) { + str = str.replace( + new RegExp('\\{' + keys[i] + '\\}', 'gi'), + args[keys[i]] + ) + } + return str +} + +//helper function that generates a random string +function randomString(holder) { + var chars, randomstring, i + if (!holder) { + throw new Error( + 'cannot create a random attribute name for an undefined object' + ) + } + chars = 'ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz' + randomstring = '' + do { + randomstring = '' + for (i = 0; i < 12; i++) { + randomstring += chars[Math.floor(Math.random() * chars.length)] + } + } while (holder[randomstring]) + return randomstring +} + +//helper function to map named to numbered entities +function createNamedToNumberedLookup(items, radix) { + var i, + entity, + lookup = {}, + base10, + base16 + items = items.split(',') + radix = radix || 10 + // Map from named to numbered entities. + for (i = 0; i < items.length; i += 2) { + entity = '&' + items[i + 1] + ';' + base10 = parseInt(items[i], radix) + lookup[entity] = '&#' + base10 + ';' + } + //FF and IE need to create a regex from hex values ie   == \xa0 + lookup['\\xa0'] = ' ' + return lookup +} + +//helper function to map canvas-textAlign to svg-textAnchor +function getTextAnchor(textAlign) { + //TODO: support rtl languages + var mapping = { + left: 'start', + right: 'end', + center: 'middle', + start: 'start', + end: 'end', + } + return mapping[textAlign] || mapping.start +} + +//helper function to map canvas-textBaseline to svg-dominantBaseline +function getDominantBaseline(textBaseline) { + //INFO: not supported in all browsers + var mapping = { + alphabetic: 'alphabetic', + hanging: 'hanging', + top: 'text-before-edge', + bottom: 'text-after-edge', + middle: 'central', + } + return mapping[textBaseline] || mapping.alphabetic +} + +// Unpack entities lookup where the numbers are in radix 32 to reduce the size +// entity mapping courtesy of tinymce +namedEntities = createNamedToNumberedLookup( + '50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,' + + '5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,' + + '5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,' + + '5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,' + + '68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,' + + '6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,' + + '6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,' + + '75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,' + + '7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,' + + '7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,' + + 'sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,' + + 'st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,' + + 't9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,' + + 'tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,' + + 'u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,' + + '81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,' + + '8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,' + + '8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,' + + '8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,' + + '8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,' + + 'nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,' + + 'rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,' + + 'Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,' + + '80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,' + + '811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro', + 32 +) + +//Some basic mappings for attributes and default values. +STYLES = { + strokeStyle: { + svgAttr: 'stroke', //corresponding svg attribute + canvas: '#000000', //canvas default + svg: 'none', //svg default + apply: 'stroke', //apply on stroke() or fill() + }, + fillStyle: { + svgAttr: 'fill', + canvas: '#000000', + svg: null, //svg default is black, but we need to special case this to handle canvas stroke without fill + apply: 'fill', + }, + lineCap: { + svgAttr: 'stroke-linecap', + canvas: 'butt', + svg: 'butt', + apply: 'stroke', + }, + lineJoin: { + svgAttr: 'stroke-linejoin', + canvas: 'miter', + svg: 'miter', + apply: 'stroke', + }, + miterLimit: { + svgAttr: 'stroke-miterlimit', + canvas: 10, + svg: 4, + apply: 'stroke', + }, + lineWidth: { + svgAttr: 'stroke-width', + canvas: 1, + svg: 1, + apply: 'stroke', + }, + globalAlpha: { + svgAttr: 'opacity', + canvas: 1, + svg: 1, + apply: 'fill stroke', + }, + font: { + //font converts to multiple svg attributes, there is custom logic for this + canvas: '10px sans-serif', + }, + shadowColor: { + canvas: '#000000', + }, + shadowOffsetX: { + canvas: 0, + }, + shadowOffsetY: { + canvas: 0, + }, + shadowBlur: { + canvas: 0, + }, + textAlign: { + canvas: 'start', + }, + textBaseline: { + canvas: 'alphabetic', + }, + lineDash: { + svgAttr: 'stroke-dasharray', + canvas: [], + svg: null, + apply: 'stroke', + }, +} + +/** + * + * @param gradientNode - reference to the gradient + * @constructor + */ +CanvasGradient = function (gradientNode, ctx) { + this.__root = gradientNode + this.__ctx = ctx +} + +/** + * Adds a color stop to the gradient root + */ +CanvasGradient.prototype.addColorStop = function (offset, color) { + var stop = this.__ctx.__createElement('stop'), + regex, + matches + stop.setAttribute('offset', offset) + if (color.indexOf('rgba') !== -1) { + //separate alpha value, since webkit can't handle it + regex = + /rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d?\.?\d*)\s*\)/gi + matches = regex.exec(color) + stop.setAttribute( + 'stop-color', + format('rgb({r},{g},{b})', { + r: matches[1], + g: matches[2], + b: matches[3], + }) + ) + stop.setAttribute('stop-opacity', matches[4]) + } else { + stop.setAttribute('stop-color', color) + } + this.__root.appendChild(stop) +} + +CanvasPattern = function (pattern, ctx) { + this.__root = pattern + this.__ctx = ctx +} + +/** + * The mock canvas context + * @param o - options include: + * ctx - existing Context2D to wrap around + * width - width of your canvas (defaults to 500) + * height - height of your canvas (defaults to 500) + * enableMirroring - enables canvas mirroring (get image data) (defaults to false) + * document - the document object (defaults to the current document) + */ +ctx = function (o) { + var defaultOptions = { width: 500, height: 500, enableMirroring: false }, + options + + //keep support for this way of calling C2S: new C2S(width,height) + if (arguments.length > 1) { + options = defaultOptions + options.width = arguments[0] + options.height = arguments[1] + } else if (!o) { + options = defaultOptions + } else { + options = o + } + + if (!(this instanceof ctx)) { + //did someone call this without new? + return new ctx(options) + } + + //setup options + this.width = options.width || defaultOptions.width + this.height = options.height || defaultOptions.height + this.enableMirroring = + options.enableMirroring !== undefined + ? options.enableMirroring + : defaultOptions.enableMirroring + + this.canvas = this ///point back to this instance! + this.__document = options.document || document + + // allow passing in an existing context to wrap around + // if a context is passed in, we know a canvas already exist + if (options.ctx) { + this.__ctx = options.ctx + } else { + this.__canvas = this.__document.createElement('canvas') + this.__ctx = this.__canvas.getContext('2d') + } + + this.__setDefaultStyles() + this.__stack = [this.__getStyleState()] + this.__groupStack = [] + + //the root svg element + this.__root = this.__document.createElementNS( + 'http://www.w3.org/2000/svg', + 'svg' + ) + this.__root.setAttribute('version', 1.1) + this.__root.setAttribute('xmlns', 'http://www.w3.org/2000/svg') + this.__root.setAttributeNS( + 'http://www.w3.org/2000/xmlns/', + 'xmlns:xlink', + 'http://www.w3.org/1999/xlink' + ) + this.__root.setAttribute('width', this.width) + this.__root.setAttribute('height', this.height) + + //make sure we don't generate the same ids in defs + this.__ids = {} + + //defs tag + this.__defs = this.__document.createElementNS( + 'http://www.w3.org/2000/svg', + 'defs' + ) + this.__root.appendChild(this.__defs) + + //also add a group child. the svg element can't use the transform attribute + this.__currentElement = this.__document.createElementNS( + 'http://www.w3.org/2000/svg', + 'g' + ) + this.__root.appendChild(this.__currentElement) +} + +/** + * Creates the specified svg element + * @private + */ +ctx.prototype.__createElement = function (elementName, properties, resetFill) { + if (typeof properties === 'undefined') { + properties = {} + } + + var element = this.__document.createElementNS( + 'http://www.w3.org/2000/svg', + elementName + ), + keys = Object.keys(properties), + i, + key + if (resetFill) { + //if fill or stroke is not specified, the svg element should not display. By default SVG's fill is black. + element.setAttribute('fill', 'none') + element.setAttribute('stroke', 'none') + } + for (i = 0; i < keys.length; i++) { + key = keys[i] + element.setAttribute(key, properties[key]) + } + return element +} + +/** + * Applies default canvas styles to the context + * @private + */ +ctx.prototype.__setDefaultStyles = function () { + //default 2d canvas context properties see:http://www.w3.org/TR/2dcontext/ + var keys = Object.keys(STYLES), + i, + key + for (i = 0; i < keys.length; i++) { + key = keys[i] + this[key] = STYLES[key].canvas + } +} + +/** + * Applies styles on restore + * @param styleState + * @private + */ +ctx.prototype.__applyStyleState = function (styleState) { + var keys = Object.keys(styleState), + i, + key + for (i = 0; i < keys.length; i++) { + key = keys[i] + this[key] = styleState[key] + } +} + +/** + * Gets the current style state + * @return {Object} + * @private + */ +ctx.prototype.__getStyleState = function () { + var i, + styleState = {}, + keys = Object.keys(STYLES), + key + for (i = 0; i < keys.length; i++) { + key = keys[i] + styleState[key] = this[key] + } + return styleState +} + +/** + * Apples the current styles to the current SVG element. On "ctx.fill" or "ctx.stroke" + * @param type + * @private + */ +ctx.prototype.__applyStyleToCurrentElement = function (type) { + var currentElement = this.__currentElement + var currentStyleGroup = this.__currentElementsToStyle + if (currentStyleGroup) { + currentElement.setAttribute(type, '') + currentElement = currentStyleGroup.element + currentStyleGroup.children.forEach(function (node) { + node.setAttribute(type, '') + }) + } + + var keys = Object.keys(STYLES), + i, + style, + value, + id, + regex, + matches + for (i = 0; i < keys.length; i++) { + style = STYLES[keys[i]] + value = this[keys[i]] + if (style.apply) { + //is this a gradient or pattern? + if (value instanceof CanvasPattern) { + //pattern + if (value.__ctx) { + //copy over defs + while (value.__ctx.__defs.childNodes.length) { + id = value.__ctx.__defs.childNodes[0].getAttribute('id') + this.__ids[id] = id + this.__defs.appendChild( + value.__ctx.__defs.childNodes[0] + ) + } + } + currentElement.setAttribute( + style.apply, + format('url(#{id})', { + id: value.__root.getAttribute('id'), + }) + ) + } else if (value instanceof CanvasGradient) { + //gradient + currentElement.setAttribute( + style.apply, + format('url(#{id})', { + id: value.__root.getAttribute('id'), + }) + ) + } else if ( + style.apply.indexOf(type) !== -1 && + style.svg !== value + ) { + if ( + (style.svgAttr === 'stroke' || style.svgAttr === 'fill') && + value.indexOf('rgba') !== -1 + ) { + //separate alpha value, since illustrator can't handle it + regex = + /rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d?\.?\d*)\s*\)/gi + matches = regex.exec(value) + currentElement.setAttribute( + style.svgAttr, + format('rgb({r},{g},{b})', { + r: matches[1], + g: matches[2], + b: matches[3], + }) + ) + //should take globalAlpha here + var opacity = matches[4] + var globalAlpha = this.globalAlpha + if (globalAlpha != null) { + opacity *= globalAlpha + } + currentElement.setAttribute( + style.svgAttr + '-opacity', + opacity + ) + } else { + var attr = style.svgAttr + if (keys[i] === 'globalAlpha') { + attr = type + '-' + style.svgAttr + if (currentElement.getAttribute(attr)) { + //fill-opacity or stroke-opacity has already been set by stroke or fill. + continue + } + } + //otherwise only update attribute if right type, and not svg default + currentElement.setAttribute(attr, value) + } + } + } + } +} + +/** + * Will return the closest group or svg node. May return the current element. + * @private + */ +ctx.prototype.__closestGroupOrSvg = function (node) { + node = node || this.__currentElement + if (node.nodeName === 'g' || node.nodeName === 'svg') { + return node + } else { + return this.__closestGroupOrSvg(node.parentNode) + } +} + +/** + * Returns the serialized value of the svg so far + * @param fixNamedEntities - Standalone SVG doesn't support named entities, which document.createTextNode encodes. + * If true, we attempt to find all named entities and encode it as a numeric entity. + * @return serialized svg + */ +ctx.prototype.getSerializedSvg = function (fixNamedEntities) { + var serialized = new XMLSerializer().serializeToString(this.__root), + keys, + i, + key, + value, + regexp, + xmlns + + //IE search for a duplicate xmnls because they didn't implement setAttributeNS correctly + xmlns = + /xmlns="http:\/\/www\.w3\.org\/2000\/svg".+xmlns="http:\/\/www\.w3\.org\/2000\/svg/gi + if (xmlns.test(serialized)) { + serialized = serialized.replace( + 'xmlns="http://www.w3.org/2000/svg', + 'xmlns:xlink="http://www.w3.org/1999/xlink' + ) + } + + if (fixNamedEntities) { + keys = Object.keys(namedEntities) + //loop over each named entity and replace with the proper equivalent. + for (i = 0; i < keys.length; i++) { + key = keys[i] + value = namedEntities[key] + regexp = new RegExp(key, 'gi') + if (regexp.test(serialized)) { + serialized = serialized.replace(regexp, value) + } + } + } + + return serialized +} + +/** + * Returns the root svg + * @return svg + */ +ctx.prototype.getSvg = function () { + return this.__root +} +/** + * Will generate a group tag. + */ +ctx.prototype.save = function () { + var group = this.__createElement('g') + var parent = this.__closestGroupOrSvg() + this.__groupStack.push(parent) + parent.appendChild(group) + this.__currentElement = group + this.__stack.push(this.__getStyleState()) +} +/** + * Sets current element to parent, or just root if already root + */ +ctx.prototype.restore = function () { + this.__currentElement = this.__groupStack.pop() + this.__currentElementsToStyle = null + //Clearing canvas will make the poped group invalid, currentElement is set to the root group node. + if (!this.__currentElement) { + this.__currentElement = this.__root.childNodes[1] + } + var state = this.__stack.pop() + this.__applyStyleState(state) +} + +/** + * Helper method to add transform + * @private + */ +ctx.prototype.__addTransform = function (t) { + //if the current element has siblings, add another group + var parent = this.__closestGroupOrSvg() + if (parent.childNodes.length > 0) { + if (this.__currentElement.nodeName === 'path') { + if (!this.__currentElementsToStyle) + this.__currentElementsToStyle = { + element: parent, + children: [], + } + this.__currentElementsToStyle.children.push(this.__currentElement) + this.__applyCurrentDefaultPath() + } + + var group = this.__createElement('g') + parent.appendChild(group) + this.__currentElement = group + } + + var transform = this.__currentElement.getAttribute('transform') + if (transform) { + transform += ' ' + } else { + transform = '' + } + transform += t + this.__currentElement.setAttribute('transform', transform) +} + +/** + * scales the current element + */ +ctx.prototype.scale = function (x, y) { + if (y === undefined) { + y = x + } + this.__addTransform(format('scale({x},{y})', { x: x, y: y })) +} + +/** + * rotates the current element + */ +ctx.prototype.rotate = function (angle) { + var degrees = (angle * 180) / Math.PI + this.__addTransform( + format('rotate({angle},{cx},{cy})', { angle: degrees, cx: 0, cy: 0 }) + ) +} + +/** + * translates the current element + */ +ctx.prototype.translate = function (x, y) { + this.__addTransform(format('translate({x},{y})', { x: x, y: y })) +} + +/** + * applies a transform to the current element + */ +ctx.prototype.transform = function (a, b, c, d, e, f) { + this.__addTransform( + format('matrix({a},{b},{c},{d},{e},{f})', { + a: a, + b: b, + c: c, + d: d, + e: e, + f: f, + }) + ) +} + +/** + * Create a new Path Element + */ +ctx.prototype.beginPath = function () { + var path, parent + + // Note that there is only one current default path, it is not part of the drawing state. + // See also: https://html.spec.whatwg.org/multipage/scripting.html#current-default-path + this.__currentDefaultPath = '' + this.__currentPosition = {} + + path = this.__createElement('path', {}, true) + parent = this.__closestGroupOrSvg() + parent.appendChild(path) + this.__currentElement = path +} + +/** + * Helper function to apply currentDefaultPath to current path element + * @private + */ +ctx.prototype.__applyCurrentDefaultPath = function () { + var currentElement = this.__currentElement + if (currentElement.nodeName === 'path') { + currentElement.setAttribute('d', this.__currentDefaultPath) + } else { + console.error( + 'Attempted to apply path command to node', + currentElement.nodeName + ) + } +} + +/** + * Helper function to add path command + * @private + */ +ctx.prototype.__addPathCommand = function (command) { + this.__currentDefaultPath += ' ' + this.__currentDefaultPath += command +} + +/** + * Adds the move command to the current path element, + * if the currentPathElement is not empty create a new path element + */ +ctx.prototype.moveTo = function (x, y) { + if (this.__currentElement.nodeName !== 'path') { + this.beginPath() + } + + // creates a new subpath with the given point + this.__currentPosition = { x: x, y: y } + this.__addPathCommand(format('M {x} {y}', { x: x, y: y })) +} + +/** + * Closes the current path + */ +ctx.prototype.closePath = function () { + if (this.__currentDefaultPath) { + this.__addPathCommand('Z') + } +} + +/** + * Adds a line to command + */ +ctx.prototype.lineTo = function (x, y) { + this.__currentPosition = { x: x, y: y } + if (this.__currentDefaultPath.indexOf('M') > -1) { + this.__addPathCommand(format('L {x} {y}', { x: x, y: y })) + } else { + this.__addPathCommand(format('M {x} {y}', { x: x, y: y })) + } +} + +/** + * Add a bezier command + */ +ctx.prototype.bezierCurveTo = function (cp1x, cp1y, cp2x, cp2y, x, y) { + this.__currentPosition = { x: x, y: y } + this.__addPathCommand( + format('C {cp1x} {cp1y} {cp2x} {cp2y} {x} {y}', { + cp1x: cp1x, + cp1y: cp1y, + cp2x: cp2x, + cp2y: cp2y, + x: x, + y: y, + }) + ) +} + +/** + * Adds a quadratic curve to command + */ +ctx.prototype.quadraticCurveTo = function (cpx, cpy, x, y) { + this.__currentPosition = { x: x, y: y } + this.__addPathCommand( + format('Q {cpx} {cpy} {x} {y}', { cpx: cpx, cpy: cpy, x: x, y: y }) + ) +} + +/** + * Return a new normalized vector of given vector + */ +var normalize = function (vector) { + var len = Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1]) + return [vector[0] / len, vector[1] / len] +} + +/** + * Adds the arcTo to the current path + * + * @see http://www.w3.org/TR/2015/WD-2dcontext-20150514/#dom-context-2d-arcto + */ +ctx.prototype.arcTo = function (x1, y1, x2, y2, radius) { + // Let the point (x0, y0) be the last point in the subpath. + var x0 = this.__currentPosition && this.__currentPosition.x + var y0 = this.__currentPosition && this.__currentPosition.y + + // First ensure there is a subpath for (x1, y1). + if (typeof x0 == 'undefined' || typeof y0 == 'undefined') { + return + } + + // Negative values for radius must cause the implementation to throw an IndexSizeError exception. + if (radius < 0) { + throw new Error( + 'IndexSizeError: The radius provided (' + radius + ') is negative.' + ) + } + + // If the point (x0, y0) is equal to the point (x1, y1), + // or if the point (x1, y1) is equal to the point (x2, y2), + // or if the radius radius is zero, + // then the method must add the point (x1, y1) to the subpath, + // and connect that point to the previous point (x0, y0) by a straight line. + if ((x0 === x1 && y0 === y1) || (x1 === x2 && y1 === y2) || radius === 0) { + this.lineTo(x1, y1) + return + } + + // Otherwise, if the points (x0, y0), (x1, y1), and (x2, y2) all lie on a single straight line, + // then the method must add the point (x1, y1) to the subpath, + // and connect that point to the previous point (x0, y0) by a straight line. + var unit_vec_p1_p0 = normalize([x0 - x1, y0 - y1]) + var unit_vec_p1_p2 = normalize([x2 - x1, y2 - y1]) + if ( + unit_vec_p1_p0[0] * unit_vec_p1_p2[1] === + unit_vec_p1_p0[1] * unit_vec_p1_p2[0] + ) { + this.lineTo(x1, y1) + return + } + + // Otherwise, let The Arc be the shortest arc given by circumference of the circle that has radius radius, + // and that has one point tangent to the half-infinite line that crosses the point (x0, y0) and ends at the point (x1, y1), + // and that has a different point tangent to the half-infinite line that ends at the point (x1, y1), and crosses the point (x2, y2). + // The points at which this circle touches these two lines are called the start and end tangent points respectively. + + // note that both vectors are unit vectors, so the length is 1 + var cos = + unit_vec_p1_p0[0] * unit_vec_p1_p2[0] + + unit_vec_p1_p0[1] * unit_vec_p1_p2[1] + var theta = Math.acos(Math.abs(cos)) + + // Calculate origin + var unit_vec_p1_origin = normalize([ + unit_vec_p1_p0[0] + unit_vec_p1_p2[0], + unit_vec_p1_p0[1] + unit_vec_p1_p2[1], + ]) + var len_p1_origin = radius / Math.sin(theta / 2) + var x = x1 + len_p1_origin * unit_vec_p1_origin[0] + var y = y1 + len_p1_origin * unit_vec_p1_origin[1] + + // Calculate start angle and end angle + // rotate 90deg clockwise (note that y axis points to its down) + var unit_vec_origin_start_tangent = [-unit_vec_p1_p0[1], unit_vec_p1_p0[0]] + // rotate 90deg counter clockwise (note that y axis points to its down) + var unit_vec_origin_end_tangent = [unit_vec_p1_p2[1], -unit_vec_p1_p2[0]] + var getAngle = function (vector) { + // get angle (clockwise) between vector and (1, 0) + var x = vector[0] + var y = vector[1] + if (y >= 0) { + // note that y axis points to its down + return Math.acos(x) + } else { + return -Math.acos(x) + } + } + var startAngle = getAngle(unit_vec_origin_start_tangent) + var endAngle = getAngle(unit_vec_origin_end_tangent) + + // Connect the point (x0, y0) to the start tangent point by a straight line + this.lineTo( + x + unit_vec_origin_start_tangent[0] * radius, + y + unit_vec_origin_start_tangent[1] * radius + ) + + // Connect the start tangent point to the end tangent point by arc + // and adding the end tangent point to the subpath. + this.arc(x, y, radius, startAngle, endAngle) +} + +/** + * Sets the stroke property on the current element + */ +ctx.prototype.stroke = function () { + if (this.__currentElement.nodeName === 'path') { + this.__currentElement.setAttribute('paint-order', 'fill stroke markers') + } + this.__applyCurrentDefaultPath() + this.__applyStyleToCurrentElement('stroke') +} + +/** + * Sets fill properties on the current element + */ +ctx.prototype.fill = function () { + if (this.__currentElement.nodeName === 'path') { + this.__currentElement.setAttribute('paint-order', 'stroke fill markers') + } + this.__applyCurrentDefaultPath() + this.__applyStyleToCurrentElement('fill') +} + +/** + * Adds a rectangle to the path. + */ +ctx.prototype.rect = function (x, y, width, height) { + if (this.__currentElement.nodeName !== 'path') { + this.beginPath() + } + this.moveTo(x, y) + this.lineTo(x + width, y) + this.lineTo(x + width, y + height) + this.lineTo(x, y + height) + this.lineTo(x, y) + this.closePath() +} + +/** + * adds a rectangle element + */ +ctx.prototype.fillRect = function (x, y, width, height) { + var rect, parent + rect = this.__createElement( + 'rect', + { + x: x, + y: y, + width: width, + height: height, + }, + true + ) + parent = this.__closestGroupOrSvg() + parent.appendChild(rect) + this.__currentElement = rect + this.__applyStyleToCurrentElement('fill') +} + +/** + * Draws a rectangle with no fill + * @param x + * @param y + * @param width + * @param height + */ +ctx.prototype.strokeRect = function (x, y, width, height) { + var rect, parent + rect = this.__createElement( + 'rect', + { + x: x, + y: y, + width: width, + height: height, + }, + true + ) + parent = this.__closestGroupOrSvg() + parent.appendChild(rect) + this.__currentElement = rect + this.__applyStyleToCurrentElement('stroke') +} + +/** + * Clear entire canvas: + * 1. save current transforms + * 2. remove all the childNodes of the root g element + */ +ctx.prototype.__clearCanvas = function () { + var current = this.__closestGroupOrSvg(), + transform = current.getAttribute('transform') + var rootGroup = this.__root.childNodes[1] + var childNodes = rootGroup.childNodes + for (var i = childNodes.length - 1; i >= 0; i--) { + if (childNodes[i]) { + rootGroup.removeChild(childNodes[i]) + } + } + this.__currentElement = rootGroup + //reset __groupStack as all the child group nodes are all removed. + this.__groupStack = [] + if (transform) { + this.__addTransform(transform) + } +} + +/** + * "Clears" a canvas by just drawing a white rectangle in the current group. + */ +ctx.prototype.clearRect = function (x, y, width, height) { + //clear entire canvas + if (x === 0 && y === 0 && width === this.width && height === this.height) { + this.__clearCanvas() + return + } + var rect, + parent = this.__closestGroupOrSvg() + rect = this.__createElement( + 'rect', + { + x: x, + y: y, + width: width, + height: height, + fill: '#FFFFFF', + }, + true + ) + parent.appendChild(rect) +} + +/** + * Adds a linear gradient to a defs tag. + * Returns a canvas gradient object that has a reference to it's parent def + */ +ctx.prototype.createLinearGradient = function (x1, y1, x2, y2) { + var grad = this.__createElement( + 'linearGradient', + { + id: randomString(this.__ids), + x1: x1 + 'px', + x2: x2 + 'px', + y1: y1 + 'px', + y2: y2 + 'px', + gradientUnits: 'userSpaceOnUse', + }, + false + ) + this.__defs.appendChild(grad) + return new CanvasGradient(grad, this) +} + +/** + * Adds a radial gradient to a defs tag. + * Returns a canvas gradient object that has a reference to it's parent def + */ +ctx.prototype.createRadialGradient = function (x0, y0, r0, x1, y1, r1) { + var grad = this.__createElement( + 'radialGradient', + { + id: randomString(this.__ids), + cx: x1 + 'px', + cy: y1 + 'px', + r: r1 + 'px', + fx: x0 + 'px', + fy: y0 + 'px', + gradientUnits: 'userSpaceOnUse', + }, + false + ) + this.__defs.appendChild(grad) + return new CanvasGradient(grad, this) +} + +/** + * Parses the font string and returns svg mapping + * @private + */ +ctx.prototype.__parseFont = function () { + var regex = + /^\s*(?=(?:(?:[-a-z]+\s*){0,2}(italic|oblique))?)(?=(?:(?:[-a-z]+\s*){0,2}(small-caps))?)(?=(?:(?:[-a-z]+\s*){0,2}(bold(?:er)?|lighter|[1-9]00))?)(?:(?:normal|\1|\2|\3)\s*){0,3}((?:xx?-)?(?:small|large)|medium|smaller|larger|[.\d]+(?:\%|in|[cem]m|ex|p[ctx]))(?:\s*\/\s*(normal|[.\d]+(?:\%|in|[cem]m|ex|p[ctx])))?\s*([-,\'\"\sa-z0-9]+?)\s*$/i + var fontPart = regex.exec(this.font) + var data = { + style: fontPart[1] || 'normal', + size: fontPart[4] || '10px', + family: fontPart[6] || 'sans-serif', + weight: fontPart[3] || 'normal', + decoration: fontPart[2] || 'normal', + href: null, + } + + //canvas doesn't support underline natively, but we can pass this attribute + if (this.__fontUnderline === 'underline') { + data.decoration = 'underline' + } + + //canvas also doesn't support linking, but we can pass this as well + if (this.__fontHref) { + data.href = this.__fontHref + } + + return data +} + +/** + * Helper to link text fragments + * @param font + * @param element + * @return {*} + * @private + */ +ctx.prototype.__wrapTextLink = function (font, element) { + if (font.href) { + var a = this.__createElement('a') + a.setAttributeNS( + 'http://www.w3.org/1999/xlink', + 'xlink:href', + font.href + ) + a.appendChild(element) + return a + } + return element +} + +/** + * Fills or strokes text + * @param text + * @param x + * @param y + * @param action - stroke or fill + * @private + */ +ctx.prototype.__applyText = function (text, x, y, action) { + var font = this.__parseFont(), + parent = this.__closestGroupOrSvg(), + textElement = this.__createElement( + 'text', + { + 'font-family': font.family, + 'font-size': font.size, + 'font-style': font.style, + 'font-weight': font.weight, + 'text-decoration': font.decoration, + x: x, + y: y, + 'text-anchor': getTextAnchor(this.textAlign), + 'dominant-baseline': getDominantBaseline(this.textBaseline), + }, + true + ) + + textElement.appendChild(this.__document.createTextNode(text)) + this.__currentElement = textElement + this.__applyStyleToCurrentElement(action) + parent.appendChild(this.__wrapTextLink(font, textElement)) +} + +/** + * Creates a text element + * @param text + * @param x + * @param y + */ +ctx.prototype.fillText = function (text, x, y) { + this.__applyText(text, x, y, 'fill') +} + +/** + * Strokes text + * @param text + * @param x + * @param y + */ +ctx.prototype.strokeText = function (text, x, y) { + this.__applyText(text, x, y, 'stroke') +} + +/** + * No need to implement this for svg. + * @param text + * @return {TextMetrics} + */ +ctx.prototype.measureText = function (text) { + this.__ctx.font = this.font + return this.__ctx.measureText(text) +} + +/** + * Arc command! + */ +ctx.prototype.arc = function ( + x, + y, + radius, + startAngle, + endAngle, + counterClockwise +) { + // in canvas no circle is drawn if no angle is provided. + if (startAngle === endAngle) { + return + } + startAngle = startAngle % (2 * Math.PI) + endAngle = endAngle % (2 * Math.PI) + if (startAngle === endAngle) { + //circle time! subtract some of the angle so svg is happy (svg elliptical arc can't draw a full circle) + endAngle = + (endAngle + 2 * Math.PI - 0.001 * (counterClockwise ? -1 : 1)) % + (2 * Math.PI) + } + var endX = x + radius * Math.cos(endAngle), + endY = y + radius * Math.sin(endAngle), + startX = x + radius * Math.cos(startAngle), + startY = y + radius * Math.sin(startAngle), + sweepFlag = counterClockwise ? 0 : 1, + largeArcFlag = 0, + diff = endAngle - startAngle + + // https://github.com/gliffy/canvas2svg/issues/4 + if (diff < 0) { + diff += 2 * Math.PI + } + + if (counterClockwise) { + largeArcFlag = diff > Math.PI ? 0 : 1 + } else { + largeArcFlag = diff > Math.PI ? 1 : 0 + } + + this.lineTo(startX, startY) + this.__addPathCommand( + format( + 'A {rx} {ry} {xAxisRotation} {largeArcFlag} {sweepFlag} {endX} {endY}', + { + rx: radius, + ry: radius, + xAxisRotation: 0, + largeArcFlag: largeArcFlag, + sweepFlag: sweepFlag, + endX: endX, + endY: endY, + } + ) + ) + + this.__currentPosition = { x: endX, y: endY } +} + +/** + * Generates a ClipPath from the clip command. + */ +ctx.prototype.clip = function () { + var group = this.__closestGroupOrSvg(), + clipPath = this.__createElement('clipPath'), + id = randomString(this.__ids), + newGroup = this.__createElement('g') + + this.__applyCurrentDefaultPath() + group.removeChild(this.__currentElement) + clipPath.setAttribute('id', id) + clipPath.appendChild(this.__currentElement) + + this.__defs.appendChild(clipPath) + + //set the clip path to this group + group.setAttribute('clip-path', format('url(#{id})', { id: id })) + + //clip paths can be scaled and transformed, we need to add another wrapper group to avoid later transformations + // to this path + group.appendChild(newGroup) + + this.__currentElement = newGroup +} + +/** + * Draws a canvas, image or mock context to this canvas. + * Note that all svg dom manipulation uses node.childNodes rather than node.children for IE support. + * http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-drawimage + */ +ctx.prototype.drawImage = function () { + //convert arguments to a real array + var args = Array.prototype.slice.call(arguments), + image = args[0], + dx, + dy, + dw, + dh, + sx = 0, + sy = 0, + sw, + sh, + parent, + svg, + defs, + group, + currentElement, + svgImage, + canvas, + context, + id + + if (args.length === 3) { + dx = args[1] + dy = args[2] + sw = image.width + sh = image.height + dw = sw + dh = sh + } else if (args.length === 5) { + dx = args[1] + dy = args[2] + dw = args[3] + dh = args[4] + sw = image.width + sh = image.height + } else if (args.length === 9) { + sx = args[1] + sy = args[2] + sw = args[3] + sh = args[4] + dx = args[5] + dy = args[6] + dw = args[7] + dh = args[8] + } else { + throw new Error( + 'Inavlid number of arguments passed to drawImage: ' + + arguments.length + ) + } + + parent = this.__closestGroupOrSvg() + currentElement = this.__currentElement + var translateDirective = 'translate(' + dx + ', ' + dy + ')' + if (image instanceof ctx) { + //canvas2svg mock canvas context. In the future we may want to clone nodes instead. + //also I'm currently ignoring dw, dh, sw, sh, sx, sy for a mock context. + svg = image.getSvg().cloneNode(true) + if (svg.childNodes && svg.childNodes.length > 1) { + defs = svg.childNodes[0] + while (defs.childNodes.length) { + id = defs.childNodes[0].getAttribute('id') + this.__ids[id] = id + this.__defs.appendChild(defs.childNodes[0]) + } + group = svg.childNodes[1] + if (group) { + //save original transform + var originTransform = group.getAttribute('transform') + var transformDirective + if (originTransform) { + transformDirective = + originTransform + ' ' + translateDirective + } else { + transformDirective = translateDirective + } + group.setAttribute('transform', transformDirective) + parent.appendChild(group) + } + } + } else if (image.nodeName === 'CANVAS' || image.nodeName === 'IMG') { + //canvas or image + svgImage = this.__createElement('image') + svgImage.setAttribute('width', dw) + svgImage.setAttribute('height', dh) + svgImage.setAttribute('preserveAspectRatio', 'none') + + if (sx || sy || sw !== image.width || sh !== image.height) { + //crop the image using a temporary canvas + canvas = this.__document.createElement('canvas') + canvas.width = dw + canvas.height = dh + context = canvas.getContext('2d') + context.drawImage(image, sx, sy, sw, sh, 0, 0, dw, dh) + image = canvas + } + svgImage.setAttribute('transform', translateDirective) + svgImage.setAttributeNS( + 'http://www.w3.org/1999/xlink', + 'xlink:href', + image.nodeName === 'CANVAS' + ? image.toDataURL() + : image.getAttribute('src') + ) + parent.appendChild(svgImage) + } +} + +/** + * Generates a pattern tag + */ +ctx.prototype.createPattern = function (image, repetition) { + var pattern = this.__document.createElementNS( + 'http://www.w3.org/2000/svg', + 'pattern' + ), + id = randomString(this.__ids), + img + pattern.setAttribute('id', id) + pattern.setAttribute('width', image.width) + pattern.setAttribute('height', image.height) + if (image.nodeName === 'CANVAS' || image.nodeName === 'IMG') { + img = this.__document.createElementNS( + 'http://www.w3.org/2000/svg', + 'image' + ) + img.setAttribute('width', image.width) + img.setAttribute('height', image.height) + img.setAttributeNS( + 'http://www.w3.org/1999/xlink', + 'xlink:href', + image.nodeName === 'CANVAS' + ? image.toDataURL() + : image.getAttribute('src') + ) + pattern.appendChild(img) + this.__defs.appendChild(pattern) + } else if (image instanceof ctx) { + pattern.appendChild(image.__root.childNodes[1]) + this.__defs.appendChild(pattern) + } + return new CanvasPattern(pattern, this) +} + +ctx.prototype.setLineDash = function (dashArray) { + if (dashArray && dashArray.length > 0) { + this.lineDash = dashArray.join(',') + } else { + this.lineDash = null + } +} + +/** + * Not yet implemented + */ +ctx.prototype.drawFocusRing = function () {} +ctx.prototype.createImageData = function () {} +ctx.prototype.getImageData = function () {} +ctx.prototype.putImageData = function () {} +ctx.prototype.globalCompositeOperation = function () {} +ctx.prototype.setTransform = function () {} + +//add options for alternative namespace +// if (typeof window === "object") { +// window.C2S = ctx; +// } + +// CommonJS/Browserify +// if (typeof module === "object" && typeof module.exports === "object") { +// module.exports = ctx; +// } + +export default ctx diff --git a/v1/src/simulator/src/canvasApi.js b/v1/src/simulator/src/canvasApi.js new file mode 100644 index 00000000..08ee65d3 --- /dev/null +++ b/v1/src/simulator/src/canvasApi.js @@ -0,0 +1,624 @@ +/* eslint-disable no-param-reassign */ +import backgroundArea from './backgroundArea' +import simulationArea from './simulationArea' +import miniMapArea, { removeMiniMap, updatelastMinimapShown } from './minimap' +import { colors } from './themer/themer' + +var unit = 10 + +export function findDimensions(scope = globalScope) { + var totalObjects = 0 + simulationArea.minWidth = undefined + simulationArea.maxWidth = undefined + simulationArea.minHeight = undefined + simulationArea.maxHeight = undefined + for (var i = 0; i < updateOrder.length; i++) { + if (updateOrder[i] !== 'wires') { + for (var j = 0; j < scope[updateOrder[i]].length; j++) { + totalObjects += 1 + var obj = scope[updateOrder[i]][j] + if (totalObjects === 1) { + simulationArea.minWidth = obj.absX() + simulationArea.minHeight = obj.absY() + simulationArea.maxWidth = obj.absX() + simulationArea.maxHeight = obj.absY() + } + if (obj.objectType !== 'Node') { + if (obj.y - obj.upDimensionY < simulationArea.minHeight) { + simulationArea.minHeight = obj.y - obj.upDimensionY + } + if (obj.y + obj.downDimensionY > simulationArea.maxHeight) { + simulationArea.maxHeight = obj.y + obj.downDimensionY + } + if (obj.x - obj.leftDimensionX < simulationArea.minWidth) { + simulationArea.minWidth = obj.x - obj.leftDimensionX + } + if (obj.x + obj.rightDimensionX > simulationArea.maxWidth) { + simulationArea.maxWidth = obj.x + obj.rightDimensionX + } + } else { + if (obj.absY() < simulationArea.minHeight) { + simulationArea.minHeight = obj.absY() + } + if (obj.absY() > simulationArea.maxHeight) { + simulationArea.maxHeight = obj.absY() + } + if (obj.absX() < simulationArea.minWidth) { + simulationArea.minWidth = obj.absX() + } + if (obj.absX() > simulationArea.maxWidth) { + simulationArea.maxWidth = obj.absX() + } + } + } + } + } + simulationArea.objectList = updateOrder +} + +// Function used to change the zoom level wrt to a point +// fn to change scale (zoom) - It also shifts origin so that the position +// of the object in focus doesn't change +export function changeScale(delta, xx, yy, method = 1) { + // method = 3/2 - Zoom wrt center of screen + // method = 1 - Zoom wrt position of mouse + // Otherwise zoom wrt to selected object + + if (method === 3) { + xx = (width / 2 - globalScope.ox) / globalScope.scale + yy = (height / 2 - globalScope.oy) / globalScope.scale + } else if ( + xx === undefined || + yy === undefined || + xx === 'zoomButton' || + yy === 'zoomButton' + ) { + if ( + simulationArea.lastSelected && + simulationArea.lastSelected.objectType !== 'Wire' + ) { + // selected object + xx = simulationArea.lastSelected.x + yy = simulationArea.lastSelected.y + } else { + // mouse location + // eslint-disable-next-line no-lonely-if + if (method === 1) { + xx = simulationArea.mouseX + yy = simulationArea.mouseY + } else if (method === 2) { + xx = (width / 2 - globalScope.ox) / globalScope.scale + yy = (height / 2 - globalScope.oy) / globalScope.scale + } + } + } + + var oldScale = globalScope.scale + globalScope.scale = Math.max( + 0.5, + Math.min(4 * DPR, globalScope.scale + delta) + ) + globalScope.scale = Math.round(globalScope.scale * 10) / 10 + globalScope.ox -= Math.round(xx * (globalScope.scale - oldScale)) // Shift accordingly, so that we zoom wrt to the selected point + globalScope.oy -= Math.round(yy * (globalScope.scale - oldScale)) + // dots(true,false); + + // MiniMap + if (!embed && !lightMode) { + findDimensions(globalScope) + miniMapArea.setup() + $('#miniMap').show() + updatelastMinimapShown() + $('#miniMap').show() + setTimeout(removeMiniMap, 2000) + } +} +// fn to draw Dots on screen +// the function is called only when the zoom level or size of screen changes. +// Otherwise for normal panning, the canvas itself is moved to give the illusion of movement + +export function dots( + dots = true, + transparentBackground = false, + force = false +) { + const scale = unit * globalScope.scale + const ox = globalScope.ox % scale // offset + const oy = globalScope.oy % scale // offset + + const backgroundCtx = backgroundArea.context + if (!backgroundCtx) return + + const canvasWidth = backgroundArea.canvas.width // max X distance + const canvasHeight = backgroundArea.canvas.height // max Y distance + + backgroundArea.canvas.style.left = `${(ox - scale) / DPR}px` // adjust left position of canvas + backgroundArea.canvas.style.top = `${(oy - scale) / DPR}px` // adjust top position of canvas + + if (globalScope.scale === simulationArea.prevScale && !force) return + + simulationArea.prevScale = globalScope.scale // set the previous scale to current scale + + backgroundCtx.beginPath() + backgroundArea.clear() + + if (!transparentBackground) { + backgroundCtx.fillStyle = colors['canvas_fill'] + backgroundCtx.fillRect(0, 0, canvasWidth, canvasHeight) + } + + if (dots) { + backgroundCtx.fillStyle = colors['dot_fill'] + for (let i = 0; i < canvasWidth; i += scale) { + for (let j = 0; j < canvasHeight; j += scale) { + backgroundCtx.beginPath() + backgroundCtx.arc(i, j, scale / 10, 0, Math.PI * 2) + backgroundCtx.fill() + } + } + } + + backgroundCtx.strokeStyle = colors['canvas_stroke'] + backgroundCtx.lineWidth = 1 + + if (!embed) { + const correction = 0.5 * (backgroundCtx.lineWidth % 2) + for (let i = 0; i < canvasWidth; i += scale) { + backgroundCtx.moveTo(Math.round(i + correction) - correction, 0) + backgroundCtx.lineTo( + Math.round(i + correction) - correction, + canvasHeight + ) + } + for (let j = 0; j < canvasHeight; j += scale) { + backgroundCtx.moveTo(0, Math.round(j + correction) - correction) + backgroundCtx.lineTo( + canvasWidth, + Math.round(j + correction) - correction + ) + } + backgroundCtx.stroke() + } + + // Old Code + // function drawPixel(x, y, r, g, b, a) { + // var index = (x + y * canvasWidth) * 4; + // canvasData.data[index + 0] = r; + // canvasData.data[index + 1] = g; + // canvasData.data[index + 2] = b; + // canvasData.data[index + 3] = a; + // } + // if (dots) { + // var canvasData = ctx.getImageData(0, 0, canvasWidth, canvasHeight); + // + // + // + // for (var i = 0 + ox; i < canvasWidth; i += scale) + // for (var j = 0 + oy; j < canvasHeight; j += scale) + // drawPixel(i, j, 0, 0, 0, 255); + // ctx.putImageData(canvasData, 0, 0); + // } +} + +// Helper canvas API starts here +// All canvas functions are wrt to a center point (xx,yy), +// direction is used to abstract rotation of everything by a certain angle +// Possible values for direction = "RIGHT" (default), "LEFT", "UP", "DOWN" + +export function bezierCurveTo(x1, y1, x2, y2, x3, y3, xx, yy, dir) { + ;[x1, y1] = rotate(x1, y1, dir) + ;[x2, y2] = rotate(x2, y2, dir) + ;[x3, y3] = rotate(x3, y3, dir) + var { ox } = globalScope + var { oy } = globalScope + x1 *= globalScope.scale + y1 *= globalScope.scale + x2 *= globalScope.scale + y2 *= globalScope.scale + x3 *= globalScope.scale + y3 *= globalScope.scale + xx *= globalScope.scale + yy *= globalScope.scale + var ctx = simulationArea.context + ctx.bezierCurveTo( + Math.round(xx + ox + x1), + Math.round(yy + oy + y1), + Math.round(xx + ox + x2), + Math.round(yy + oy + y2), + Math.round(xx + ox + x3), + Math.round(yy + oy + y3) + ) +} + +export function moveTo(ctx, x1, y1, xx, yy, dir, bypass = false) { + var correction = 0.5 * (ctx.lineWidth % 2) + let newX + let newY + ;[newX, newY] = rotate(x1, y1, dir) + newX *= globalScope.scale + newY *= globalScope.scale + xx *= globalScope.scale + yy *= globalScope.scale + if (bypass) { + ctx.moveTo(xx + globalScope.ox + newX, yy + globalScope.oy + newY) + } else { + ctx.moveTo( + Math.round(xx + globalScope.ox + newX - correction) + correction, + Math.round(yy + globalScope.oy + newY - correction) + correction + ) + } +} + +export function lineTo(ctx, x1, y1, xx, yy, dir) { + let newX + let newY + + var correction = 0.5 * (ctx.lineWidth % 2) + ;[newX, newY] = rotate(x1, y1, dir) + newX *= globalScope.scale + newY *= globalScope.scale + xx *= globalScope.scale + yy *= globalScope.scale + ctx.lineTo( + Math.round(xx + globalScope.ox + newX - correction) + correction, + Math.round(yy + globalScope.oy + newY - correction) + correction + ) +} + +export function arc(ctx, sx, sy, radius, start, stop, xx, yy, dir) { + // ox-x of origin, xx- x of element , sx - shift in x from element + let Sx + let Sy + let newStart + let newStop + let counterClock + var correction = 0.5 * (ctx.lineWidth % 2) + ;[Sx, Sy] = rotate(sx, sy, dir) + Sx *= globalScope.scale + Sy *= globalScope.scale + xx *= globalScope.scale + yy *= globalScope.scale + radius *= globalScope.scale + ;[newStart, newStop, counterClock] = rotateAngle(start, stop, dir) + ctx.arc( + Math.round(xx + globalScope.ox + Sx + correction) - correction, + Math.round(yy + globalScope.oy + Sy + correction) - correction, + Math.round(radius), + newStart, + newStop, + counterClock + ) +} + +export function arc2(ctx, sx, sy, radius, start, stop, xx, yy, dir) { + // ox-x of origin, xx- x of element , sx - shift in x from element + let Sx + let Sy + let newStart + let newStop + let counterClock + var correction = 0.5 * (ctx.lineWidth % 2) + ;[Sx, Sy] = rotate(sx, sy, dir) + Sx *= globalScope.scale + Sy *= globalScope.scale + xx *= globalScope.scale + yy *= globalScope.scale + radius *= globalScope.scale + ;[newStart, newStop, counterClock] = rotateAngle(start, stop, dir) + var pi = 0 + if (counterClock) { + pi = Math.PI + } + ctx.arc( + Math.round(xx + globalScope.ox + Sx + correction) - correction, + Math.round(yy + globalScope.oy + Sy + correction) - correction, + Math.round(radius), + newStart + pi, + newStop + pi + ) +} + +export function drawCircle2(ctx, sx, sy, radius, xx, yy, dir) { + // ox-x of origin, xx- x of element , sx - shift in x from element + let Sx + let Sy + ;[Sx, Sy] = rotate(sx, sy, dir) + Sx *= globalScope.scale + Sy *= globalScope.scale + xx *= globalScope.scale + yy *= globalScope.scale + radius *= globalScope.scale + ctx.arc( + Math.round(xx + globalScope.ox + Sx), + Math.round(yy + globalScope.oy + Sy), + Math.round(radius), + 0, + 2 * Math.PI + ) +} + +export function rect(ctx, x1, y1, x2, y2) { + var correction = 0.5 * (ctx.lineWidth % 2) + x1 *= globalScope.scale + y1 *= globalScope.scale + x2 *= globalScope.scale + y2 *= globalScope.scale + ctx.rect( + Math.round(globalScope.ox + x1 - correction) + correction, + Math.round(globalScope.oy + y1 - correction) + correction, + Math.round(x2), + Math.round(y2) + ) +} + +export function drawImage(ctx, img, x1, y1, w_canvas, h_canvas) { + x1 *= globalScope.scale + y1 *= globalScope.scale + x1 += globalScope.ox + y1 += globalScope.oy + + w_canvas *= globalScope.scale + h_canvas *= globalScope.scale + ctx.drawImage(img, x1, y1, w_canvas, h_canvas) +} + +export function rect2(ctx, x1, y1, x2, y2, xx, yy, dir = 'RIGHT') { + var correction = 0.5 * (ctx.lineWidth % 2) + ;[x1, y1] = rotate(x1, y1, dir) + ;[x2, y2] = rotate(x2, y2, dir) + x1 *= globalScope.scale + y1 *= globalScope.scale + x2 *= globalScope.scale + y2 *= globalScope.scale + xx *= globalScope.scale + yy *= globalScope.scale + ctx.rect( + Math.round(globalScope.ox + xx + x1 - correction) + correction, + Math.round(globalScope.oy + yy + y1 - correction) + correction, + Math.round(x2), + Math.round(y2) + ) +} + +export function rotate(x1, y1, dir) { + if (dir === 'LEFT') { + return [-x1, y1] + } + if (dir === 'DOWN') { + return [y1, x1] + } + if (dir === 'UP') { + return [y1, -x1] + } + return [x1, y1] +} + +export function correctWidth(w) { + return Math.max(1, Math.round(w * globalScope.scale)) +} + +function rotateAngle(start, stop, dir) { + if (dir === 'LEFT') { + return [start, stop, true] + } + if (dir === 'DOWN') { + return [start - Math.PI / 2, stop - Math.PI / 2, true] + } + if (dir === 'UP') { + return [start - Math.PI / 2, stop - Math.PI / 2, false] + } + return [start, stop, false] +} + +export function drawLine(ctx, x1, y1, x2, y2, color, width) { + x1 *= globalScope.scale + y1 *= globalScope.scale + x2 *= globalScope.scale + y2 *= globalScope.scale + ctx.beginPath() + ctx.strokeStyle = color + ctx.lineCap = 'round' + ctx.lineWidth = correctWidth(width) //* globalScope.scale; + var correction = 0.5 * (ctx.lineWidth % 2) + var hCorrection = 0 + var vCorrection = 0 + if (y1 === y2) vCorrection = correction + if (x1 === x2) hCorrection = correction + ctx.moveTo( + Math.round(x1 + globalScope.ox + hCorrection) - hCorrection, + Math.round(y1 + globalScope.oy + vCorrection) - vCorrection + ) + ctx.lineTo( + Math.round(x2 + globalScope.ox + hCorrection) - hCorrection, + Math.round(y2 + globalScope.oy + vCorrection) - vCorrection + ) + ctx.stroke() +} + +// Checks if string color is a valid color using a hack +export function validColor(color) { + var $div = $('
') + $div.css('border', `1px solid ${color}`) + return $div.css('border-color') !== '' +} + +// Helper function to color "RED" to RGBA +export function colorToRGBA(color) { + var cvs + var ctx + cvs = document.createElement('canvas') + cvs.height = 1 + cvs.width = 1 + ctx = cvs.getContext('2d') + ctx.fillStyle = color + ctx.fillRect(0, 0, 1, 1) + return ctx.getImageData(0, 0, 1, 1).data +} + +export function drawCircle(ctx, x1, y1, r, color) { + x1 *= globalScope.scale + y1 *= globalScope.scale + ctx.beginPath() + ctx.fillStyle = color + ctx.arc( + Math.round(x1 + globalScope.ox), + Math.round(y1 + globalScope.oy), + Math.round(r * globalScope.scale), + 0, + Math.PI * 2, + false + ) + ctx.closePath() + ctx.fill() +} + +// To show message like values, node name etc +export function canvasMessage(ctx, str, x1, y1, fontSize = 10) { + if (!str || !str.length) return + + ctx.font = `${Math.round(fontSize * globalScope.scale)}px Raleway` + ctx.textAlign = 'center' + var width = ctx.measureText(str).width / globalScope.scale + 8 + var height = 13 + ctx.strokeStyle = 'black' + ctx.lineWidth = correctWidth(1) + ctx.fillStyle = 'yellow' + ctx.save() + ctx.beginPath() + rect(ctx, x1 - width / 2, y1 - height / 2 - 3, width, height) + ctx.shadowColor = '#999' + ctx.shadowBlur = 10 + ctx.shadowOffsetX = 3 + ctx.shadowOffsetY = 3 + ctx.stroke() + ctx.fill() + ctx.restore() + x1 *= globalScope.scale + y1 *= globalScope.scale + ctx.beginPath() + ctx.fillStyle = 'black' + ctx.fillText( + str, + Math.round(x1 + globalScope.ox), + Math.round(y1 + globalScope.oy) + ) + ctx.fill() +} + +export function fillText(ctx, str, x1, y1, fontSize = 20) { + x1 *= globalScope.scale + y1 *= globalScope.scale + ctx.font = `${Math.round(fontSize * globalScope.scale)}px Raleway` + ctx.fillText( + str, + Math.round(x1 + globalScope.ox), + Math.round(y1 + globalScope.oy) + ) +} + +export function fillText2(ctx, str, x1, y1, xx, yy, dir) { + var angle = { + RIGHT: 0, + LEFT: 0, + DOWN: Math.PI / 2, + UP: -Math.PI / 2, + } + x1 *= globalScope.scale + y1 *= globalScope.scale + ;[x1, y1] = rotate(x1, y1, dir) + xx *= globalScope.scale + yy *= globalScope.scale + + ctx.font = `${Math.round(14 * globalScope.scale)}px Raleway` + ctx.save() + ctx.translate( + Math.round(xx + x1 + globalScope.ox), + Math.round(yy + y1 + globalScope.oy) + ) + ctx.rotate(angle[dir]) + ctx.textAlign = 'center' + ctx.fillText( + str, + 0, + Math.round(4 * globalScope.scale) * (1 - 0 * +(dir === 'DOWN')) + ) + ctx.restore() +} + +export function fillText4( + ctx, + str, + x1, + y1, + xx, + yy, + dir, + fontSize = 14, + textAlign = 'center' +) { + var angle = { + RIGHT: 0, + LEFT: 0, + DOWN: Math.PI / 2, + UP: -Math.PI / 2, + } + x1 *= globalScope.scale + y1 *= globalScope.scale + ;[x1, y1] = rotate(x1, y1, dir) + xx *= globalScope.scale + yy *= globalScope.scale + + ctx.font = `${Math.round(fontSize * globalScope.scale)}px Raleway` + // ctx.font = 20+"px Raleway"; + ctx.textAlign = textAlign + ctx.fillText( + str, + xx + x1 + globalScope.ox, + yy + + y1 + + globalScope.oy + + Math.round((fontSize / 3) * globalScope.scale) + ) +} + +export function fillText3( + ctx, + str, + x1, + y1, + xx = 0, + yy = 0, + fontSize = 14, + font = 'Raleway', + textAlign = 'center' +) { + x1 *= globalScope.scale + y1 *= globalScope.scale + xx *= globalScope.scale + yy *= globalScope.scale + + ctx.font = `${Math.round(fontSize * globalScope.scale)}px ${font}` + ctx.textAlign = textAlign + ctx.fillText( + str, + Math.round(xx + x1 + globalScope.ox), + Math.round(yy + y1 + globalScope.oy) + ) +} + +export const oppositeDirection = { + RIGHT: 'LEFT', + LEFT: 'RIGHT', + DOWN: 'UP', + UP: 'DOWN', +} +export const fixDirection = { + right: 'LEFT', + left: 'RIGHT', + down: 'UP', + up: 'DOWN', + LEFT: 'LEFT', + RIGHT: 'RIGHT', + UP: 'UP', + DOWN: 'DOWN', +} diff --git a/v1/src/simulator/src/circuit.js b/v1/src/simulator/src/circuit.js new file mode 100644 index 00000000..f86429e3 --- /dev/null +++ b/v1/src/simulator/src/circuit.js @@ -0,0 +1,483 @@ +/* eslint-disable import/no-cycle */ +/* eslint-disable no-bitwise */ +/* eslint-disable guard-for-in */ +/* eslint-disable no-restricted-syntax */ +/* eslint-disable no-restricted-globals */ +/* eslint-disable consistent-return */ +/* eslint-disable func-names */ +/* eslint-disable array-callback-return */ +/* eslint-disable no-use-before-define */ +/* eslint-disable no-param-reassign */ +/* eslint-disable no-alert */ +import CircuitElement from './circuitElement' +import plotArea from './plotArea' +import simulationArea, { changeClockTime } from './simulationArea' +import { + stripTags, + uniq, + showMessage, + showError, + truncateString, +} from './utils' +import { findDimensions, dots } from './canvasApi' +import { updateRestrictedElementsList } from './restrictedElementDiv' +import { scheduleBackup } from './data/backupCircuit' +import { showProperties } from './ux' +import { + scheduleUpdate, + updateSimulationSet, + updateCanvasSet, + updateSubcircuitSet, + forceResetNodesSet, + changeLightMode, +} from './engine' +import { toggleLayoutMode, layoutModeGet } from './layoutMode' +import { setProjectName, getProjectName } from './data/save' +import { changeClockEnable } from './sequential' +import { changeInputSize } from './modules' +import { verilogModeGet, verilogModeSet } from './Verilog2CV' +import { updateTestbenchUI } from './testbench' +import { SimulatorStore } from '#/store/SimulatorStore/SimulatorStore' +import { toRef, toRefs } from 'vue' +import { provideCircuitName } from '#/components/helpers/promptComponent/PromptComponent.vue' +import { deleteCurrentCircuit } from '#/components/helpers/deleteCircuit/DeleteCircuit.vue' + +export const circuitProperty = { + toggleLayoutMode, + setProjectName, + changeCircuitName, + // changeClockTime, + deleteCurrentCircuit, + changeClockEnable, + changeInputSize, + changeLightMode, +} + +export var scopeList = {} +export function resetScopeList() { + const simulatorStore = SimulatorStore() + const { circuit_list } = toRefs(simulatorStore) + scopeList = {} + circuit_list.value = [] +} +/** + * Function used to change the current focusedCircuit + * Disables layoutMode if enabled + * Changes UI tab etc + * Sets flags to make updates, resets most of the things + * @param {string} id - identifier for circuit + * @category circuit + */ +export function switchCircuit(id) { + // TODO: fix tomorrow + const simulatorStore = SimulatorStore() + const { circuit_list } = toRefs(simulatorStore) + const { activeCircuit } = toRefs(simulatorStore) + + if (layoutModeGet()) { + toggleLayoutMode() + } + if (!scopeList[id].verilogMetadata.isVerilogCircuit) { + verilogModeSet(false) + } + + // globalScope.fixLayout(); + scheduleBackup() + if (id === globalScope.id) return + // $(`.circuits`).removeClass('current') + circuit_list.value.forEach((circuit) => + circuit.focussed ? (circuit.focussed = false) : null + ) + simulationArea.lastSelected = undefined + simulationArea.multipleObjectSelections = [] + simulationArea.copyList = [] + globalScope = scopeList[id] + if (globalScope.verilogMetadata.isVerilogCircuit) { + verilogModeSet(true) + } + if (globalScope.isVisible()) { + // $(`#${id}`).addClass('current') + const index = circuit_list.value.findIndex( + (circuit) => circuit.id == id + ) // TODO: add strict equality after typescript + circuit_list.value[index].focussed = true + activeCircuit.value.id = globalScope.id + activeCircuit.value.name = globalScope.name + } + updateSimulationSet(true) + updateSubcircuitSet(true) + forceResetNodesSet(true) + dots(false) + simulationArea.lastSelected = globalScope.root + if (!embed) { + showProperties(simulationArea.lastSelected) + updateTestbenchUI() + plotArea.reset() + } + updateCanvasSet(true) + scheduleUpdate() + + // to update the restricted elements information + updateRestrictedElementsList() +} + +export function getDependenciesList(scopeId) { + let scope = scopeList[scopeId] + if (scope == undefined) scope = scopeList[globalScope.id] + + let dependencies = '' + for (id in scopeList) { + if (id != scope.id && scopeList[id].checkDependency(scope.id)) { + if (dependencies === '') { + dependencies = scopeList[id].name + } else { + dependencies += `, ${scopeList[id].name}` + } + } + } + return dependencies +} + +// /** +// * Deletes the current circuit +// * Ensures that at least one circuit is there +// * Ensures that no circuit depends on the current circuit +// * Switched to a random circuit +// * @category circuit +// */ +// export function deleteCurrentCircuit(scopeId = globalScope.id) { +// const simulatorStore = SimulatorStore() +// const { circuit_list } = toRefs(simulatorStore) +// let scope = scopeList[scopeId] +// if (scope == undefined) scope = scopeList[globalScope.id] + +// if (scope.verilogMetadata.isVerilogCircuit) { +// scope.initialize() +// for (var id in scope.verilogMetadata.subCircuitScopeIds) +// delete scopeList[id] +// } +// // $(`#${scope.id}`).remove() +// const index = circuit_list.value.findIndex( +// (circuit) => circuit.id === scope.id +// ) +// circuit_list.value.splice(index, 1) +// delete scopeList[scope.id] +// if (scope.id == globalScope.id) { +// switchCircuit(Object.keys(scopeList)[0]) +// } +// showMessage('Circuit was successfully closed') +// } +/** + * Wrapper function around newCircuit to be called from + button on UI + */ +export async function createNewCircuitScope( + name, + id = undefined, + isVerilog = false, + isVerilogMain = false +) { + name = name ?? (await provideCircuitName()) + if (name instanceof Error) return // if user cancels the prompt + if (name.trim() == '') { + name = 'Untitled-Circuit' + } + simulationArea.lastSelected = undefined + newCircuit(name, id, isVerilog, isVerilogMain) + if (!embed) { + showProperties(simulationArea.lastSelected) + updateTestbenchUI() + plotArea.reset() + } + return true +} + +/** + * Function to create new circuit + * Function creates button in tab, creates scope and switches to this circuit + * @param {string} name - name of the new circuit + * @param {string} id - identifier for circuit + * @category circuit + */ +export function newCircuit(name, id, isVerilog = false, isVerilogMain = false) { + const simulatorStore = SimulatorStore() + const { circuit_list } = toRefs(simulatorStore) + const { activeCircuit } = toRefs(simulatorStore) + if (layoutModeGet()) { + toggleLayoutMode() + } + if (verilogModeGet()) { + verilogModeSet(false) + } + name = name || 'Untitled-Circuit' + name = stripTags(name) + if (!name) return + const scope = new Scope(name) + if (id) scope.id = id + scopeList[scope.id] = scope + let currCircuit = { + id: scope.id, + name: scope.name, // fix for tab name issue - vue - to be reviewed @devartstar + } + + circuit_list.value.push(currCircuit) + if (isVerilog) { + scope.verilogMetadata.isVerilogCircuit = true + // TODO: remove later if not required after fixing verilog code loading from saved file + circuit_list.value.forEach((circuit) => (circuit.isVerilog = false)) + circuit_list.value[circuit_list.value.length - 1].isVerilog = true + scope.verilogMetadata.isMainCircuit = isVerilogMain + } + globalScope = scope + // $('.circuits').removeClass('current') + circuit_list.value.forEach((circuit) => (circuit.focussed = false)) + circuit_list.value[circuit_list.value.length - 1].focussed = true + activeCircuit.value.id = scope.id + activeCircuit.value.name = scope.name + + if (!isVerilog || isVerilogMain) { + if (embed) { + // added calss - embed-tab using vue logic + // var html = `
${truncateString( + // name, + // 18 + // )}
` + // $('#tabsBar').append(html) + // $('#tabsBar').addClass('embed-tabs') + } else { + // logic implemented in vue + } + + // Remove listeners + //$('.circuits').off('click') + $('.circuitName').off('click') + //$('.tabsCloseButton').off('click') + + // switch circuit function moved inside vue component + + if (!embed) { + $('.circuitName').on('click', () => { + simulationArea.lastSelected = globalScope.root + setTimeout(() => { + // here link with the properties panel + document.getElementById('circname').select() + }, 100) + }) + } + // moved inside vue - component + // $('.tabsCloseButton').on('click', function (e) { + // e.stopPropagation() + // deleteCurrentCircuit(this.id) + // }) + + if (!embed) { + showProperties(scope.root) + } + dots(false) + } + return scope +} + +/** + * Used to change name of a circuit + * @param {string} name - new name + * @param {string} id - id of the circuit + * @category circuit + */ +export function changeCircuitName(name, id = globalScope.id) { + const simulatorStore = SimulatorStore() + const { circuit_list } = toRefs(simulatorStore) + // const { activeCircuit } = toRefs(simulatorStore) + name = name || 'Untitled' + name = stripTags(name) + scopeList[id].name = name + const index = circuit_list.value.findIndex((circuit) => circuit.id === id) + circuit_list.value[index].name = name + // activeCircuit.value.name = name // add later if necessary at current stage not important handled by projectProperty on switching circuit +} + +/** + * Class representing a Scope + * @class + * @param {string} name - name of the circuit + * @param {number=} id - a random id for the circuit + * @category circuit + */ +export default class Scope { + constructor(name = 'localScope', id = undefined) { + this.restrictedCircuitElementsUsed = [] + this.id = id || Math.floor(Math.random() * 100000000000 + 1) + this.CircuitElement = [] + this.name = name + + // root object for referring to main canvas - intermediate node uses this + this.root = new CircuitElement(0, 0, this, 'RIGHT', 1) + this.backups = [] + // maintaining a state (history) for redo function + this.history = [] + this.timeStamp = new Date().getTime() + this.verilogMetadata = { + isVerilogCircuit: false, + isMainCircuit: false, + code: '// Write Some Verilog Code Here!', + subCircuitScopeIds: [], + } + + this.ox = 0 + this.oy = 0 + this.scale = DPR + this.stack = [] + + this.initialize() + + // Setting default layout + this.layout = { + // default position + width: 100, + height: 40, + title_x: 50, + title_y: 13, + titleEnabled: true, + } + } + + isVisible() { + if (!this.verilogMetadata.isVerilogCircuit) return true + return this.verilogMetadata.isMainCircuit + } + + initialize() { + this.tunnelList = {} + this.pending = [] + this.nodes = [] // intermediate nodes only + this.allNodes = [] + this.wires = [] + + // Creating arrays for other module elements + for (let i = 0; i < moduleList.length; i++) { + this[moduleList[i]] = [] + } + } + + /** + * Resets all nodes recursively + */ + reset() { + for (let i = 0; i < this.allNodes.length; i++) { + this.allNodes[i].reset() + } + for (let i = 0; i < this.Splitter.length; i++) { + this.Splitter[i].reset() + } + for (let i = 0; i < this.SubCircuit.length; i++) { + this.SubCircuit[i].reset() + } + } + + /** + * Adds all inputs to simulationQueue + */ + addInputs() { + for (let i = 0; i < inputList.length; i++) { + for (var j = 0; j < this[inputList[i]].length; j++) { + simulationArea.simulationQueue.add(this[inputList[i]][j], 0) + } + } + + for (let i = 0; i < this.SubCircuit.length; i++) { + this.SubCircuit[i].addInputs() + } + } + + /** + * Ticks clocks recursively -- needs to be deprecated and synchronize all clocks with a global clock + */ + clockTick() { + for (let i = 0; i < this.Clock.length; i++) { + this.Clock[i].toggleState() + } // tick clock! + for (let i = 0; i < this.SubCircuit.length; i++) { + this.SubCircuit[i].localScope.clockTick() + } // tick clock! + } + + /** + * Checks if this circuit contains directly or indirectly scope with id + * Recursive nature + */ + checkDependency(id) { + if (id === this.id) return true + for (let i = 0; i < this.SubCircuit.length; i++) { + if (this.SubCircuit[i].id === id) return true + } + + for (let i = 0; i < this.SubCircuit.length; i++) { + if (scopeList[this.SubCircuit[i].id].checkDependency(id)) + return true + } + + return false + } + + /** + * Get dependency list - list of all circuits, this circuit depends on + */ + getDependencies() { + var list = [] + for (let i = 0; i < this.SubCircuit.length; i++) { + list.push(this.SubCircuit[i].id) + list.extend(scopeList[this.SubCircuit[i].id].getDependencies()) + } + return uniq(list) + } + + /** + * helper function to reduce layout size + */ + fixLayout() { + var maxY = 20 + for (let i = 0; i < this.Input.length; i++) { + maxY = Math.max(this.Input[i].layoutProperties.y, maxY) + } + for (let i = 0; i < this.Output.length; i++) { + maxY = Math.max(this.Output[i].layoutProperties.y, maxY) + } + if (maxY !== this.layout.height) { + this.layout.height = maxY + 10 + } + } + + /** + * Function which centers the circuit to the correct zoom level + */ + centerFocus(zoomIn = true) { + if (layoutModeGet()) return + findDimensions(this) + + var ytoolbarOffset = embed ? 0 : 60 * DPR // Some part ofcanvas is hidden behind the toolbar + + var minX = simulationArea.minWidth || 0 + var minY = simulationArea.minHeight || 0 + var maxX = simulationArea.maxWidth || 0 + var maxY = simulationArea.maxHeight || 0 + + var reqWidth = maxX - minX + 75 * DPR + var reqHeight = maxY - minY + 75 * DPR + + this.scale = Math.min( + width / reqWidth, + (height - ytoolbarOffset) / reqHeight + ) + + if (!zoomIn) { + this.scale = Math.min(this.scale, DPR) + } + this.scale = Math.max(this.scale, DPR / 10) + + this.ox = -minX * this.scale + (width - (maxX - minX) * this.scale) / 2 + this.oy = + -minY * this.scale + + (height - ytoolbarOffset - (maxY - minY) * this.scale) / 2 + } +} diff --git a/v1/src/simulator/src/circuitElement.js b/v1/src/simulator/src/circuitElement.js new file mode 100644 index 00000000..cd368c4d --- /dev/null +++ b/v1/src/simulator/src/circuitElement.js @@ -0,0 +1,1018 @@ +/* eslint-disable no-multi-assign */ +/* eslint-disable no-bitwise */ +import { scheduleUpdate } from './engine' +import simulationArea from './simulationArea' +import { + fixDirection, + fillText, + correctWidth, + rect2, + oppositeDirection, +} from './canvasApi' +import { colors } from './themer/themer' +import { layoutModeGet, tempBuffer } from './layoutMode' +import { fillSubcircuitElements } from './ux' +import { generateNodeName } from './verilogHelpers' + +/** + * Base class for circuit elements. + * @param {number} x - x coordinate of the element + * @param {number} y - y coordinate of the element + * @param {Scope} scope - The circuit on which circuit element is being drawn + * @param {string} dir - The direction of circuit element + * @param {number} bitWidth - the number of bits per node. + * @category circuitElement + */ +export default class CircuitElement { + constructor(x, y, scope, dir, bitWidth) { + // Data member initializations + this.x = x + this.y = y + this.hover = false + if (this.x === undefined || this.y === undefined) { + this.x = simulationArea.mouseX + this.y = simulationArea.mouseY + this.newElement = true + this.hover = true + } + this.deleteNodesWhenDeleted = true // FOR NOW - TO CHECK LATER + this.nodeList = [] + this.clicked = false + + this.oldx = x + this.oldy = y + + // The following attributes help in setting the touch area bound. They are the distances from the center. + // Note they are all positive distances from center. They will automatically be rotated when direction is changed. + // To stop the rotation when direction is changed, check overrideDirectionRotation attribute. + this.leftDimensionX = 10 + this.rightDimensionX = 10 + this.upDimensionY = 10 + this.downDimensionY = 10 + + this.label = '' + this.scope = scope + this.baseSetup() + + this.bitWidth = bitWidth || parseInt(prompt('Enter bitWidth'), 10) || 1 + this.direction = dir + this.directionFixed = false + this.labelDirectionFixed = false + this.labelDirection = oppositeDirection[dir] + this.orientationFixed = true + this.fixedBitWidth = false + + scheduleUpdate() + + this.queueProperties = { + inQueue: false, + time: undefined, + index: undefined, + } + + if (this.canShowInSubcircuit) { + this.subcircuitMetadata = { + showInSubcircuit: false, // if canShowInSubcircuit == true, showInSubcircuit determines wheter the user has added the element in the subcircuit + showLabelInSubcircuit: true, // determines whether the label of the element is to be showin the subcircuit + labelDirection: this.labelDirection, // determines the direction of the label of the element in the subcircuit + // coordinates of the element in the subcircuit relative to the subcircuit + x: 0, + y: 0, + } + } + } + + /** + * Function to flip bits + * @param {number} val - the value of flipped bits + * @returns {number} - The number of flipped bits + */ + flipBits(val) { + return ((~val >>> 0) << (32 - this.bitWidth)) >>> (32 - this.bitWidth) + } + + /** + * Function to get absolute value of x coordinate of the element + * @param {number} x - value of x coordinate of the element + * @return {number} - absolute value of x + */ + absX() { + return this.x + } + + /** + * Function to get absolute value of y coordinate of the element + * @param {number} y - value of y coordinate of the element + * @return {number} - absolute value of y + */ + absY() { + return this.y + } + + /** + * adds the element to scopeList + */ + baseSetup() { + this.scope[this.objectType].push(this) + } + + /** + * Function to copy the circuit element obj to a new circuit element + * @param {CircuitElement} obj - element to be copied from + */ + copyFrom(obj) { + var properties = ['label', 'labelDirection'] + for (let i = 0; i < properties.length; i++) { + if (obj[properties[i]] !== undefined) { + this[properties[i]] = obj[properties[i]] + } + } + } + + /** Methods to be Implemented for derivedClass + * saveObject(); //To generate JSON-safe data that can be loaded + * customDraw(); //This is to draw the custom design of the circuit(Optional) + * resolve(); // To execute digital logic(Optional) + * override isResolvable(); // custom logic for checking if module is ready + * override newDirection(dir) //To implement custom direction logic(Optional) + * newOrientation(dir) //To implement custom orientation logic(Optional) + */ + + // Method definitions + + /** + * Function to update the scope when a new element is added. + * @param {Scope} scope - the circuit in which we add element + */ + updateScope(scope) { + this.scope = scope + for (let i = 0; i < this.nodeList.length; i++) { + this.nodeList[i].scope = scope + } + } + + /** + * To generate JSON-safe data that can be loaded + * @memberof CircuitElement + * @return {JSON} - the data to be saved + */ + saveObject() { + var data = { + x: this.x, + y: this.y, + objectType: this.objectType, + label: this.label, + direction: this.direction, + labelDirection: this.labelDirection, + propagationDelay: this.propagationDelay, + customData: this.customSave(), + } + + if (this.canShowInSubcircuit) + data.subcircuitMetadata = this.subcircuitMetadata + return data + } + + /** + * Always overriden + * @memberof CircuitElement + * @return {JSON} - the data to be saved + */ + // eslint-disable-next-line class-methods-use-this + customSave() { + return { + values: {}, + nodes: {}, + constructorParamaters: [], + } + } + + /** + * check hover over the element + * @return {boolean} + */ + checkHover() { + if (simulationArea.mouseDown) return + for (let i = 0; i < this.nodeList.length; i++) { + this.nodeList[i].checkHover() + } + if (!simulationArea.mouseDown) { + if (simulationArea.hover === this) { + this.hover = this.isHover() + if (!this.hover) simulationArea.hover = undefined + } else if (!simulationArea.hover) { + this.hover = this.isHover() + if (this.hover) simulationArea.hover = this + } else { + this.hover = false + } + } + } + + /** + * This sets the width and height of the element if its rectangular + * and the reference point is at the center of the object. + * width and height define the X and Y distance from the center. + * Effectively HALF the actual width and height. + * NOT OVERRIDABLE + * @param {number} w - width + * @param {number} h - height + */ + setDimensions(width, height) { + this.leftDimensionX = this.rightDimensionX = width + this.downDimensionY = this.upDimensionY = height + } + + /** + * @memberof CircuitElement + * @param {number} w -width + */ + setWidth(width) { + this.leftDimensionX = this.rightDimensionX = width + } + + /** + * @param {number} h -height + */ + setHeight(height) { + this.downDimensionY = this.upDimensionY = height + } + + /** + * Helper Function to drag element to a new position + */ + startDragging() { + if (!layoutModeGet()) { + this.oldx = this.x + this.oldy = this.y + } else { + this.oldx = this.subcircuitMetadata.x + this.oldy = this.subcircuitMetadata.y + } + } + + /** + * Helper Function to drag element to a new position + * @memberof CircuitElement + */ + drag() { + if (!layoutModeGet()) { + this.x = + this.oldx + simulationArea.mouseX - simulationArea.mouseDownX + this.y = + this.oldy + simulationArea.mouseY - simulationArea.mouseDownY + } else { + this.subcircuitMetadata.x = + this.oldx + simulationArea.mouseX - simulationArea.mouseDownX + this.subcircuitMetadata.y = + this.oldy + simulationArea.mouseY - simulationArea.mouseDownY + } + } + + /** + * The update method is used to change the parameters of the object on mouse click and hover. + * Return Value: true if state has changed else false + * NOT OVERRIDABLE + */ + update() { + if (layoutModeGet()) { + return this.layoutUpdate() + } + let update = false + + update |= this.newElement + if (this.newElement) { + if (this.centerElement) { + this.x = + Math.round( + (simulationArea.mouseX - + (this.rightDimensionX - this.leftDimensionX) / 2) / + 10 + ) * 10 + this.y = + Math.round( + (simulationArea.mouseY - + (this.downDimensionY - this.upDimensionY) / 2) / + 10 + ) * 10 + } else { + this.x = simulationArea.mouseX + this.y = simulationArea.mouseY + } + + if (simulationArea.mouseDown) { + this.newElement = false + simulationArea.lastSelected = this + } else return update + } + + for (let i = 0; i < this.nodeList.length; i++) { + update |= this.nodeList[i].update() + } + + if (!simulationArea.hover || simulationArea.hover === this) { + this.hover = this.isHover() + } + + if (!simulationArea.mouseDown) this.hover = false + + if ((this.clicked || !simulationArea.hover) && this.isHover()) { + this.hover = true + simulationArea.hover = this + } else if ( + !simulationArea.mouseDown && + this.hover && + this.isHover() === false + ) { + if (this.hover) simulationArea.hover = undefined + this.hover = false + } + + if (simulationArea.mouseDown && this.clicked) { + this.drag() + if ( + !simulationArea.shiftDown && + simulationArea.multipleObjectSelections.contains(this) + ) { + for ( + let i = 0; + i < simulationArea.multipleObjectSelections.length; + i++ + ) { + simulationArea.multipleObjectSelections[i].drag() + } + } + + update |= true + } else if (simulationArea.mouseDown && !simulationArea.selected) { + this.startDragging() + if ( + !simulationArea.shiftDown && + simulationArea.multipleObjectSelections.contains(this) + ) { + for ( + let i = 0; + i < simulationArea.multipleObjectSelections.length; + i++ + ) { + simulationArea.multipleObjectSelections[i].startDragging() + } + } + simulationArea.selected = this.clicked = this.hover + + update |= this.clicked + } else { + if (this.clicked) simulationArea.selected = false + this.clicked = false + this.wasClicked = false + // If this is SubCircuit, then call releaseClick to recursively release clicks on each subcircuit object + if (this.objectType == 'SubCircuit') this.releaseClick() + } + + if (simulationArea.mouseDown && !this.wasClicked) { + if (this.clicked) { + this.wasClicked = true + if (this.click) this.click() + if (simulationArea.shiftDown) { + simulationArea.lastSelected = undefined + if ( + simulationArea.multipleObjectSelections.contains(this) + ) { + simulationArea.multipleObjectSelections.clean(this) + } else { + simulationArea.multipleObjectSelections.push(this) + } + } else { + simulationArea.lastSelected = this + } + } + } + + return update + } + + /** + * Used to update the state of the elements inside the subcircuit in layout mode + * Return Value: true if the state has changed, false otherwise + **/ + + layoutUpdate() { + var update = false + update |= this.newElement + if (this.newElement) { + this.subcircuitMetadata.x = simulationArea.mouseX + this.subcircuitMetadata.y = simulationArea.mouseY + + if (simulationArea.mouseDown) { + this.newElement = false + simulationArea.lastSelected = this + } else return + } + + if (!simulationArea.hover || simulationArea.hover == this) + this.hover = this.isHover() + + if ((this.clicked || !simulationArea.hover) && this.isHover()) { + this.hover = true + simulationArea.hover = this + } else if ( + !simulationArea.mouseDown && + this.hover && + this.isHover() == false + ) { + if (this.hover) simulationArea.hover = undefined + this.hover = false + } + + if (simulationArea.mouseDown && this.clicked) { + this.drag() + update |= true + } else if (simulationArea.mouseDown && !simulationArea.selected) { + this.startDragging() + simulationArea.selected = this.clicked = this.hover + update |= this.clicked + } else { + if (this.clicked) simulationArea.selected = false + this.clicked = false + this.wasClicked = false + } + + if (simulationArea.mouseDown && !this.wasClicked) { + if (this.clicked) { + this.wasClicked = true + simulationArea.lastSelected = this + } + } + + if (!this.clicked && !this.newElement) { + let x = this.subcircuitMetadata.x + let y = this.subcircuitMetadata.y + let yy = tempBuffer.layout.height + let xx = tempBuffer.layout.width + + let rX = this.layoutProperties.rightDimensionX + let lX = this.layoutProperties.leftDimensionX + let uY = this.layoutProperties.upDimensionY + let dY = this.layoutProperties.downDimensionY + + if (lX <= x && x + rX <= xx && y >= uY && y + dY <= yy) return + + this.subcircuitMetadata.showInSubcircuit = false + fillSubcircuitElements() + } + + return update + } + + /** + * Helper Function to correct the direction of element + */ + fixDirection() { + this.direction = fixDirection[this.direction] || this.direction + this.labelDirection = + fixDirection[this.labelDirection] || this.labelDirection + } + + /** + * The isHover method is used to check if the mouse is hovering over the object. + * Return Value: true if mouse is hovering over object else false + * NOT OVERRIDABLE + */ + isHover() { + var mX = simulationArea.mouseXf - this.x + var mY = this.y - simulationArea.mouseYf + + var rX = this.rightDimensionX + var lX = this.leftDimensionX + var uY = this.upDimensionY + var dY = this.downDimensionY + + if (layoutModeGet()) { + var mX = simulationArea.mouseXf - this.subcircuitMetadata.x + var mY = this.subcircuitMetadata.y - simulationArea.mouseYf + + var rX = this.layoutProperties.rightDimensionX + var lX = this.layoutProperties.leftDimensionX + var uY = this.layoutProperties.upDimensionY + var dY = this.layoutProperties.downDimensionY + } + + if (!this.directionFixed && !this.overrideDirectionRotation) { + if (this.direction === 'LEFT') { + lX = this.rightDimensionX + rX = this.leftDimensionX + } else if (this.direction === 'DOWN') { + lX = this.downDimensionY + rX = this.upDimensionY + uY = this.leftDimensionX + dY = this.rightDimensionX + } else if (this.direction === 'UP') { + lX = this.downDimensionY + rX = this.upDimensionY + dY = this.leftDimensionX + uY = this.rightDimensionX + } + } + + return -lX <= mX && mX <= rX && -dY <= mY && mY <= uY + } + + isSubcircuitHover(xoffset = 0, yoffset = 0) { + var mX = simulationArea.mouseXf - this.subcircuitMetadata.x - xoffset + var mY = yoffset + this.subcircuitMetadata.y - simulationArea.mouseYf + + var rX = this.layoutProperties.rightDimensionX + var lX = this.layoutProperties.leftDimensionX + var uY = this.layoutProperties.upDimensionY + var dY = this.layoutProperties.downDimensionY + + return -lX <= mX && mX <= rX && -dY <= mY && mY <= uY + } + + /** + * Helper Function to set label of an element. + * @memberof CircuitElement + * @param {string} label - the label for element + */ + setLabel(label) { + this.label = label || '' + } + + /** + * Method that draws the outline of the module and calls draw function on module Nodes. + * NOT OVERRIDABLE + */ + draw() { + // + var ctx = simulationArea.context + this.checkHover() + + if ( + this.x * this.scope.scale + this.scope.ox < + -this.rightDimensionX * this.scope.scale - 0 || + this.x * this.scope.scale + this.scope.ox > + width + this.leftDimensionX * this.scope.scale + 0 || + this.y * this.scope.scale + this.scope.oy < + -this.downDimensionY * this.scope.scale - 0 || + this.y * this.scope.scale + this.scope.oy > + height + 0 + this.upDimensionY * this.scope.scale + ) + return + + // Draws rectangle and highlights + if (this.rectangleObject) { + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.lineWidth = correctWidth(3) + ctx.beginPath() + rect2( + ctx, + -this.leftDimensionX, + -this.upDimensionY, + this.leftDimensionX + this.rightDimensionX, + this.upDimensionY + this.downDimensionY, + this.x, + this.y, + [this.direction, 'RIGHT'][+this.directionFixed] + ) + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + } + if (this.label !== '') { + var rX = this.rightDimensionX + var lX = this.leftDimensionX + var uY = this.upDimensionY + var dY = this.downDimensionY + if (!this.directionFixed) { + if (this.direction === 'LEFT') { + lX = this.rightDimensionX + rX = this.leftDimensionX + } else if (this.direction === 'DOWN') { + lX = this.downDimensionY + rX = this.upDimensionY + uY = this.leftDimensionX + dY = this.rightDimensionX + } else if (this.direction === 'UP') { + lX = this.downDimensionY + rX = this.upDimensionY + dY = this.leftDimensionX + uY = this.rightDimensionX + } + } + + if (this.labelDirection === 'LEFT') { + ctx.beginPath() + ctx.textAlign = 'right' + ctx.fillStyle = colors['text'] + fillText(ctx, this.label, this.x - lX - 10, this.y + 5, 14) + ctx.fill() + } else if (this.labelDirection === 'RIGHT') { + ctx.beginPath() + ctx.textAlign = 'left' + ctx.fillStyle = colors['text'] + fillText(ctx, this.label, this.x + rX + 10, this.y + 5, 14) + ctx.fill() + } else if (this.labelDirection === 'UP') { + ctx.beginPath() + ctx.textAlign = 'center' + ctx.fillStyle = colors['text'] + fillText(ctx, this.label, this.x, this.y + 5 - uY - 10, 14) + ctx.fill() + } else if (this.labelDirection === 'DOWN') { + ctx.beginPath() + ctx.textAlign = 'center' + ctx.fillStyle = colors['text'] + fillText(ctx, this.label, this.x, this.y + 5 + dY + 10, 14) + ctx.fill() + } + } + + // calls the custom circuit design + if (this.customDraw) { + this.customDraw() + } + + // draws nodes - Moved to renderCanvas + // for (let i = 0; i < this.nodeList.length; i++) + // this.nodeList[i].draw(); + } + + /** + Draws element in layout mode (inside the subcircuit) + @param {number} xOffset - x position of the subcircuit + @param {number} yOffset - y position of the subcircuit + + Called by subcirucit.js/customDraw() - for drawing as a part of another circuit + and layoutMode.js/renderLayout() - for drawing in layoutMode + **/ + drawLayoutMode(xOffset = 0, yOffset = 0) { + var ctx = simulationArea.context + if (layoutModeGet()) { + this.checkHover() + } + if ( + this.subcircuitMetadata.x * this.scope.scale + this.scope.ox < + -this.layoutProperties.rightDimensionX * this.scope.scale || + this.subcircuitMetadata.x * this.scope.scale + this.scope.ox > + width + + this.layoutProperties.leftDimensionX * this.scope.scale || + this.subcircuitMetadata.y * this.scope.scale + this.scope.oy < + -this.layoutProperties.downDimensionY * this.scope.scale || + this.subcircuitMetadata.y * this.scope.scale + this.scope.oy > + height + this.layoutProperties.upDimensionY * this.scope.scale + ) + return + + if (this.subcircuitMetadata.showLabelInSubcircuit) { + var rX = this.layoutProperties.rightDimensionX + var lX = this.layoutProperties.leftDimensionX + var uY = this.layoutProperties.upDimensionY + var dY = this.layoutProperties.downDimensionY + + // this.subcircuitMetadata.labelDirection + if (this.subcircuitMetadata.labelDirection == 'LEFT') { + ctx.beginPath() + ctx.textAlign = 'right' + ctx.fillStyle = 'black' + fillText( + ctx, + this.label, + this.subcircuitMetadata.x + xOffset - lX - 10, + this.subcircuitMetadata.y + yOffset + 5, + 10 + ) + ctx.fill() + } else if (this.subcircuitMetadata.labelDirection == 'RIGHT') { + ctx.beginPath() + ctx.textAlign = 'left' + ctx.fillStyle = 'black' + fillText( + ctx, + this.label, + this.subcircuitMetadata.x + xOffset + rX + 10, + this.subcircuitMetadata.y + yOffset + 5, + 10 + ) + ctx.fill() + } else if (this.subcircuitMetadata.labelDirection == 'UP') { + ctx.beginPath() + ctx.textAlign = 'center' + ctx.fillStyle = 'black' + fillText( + ctx, + this.label, + this.subcircuitMetadata.x + xOffset, + this.subcircuitMetadata.y + yOffset + 5 - uY - 10, + 10 + ) + ctx.fill() + } else if (this.subcircuitMetadata.labelDirection == 'DOWN') { + ctx.beginPath() + ctx.textAlign = 'center' + ctx.fillStyle = 'black' + fillText( + ctx, + this.label, + this.subcircuitMetadata.x + xOffset, + this.subcircuitMetadata.y + yOffset + 5 + dY + 10, + 10 + ) + ctx.fill() + } + } + // calls the subcircuitDraw function in the element to draw it to canvas + this.subcircuitDraw(xOffset, yOffset) + } + + // method to delete object + // OVERRIDE WITH CAUTION + delete() { + simulationArea.lastSelected = undefined + this.scope[this.objectType].clean(this) // CHECK IF THIS IS VALID + if (this.deleteNodesWhenDeleted) { + this.deleteNodes() + } else { + for (let i = 0; i < this.nodeList.length; i++) { + if (this.nodeList[i].connections.length) { + this.nodeList[i].converToIntermediate() + } else { + this.nodeList[i].delete() + } + } + } + this.deleted = true + } + + /** + * method to delete object + * OVERRIDE WITH CAUTION + * @memberof CircuitElement + */ + cleanDelete() { + this.deleteNodesWhenDeleted = true + this.delete() + } + + /** + * Helper Function to delete the element and all the node attached to it. + */ + deleteNodes() { + for (let i = 0; i < this.nodeList.length; i++) { + this.nodeList[i].delete() + } + } + + /** + * method to change direction + * OVERRIDE WITH CAUTION + * @param {string} dir - new direction + */ + newDirection(dir) { + if (this.direction === dir) return + // Leave this for now + if (this.directionFixed && this.orientationFixed) return + if (this.directionFixed) { + this.newOrientation(dir) + return // Should it return ? + } + + // if (obj.direction === undefined) return; + this.direction = dir + for (let i = 0; i < this.nodeList.length; i++) { + this.nodeList[i].refresh() + } + } + + /** + * Helper Function to change label direction of the element. + * @memberof CircuitElement + * @param {string} dir - new direction + */ + newLabelDirection(dir) { + if (layoutModeGet()) this.subcircuitMetadata.labelDirection = dir + else this.labelDirection = dir + } + + /** + * Method to check if object can be resolved + * OVERRIDE if necessary + * @return {boolean} + */ + isResolvable() { + if (this.alwaysResolve) return true + for (let i = 0; i < this.nodeList.length; i++) { + if ( + this.nodeList[i].type === 0 && + this.nodeList[i].value === undefined + ) + return false + } + return true + } + + /** + * Method to change object Bitwidth + * OVERRIDE if necessary + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + if (this.fixedBitWidth) return + if (this.bitWidth === undefined) return + if (this.bitWidth < 1) return + this.bitWidth = bitWidth + for (let i = 0; i < this.nodeList.length; i++) { + this.nodeList[i].bitWidth = bitWidth + } + } + + /** + * Method to change object delay + * OVERRIDE if necessary + * @param {number} delay - new delay + */ + changePropagationDelay(delay) { + if (this.propagationDelayFixed) return + if (delay === undefined) return + if (delay === '') return + var tmpDelay = parseInt(delay, 10) + if (tmpDelay < 0) return + this.propagationDelay = tmpDelay + } + + /** + * Dummy resolve function + * OVERRIDE if necessary + */ + resolve() {} + + /** + * Helper Function to process verilog + */ + processVerilog() { + // Output count used to sanitize output + var output_total = 0 + for (var i = 0; i < this.nodeList.length; i++) { + if ( + this.nodeList[i].type == NODE_OUTPUT && + this.nodeList[i].connections.length > 0 + ) + output_total++ + } + + var output_count = 0 + for (var i = 0; i < this.nodeList.length; i++) { + if (this.nodeList[i].type == NODE_OUTPUT) { + if ( + this.objectType != 'Input' && + this.objectType != 'Clock' && + this.nodeList[i].connections.length > 0 + ) { + this.nodeList[i].verilogLabel = generateNodeName( + this.nodeList[i], + output_count, + output_total + ) + + if ( + !this.scope.verilogWireList[ + this.nodeList[i].bitWidth + ].contains(this.nodeList[i].verilogLabel) + ) + this.scope.verilogWireList[ + this.nodeList[i].bitWidth + ].push(this.nodeList[i].verilogLabel) + output_count++ + } + this.scope.stack.push(this.nodeList[i]) + } + } + } + + /** + * Helper Function to check if verilog resolvable + * @return {boolean} + */ + isVerilogResolvable() { + var backupValues = [] + for (let i = 0; i < this.nodeList.length; i++) { + backupValues.push(this.nodeList[i].value) + this.nodeList[i].value = undefined + } + + for (let i = 0; i < this.nodeList.length; i++) { + if (this.nodeList[i].verilogLabel) { + this.nodeList[i].value = 1 + } + } + + var res = this.isResolvable() + + for (let i = 0; i < this.nodeList.length; i++) { + this.nodeList[i].value = backupValues[i] + } + + return res + } + + /** + * Helper Function to remove proporgation. + */ + removePropagation() { + for (let i = 0; i < this.nodeList.length; i++) { + if (this.nodeList[i].type === NODE_OUTPUT) { + if (this.nodeList[i].value !== undefined) { + this.nodeList[i].value = undefined + simulationArea.simulationQueue.add(this.nodeList[i]) + } + } + } + } + + /** + * Helper Function to name the verilog. + * @return {string} + */ + verilogName() { + return this.verilogType || this.objectType + } + + verilogBaseType() { + return this.verilogName() + } + + verilogParametrizedType() { + var type = this.verilogBaseType() + // Suffix bitwidth for multi-bit inputs + // Example: DflipFlop #(2) DflipFlop_0 + if (this.bitWidth != undefined && this.bitWidth > 1) + type += ' #(' + this.bitWidth + ')' + return type + } + + /** + * Helper Function to generate verilog + * @return {JSON} + */ + generateVerilog() { + // Example: and and_1(_out, _out, _Q[0]); + var inputs = [] + var outputs = [] + + for (var i = 0; i < this.nodeList.length; i++) { + if (this.nodeList[i].type == NODE_INPUT) { + inputs.push(this.nodeList[i]) + } else { + if (this.nodeList[i].connections.length > 0) + outputs.push(this.nodeList[i]) + else outputs.push('') // Don't create a wire + } + } + + var list = outputs.concat(inputs) + var res = this.verilogParametrizedType() + var moduleParams = list.map((x) => x.verilogLabel).join(', ') + res += ` ${this.verilogLabel}(${moduleParams});` + return res + } + + /** + * Toggles the visibility of the labels of subcircuit elements. Called by event handlers in ux.js + **/ + toggleLabelInLayoutMode() { + this.subcircuitMetadata.showLabelInSubcircuit = + !this.subcircuitMetadata.showLabelInSubcircuit + } +} + +CircuitElement.prototype.alwaysResolve = false +CircuitElement.prototype.propagationDelay = 10 +CircuitElement.prototype.tooltip = undefined +CircuitElement.prototype.propagationDelayFixed = false +CircuitElement.prototype.rectangleObject = true +CircuitElement.prototype.objectType = 'CircuitElement' +CircuitElement.prototype.canShowInSubcircuit = false // determines whether the element is supported to be shown inside a subcircuit +CircuitElement.prototype.subcircuitMetadata = {} // stores the coordinates and stuff for the elements in the subcircuit +CircuitElement.prototype.layoutProperties = { + rightDimensionX: 5, + leftDimensionX: 5, + upDimensionY: 5, + downDimensionY: 5, +} +CircuitElement.prototype.subcircuitMutableProperties = { + label: { + name: 'label: ', + type: 'text', + func: 'setLabel', + }, + 'show label': { + name: 'show label ', + type: 'checkbox', + func: 'toggleLabelInLayoutMode', + }, +} diff --git a/v1/src/simulator/src/combinationalAnalysis.js b/v1/src/simulator/src/combinationalAnalysis.js new file mode 100644 index 00000000..e45cb1b8 --- /dev/null +++ b/v1/src/simulator/src/combinationalAnalysis.js @@ -0,0 +1,667 @@ +/* eslint-disable import/no-cycle */ +/* eslint-disable guard-for-in */ +/* eslint-disable no-restricted-syntax */ +import Node from './node' +import { scheduleBackup } from './data/backupCircuit' +import BooleanMinimize from './quinMcCluskey' +import Input from './modules/Input' +import ConstantVal from './modules/ConstantVal' +import Output from './modules/Output' +import AndGate from './modules/AndGate' +import OrGate from './modules/OrGate' +import NotGate from './modules/NotGate' +import { stripTags } from './utils' +import simulationArea from './simulationArea' +import { findDimensions } from './canvasApi' +import { SimulatorStore } from '#/store/SimulatorStore/SimulatorStore' + +// var inputSample = 5 +// var dataSample = [ +// ['01---', '11110', '01---', '00000'], +// ['01110', '1-1-1', '----0'], +// ['01---', '11110', '01110', '1-1-1', '0---0'], +// ['----1'], +// ] + +// var sampleInputListNames = ['A', 'B'] +// var sampleOutputListNames = ['X'] + +/** + * The prompt for combinational analysis + * @param {Scope=} - the circuit in which we want combinational analysis + * @category combinationalAnalysis + */ +export function createCombinationalAnalysisPrompt(scope = globalScope) { + scheduleBackup() + SimulatorStore().dialogBox.combinationalanalysis_dialog = true + /* + $('#combinationalAnalysis').empty() + $('#combinationalAnalysis').append( + "

Enter Input names separated by commas:

" + ) + $('#combinationalAnalysis').append( + "

Enter Output names separated by commas:

" + ) + $('#combinationalAnalysis').append("

OR

") + $('#combinationalAnalysis').append( + "

Enter Boolean Function:

" + ) + $('#combinationalAnalysis').append( + "" + ) + $('#combinationalAnalysis').dialog({ + resizable: false, + width: 'auto', + buttons: [ + { + style: 'padding: 5px', + text: 'Next', + click() { + var inputList = stripTags($('#inputNameList').val()).split( + ',' + ) + var outputList = stripTags( + $('#outputNameList').val() + ).split(',') + var booleanExpression = $('#booleanExpression').val() + inputList = inputList.map((x) => x.trim()) + inputList = inputList.filter((e) => e) + outputList = outputList.map((x) => x.trim()) + outputList = outputList.filter((e) => e) + booleanExpression = booleanExpression.replace(/ /g, '') + booleanExpression = booleanExpression.toUpperCase() + var booleanInputVariables = [] + for (var i = 0; i < booleanExpression.length; i++) { + if ( + booleanExpression[i] >= 'A' && + booleanExpression[i] <= 'Z' + ) { + if ( + booleanExpression.indexOf( + booleanExpression[i] + ) == i + ) { + booleanInputVariables.push(booleanExpression[i]) + } + } + } + booleanInputVariables.sort() + if ( + inputList.length > 0 && + outputList.length > 0 && + booleanInputVariables.length == 0 + ) { + $(this).dialog('close') + createBooleanPrompt(inputList, outputList, null, scope) + } else if ( + booleanInputVariables.length > 0 && + inputList.length == 0 && + outputList.length == 0 + ) { + $(this).dialog('close') + var output = solveBooleanFunction( + booleanInputVariables, + booleanExpression + ) + if (output != null) { + createBooleanPrompt( + booleanInputVariables, + booleanExpression, + output, + scope + ) + } + } else if ( + (inputList.length == 0 || outputList.length == 0) && + booleanInputVariables == 0 + ) { + alert( + 'Enter Input / Output Variable(s) OR Boolean Function!' + ) + } else { + alert( + 'Use Either Combinational Analysis Or Boolean Function To Generate Circuit!' + ) + } + }, + }, + ], + }) + + $('#combinationalAnalysis').checkBo() + */ +} +// /** +// * This funciton hashes the output array and makes required JSON using +// * a BooleanMinimize class defined in Quin_Mcluskey.js var s which will +// * be output table is also initialied here +// * @param {Array} inputListNames - labels of input nodes +// * @param {Array} outputListNames - labels of output nodes +// * @param {Scope=} scope - h circuit +// * @category combinationalAnalysis +// */ +/* + function createBooleanPrompt( + inputListNames, + outputListNames, + output, + scope = globalScope + ) { + var inputListNames = + inputListNames || prompt('Enter inputs separated by commas').split(',') + var outputListNames = + outputListNames || + prompt('Enter outputs separated by commas').split(',') + var outputListNamesInteger = [] + if (output == null) { + for (var i = 0; i < outputListNames.length; i++) { + outputListNamesInteger[i] = 7 * i + 13 + } // assigning an integer to the value, 7*i + 13 is random + } else { + outputListNamesInteger = [13] + } + var s = '' + s += '' + s += '' + if ($('#decimalColumnBox').is(':checked')) { + s += '' + } + for (var i = 0; i < inputListNames.length; i++) { + s += `` + } + if (output == null) { + for (var i = 0; i < outputListNames.length; i++) { + s += `` + } + } else { + s += `` + } + s += '' + + var matrix = [] + for (var i = 0; i < inputListNames.length; i++) { + matrix[i] = new Array(1 << inputListNames.length) + } + + for (var i = 0; i < inputListNames.length; i++) { + for (var j = 0; j < 1 << inputListNames.length; j++) { + matrix[i][j] = +((j & (1 << (inputListNames.length - i - 1))) != 0) + } + } + + for (var j = 0; j < 1 << inputListNames.length; j++) { + s += '' + if ($('#decimalColumnBox').is(':checked')) { + s += `` + } + for (var i = 0; i < inputListNames.length; i++) { + s += `` + } + for (var i = 0; i < outputListNamesInteger.length; i++) { + if (output == null) { + s += + `' + // using hash values as they'll be used in the generateBooleanTableData function + } + } + if (output != null) { + s += + `' + } + s += '' + } + s += '' + s += '
' + 'dec' + '${inputListNames[i]}${outputListNames[i]}${outputListNames}
${j}${matrix[i][j]}` + + 'x' + + '` + + `${output[j]}` + + '
' + $('#combinationalAnalysis').empty() + $('#combinationalAnalysis').append(s) + $('#combinationalAnalysis').dialog({ + resizable: false, + width: 'auto', + buttons: [ + { + style: 'padding: 6px', + text: 'Generate Circuit', + click() { + $(this).dialog('close') + var data = generateBooleanTableData(outputListNamesInteger) + // passing the hash values to avoid spaces being passed which is causing a problem + var minimizedCircuit = [] + let inputCount = inputListNames.length + for (const output in data) { + let oneCount = data[output][1].length // Number of ones + let zeroCount = data[output][0].length // Number of zeroes + if (oneCount == 0) { + // Hardcode to 0 as output + minimizedCircuit.push([ + '-'.repeat(inputCount) + '0', + ]) + } else if (zeroCount == 0) { + // Hardcode to 1 as output + minimizedCircuit.push([ + '-'.repeat(inputCount) + '1', + ]) + } else { + // Perform KMap like minimzation + const temp = new BooleanMinimize( + inputListNames.length, + data[output][1].map(Number), + data[output].x.map(Number) + ) + minimizedCircuit.push(temp.result) + } + } + if (output == null) { + drawCombinationalAnalysis( + minimizedCircuit, + inputListNames, + outputListNames, + scope + ) + } else { + drawCombinationalAnalysis( + minimizedCircuit, + inputListNames, + [`${outputListNames}`], + scope + ) + } + }, + }, + { + style: 'padding: 6px', + text: 'Print Truth Table', + click() { + var sTable = document.getElementById( + 'combinationalAnalysis' + ).innerHTML + var style = + '' + var win = window.open('', '', 'height=700,width=700') + var htmlBody = ` + \ + Boolean Logic Table\ + ${style}\ + \ + \ +
${sTable}
\ + + ` + win.document.write(htmlBody) + win.document.close() + win.print() + }, + }, + ], + }) + + $('.output').on('click', function () { + var v = $(this).html() + if (v == 0) v = $(this).html(1) + else if (v == 1) v = $(this).html('x') + else if (v == 'x') v = $(this).html(0) + }) + } +*/ + +// function generateBooleanTableData(outputListNames) { +// var data = {} +// for (var i = 0; i < outputListNames.length; i++) { +// data[outputListNames[i]] = { +// x: [], +// 1: [], +// 0: [], +// } +// var rows = $(`.${outputListNames[i]}`) +// for (let j = 0; j < rows.length; j++) { +// data[outputListNames[i]][rows[j].innerHTML].push(rows[j].id) +// } +// } +// return data +// } + +// function drawCombinationalAnalysis( +// combinationalData, +// inputList, +// outputListNames, +// scope = globalScope +// ) { +// findDimensions(scope) +// var inputCount = inputList.length +// var maxTerms = 0 +// for (var i = 0; i < combinationalData.length; i++) { +// maxTerms = Math.max(maxTerms, combinationalData[i].length) +// } + +// var startPosX = 200 +// var startPosY = 200 + +// var currentPosY = 300 + +// if (simulationArea.maxWidth && simulationArea.maxHeight) { +// if (simulationArea.maxHeight + currentPosY > simulationArea.maxWidth) { +// startPosX += simulationArea.maxWidth +// } else { +// startPosY += simulationArea.maxHeight +// currentPosY += simulationArea.maxHeight +// } +// } +// var andPosX = startPosX + inputCount * 40 + 40 + 40 +// var orPosX = andPosX + Math.floor(maxTerms / 2) * 10 + 80 +// var outputPosX = orPosX + 60 +// var inputObjects = [] + +// var logixNodes = [] + +// // Appending constant input to the end of inputObjects +// for (var i = 0; i <= inputCount; i++) { +// if (i < inputCount) { +// // Regular Input +// inputObjects.push( +// new Input(startPosX + i * 40, startPosY, scope, 'DOWN', 1) +// ) +// inputObjects[i].setLabel(inputList[i]) +// } else { +// // Constant Input +// inputObjects.push( +// new ConstantVal( +// startPosX + i * 40, +// startPosY, +// scope, +// 'DOWN', +// 1, +// '1' +// ) +// ) +// inputObjects[i].setLabel('_C_') +// } + +// inputObjects[i].newLabelDirection('UP') +// var v1 = new Node(startPosX + i * 40, startPosY + 20, 2, scope.root) +// inputObjects[i].output1.connect(v1) +// var v2 = new Node( +// startPosX + i * 40 + 20, +// startPosY + 20, +// 2, +// scope.root +// ) +// v1.connect(v2) +// var notG = new NotGate( +// startPosX + i * 40 + 20, +// startPosY + 40, +// scope, +// 'DOWN', +// 1 +// ) +// notG.inp1.connect(v2) +// logixNodes.push(v1) +// logixNodes.push(notG.output1) +// } + +// function countTerm(s) { +// var c = 0 +// for (var i = 0; i < s.length; i++) { +// if (s[i] !== '-') c++ +// } +// return c +// } + +// for (var i = 0; i < combinationalData.length; i++) { +// var andGateNodes = [] +// for (var j = 0; j < combinationalData[i].length; j++) { +// var c = countTerm(combinationalData[i][j]) +// if (c > 1) { +// var andGate = new AndGate( +// andPosX, +// currentPosY, +// scope, +// 'RIGHT', +// c, +// 1 +// ) +// andGateNodes.push(andGate.output1) +// var misses = 0 +// for (var k = 0; k < combinationalData[i][j].length; k++) { +// if (combinationalData[i][j][k] == '-') { +// misses++ +// continue +// } +// var index = 2 * k + (combinationalData[i][j][k] == 0) +// var v = new Node( +// logixNodes[index].absX(), +// andGate.inp[k - misses].absY(), +// 2, +// scope.root +// ) +// logixNodes[index].connect(v) +// logixNodes[index] = v +// v.connect(andGate.inp[k - misses]) +// } +// } else { +// for (var k = 0; k < combinationalData[i][j].length; k++) { +// if (combinationalData[i][j][k] == '-') continue +// var index = 2 * k + (combinationalData[i][j][k] == 0) +// var andGateSubstituteNode = new Node( +// andPosX, +// currentPosY, +// 2, +// scope.root +// ) +// var v = new Node( +// logixNodes[index].absX(), +// andGateSubstituteNode.absY(), +// 2, +// scope.root +// ) +// logixNodes[index].connect(v) +// logixNodes[index] = v +// v.connect(andGateSubstituteNode) +// andGateNodes.push(andGateSubstituteNode) +// } +// } +// currentPosY += c * 10 + 30 +// } + +// var andGateCount = andGateNodes.length +// var midWay = Math.floor(andGateCount / 2) +// var orGatePosY = +// (andGateNodes[midWay].absY() + +// andGateNodes[Math.floor((andGateCount - 1) / 2)].absY()) / +// 2 +// if (orGatePosY % 10 == 5) { +// orGatePosY += 5 +// } // To make or gate fall in grid +// if (andGateCount > 1) { +// var o = new OrGate( +// orPosX, +// orGatePosY, +// scope, +// 'RIGHT', +// andGateCount, +// 1 +// ) +// if (andGateCount % 2 == 1) +// andGateNodes[midWay].connect(o.inp[midWay]) +// for (var j = 0; j < midWay; j++) { +// var v = new Node( +// andPosX + 30 + (midWay - j) * 10, +// andGateNodes[j].absY(), +// 2, +// scope.root +// ) +// v.connect(andGateNodes[j]) +// var v2 = new Node( +// andPosX + 30 + (midWay - j) * 10, +// o.inp[j].absY(), +// 2, +// scope.root +// ) +// v2.connect(v) +// o.inp[j].connect(v2) + +// var v = new Node( +// andPosX + 30 + (midWay - j) * 10, +// andGateNodes[andGateCount - j - 1].absY(), +// 2, +// scope.root +// ) +// v.connect(andGateNodes[andGateCount - j - 1]) +// var v2 = new Node( +// andPosX + 30 + (midWay - j) * 10, +// o.inp[andGateCount - j - 1].absY(), +// 2, +// scope.root +// ) +// v2.connect(v) +// o.inp[andGateCount - j - 1].connect(v2) +// } +// var out = new Output(outputPosX, o.y, scope, 'LEFT', 1) +// out.inp1.connect(o.output1) +// } else { +// var out = new Output( +// outputPosX, +// andGateNodes[0].absY(), +// scope, +// 'LEFT', +// 1 +// ) +// out.inp1.connect(andGateNodes[0]) +// } +// out.setLabel(outputListNames[i]) +// out.newLabelDirection('RIGHT') +// } +// for (var i = 0; i < logixNodes.length; i++) { +// if (logixNodes[i].absY() != currentPosY) { +// var v = new Node(logixNodes[i].absX(), currentPosY, 2, scope.root) +// logixNodes[i].connect(v) +// } +// } +// globalScope.centerFocus() +// } + +// /** +// * This function solves passed boolean expression and returns +// * output array which contains solution of the truth table +// * of given boolean expression +// * @param {Array} inputListNames - labels for input nodes +// * @param {String} booleanExpression - boolean expression which is to be solved +// */ +// function solveBooleanFunction(inputListNames, booleanExpression) { +// let i +// let j +// let output = [] + +// if ( +// booleanExpression.match( +// /[^ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01+'() ]/g +// ) != null +// ) { +// alert('One of the characters is not allowed.') +// return +// } + +// if (inputListNames.length > 8) { +// alert('You can only have 8 variables at a time.') +// return +// } + +// var s = '' +// s += '' +// s += '' +// if ($('#decimalColumnBox').is(':checked')) { +// s += '' +// } +// for (i = 0; i < inputListNames.length; i++) { +// s += `` +// } +// s += `` +// s += '' +// var matrix = [] +// for (i = 0; i < inputListNames.length; i++) { +// matrix[i] = new Array(inputListNames.length) +// } + +// for (i = 0; i < inputListNames.length; i++) { +// for (j = 0; j < 1 << inputListNames.length; j++) { +// matrix[i][j] = +((j & (1 << (inputListNames.length - i - 1))) != 0) +// } +// } +// // generate equivalent expression by replacing input vars with possible combinations of o and 1 +// for (i = 0; i < 2 ** inputListNames.length; i++) { +// const data = [] +// for (j = 0; j < inputListNames.length; j++) { +// data[j] = +// Math.floor(i / Math.pow(2, inputListNames.length - j - 1)) % 2 +// } +// let equation = booleanExpression +// for (j = 0; j < inputListNames.length; j++) { +// equation = equation.replace( +// new RegExp(inputListNames[j], 'g'), +// data[j] +// ) +// } + +// output[i] = solve(equation) +// } + +// for (j = 0; j < 1 << inputListNames.length; j++) { +// s += '' +// if ($('#decimalColumnBox').is(':checked')) { +// s += `` +// } +// for (i = 0; i < inputListNames.length; i++) { +// s += `` +// } + +// s += `' +// s += '' +// } + +// s += '' +// s += '
' + 'dec' + '${inputListNames[i]}${booleanExpression}
${j}${matrix[i][j]}` + `${output[j]}` + '
' +// // generates solution for the truth table of booleanexpression +// function solve(equation) { +// while (equation.indexOf('(') != -1) { +// const start = equation.lastIndexOf('(') +// const end = equation.indexOf(')', start) +// if (start != -1) { +// equation = +// equation.substring(0, start) + +// solve(equation.substring(start + 1, end)) + +// equation.substring(end + 1) +// } +// } +// equation = equation.replace(/''/g, '') +// equation = equation.replace(/0'/g, '1') +// equation = equation.replace(/1'/g, '0') +// for (let i = 0; i < equation.length - 1; i++) { +// if ( +// (equation[i] == '0' || equation[i] == '1') && +// (equation[i + 1] == '0' || equation[i + 1] == '1') +// ) { +// equation = +// equation.substring(0, i + 1) + +// '*' + +// equation.substring(i + 1, equation.length) +// } +// } +// try { +// const safeEval = eval +// const answer = safeEval(equation) +// if (answer == 0) { +// return 0 +// } +// if (answer > 0) { +// return 1 +// } +// return '' +// } catch (e) { +// return '' +// } +// } + +// return output +// } diff --git a/v1/src/simulator/src/data.js b/v1/src/simulator/src/data.js new file mode 100644 index 00000000..4e573c21 --- /dev/null +++ b/v1/src/simulator/src/data.js @@ -0,0 +1,62 @@ +import { fullView } from './ux' +import { createSubCircuitPrompt } from './subcircuit' +import save from './data/save' +import load from './data/load' +import createSaveAsImgPrompt from './data/saveImage' +import { + clearProject, + newProject, + saveOffline, + openOffline, + recoverProject, +} from './data/project' +import { newCircuit, createNewCircuitScope } from './circuit' +import { createCombinationalAnalysisPrompt } from './combinationalAnalysis' +import { colorThemes } from './themer/themer' +import { showTourGuide } from './tutorials' +import { + createVerilogCircuit, + // saveVerilogCode, + // resetVerilogCode, + // applyVerilogTheme, +} from './Verilog2CV' +import { generateVerilog } from './verilog' +import { bitConverterDialog } from './utils' +import { keyBinder } from '#/components/DialogBox/CustomShortcut.vue' +import { ExportProject } from '#/components/DialogBox/ExportProject.vue' +import { ImportProject } from '#/components/DialogBox/ImportProject.vue' + +const logixFunction = {} +logixFunction.save = save +logixFunction.load = load +logixFunction.createSaveAsImgPrompt = createSaveAsImgPrompt +logixFunction.clearProject = clearProject +logixFunction.newProject = newProject +logixFunction.saveOffline = saveOffline +// logixFunction.newCircuit = newCircuit +logixFunction.createOpenLocalPrompt = openOffline +logixFunction.recoverProject = recoverProject +logixFunction.createSubCircuitPrompt = createSubCircuitPrompt +logixFunction.createCombinationalAnalysisPrompt = + createCombinationalAnalysisPrompt +logixFunction.fullViewOption = fullView +logixFunction.colorThemes = colorThemes +logixFunction.showTourGuide = showTourGuideHelper +logixFunction.newVerilogModule = createVerilogCircuit +// logixFunction.saveVerilogCode = saveVerilogCode +// logixFunction.resetVerilogCode = resetVerilogCode +logixFunction.generateVerilog = generateVerilog +// logixFunction.applyVerilogTheme = applyVerilogTheme +logixFunction.bitconverter = bitConverterDialog +logixFunction.createNewCircuitScope = createNewCircuitScope +logixFunction.customShortcut = keyBinder +logixFunction.ExportProject = ExportProject +logixFunction.ImportProject = ImportProject +export default logixFunction + +// Hack to restart tour guide +function showTourGuideHelper() { + setTimeout(() => { + showTourGuide() + }, 100) +} diff --git a/v1/src/simulator/src/data/backupCircuit.js b/v1/src/simulator/src/data/backupCircuit.js new file mode 100644 index 00000000..c17a93c3 --- /dev/null +++ b/v1/src/simulator/src/data/backupCircuit.js @@ -0,0 +1,83 @@ +import { projectSavedSet } from './project' +/* eslint-disable no-param-reassign */ +function extract(obj) { + return obj.saveObject() +} + +// Check if there is anything to backup - to be deprecated +/** + * Check if backup is available + * @param {Scope} scope + * @return {boolean} + * @category data + */ +export function checkIfBackup(scope) { + for (let i = 0; i < updateOrder.length; i++) { + if (scope[updateOrder[i]].length) return true + } + return false +} + +export function backUp(scope = globalScope) { + // Disconnection of subcircuits are needed because these are the connections between nodes + // in current scope and those in the subcircuit's scope + for (let i = 0; i < scope.SubCircuit.length; i++) { + scope.SubCircuit[i].removeConnections() + } + + var data = {} + + // Storing layout + data.layout = scope.layout + + // Storing Verilog Properties + data.verilogMetadata = scope.verilogMetadata + + // Storing all nodes + data.allNodes = scope.allNodes.map(extract) + + // Storing test attached to scope + data.testbenchData = scope.testbenchData + + // Storing other details + data.id = scope.id + data.name = scope.name + + // Storing details of all module objects + for (let i = 0; i < moduleList.length; i++) { + if (scope[moduleList[i]].length) { + data[moduleList[i]] = scope[moduleList[i]].map(extract) + } + } + + // Adding restricted circuit elements used in the save data + data.restrictedCircuitElementsUsed = scope.restrictedCircuitElementsUsed + + // Storing intermediate nodes (nodes in wires) + data.nodes = [] + for (let i = 0; i < scope.nodes.length; i++) { + data.nodes.push(scope.allNodes.indexOf(scope.nodes[i])) + } + + // Restoring the connections + for (let i = 0; i < scope.SubCircuit.length; i++) { + scope.SubCircuit[i].makeConnections() + } + + return data +} + +export function scheduleBackup(scope = globalScope) { + var backup = JSON.stringify(backUp(scope)) + if ( + scope.backups.length === 0 || + scope.backups[scope.backups.length - 1] !== backup + ) { + scope.backups.push(backup) + scope.history = [] + scope.timeStamp = new Date().getTime() + projectSavedSet(false) + } + + return backup +} diff --git a/v1/src/simulator/src/data/load.js b/v1/src/simulator/src/data/load.js new file mode 100644 index 00000000..f9d7b89b --- /dev/null +++ b/v1/src/simulator/src/data/load.js @@ -0,0 +1,294 @@ +import { resetScopeList, newCircuit, switchCircuit } from '../circuit' +import { setProjectName } from './save' +import { + scheduleUpdate, + update, + updateSimulationSet, + updateCanvasSet, + gridUpdateSet, +} from '../engine' +import { updateRestrictedElementsInScope } from '../restrictedElementDiv' +import simulationArea from '../simulationArea' + +import { loadSubCircuit } from '../subcircuit' +import { scheduleBackup } from './backupCircuit' +import { showProperties } from '../ux' +import { constructNodeConnections, loadNode, replace } from '../node' +import { generateId } from '../utils' +import modules from '../modules' +import { oppositeDirection } from '../canvasApi' +import plotArea from '../plotArea' +import { updateTestbenchUI, TestbenchData } from '../testbench' +import { SimulatorStore } from '#/store/SimulatorStore/SimulatorStore' +import { toRefs } from 'vue' +/** + * Backward compatibility - needs to be deprecated + * @param {CircuitElement} obj - the object to be rectified + * @category data + */ +function rectifyObjectType(obj) { + const rectify = { + FlipFlop: 'DflipFlop', + Ram: 'Rom', + } + return rectify[obj] || obj +} + +/** + * Function to load CircuitElements + * @param {JSON} data - JSOn data + * @param {Scope} scope - circuit in which we want to load modules + * @category data + */ +function loadModule(data, scope) { + // Create circuit element + var obj = new modules[rectifyObjectType(data.objectType)]( + data.x, + data.y, + scope, + ...(data.customData.constructorParamaters || []) + ) + // Sets directions + obj.label = data.label + obj.labelDirection = + data.labelDirection || oppositeDirection[fixDirection[obj.direction]] + + // Sets delay + obj.propagationDelay = data.propagationDelay || obj.propagationDelay + obj.fixDirection() + + // Restore other values + if (data.customData.values) { + for (var prop in data.customData.values) { + obj[prop] = data.customData.values[prop] + } + } + + // Replace new nodes with the correct old nodes (with connections) + if (data.customData.nodes) { + for (const node in data.customData.nodes) { + const n = data.customData.nodes[node] + if (n instanceof Array) { + for (let i = 0; i < n.length; i++) { + obj[node][i] = replace(obj[node][i], n[i]) + } + } else { + obj[node] = replace(obj[node], n) + } + } + } + if (data.subcircuitMetadata) + obj.subcircuitMetadata = data['subcircuitMetadata'] +} + +/** + * This function shouldn't ideally exist. But temporary fix + * for some issues while loading nodes. + * @category data + */ +function removeBugNodes(scope = globalScope) { + let x = scope.allNodes.length + for (let i = 0; i < x; i++) { + if ( + scope.allNodes[i].type !== 2 && + scope.allNodes[i].parent.objectType === 'CircuitElement' + ) { + scope.allNodes[i].delete() + } + if (scope.allNodes.length !== x) { + i = 0 + x = scope.allNodes.length + } + } +} + +/** + * Function to load a full circuit + * @param {Scope} scope + * @param {JSON} data + * @category data + */ +export function loadScope(scope, data) { + const ML = moduleList.slice() // Module List copy + scope.restrictedCircuitElementsUsed = data.restrictedCircuitElementsUsed + + // Load all nodes + data.allNodes.map((x) => loadNode(x, scope)) + + // Make all connections + for (let i = 0; i < data.allNodes.length; i++) { + constructNodeConnections(scope.allNodes[i], data.allNodes[i]) + } + // Load all modules + for (let i = 0; i < ML.length; i++) { + if (data[ML[i]]) { + if (ML[i] === 'SubCircuit') { + // Load subcircuits differently + for (let j = 0; j < data[ML[i]].length; j++) { + loadSubCircuit(data[ML[i]][j], scope) + } + } else { + // Load everything else similarly + for (let j = 0; j < data[ML[i]].length; j++) { + loadModule(data[ML[i]][j], scope) + } + } + } + } + // Update wires according + scope.wires.map((x) => { + x.updateData(scope) + }) + removeBugNodes(scope) // To be deprecated + + // If Verilog Circuit Metadata exists, then restore + if (data.verilogMetadata) { + scope.verilogMetadata = data.verilogMetadata + } + + // If Test exists, then restore + if (data.testbenchData) { + globalScope.testbenchData = new TestbenchData( + data.testbenchData.testData, + data.testbenchData.currentGroup, + data.testbenchData.currentCase + ) + } + + // If layout exists, then restore + if (data.layout) { + scope.layout = data.layout + } else { + // Else generate new layout according to how it would have been otherwise (backward compatibility) + scope.layout = {} + scope.layout.width = 100 + scope.layout.height = + Math.max(scope.Input.length, scope.Output.length) * 20 + 20 + scope.layout.title_x = 50 + scope.layout.title_y = 13 + for (let i = 0; i < scope.Input.length; i++) { + scope.Input[i].layoutProperties = { + x: 0, + y: + scope.layout.height / 2 - + scope.Input.length * 10 + + 20 * i + + 10, + id: generateId(), + } + } + for (let i = 0; i < scope.Output.length; i++) { + scope.Output[i].layoutProperties = { + x: scope.layout.width, + y: + scope.layout.height / 2 - + scope.Output.length * 10 + + 20 * i + + 10, + id: generateId(), + } + } + } + // Backward compatibility + if (scope.layout.titleEnabled === undefined) { + scope.layout.titleEnabled = true + } +} + +// Function to load project from data +/** + * loads a saved project + * @param {JSON} data - the json data of the + * @category data + * @exports load + */ +export default function load(data) { + // If project is new and no data is there, then just set project name + const simulatorStore = SimulatorStore() + const { circuit_list } = toRefs(simulatorStore) + + if (!data) { + setProjectName(__projectName) + return + } + + var { projectId } = data + setProjectName(data.name) + + globalScope = undefined + resetScopeList() // Remove default scope + // $('.circuits').remove() // Delete default scope + + // Load all according to the dependency order + for (let i = 0; i < data.scopes.length; i++) { + var isVerilogCircuit = false + var isMainCircuit = false + if (data.scopes[i].verilogMetadata) { + isVerilogCircuit = data.scopes[i].verilogMetadata.isVerilogCircuit + isMainCircuit = data.scopes[i].verilogMetadata.isMainCircuit + } + // Create new circuit + const scope = newCircuit( + data.scopes[i].name || 'Untitled', + data.scopes[i].id, + isVerilogCircuit, + isMainCircuit + ) + + // Load circuit data + loadScope(scope, data.scopes[i]) + + // Focus circuit + globalScope = scope + + // Center circuit + if (embed) { + globalScope.centerFocus(true) + } else { + globalScope.centerFocus(false) + } + + // update and backup circuit once + update(globalScope, true) + + // Updating restricted element list initially on loading + updateRestrictedElementsInScope() + + scheduleBackup() + } + + // Restore clock + simulationArea.changeClockTime(data.timePeriod || 500) + simulationArea.clockEnabled = + data.clockEnabled === undefined ? true : data.clockEnabled + + if (!embed) { + showProperties(simulationArea.lastSelected) + } + + // Reorder tabs according to the saved order + if (data.orderedTabs) { + // var unorderedTabs = $('.circuits').detach() + // var plusButton = $('#tabsBar').children().detach() + // for (const tab of data.orderedTabs) { + // $('#tabsBar').append(unorderedTabs.filter(`#${tab}`)) + // } + // $('#tabsBar').append(plusButton) + circuit_list.value.sort((a, b) => { + return data.orderedTabs.indexOf(String(a.id)) - data.orderedTabs.indexOf(String(b.id)); + }) + } + + // Switch to last focussedCircuit + if (data.focussedCircuit) switchCircuit(String(data.focussedCircuit)) + + // Update the testbench UI + updateTestbenchUI() + + updateSimulationSet(true) + updateCanvasSet(true) + gridUpdateSet(true) + // Reset Timing + if (!embed) plotArea.reset() + scheduleUpdate(1) +} diff --git a/v1/src/simulator/src/data/project.js b/v1/src/simulator/src/data/project.js new file mode 100644 index 00000000..5a88fdd5 --- /dev/null +++ b/v1/src/simulator/src/data/project.js @@ -0,0 +1,175 @@ +/* eslint-disable guard-for-in */ +/* eslint-disable no-bitwise */ +/* eslint-disable import/no-cycle */ +/* eslint-disable no-restricted-globals */ +/* eslint-disable no-alert */ +import { resetScopeList, scopeList, newCircuit } from '../circuit' +import { showMessage, showError, generateId } from '../utils' +import { checkIfBackup } from './backupCircuit' +import { generateSaveData, getProjectName, setProjectName } from './save' +import load from './load' +import { SimulatorStore } from '#/store/SimulatorStore/SimulatorStore' +import { confirmOption } from '#/components/helpers/confirmComponent/ConfirmComponent.vue' + +/** + * Helper function to recover unsaved data + * @category data + */ +export async function recoverProject() { + if (localStorage.getItem('recover')) { + var data = JSON.parse(localStorage.getItem('recover')) + if (await confirmOption(`Would you like to recover: ${data.name}`)) { + load(data) + } + localStorage.removeItem('recover') + } else { + showError('No recover project found') + } +} + +/** + * Prompt to restore from localStorage + * @category data + */ +export function openOffline() { + const simulatorStore = SimulatorStore() + simulatorStore.dialogBox.open_project_dialog = true + /* + $('#openProjectDialog').empty() + const projectList = JSON.parse(localStorage.getItem('projectList')) + let flag = true + for (id in projectList) { + flag = false + $('#openProjectDialog').append( + `` + ) + } + if (flag) + $('#openProjectDialog').append( + '

Looks like no circuit has been saved yet. Create a new one and save it!

' + ) + $('#openProjectDialog').dialog({ + resizable: false, + width: 'auto', + buttons: !flag + ? [ + { + id: 'Open_offline_btn', + text: 'Open Project', + click() { + if (!$('input[name=projectId]:checked').val()) return + load( + JSON.parse( + localStorage.getItem( + $('input[name=projectId]:checked').val() + ) + ) + ) + window.projectId = $( + 'input[name=projectId]:checked' + ).val() + $(this).dialog('close') + }, + }, + ] + : [], + }) + */ +} +/** + * Flag for project saved or not + * @type {boolean} + * @category data + */ +var projectSaved = true +export function projectSavedSet(param) { + projectSaved = param +} + +/** + * Helper function to store to localStorage -- needs to be deprecated/removed + * @category data + */ +export async function saveOffline() { + const data = await generateSaveData() + if (data instanceof Error) return + localStorage.setItem(projectId, data) + const temp = JSON.parse(localStorage.getItem('projectList')) || {} + temp[projectId] = getProjectName() + localStorage.setItem('projectList', JSON.stringify(temp)) + showMessage( + `We have saved your project: ${getProjectName()} in your browser's localStorage` + ) +} + +/** + * Checks if any circuit has unsaved data + * @category data + */ +function checkToSave() { + let saveFlag = false + // eslint-disable-next-line no-restricted-syntax + for (id in scopeList) { + saveFlag |= checkIfBackup(scopeList[id]) + } + return saveFlag +} + +/** + * Prompt user to save data if unsaved + * @category data + */ +window.onbeforeunload = async function () { + if (projectSaved || embed) return + + if (!checkToSave()) return + + alert( + 'You have unsaved changes on this page. Do you want to leave this page and discard your changes or stay on this page?' + ) + // await confirmSingleOption( + // 'You have unsaved changes on this page. Do you want to leave this page and discard your changes or stay on this page?' + // ) + const data = await generateSaveData('Untitled') + localStorage.setItem('recover', await data) + // eslint-disable-next-line consistent-return + return 'Are u sure u want to leave? Any unsaved changes may not be recoverable' +} + +/** + * Function to clear project + * @category data + */ +export async function clearProject() { + if (await confirmOption('Would you like to clear the project?')) { + globalScope = undefined + resetScopeList() + // $('.circuits').remove() + newCircuit('main') + showMessage('Your project is as good as new!') + } +} + +/** + Function used to start a new project while prompting confirmation from the user + * @param {boolean} verify - flag to verify a new project + * @category data + */ +export async function newProject(verify) { + if ( + verify || + projectSaved || + !checkToSave() || + (await confirmOption( + 'What you like to start a new project? Any unsaved changes will be lost.' + )) + ) { + clearProject() + localStorage.removeItem('recover') + window.location = '/simulator' + + setProjectName(undefined) + projectId = generateId() + showMessage('New Project has been created!') + } +} diff --git a/v1/src/simulator/src/data/redo.js b/v1/src/simulator/src/data/redo.js new file mode 100644 index 00000000..bc252a98 --- /dev/null +++ b/v1/src/simulator/src/data/redo.js @@ -0,0 +1,47 @@ +/* eslint-disable import/no-cycle */ +/** + * Function to restore copy from backup + * @param {Scope=} scope - The circuit on which redo is called + * @category data + */ +import { layoutModeGet } from '../layoutMode' +import Scope, { scopeList } from '../circuit' +import { loadScope } from './load' +import { updateRestrictedElementsInScope } from '../restrictedElementDiv' +import { forceResetNodesSet } from '../engine' +/** + * Function called to generate a prompt to save an image + * @param {Scope=} - the circuit in which we want to call redo + * @category data + * @exports redo + */ +export default function redo(scope = globalScope) { + if (layoutModeGet()) return + if (scope.history.length === 0) return + const backupOx = globalScope.ox + const backupOy = globalScope.oy + const backupScale = globalScope.scale + globalScope.ox = 0 + globalScope.oy = 0 + const tempScope = new Scope(scope.name) + loading = true + const redoData = scope.history.pop() + scope.backups.push(redoData) + loadScope(tempScope, JSON.parse(redoData)) + tempScope.backups = scope.backups + tempScope.history = scope.history + tempScope.id = scope.id + tempScope.name = scope.name + tempScope.testbenchData = scope.testbenchData + scopeList[scope.id] = tempScope + globalScope = tempScope + globalScope.ox = backupOx + globalScope.oy = backupOy + globalScope.scale = backupScale + loading = false + forceResetNodesSet(true) + + // Updated restricted elements + updateRestrictedElementsInScope() +} +// for html file diff --git a/v1/src/simulator/src/data/save.js b/v1/src/simulator/src/data/save.js new file mode 100644 index 00000000..3ffb7788 --- /dev/null +++ b/v1/src/simulator/src/data/save.js @@ -0,0 +1,514 @@ +import { scopeList } from '../circuit' +import { resetup } from '../setup' +import { update } from '../engine' +import { stripTags, showMessage } from '../utils' +import { backUp } from './backupCircuit' +import simulationArea from '../simulationArea' +import backgroundArea from '../backgroundArea' +import { findDimensions } from '../canvasApi' +import { projectSavedSet } from './project' +import { colors } from '../themer/themer' +import { layoutModeGet, toggleLayoutMode } from '../layoutMode' +import { verilogModeGet } from '../Verilog2CV' +import domtoimage from 'dom-to-image' +import '../../vendor/canvas2svg' +import { useProjectStore } from '#/store/projectStore' +import { provideProjectName } from '#/components/helpers/promptComponent/PromptComponent.vue' +import { UpdateProjectDetail } from '#/components/helpers/createNewProject/UpdateProjectDetail.vue' +import { confirmOption } from '#/components/helpers/confirmComponent/ConfirmComponent.vue' +import { getToken } from '#/pages/simulatorHandler.vue' + +// var projectName = undefined + +/** + * Function to set the name of project. + * @param {string} name - name for project + * @category data + */ +export function setProjectName(name) { + const projectStore = useProjectStore() + if (name == undefined) { + // $('#projectName').html('Untitled') + return + } + name = stripTags(name) + // projectName = name + // $('#projectName').html(name) + projectStore.setProjectName(name) +} + +/** + * Function to set the name of project. + * @param {string} name - name for project + * @category data + */ +export function getProjectName() { + const projectStore = useProjectStore() + if (projectStore.getProjectNameDefined) + return projectStore.getProjectName.trim() + else return undefined +} +/** + * Helper function to save canvas as image based on image type + * @param {string} name -name of the circuit + * @param {string} imgType - image type ex: png,jpg etc. + * @category data + */ +function downloadAsImg(name, imgType) { + const gh = simulationArea.canvas.toDataURL(`image/${imgType}`) + const anchor = document.createElement('a') + anchor.href = gh + anchor.download = `${name}.${imgType}` + anchor.click() +} + +/** + * Returns the order of tabs in the project + */ +export function getTabsOrder() { + var tabs = document.getElementById('tabsBar').firstChild.children + var order = [] + for (let i = 0; i < tabs.length; i++) { + order.push(tabs[i].id) + } + return order +} + +/** + * Generates JSON of the entire project + * @param {string} name - the name of project + * @return {JSON} + * @category data + */ +export async function generateSaveData(name, setName = true) { + let data = {} + + // Prompts for name, defaults to Untitled + name = getProjectName() || name || (await provideProjectName()) + if (name instanceof Error) { + return new Error('cancel') + // throw 'save has been canceled' + } else if (name == '') { + name = 'Untitled' + } + data.name = stripTags(name) + if (setName) setProjectName(data.name) + + // Save project details + data.timePeriod = simulationArea.timePeriod + data.clockEnabled = simulationArea.clockEnabled + data.projectId = projectId + data.focussedCircuit = globalScope.id + data.orderedTabs = getTabsOrder() + + // Project Circuits, each scope is one circuit + data.scopes = [] + const dependencyList = {} + const completed = {} + // Getting list of dependencies for each circuit + for (id in scopeList) { + dependencyList[id] = scopeList[id].getDependencies() + } + + // Helper function to save Scope + // Recursively saves inner subcircuits first, before saving parent circuits + function saveScope(id) { + if (completed[id]) return + + for (let i = 0; i < dependencyList[id].length; i++) { + // Save inner subcircuits + saveScope(dependencyList[id][i]) + } + + completed[id] = true + update(scopeList[id], true) // For any pending integrity checks on subcircuits + data.scopes.push(backUp(scopeList[id])) + } + + // Save all circuits + for (let id in scopeList) { + saveScope(id) + } + + // convert to text + data = JSON.stringify(data) + return data +} + +// Helper function to download text +function download(filename, text) { + var pom = document.createElement('a') + pom.setAttribute( + 'href', + 'data:text/plain;charset=utf-8,' + encodeURIComponent(text) + ) + pom.setAttribute('download', filename) + + if (document.createEvent) { + var event = document.createEvent('MouseEvents') + event.initEvent('click', true, true) + pom.dispatchEvent(event) + } else { + pom.click() + } +} + +/** + * Function to generate image for the circuit + * @param {string} imgType - ex: png,jpg etc. + * @param {string} view - view type ex: full + * @param {boolean} transparent - tranparent bg or not + * @param {number} resolution - resolution of the image + * @param {boolean=} down - will download if true + * @category data + */ +export function generateImage( + imgType, + view, + transparent, + resolution, + down = true +) { + // Backup all data + const backUpOx = globalScope.ox + const backUpOy = globalScope.oy + const backUpWidth = width + const backUpHeight = height + const backUpScale = globalScope.scale + const backUpContextBackground = backgroundArea.context + const backUpContextSimulation = simulationArea.context + + backgroundArea.context = simulationArea.context + + globalScope.ox *= 1 / backUpScale + globalScope.oy *= 1 / backUpScale + + // If SVG, create SVG context - using canvas2svg here + if (imgType === 'svg') { + simulationArea.context = new C2S(width, height) + resolution = 1 + } else if (imgType !== 'png') { + transparent = false + } + + globalScope.scale = resolution + + const scope = globalScope + + // Focus circuit + var flag = 1 + if (flag) { + if (view === 'full') { + findDimensions() + const minX = simulationArea.minWidth + const minY = simulationArea.minHeight + const maxX = simulationArea.maxWidth + const maxY = simulationArea.maxHeight + width = (maxX - minX + 100) * resolution + height = (maxY - minY + 100) * resolution + + globalScope.ox = (-minX + 50) * resolution + globalScope.oy = (-minY + 50) * resolution + } else { + globalScope.ox *= resolution + globalScope.oy *= resolution + width = (width * resolution) / backUpScale + height = (height * resolution) / backUpScale + } + } + + globalScope.ox = Math.round(globalScope.ox) + globalScope.oy = Math.round(globalScope.oy) + + simulationArea.canvas.width = width + simulationArea.canvas.height = height + backgroundArea.canvas.width = width + backgroundArea.canvas.height = height + + backgroundArea.context = simulationArea.context + + simulationArea.clear() + + // Background + if (!transparent) { + simulationArea.context.fillStyle = colors['canvas_fill'] + simulationArea.context.rect(0, 0, width, height) + simulationArea.context.fill() + } + + // Draw circuits, why is it updateOrder and not renderOrder? + for (let i = 0; i < renderOrder.length; i++) { + for (let j = 0; j < scope[renderOrder[i]].length; j++) { + scope[renderOrder[i]][j].draw() + } + } + + let returnData + // If circuit is to be downloaded, download, other wise return dataURL + if (down) { + if (imgType === 'svg') { + const mySerializedSVG = simulationArea.context.getSerializedSvg() // true here, if you need to convert named to numbered entities. + download(`${globalScope.name}.svg`, mySerializedSVG) + } else { + downloadAsImg(globalScope.name, imgType) + } + } else { + returnData = simulationArea.canvas.toDataURL(`image/${imgType}`) + } + + // Restore everything + width = backUpWidth + height = backUpHeight + simulationArea.canvas.width = width + simulationArea.canvas.height = height + backgroundArea.canvas.width = width + backgroundArea.canvas.height = height + globalScope.scale = backUpScale + backgroundArea.context = backUpContextBackground + simulationArea.context = backUpContextSimulation + globalScope.ox = backUpOx + globalScope.oy = backUpOy + + resetup() + + if (!down) return returnData +} + +async function crop(dataURL, w, h) { + //get empty second canvas + var myCanvas = document.createElement('CANVAS') + myCanvas.width = w + myCanvas.height = h + var myContext = myCanvas.getContext('2d') + var myImage + var img = new Image() + return new Promise(function (resolved, rejected) { + img.src = dataURL + img.onload = () => { + myContext.drawImage(img, 0, 0, w, h, 0, 0, w, h) + myContext.save() + + //create a new data URL + myImage = myCanvas.toDataURL('image/jpeg') + resolved(myImage) + } + }) +} + +/** + * Function that is used to save image for display in the website + * @return {JSON} + * @category data + */ +async function generateImageForOnline() { + // Verilog Mode -> Different logic + // Fix aspect ratio to 1.6 + // Ensure image is approximately 700 x 440 + var ratio = 1.6 + if (verilogModeGet()) { + var node = document.getElementsByClassName('CodeMirror')[0] + // var node = document.getElementsByClassName('CodeMirror')[0]; + var prevHeight = $(node).css('height') + var prevWidth = $(node).css('width') + var baseWidth = 500 + var baseHeight = Math.round(baseWidth / ratio) + $(node).css('height', baseHeight) + $(node).css('width', baseWidth) + + var data = await domtoimage.toJpeg(node) + $(node).css('width', prevWidth) + $(node).css('height', prevHeight) + data = await crop(data, baseWidth, baseHeight) + return data + } + + simulationArea.lastSelected = undefined // Unselect any selections + + // Fix aspect ratio to 1.6 + if (width > height * ratio) { + height = width / ratio + } else { + width = height * 1.6 + } + + // Center circuits + globalScope.centerFocus() + + // Ensure image is approximately 700 x 440 + const resolution = Math.min( + 700 / (simulationArea.maxWidth - simulationArea.minWidth), + 440 / (simulationArea.maxHeight - simulationArea.minHeight) + ) + + data = generateImage('jpeg', 'current', false, resolution, false) + + // Restores Focus + globalScope.centerFocus(false) + return data +} +/** + * Function called when you save acircuit online + * @category data + * @exports save + */ +export default async function save() { + if (layoutModeGet()) toggleLayoutMode() + + projectSavedSet(true) + + const data = await generateSaveData() + if (data instanceof Error) return + $('.loadingIcon').fadeIn() + + const projectName = getProjectName() + var imageData = await generateImageForOnline() + + const headers = { + 'Content-Type': 'application/json', + 'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content'), + Authorization: `Token ${getToken('cvt')}`, + } + + if (!window.isUserLoggedIn) { + // user not signed in, save locally temporarily and force user to sign in + localStorage.setItem('recover_login', data) + // Asking user whether they want to login. + if ( + await confirmOption( + 'You have to login to save the project, you will be redirected to the login page.' + ) + ) + window.location.href = '/users/sign_in' + else $('.loadingIcon').fadeOut() + // eslint-disable-next-line camelcase + } else if ([0, undefined, null, '', '0'].includes(window.logixProjectId)) { + // Create new project - this part needs to be improved and optimised + // const form = $('', { + // action: '/api/v1/simulator/create', + // method: 'post', + // }) + // form.append( + // $('', { + // type: 'hidden', + // name: 'authenticity_token', + // value: $('meta[name="csrf-token"]').attr('content'), + // }) + // ) + // form.append( + // $('', { + // type: 'text', + // name: 'data', + // value: data, + // }) + // ) + // form.append( + // $('', { + // type: 'text', + // name: 'image', + // value: imageData, + // }) + // ) + // form.append( + // $('', { + // type: 'text', + // name: 'name', + // value: projectName, + // }) + // ) + // $('body').append(form) + // form.submit() + + fetch('/api/v1/projects', { + method: 'POST', + headers, + body: JSON.stringify({ + data, + image: imageData, + name: projectName, + }), + }) + .then((response) => { + if (response.ok) { + showMessage( + `We have Created a new project: ${projectName} in our servers.` + ) + $('.loadingIcon').fadeOut() + localStorage.removeItem('recover') + const responseJson = response.json() + responseJson.then((data) => { + UpdateProjectDetail(data) + }) + } + }) + .catch((error) => { + console.error('Error:', error) + }) + } else { + // updates project - this part needs to be improved and optimised + // $.ajax({ + // url: '/api/v1/simulator/update', + // type: 'PATCH', + // contentType: 'application/json', + // beforeSend(xhr) { + // xhr.setRequestHeader( + // 'X-CSRF-Token', + // $('meta[name="csrf-token"]').attr('content') + // ) + // }, + // data: JSON.stringify({ + // data, + // id: logixProjectId, + // image: imageData, + // name: projectName, + // }), + // success(response) { + // showMessage( + // `We have saved your project: ${projectName} in our servers.` + // ) + // $('.loadingIcon').fadeOut() + // localStorage.removeItem('recover') + // }, + // failure(err) { + // showMessage( + // "There was an error, we couldn't save to our servers" + // ) + // $('.loadingIcon').fadeOut() + // }, + // }) + // function getCookie(name) { + // const value = `; ${document.cookie}`; + // const parts = value.split(`; ${name}=`); + // if (parts.length === 2) return parts.pop().split(';').shift(); + // } + + fetch('/api/v1/projects/update_circuit', { + method: 'PATCH', + headers, + body: JSON.stringify({ + data, + id: window.logixProjectId, + image: imageData, + name: projectName, + }), + }) + .then((response) => { + if (response.ok) { + showMessage( + `We have saved your project: ${projectName} in our servers.` + ) + localStorage.removeItem('recover') + } else { + showMessage( + "There was an error, we couldn't save to our servers" + ) + } + $('.loadingIcon').fadeOut() + }) + .catch((error) => { + console.error('Error:', error) + }) + } + + // Restore everything + resetup() +} diff --git a/v1/src/simulator/src/data/saveImage.js b/v1/src/simulator/src/data/saveImage.js new file mode 100644 index 00000000..8e2b813d --- /dev/null +++ b/v1/src/simulator/src/data/saveImage.js @@ -0,0 +1,18 @@ +/** + * Helper function to show prompt to save image + * Options - resolution, image type, view + * @param {Scope=} scope - useless though + * @category data + */ +import { SimulatorStore } from '#/store/SimulatorStore/SimulatorStore' + +/** + * Function called to generate a prompt to save an image + * @category data + * @param {Scope=} - circuit whose image we want + * @exports createSaveAsImgPrompt + */ +export default function createSaveAsImgPrompt(scope = globalScope) { + const simulatorStore = SimulatorStore() + simulatorStore.dialogBox.saveimage_dialog = true +} diff --git a/v1/src/simulator/src/data/undo.js b/v1/src/simulator/src/data/undo.js new file mode 100644 index 00000000..67f22005 --- /dev/null +++ b/v1/src/simulator/src/data/undo.js @@ -0,0 +1,51 @@ +/* eslint-disable import/no-cycle */ +/** + * Function to restore copy from backup + * @param {Scope=} scope - The circuit on which undo is called + * @category data + */ +import { layoutModeGet } from '../layoutMode' +import Scope, { scopeList } from '../circuit' +import { loadScope } from './load' +import { updateRestrictedElementsInScope } from '../restrictedElementDiv' +import { forceResetNodesSet } from '../engine' +/** + * Function called to generate a prompt to save an image + * @param {Scope=} - the circuit in which we want to call undo + * @category data + * @exports undo + */ +export default function undo(scope = globalScope) { + if (layoutModeGet()) return + if (scope.backups.length < 2) return + const backupOx = globalScope.ox + const backupOy = globalScope.oy + const backupScale = globalScope.scale + globalScope.ox = 0 + globalScope.oy = 0 + const tempScope = new Scope(scope.name) + loading = true + const undoData = scope.backups.pop() + scope.history.push(undoData) + scope.backups.length !== 0 && + loadScope( + tempScope, + JSON.parse(scope.backups[scope.backups.length - 1]) + ) + tempScope.backups = scope.backups + tempScope.history = scope.history + tempScope.id = scope.id + tempScope.name = scope.name + tempScope.testbenchData = scope.testbenchData + scopeList[scope.id] = tempScope + globalScope = tempScope + globalScope.ox = backupOx + globalScope.oy = backupOy + globalScope.scale = backupScale + loading = false + forceResetNodesSet(true) + + // Updated restricted elements + updateRestrictedElementsInScope() +} +// for html file diff --git a/v1/src/simulator/src/doc/Circuit2Verilog documentation.md b/v1/src/simulator/src/doc/Circuit2Verilog documentation.md new file mode 100644 index 00000000..42d6b89d --- /dev/null +++ b/v1/src/simulator/src/doc/Circuit2Verilog documentation.md @@ -0,0 +1,70 @@ +## Circuit2Verilog Module + +**Primary Contributors:** + +1. James H - J Yeh, Ph.D. +2. Satvik Ramaprasad + +## Introduction + +This is an experimental module that generates Verilog netlist (structural +Verilog) given the circuit. Currently, the module generates fully functional +Verilog code for basic circuits. For a complex circuit, additional (manual) work +may need to be done in order to make it work. We are continuously improving this +module to work with more and more complex circuits. + +# Algorithm + +The basic algorithm is fairly straightforward. We have the circuit graph in +memory. We just need to convert this graph into Verilog netlist. It is done by +performing a DFS on the circuit graph. The DFS involves the following steps + +1. Creating Verilog wires as and when required +2. Connecting Verilog wires in element instantiations + +## Some background information + +The different sub-circuits form a DAG (Directed Acyclic Graph) or dependency +graph. Each sub-circuit itself (called scope internally) is actually a (cyclic) +graph on its own. Therefore the Verilog generation is done in a 2 step DFS +approach. The first DFS is performed on the dependency graph. The second DFS is +done on an individual sub-circuit (scope). + +## Code/Algorithm workflow + +1. `exportVerilog()` - entry point +2. `exportVerilogScope()` - DFS(1) on Sub Circuits Dependency Graph + 1. Set Verilog Labels for all elements + 2. `generateHeader()` - Generates Module Header + 3. `generateOutputList()` - Output Output List + 4. `generateInputList()` - Generates Input List + 5. `processGraph()` - DFS(2) on individual subcircuit/scope + 1. DFS starts from inputs + 2. Calls `processVerilog()` on all circuit elements (graph nodes) - resolves label names and adds neighbors to DFS stack. + 3. Calls `generateVerilog()` on all circuit elements to get the final Verilog. + 6. Generate Wire initializations + +## Functions + +**Verilog Module Functions:** + +1. `verilog.exportVerilog()` - Entry point +1. `verilog.exportVerilogScope()` - Recursive DFS function on subcircuit graph +1. `verilog.processGraph()` - Iterative DFS function on subcircuit scope +1. `verilog.resetLabels()` - Resets labels in scope +1. `verilog.setLabels()` - Sets labels in scope +1. `verilog.generateHeader()` - Generates Verilog Module Header +1. `verilog.generateInputList()` - Generates Verilog Module Input List +1. `verilog.generateOutputList()` - Generates Verilog Module Output List +1. `sanitizeLabel()` - Sanitizes label for node/wire +1. `verilog.generateNodeName()` - Helper function to resolve node/wire name + +**CircuitElement Functions:** + +These functions can be overridden by derived classes. + +1. `CircuitElement.prototype.processVerilog()` - Graph algorithm to resolve Verilog wire labels +1. `CircuitElement.prototype.verilogName()` - Generate Verilog name +1. `CircuitElement.prototype.generateVerilog()` - Generate final Verilog code +1. `CircuitElement.prototype.verilogType` - Verilog type name +1. `CircuitElement.moduleVerilog` - Custom module Verilog for elements diff --git a/v1/src/simulator/src/doc/images/CircuitVerse Timing Diagram Size Spec.svg b/v1/src/simulator/src/doc/images/CircuitVerse Timing Diagram Size Spec.svg new file mode 100644 index 00000000..d92c7cda --- /dev/null +++ b/v1/src/simulator/src/doc/images/CircuitVerse Timing Diagram Size Spec.svg @@ -0,0 +1,3 @@ + + +
TimeLine
TimeLi...
timeLineHeight
time...
padding
padd...
padding
padd...
padding
padd...
padding
p...
flagLabelWidth
flagLabelWidth
plotHeight
plot...
plotHeight
plot...
cycleWidth
cycleW...
cycleWidth
cycleWidth
waveFormHeight
wave...
waveFormPadding
wave...
waveFormPadding
wave...
plotHeight
plot...
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/v1/src/simulator/src/drag.ts b/v1/src/simulator/src/drag.ts new file mode 100644 index 00000000..2e14abca --- /dev/null +++ b/v1/src/simulator/src/drag.ts @@ -0,0 +1,87 @@ +import interact from 'interactjs' + +interface Position { + x: number + y: number +} + +function updatePosition( + element: HTMLElement, + dx: number, + dy: number, + positions: WeakMap +): void { + if (!element) return // Check if the element is valid + + // If the element does not exist in the positions WeakMap, create it + if (!positions.has(element)) { + positions.set(element, { x: 0, y: 0 }) + } + + // Update the element's x and y position + const currentPosition = positions.get(element) + if (!currentPosition) return // Check if the currentPosition is valid + currentPosition.x += dx + currentPosition.y += dy + + // Apply the new position to the element using the CSS transform property + element.style.transform = `translate(${currentPosition.x}px, ${currentPosition.y}px)` +} + +function disableSelection(element: HTMLElement): void { + element.setAttribute('unselectable', 'on') + element.style.userSelect = 'none' + element.style.webkitUserSelect = 'none' + element.style.MozUserSelect = 'none' + element.style.msUserSelect = 'none' + element.style.OUserSelect = 'none' + element.onselectstart = () => false +} + +/** + * Make an element draggable within a specified container. + * @param {HTMLElement} targetEl - Element that triggers the drag event. + * @param {HTMLElement} DragEl - Element to be dragged. + */ +export function dragging(targetEl: HTMLElement, DragEl: HTMLElement): void { + // WeakMap to store the position of each dragged element + const positions = new WeakMap() + + // Initialize the interact.js library with the draggable element selector + interact(DragEl).draggable({ + // Specify the element that triggers the drag event + allowFrom: targetEl, + // Set up event listeners for the draggable element + listeners: { + // Update the element's position when the move event is triggered + move(event) { + updatePosition( + event.target as HTMLElement, + event.dx, + event.dy, + positions + ) + }, + }, + // Set up modifiers to apply constraints to the draggable element + modifiers: [ + interact.modifiers.restrictRect({ + // Restrict the draggable element within its parent container + restriction: 'body', + }), + ], + }) + + $(DragEl).on('mousedown', () => { + $(`.draggable-panel:not(${DragEl})`).css('z-index', '99') + $(DragEl).css('z-index', '99') + }) + + let panelElements = document.querySelectorAll( + '.elementPanel, .layoutElementPanel, #moduleProperty, #layoutDialog, #verilogEditorPanel, .timing-diagram-panel, .testbench-manual-panel, .quick-btn' + ) + + panelElements.forEach((element) => { + disableSelection(element as HTMLElement) + }) +} diff --git a/v1/src/simulator/src/embed.js b/v1/src/simulator/src/embed.js new file mode 100644 index 00000000..b17e82ba --- /dev/null +++ b/v1/src/simulator/src/embed.js @@ -0,0 +1,132 @@ +// /* eslint-disable import/no-cycle */ +// // Helper functions for when circuit is embedded +// import { scopeList, circuitProperty } from './circuit' +// import simulationArea from './simulationArea' +// import { +// scheduleUpdate, +// wireToBeCheckedSet, +// updateCanvasSet, +// gridUpdateSet, +// } from './engine' +// import { prevPropertyObjGet, prevPropertyObjSet } from './ux' +// import { ZoomIn, ZoomOut } from './listeners' + +// // circuitProperty.toggleFullScreen = toggleFullScreen; +// $(document).ready(() => { +// // Clock features +// $('#clockProperty').append( +// " " +// ) +// $('#clockProperty').append( +// `
Time:
` +// ) +// $('#clockProperty').append( +// `
Clock:
` +// ) + +// // Following codes need to be removed +// $('.objectPropertyAttributeEmbed').on( +// 'change keyup paste click', +// function () { +// scheduleUpdate() +// updateCanvasSet(true) +// wireToBeCheckedSet(1) +// if ( +// simulationArea.lastSelected && +// simulationArea.lastSelected[this.name] +// ) { +// prevPropertyObjSet( +// simulationArea.lastSelected[this.name](this.value) +// ) || prevPropertyObjGet() +// } else { +// circuitProperty[this.name](this.value) +// } +// } +// ) + +// // Following codes need to be removed +// $('.objectPropertyAttributeEmbedChecked').on( +// 'change keyup paste click', +// function () { +// scheduleUpdate() +// updateCanvasSet(true) +// wireToBeCheckedSet(1) +// if ( +// simulationArea.lastSelected && +// simulationArea.lastSelected[this.name] +// ) { +// prevPropertyObjSet( +// simulationArea.lastSelected[this.name](this.value) +// ) || prevPropertyObjGet() +// } else { +// circuitProperty[this.name](this.checked) +// } +// } +// ) + +// $('#zoom-in-embed').on('click', () => ZoomIn()) + +// $('#zoom-out-embed').on('click', () => ZoomOut()) +// }) + +// // Full screen toggle helper function +// function toggleFullScreen(value) { +// if (!getfullscreenelement()) { +// GoInFullscreen(document.documentElement) +// } else { +// GoOutFullscreen() +// } +// } +// // Center focus accordingly +// function exitHandler() { +// setTimeout(() => { +// Object.keys(scopeList).forEach((id) => { +// scopeList[id].centerFocus(true) +// }) +// gridUpdateSet(true) +// scheduleUpdate() +// }, 100) +// } + +// function GoInFullscreen(element) { +// if (element.requestFullscreen) { +// element.requestFullscreen() +// } else if (element.mozRequestFullScreen) { +// element.mozRequestFullScreen() +// } else if (element.webkitRequestFullscreen) { +// element.webkitRequestFullscreen() +// } else if (element.msRequestFullscreen) { +// element.msRequestFullscreen() +// } +// } + +// function GoOutFullscreen() { +// if (document.exitFullscreen) { +// document.exitFullscreen() +// } else if (document.mozCancelFullScreen) { +// document.mozCancelFullScreen() +// } else if (document.webkitExitFullscreen) { +// document.webkitExitFullscreen() +// } else if (document.msExitFullscreen) { +// document.msExitFullscreen() +// } +// } + +// function getfullscreenelement() { +// return ( +// document.fullscreenElement || +// document.webkitFullscreenElement || +// document.mozFullScreenElement || +// document.msFullscreenElement +// ) +// } + +// // Full screen Listeners +// if (document.addEventListener) { +// document.addEventListener('webkitfullscreenchange', exitHandler, false) +// document.addEventListener('mozfullscreenchange', exitHandler, false) +// document.addEventListener('fullscreenchange', exitHandler, false) +// document.addEventListener('MSFullscreenChange', exitHandler, false) +// } diff --git a/v1/src/simulator/src/embedListeners.js b/v1/src/simulator/src/embedListeners.js new file mode 100644 index 00000000..e7cb2d0a --- /dev/null +++ b/v1/src/simulator/src/embedListeners.js @@ -0,0 +1,259 @@ +/* eslint-disable import/no-cycle */ +// Listeners when circuit is embedded +// Refer listeners.js +import simulationArea from './simulationArea' +import { + scheduleUpdate, + update, + updateSelectionsAndPane, + wireToBeCheckedSet, + updatePositionSet, + updateSimulationSet, + updateCanvasSet, + gridUpdateSet, + errorDetectedSet, +} from './engine' +import { changeScale } from './canvasApi' +import { copy, paste } from './events' +import { ZoomIn, ZoomOut } from './listeners' + +var unit = 10 + +export default function startListeners() { + window.addEventListener('keyup', (e) => { + scheduleUpdate(1) + if (e.keyCode == 16) { + simulationArea.shiftDown = false + } + if (e.key == 'Meta' || e.key == 'Control') { + simulationArea.controlDown = false + } + }) + + document + .getElementById('simulationArea') + .addEventListener('mousedown', (e) => { + errorDetectedSet(false) + updateSimulationSet(true) + updatePositionSet(true) + updateCanvasSet(true) + + simulationArea.lastSelected = undefined + simulationArea.selected = false + simulationArea.hover = undefined + var rect = simulationArea.canvas.getBoundingClientRect() + simulationArea.mouseDownRawX = (e.clientX - rect.left) * DPR + simulationArea.mouseDownRawY = (e.clientY - rect.top) * DPR + simulationArea.mouseDownX = + Math.round( + (simulationArea.mouseDownRawX - globalScope.ox) / + globalScope.scale / + unit + ) * unit + simulationArea.mouseDownY = + Math.round( + (simulationArea.mouseDownRawY - globalScope.oy) / + globalScope.scale / + unit + ) * unit + simulationArea.mouseDown = true + simulationArea.oldx = globalScope.ox + simulationArea.oldy = globalScope.oy + + e.preventDefault() + scheduleUpdate(1) + }) + + document + .getElementById('simulationArea') + .addEventListener('mousemove', () => { + var ele = document.getElementById('elementName') + if (globalScope && simulationArea && simulationArea.objectList) { + var { objectList } = simulationArea + objectList = objectList.filter((val) => val !== 'wires') + + for (var i = 0; i < objectList.length; i++) { + for ( + var j = 0; + j < globalScope[objectList[i]].length; + j++ + ) { + if (globalScope[objectList[i]][j].isHover()) { + ele.style.display = 'block' + if (objectList[i] === 'SubCircuit') { + ele.innerHTML = `Subcircuit: ${globalScope.SubCircuit[j].data.name}` + } else { + ele.innerHTML = `CircuitElement: ${objectList[i]}` + } + return + } + } + } + } + + ele.style.display = 'none' + document.getElementById('elementName').innerHTML = '' + }) + + window.addEventListener('mousemove', (e) => { + var rect = simulationArea.canvas.getBoundingClientRect() + simulationArea.mouseRawX = (e.clientX - rect.left) * DPR + simulationArea.mouseRawY = (e.clientY - rect.top) * DPR + simulationArea.mouseXf = + (simulationArea.mouseRawX - globalScope.ox) / globalScope.scale + simulationArea.mouseYf = + (simulationArea.mouseRawY - globalScope.oy) / globalScope.scale + simulationArea.mouseX = Math.round(simulationArea.mouseXf / unit) * unit + simulationArea.mouseY = Math.round(simulationArea.mouseYf / unit) * unit + + updateCanvasSet(true) + if (simulationArea.lastSelected == globalScope.root) { + updateCanvasSet(true) + var fn + fn = function () { + updateSelectionsAndPane() + } + scheduleUpdate(0, 20, fn) + } else { + scheduleUpdate(0, 200) + } + }) + window.addEventListener('keydown', (e) => { + errorDetectedSet(false) + updateSimulationSet(true) + updatePositionSet(true) + + // zoom in (+) + if (e.key == 'Meta' || e.key == 'Control') { + simulationArea.controlDown = true + } + + if ( + simulationArea.controlDown && + (e.keyCode == 187 || e.KeyCode == 171) + ) { + e.preventDefault() + ZoomIn() + } + + // zoom out (-) + if ( + simulationArea.controlDown && + (e.keyCode == 189 || e.Keycode == 173) + ) { + e.preventDefault() + ZoomOut() + } + + if ( + simulationArea.mouseRawX < 0 || + simulationArea.mouseRawY < 0 || + simulationArea.mouseRawX > width || + simulationArea.mouseRawY > height + ) + return + + scheduleUpdate(1) + updateCanvasSet(true) + + if ( + simulationArea.lastSelected && + simulationArea.lastSelected.keyDown + ) { + if ( + e.key.toString().length == 1 || + e.key.toString() == 'Backspace' + ) { + simulationArea.lastSelected.keyDown(e.key.toString()) + return + } + } + if ( + simulationArea.lastSelected && + simulationArea.lastSelected.keyDown2 + ) { + if (e.key.toString().length == 1) { + simulationArea.lastSelected.keyDown2(e.key.toString()) + return + } + } + + // if (simulationArea.lastSelected && simulationArea.lastSelected.keyDown3) { + // if (e.key.toString() != "Backspace" && e.key.toString() != "Delete") { + // simulationArea.lastSelected.keyDown3(e.key.toString()); + // return; + // } + + // } + + if (e.key == 'T' || e.key == 't') { + simulationArea.changeClockTime(prompt('Enter Time:')) + } + }) + document + .getElementById('simulationArea') + .addEventListener('dblclick', (e) => { + scheduleUpdate(2) + if ( + simulationArea.lastSelected && + simulationArea.lastSelected.dblclick !== undefined + ) { + simulationArea.lastSelected.dblclick() + } + }) + + window.addEventListener('mouseup', (e) => { + simulationArea.mouseDown = false + errorDetectedSet(false) + updateSimulationSet(true) + updatePositionSet(true) + updateCanvasSet(true) + gridUpdateSet(true) + wireToBeCheckedSet(1) + + scheduleUpdate(1) + }) + window.addEventListener('mousedown', function (e) { + this.focus() + }) + + document + .getElementById('simulationArea') + .addEventListener('mousewheel', MouseScroll) + document + .getElementById('simulationArea') + .addEventListener('DOMMouseScroll', MouseScroll) + + function MouseScroll(event) { + updateCanvasSet(true) + + event.preventDefault() + var deltaY = event.wheelDelta ? event.wheelDelta : -event.detail + var scrolledUp = deltaY < 0 + var scrolledDown = deltaY > 0 + + if (event.ctrlKey) { + if (scrolledUp && globalScope.scale > 0.5 * DPR) { + changeScale(-0.1 * DPR) + } + if (scrolledDown && globalScope.scale < 4 * DPR) { + changeScale(0.1 * DPR) + } + } else { + if (scrolledUp && globalScope.scale < 4 * DPR) { + changeScale(0.1 * DPR) + } + if (scrolledDown && globalScope.scale > 0.5 * DPR) { + changeScale(-0.1 * DPR) + } + } + + updateCanvasSet(true) + gridUpdateSet(true) + update() // Schedule update not working, this is INEFFICENT + } +} + +var isIe = + navigator.userAgent.toLowerCase().indexOf('msie') != -1 || + navigator.userAgent.toLowerCase().indexOf('trident') != -1 diff --git a/v1/src/simulator/src/engine.js b/v1/src/simulator/src/engine.js new file mode 100644 index 00000000..2f74df6e --- /dev/null +++ b/v1/src/simulator/src/engine.js @@ -0,0 +1,559 @@ +/* eslint-disable import/no-cycle */ +/* eslint-disable no-use-before-define */ +/* eslint-disable no-continue */ +/* eslint-disable no-param-reassign */ +/* eslint-disable no-bitwise */ +import { layoutModeGet, layoutUpdate } from './layoutMode' +import plotArea from './plotArea' +import simulationArea from './simulationArea' +import { dots, canvasMessage, findDimensions, rect2 } from './canvasApi' +import { showProperties, prevPropertyObjGet } from './ux' +import { showError } from './utils' +import miniMapArea from './minimap' +import { resetup } from './setup' +import { verilogModeGet } from './Verilog2CV' + +/** + * Core of the simulation and rendering algorithm. + */ + +/** + * @type {number} engine + * @category engine + */ +var wireToBeChecked = 0 + +/** + * Used to set wireChecked boolean which updates wires in UI if true (or 1). 2 if some problem and it is handled. + * @param {number} param - value of wirechecked + * @category engine + */ +export function wireToBeCheckedSet(param) { + wireToBeChecked = param +} + +/** + * scheduleUpdate() will be called if true + * @type {boolean} + * @category engine + */ +var willBeUpdated = false + +/** + * used to set willBeUpdated variable + * @type {boolean} + * @category engine + * @category engine + */ +export function willBeUpdatedSet(param) { + willBeUpdated = param +} + +/** + * true if we have an element selected and + * is used when we are paning the grid. + * @type {boolean} + * @category engine + */ +var objectSelection = false + +/** + * used to set the value of object selection, + * @param {boolean} param + * @category engine + */ +export function objectSelectionSet(param) { + objectSelection = param +} + +/** + * Flag for updating position + * @type {boolean} + * @category engine + */ +var updatePosition = true + +/** + * used to set the value of updatePosition. + * @param {boolean} param + * @category engine + */ +export function updatePositionSet(param) { + updatePosition = param +} + +/** + * Flag for updating simulation + * @type {boolean} + * @category engine + */ +var updateSimulation = true + +/** + * used to set the value of updateSimulation. + * @param {boolean} param + * @category engine + */ +export function updateSimulationSet(param) { + updateSimulation = param +} +/** + * Flag for rendering + * @type {boolean} + * @category engine + */ +var updateCanvas = true + +/** + * used to set the value of updateCanvas. + * @param {boolean} param + * @category engine + */ +export function updateCanvasSet(param) { + updateCanvas = param +} + +/** + * Flag for updating grid + * @type {boolean} + * @category engine + */ +var gridUpdate = true + +/** + * used to set gridUpdate + * @param {boolean} param + * @category engine + */ +export function gridUpdateSet(param) { + gridUpdate = param +} + +/** + * used to get gridUpdate + * @return {boolean} + * @category engine + */ +export function gridUpdateGet() { + return gridUpdate +} +/** + * Flag for updating grid + * @type {boolean} + * @category engine + */ +var forceResetNodes = true + +/** + * used to set forceResetNodes + * @param {boolean} param + * @category engine + */ +export function forceResetNodesSet(param) { + forceResetNodes = param +} +/** + * Flag for updating grid + * @type {boolean} + * @category engine + */ +var errorDetected = false + +/** + * used to set errorDetected + * @param {boolean} param + * @category engine + */ +export function errorDetectedSet(param) { + errorDetected = param +} + +/** + * used to set errorDetected + * @returns {boolean} errorDetected + * @category engine + */ +export function errorDetectedGet() { + return errorDetected +} + +/** + * details of where and what canvas message has to be shown. + * @type {Object} + * @property {number} x - x cordinate of message + * @property {number} y - x cordinate of message + * @property {number} string - the message + * @category engine + */ +export var canvasMessageData = { + x: undefined, + y: undefined, + string: undefined, +} + +/** + * Flag for updating subCircuits + * @type {boolean} + * @category engine + */ +var updateSubcircuit = true + +/** + * used to set updateSubcircuit + * @param {boolean} param + * @category engine + */ +export function updateSubcircuitSet(param) { + if (updateSubcircuit != param) { + updateSubcircuit = param + return true + } + updateSubcircuit = param + return false +} + +/** + * turn light mode on + * @param {boolean} val -- new value for light mode + * @category engine + */ +export function changeLightMode(val) { + if (!val && lightMode) { + lightMode = false + DPR = window.devicePixelRatio || 1 + globalScope.scale *= DPR + } else if (val && !lightMode) { + lightMode = true + globalScope.scale /= DPR + DPR = 1 + $('#miniMap').fadeOut('fast') + } + resetup() +} + +/** + * Function to render Canvas according th renderupdate order + * @param {Scope} scope - The circuit whose canvas we want to render + * @category engine + */ +export function renderCanvas(scope) { + if (layoutModeGet() || verilogModeGet()) { + // Different Algorithm + return + } + var ctx = simulationArea.context + // Reset canvas + simulationArea.clear() + // Update Grid + if (gridUpdate) { + gridUpdateSet(false) + dots() + } + canvasMessageData = { + x: undefined, + y: undefined, + string: undefined, + } // Globally set in draw fn () + // Render objects + for (let i = 0; i < renderOrder.length; i++) { + for (var j = 0; j < scope[renderOrder[i]].length; j++) { + scope[renderOrder[i]][j].draw() + } + } + // Show any message + if (canvasMessageData.string !== undefined) { + canvasMessage( + ctx, + canvasMessageData.string, + canvasMessageData.x, + canvasMessageData.y + ) + } + // If multiple object selections are going on, show selected area + if (objectSelection) { + ctx.beginPath() + ctx.lineWidth = 2 + ctx.strokeStyle = 'black' + ctx.fillStyle = 'rgba(0,0,0,0.1)' + rect2( + ctx, + simulationArea.mouseDownX, + simulationArea.mouseDownY, + simulationArea.mouseX - simulationArea.mouseDownX, + simulationArea.mouseY - simulationArea.mouseDownY, + 0, + 0, + 'RIGHT' + ) + ctx.stroke() + ctx.fill() + } + if (simulationArea.hover !== undefined) { + simulationArea.canvas.style.cursor = 'pointer' + } else if (simulationArea.mouseDown) { + simulationArea.canvas.style.cursor = 'grabbing' + } else { + simulationArea.canvas.style.cursor = 'default' + } +} + +/** + * Function to move multiple objects and panes window + * deselected using dblclick right now (PR open for esc key) + * @param {Scope=} scope - the circuit in which we are selecting stuff + * @category engine + */ +export function updateSelectionsAndPane(scope = globalScope) { + if (!simulationArea.selected && simulationArea.mouseDown) { + simulationArea.selected = true + simulationArea.lastSelected = scope.root + simulationArea.hover = scope.root + // Selecting multiple objects + if (simulationArea.shiftDown) { + objectSelectionSet(true) + } else if (!embed) { + findDimensions(scope) + miniMapArea.setup() + $('#miniMap').show() + } + } else if ( + simulationArea.lastSelected === scope.root && + simulationArea.mouseDown + ) { + // pane canvas to give an idea of grid moving + if (!objectSelection) { + globalScope.ox = + simulationArea.mouseRawX - + simulationArea.mouseDownRawX + + simulationArea.oldx + globalScope.oy = + simulationArea.mouseRawY - + simulationArea.mouseDownRawY + + simulationArea.oldy + globalScope.ox = Math.round(globalScope.ox) + globalScope.oy = Math.round(globalScope.oy) + gridUpdateSet(true) + if (!embed && !lightMode) miniMapArea.setup() + } else { + // idea: kind of empty + } + } else if (simulationArea.lastSelected === scope.root) { + /* + Select multiple objects by adding them to the array + simulationArea.multipleObjectSelections when we select + using shift + mouse movement to select an area but + not shift + click + */ + simulationArea.lastSelected = undefined + simulationArea.selected = false + simulationArea.hover = undefined + if (objectSelection) { + objectSelectionSet(false) + var x1 = simulationArea.mouseDownX + var x2 = simulationArea.mouseX + var y1 = simulationArea.mouseDownY + var y2 = simulationArea.mouseY + // Sort those four points to make a selection pane + if (x1 > x2) { + const temp = x1 + x1 = x2 + x2 = temp + } + if (y1 > y2) { + const temp = y1 + y1 = y2 + y2 = temp + } + // Select the objects, push them into a list + for (let i = 0; i < updateOrder.length; i++) { + for (var j = 0; j < scope[updateOrder[i]].length; j++) { + var obj = scope[updateOrder[i]][j] + if (simulationArea.multipleObjectSelections.contains(obj)) + continue + var x + var y + if (obj.objectType === 'Node') { + x = obj.absX() + y = obj.absY() + } else if (obj.objectType !== 'Wire') { + x = obj.x + y = obj.y + } else { + continue + } + if (x > x1 && x < x2 && y > y1 && y < y2) { + simulationArea.multipleObjectSelections.push(obj) + } + } + } + } + } +} + +/** + * Main fn that resolves circuit using event driven simulation + * All inputs are added to a scope using scope.addinput() and + * the simulation starts to play. + * @param {Scope=} scope - the circuit we want to simulate + * @param {boolean} resetNodes - boolean to reset all nodes + * @category engine + */ +export function play(scope = globalScope, resetNodes = false) { + if (errorDetected) return // Don't simulate until error is fixed + if (loading === true) return // Don't simulate until loaded + + simulationArea.simulationQueue.reset() + plotArea.setExecutionTime() // Waveform thing + // Reset Nodes if required + if (resetNodes || forceResetNodes) { + scope.reset() + simulationArea.simulationQueue.reset() + forceResetNodesSet(false) + } + + // To store list of circuitselements that have shown contention but kept temporarily + // Mainly to resolve tristate bus issues + simulationArea.contentionPending = [] + // add inputs to the simulation queue + scope.addInputs() + // to check if we have infinite loop in circuit + let stepCount = 0 + let elem + while (!simulationArea.simulationQueue.isEmpty()) { + if (errorDetected) { + simulationArea.simulationQueue.reset() + return + } + elem = simulationArea.simulationQueue.pop() + elem.resolve() + stepCount++ + if (stepCount > 1000000) { + // Cyclic or infinite Circuit Detection + showError( + 'Simulation Stack limit exceeded: maybe due to cyclic paths or contention' + ) + errorDetectedSet(true) + forceResetNodesSet(true) + } + } + // Check for TriState Contentions + if (simulationArea.contentionPending.length) { + showError('Contention at TriState') + forceResetNodesSet(true) + errorDetectedSet(true) + } +} + +/** + * Function to check for any UI update, it is throttled by time + * @param {number=} count - this is used to force update + * @param {number=} time - the time throttling parameter + * @param {function} fn - function to run before updating UI + * @category engine + */ +export function scheduleUpdate(count = 0, time = 100, fn) { + if (lightMode) time *= 5 + var updateFn = layoutModeGet() ? layoutUpdate : update + if (count) { + // Force update + updateFn() + for (let i = 0; i < count; i++) { + setTimeout(updateFn, 10 + 50 * i) + } + } + if (willBeUpdated) return // Throttling + willBeUpdatedSet(true) + // Call a function before update .. + if (fn) { + setTimeout(() => { + fn() + updateFn() + }, time) + } else setTimeout(updateFn, time) +} + +/** + * fn that calls update on everything else. If any change + * is there, it resolves the circuit and draws it again. + * Also updates simulations, selection, minimap, resolves + * circuit and redraws canvas if required. + * @param {Scope=} scope - the circuit to be updated + * @param {boolean=} updateEverything - if true we update the wires, nodes and modules + * @category engine + */ +export function update(scope = globalScope, updateEverything = false) { + willBeUpdatedSet(false) + if (loading === true || layoutModeGet()) return + var updated = false + simulationArea.hover = undefined + // Update wires + if (wireToBeChecked || updateEverything) { + if (wireToBeChecked === 2) + wireToBeChecked = 0 // this required due to timing issues + else wireToBeChecked++ + // WHY IS THIS REQUIRED ???? we are checking inside wire ALSO + // Idea: we can just call length again instead of doing it during loop. + var prevLength = scope.wires.length + for (let i = 0; i < scope.wires.length; i++) { + scope.wires[i].checkConnections() + if (scope.wires.length !== prevLength) { + prevLength-- + i-- + } + } + scheduleUpdate() + } + // Update subcircuits + if (updateSubcircuit || updateEverything) { + for (let i = 0; i < scope.SubCircuit.length; i++) { + scope.SubCircuit[i].reset() + } + updateSubcircuitSet(false) + } + // Update UI position + if (updatePosition || updateEverything) { + for (let i = 0; i < updateOrder.length; i++) { + for (let j = 0; j < scope[updateOrder[i]].length; j++) { + updated |= scope[updateOrder[i]][j].update() + } + } + } + // Updates multiple objectselections and panes window + if (updatePosition || updateEverything) { + updateSelectionsAndPane(scope) + } + // Update MiniMap + if ( + !embed && + simulationArea.mouseDown && + simulationArea.lastSelected && + simulationArea.lastSelected !== globalScope.root + ) { + if (!lightMode) { + $('#miniMap').fadeOut('fast') + } + } + // Run simulation + if (updateSimulation) { + play() + } + // Show properties of selected element + if (!embed && prevPropertyObjGet() !== simulationArea.lastSelected) { + if ( + simulationArea.lastSelected && + simulationArea.lastSelected.objectType !== 'Wire' + ) { + // ideas: why show properties of project in Nodes but not wires? + showProperties(simulationArea.lastSelected) + } else { + // hideProperties(); + } + } + // Draw, render everything + if (updateCanvas) { + renderCanvas(scope) + } + updateSimulationSet(false) + updateCanvas = false + updatePositionSet(false) +} diff --git a/v1/src/simulator/src/eventQueue.js b/v1/src/simulator/src/eventQueue.js new file mode 100644 index 00000000..f40110e1 --- /dev/null +++ b/v1/src/simulator/src/eventQueue.js @@ -0,0 +1,116 @@ +/** + * Event Queue is simply a priority Queue, basic implementation O(n^2). + * @category eventQueue + */ +export default class EventQueue { + constructor(size) { + this.size = size + this.queue = new Array(size) + this.frontIndex = 0 + this.time = 0 + } + + /** + * @param {CircuitElement} obj - the elemnt to be added + * @param {number} delay - the delay in adding an object to queue + */ + add(obj, delay) { + if (obj.queueProperties.inQueue) { + obj.queueProperties.time = + this.time + (delay || obj.propagationDelay) + let i = obj.queueProperties.index + while ( + i > 0 && + obj.queueProperties.time > + this.queue[i - 1].queueProperties.time + ) { + this.swap(i, i - 1) + i-- + } + i = obj.queueProperties.index + while ( + i < this.frontIndex - 1 && + obj.queueProperties.time < + this.queue[i + 1].queueProperties.time + ) { + this.swap(i, i + 1) + i++ + } + return + } + + if (this.frontIndex == this.size) throw 'EventQueue size exceeded' + this.queue[this.frontIndex] = obj + // obj.queueProperties.time=obj.propagationDelay; + obj.queueProperties.time = this.time + (delay || obj.propagationDelay) + obj.queueProperties.index = this.frontIndex + this.frontIndex++ + obj.queueProperties.inQueue = true + let i = obj.queueProperties.index + while ( + i > 0 && + obj.queueProperties.time > this.queue[i - 1].queueProperties.time + ) { + this.swap(i, i - 1) + i-- + } + } + + /** + * To add without any delay. + * @param {CircuitElement} obj - the object to be added + */ + addImmediate(obj) { + this.queue[this.frontIndex] = obj + obj.queueProperties.time = this.time + obj.queueProperties.index = this.frontIndex + obj.queueProperties.inQueue = true + this.frontIndex++ + } + + /** + * Function to swap two objects in queue. + * @param {number} v1 + * @param {number} v2 + */ + swap(v1, v2) { + const obj1 = this.queue[v1] + obj1.queueProperties.index = v2 + + const obj2 = this.queue[v2] + obj2.queueProperties.index = v1 + + this.queue[v1] = obj2 + this.queue[v2] = obj1 + } + + /** + * function to pop element from queue. + */ + pop() { + if (this.isEmpty()) throw 'Queue Empty' + + this.frontIndex-- + const obj = this.queue[this.frontIndex] + this.time = obj.queueProperties.time + obj.queueProperties.inQueue = false + return obj + } + + /** + * function to reset queue. + */ + reset() { + for (let i = 0; i < this.frontIndex; i++) + this.queue[i].queueProperties.inQueue = false + this.time = 0 + this.frontIndex = 0 + } + + /** + * function to check if empty queue. + */ + isEmpty() { + return this.frontIndex == 0 + } +} diff --git a/v1/src/simulator/src/events.js b/v1/src/simulator/src/events.js new file mode 100644 index 00000000..c3c0edca --- /dev/null +++ b/v1/src/simulator/src/events.js @@ -0,0 +1,338 @@ +/* eslint-disable import/no-cycle */ +import Scope, { scopeList, switchCircuit, newCircuit } from './circuit' + +import { loadScope } from './data/load' +import { + scheduleUpdate, + updateSimulationSet, + updateSubcircuitSet, + forceResetNodesSet, +} from './engine' +import { backUp } from './data/backupCircuit' +import { getNextPosition } from './modules' +import { generateId } from './utils' +import simulationArea from './simulationArea' +import { TestbenchData } from './testbench' + +/** + * Helper function to paste + * @param {JSON} copyData - the data to be pasted + * @category events + */ +export function paste(copyData) { + if (copyData === undefined) return + var data = JSON.parse(copyData) + if (!data.logixClipBoardData) return + + var currentScopeId = globalScope.id + for (let i = 0; i < data.scopes.length; i++) { + if (scopeList[data.scopes[i].id] === undefined) { + var isVerilogCircuit = false + var isMainCircuit = false + if (data.scopes[i].verilogMetadata) { + isVerilogCircuit = + data.scopes[i].verilogMetadata.isVerilogCircuit + isMainCircuit = data.scopes[i].verilogMetadata.isMainCircuit + } + var scope = newCircuit( + data.scopes[i].name, + data.scopes[i].id, + isVerilogCircuit, + isMainCircuit + ) + loadScope(scope, data.scopes[i]) + scopeList[data.scopes[i].id] = scope + } + } + + switchCircuit(currentScopeId) + var tempScope = new Scope(globalScope.name, globalScope.id) + var oldOx = globalScope.ox + var oldOy = globalScope.oy + var oldScale = globalScope.scale + loadScope(tempScope, data) + + var prevLength = tempScope.allNodes.length + for (let i = 0; i < tempScope.allNodes.length; i++) { + tempScope.allNodes[i].checkDeleted() + if (tempScope.allNodes.length != prevLength) { + prevLength-- + i-- + } + } + + var approxX = 0 + var approxY = 0 + var count = 0 + + for (let i = 0; i < updateOrder.length; i++) { + for (let j = 0; j < tempScope[updateOrder[i]].length; j++) { + const obj = tempScope[updateOrder[i]][j] + obj.updateScope(globalScope) + if (obj.objectType != 'Wire') { + approxX += obj.x + approxY += obj.y + count++ + } + } + } + + for (let j = 0; j < tempScope.CircuitElement.length; j++) { + const obj = tempScope.CircuitElement[j] + obj.updateScope(globalScope) + } + + approxX /= count + approxY /= count + + approxX = Math.round(approxX / 10) * 10 + approxY = Math.round(approxY / 10) * 10 + + for (let i = 0; i < updateOrder.length; i++) { + for (let j = 0; j < tempScope[updateOrder[i]].length; j++) { + const obj = tempScope[updateOrder[i]][j] + if (obj.objectType !== 'Wire') { + obj.x += simulationArea.mouseX - approxX + obj.y += simulationArea.mouseY - approxY + } + } + } + + Object.keys(tempScope).forEach((l) => { + if ( + tempScope[l] instanceof Array && + l !== 'objects' && + l !== 'CircuitElement' + ) { + globalScope[l].extend(tempScope[l]) + } + }) + for (let i = 0; i < tempScope.Input.length; i++) { + tempScope.Input[i].layoutProperties.y = getNextPosition(0, globalScope) + tempScope.Input[i].layoutProperties.id = generateId() + } + for (let i = 0; i < tempScope.Output.length; i++) { + tempScope.Output[i].layoutProperties.x = globalScope.layout.width + tempScope.Output[i].layoutProperties.id = generateId() + tempScope.Output[i].layoutProperties.y = getNextPosition( + globalScope.layout.width, + globalScope + ) + } + var canvasUpdate = true + updateSimulationSet(true) + updateSubcircuitSet(true) + scheduleUpdate() + globalScope.ox = oldOx + globalScope.oy = oldOy + globalScope.scale = oldScale + + forceResetNodesSet(true) +} +/** + * Helper function for cut + * @param {JSON} copyList - The selected elements + * @category events + */ +export function cut(copyList) { + if (copyList.length === 0) return + var tempScope = new Scope(globalScope.name, globalScope.id) + var oldOx = globalScope.ox + var oldOy = globalScope.oy + var oldScale = globalScope.scale + d = backUp(globalScope) + loadScope(tempScope, d) + scopeList[tempScope.id] = tempScope + + for (let i = 0; i < copyList.length; i++) { + const obj = copyList[i] + if (obj.objectType === 'Node') obj.objectType = 'allNodes' + for (let j = 0; j < tempScope[obj.objectType].length; j++) { + if ( + tempScope[obj.objectType][j].x === obj.x && + tempScope[obj.objectType][j].y === obj.y && + (obj.objectType != 'Node' || obj.type === 2) + ) { + tempScope[obj.objectType][j].delete() + break + } + } + } + tempScope.backups = globalScope.backups + for (let i = 0; i < updateOrder.length; i++) { + let prevLength = globalScope[updateOrder[i]].length // LOL length of list will reduce automatically when deletion starts + for (let j = 0; j < globalScope[updateOrder[i]].length; j++) { + const obj = globalScope[updateOrder[i]][j] + if (obj.objectType != 'Wire') { + // }&&obj.objectType!='CircuitElement'){//}&&(obj.objectType!='Node'||obj.type==2)){ + if (!copyList.contains(globalScope[updateOrder[i]][j])) { + globalScope[updateOrder[i]][j].cleanDelete() + } + } + + if (globalScope[updateOrder[i]].length != prevLength) { + prevLength-- + j-- + } + } + } + + var prevLength = globalScope.wires.length + for (let i = 0; i < globalScope.wires.length; i++) { + globalScope.wires[i].checkConnections() + if (globalScope.wires.length != prevLength) { + prevLength-- + i-- + } + } + + updateSimulationSet(true) + + var data = backUp(globalScope) + data.logixClipBoardData = true + var dependencyList = globalScope.getDependencies() + data.dependencies = {} + Object.keys(dependencyList).forEach((dependency) => { + data.dependencies[dependency] = backUp(scopeList[dependency]) + }) + data.logixClipBoardData = true + data = JSON.stringify(data) + + simulationArea.multipleObjectSelections = [] // copyList.slice(); + simulationArea.copyList = [] // copyList.slice(); + var canvasUpdate = true + updateSimulationSet(true) + globalScope = tempScope + scheduleUpdate() + globalScope.ox = oldOx + globalScope.oy = oldOy + globalScope.scale = oldScale + forceResetNodesSet(true) + // eslint-disable-next-line consistent-return + return data +} +/** + * Helper function for copy + * @param {JSON} copyList - The data to copied + * @param {boolean} cutflag - flase if we want to copy + * @category events + */ +export function copy(copyList, cutflag = false) { + if (copyList.length === 0) return + var tempScope = new Scope(globalScope.name, globalScope.id) + var oldOx = globalScope.ox + var oldOy = globalScope.oy + var oldScale = globalScope.scale + var d = backUp(globalScope) + const oldTestbenchData = globalScope.testbenchData + + loadScope(tempScope, d) + scopeList[tempScope.id] = tempScope + + if (cutflag) { + for (let i = 0; i < copyList.length; i++) { + const obj = copyList[i] + if (obj.objectType === 'Node') obj.objectType = 'allNodes' + for (let j = 0; j < tempScope[obj.objectType].length; j++) { + if ( + tempScope[obj.objectType][j].x === obj.x && + tempScope[obj.objectType][j].y === obj.y && + (obj.objectType != 'Node' || obj.type === 2) + ) { + tempScope[obj.objectType][j].delete() + break + } + } + } + } + tempScope.backups = globalScope.backups + for (let i = 0; i < updateOrder.length; i++) { + let prevLength = globalScope[updateOrder[i]].length // LOL length of list will reduce automatically when deletion starts + for (let j = 0; j < globalScope[updateOrder[i]].length; j++) { + const obj = globalScope[updateOrder[i]][j] + if (obj.objectType != 'Wire') { + // }&&obj.objectType!='CircuitElement'){//}&&(obj.objectType!='Node'||obj.type==2)){ + if (!copyList.contains(globalScope[updateOrder[i]][j])) { + globalScope[updateOrder[i]][j].cleanDelete() + } + } + + if (globalScope[updateOrder[i]].length != prevLength) { + prevLength-- + j-- + } + } + } + + var prevLength = globalScope.wires.length + for (let i = 0; i < globalScope.wires.length; i++) { + globalScope.wires[i].checkConnections() + if (globalScope.wires.length != prevLength) { + prevLength-- + i-- + } + } + + updateSimulationSet(true) + + var data = backUp(globalScope) + data.scopes = [] + var dependencyList = {} + var requiredDependencies = globalScope.getDependencies() + var completed = {} + Object.keys(scopeList).forEach((id) => { + dependencyList[id] = scopeList[id].getDependencies() + }) + function saveScope(id) { + if (completed[id]) return + for (let i = 0; i < dependencyList[id].length; i++) { + saveScope(dependencyList[id][i]) + } + completed[id] = true + data.scopes.push(backUp(scopeList[id])) + } + for (let i = 0; i < requiredDependencies.length; i++) { + saveScope(requiredDependencies[i]) + } + data.logixClipBoardData = true + data.testbenchData = undefined // Don't copy testbench data + data = JSON.stringify(data) + simulationArea.multipleObjectSelections = [] // copyList.slice(); + simulationArea.copyList = [] // copyList.slice(); + var canvasUpdate = true + updateSimulationSet(true) + globalScope = tempScope + scheduleUpdate() + globalScope.ox = oldOx + globalScope.oy = oldOy + globalScope.scale = oldScale + // Restore testbench data + if (oldTestbenchData) { + globalScope.testbenchData = new TestbenchData( + oldTestbenchData.testData, + oldTestbenchData.currentGroup, + oldTestbenchData.currentCase + ) + } + + forceResetNodesSet(true) + // needs to be fixed + // eslint-disable-next-line consistent-return + return data +} + +/** + * Function selects all the elements from the scope + * @category events + */ +export function selectAll(scope = globalScope) { + moduleList.forEach((val, _, __) => { + if (scope.hasOwnProperty(val)) { + simulationArea.multipleObjectSelections.push(...scope[val]) + } + }) + + if (scope.nodes) { + simulationArea.multipleObjectSelections.push(...scope.nodes) + } +} diff --git a/v1/src/simulator/src/file/Open.js b/v1/src/simulator/src/file/Open.js new file mode 100644 index 00000000..c371be16 --- /dev/null +++ b/v1/src/simulator/src/file/Open.js @@ -0,0 +1,96 @@ +/* **************************************************************************************************** */ +/* Implemented in ImportProject.vue Kept for reference in case any bugs occur */ +/* TODO: Remove this file after testing */ +/* **************************************************************************************************** */ + +// import load from '../data/load' +// import { generateSaveData } from '../data/save' +// import { escapeHtml } from '../ux' + +// const scopeSchema = [ +// 'layout', +// 'verilogMetadata', +// 'allNodes', +// 'id', +// 'name', +// 'restrictedCircuitElementsUsed', +// 'nodes', +// ] +// const JSONSchema = [ +// 'name', +// 'timePeriod', +// 'clockEnabled', +// 'projectId', +// 'focussedCircuit', +// 'orderedTabs', +// 'scopes', +// ] + +// var circuitData = null +// const GetDialogData = () => +// '

Browse files or Drag & Drop files here
No file chosen!!
' + +// const ImportCircuitFiles = () => { +// $('#ImportCircuitFilesDialog').empty() +// $('#ImportCircuitFilesDialog').append(GetDialogData()) +// $('#ImportCircuitFilesDialog').dialog({ +// resizable: false, +// close() { +// if (circuitData) load(circuitData) +// }, +// buttons: [ +// { +// text: 'Close', +// click() { +// $(this).dialog('close') +// }, +// }, +// ], +// }) +// $('#ImportCircuitFilesDialog').focus() + +// function ValidateData(fileData) { +// try { +// const parsedFileDate = JSON.parse(fileData) +// if ( +// JSON.stringify(Object.keys(parsedFileDate)) !== +// JSON.stringify(JSONSchema) +// ) +// throw new Error('Invalid JSON data') +// parsedFileDate.scopes.forEach((scope) => { +// const keys = Object.keys(scope) // get scope keys +// scopeSchema.forEach((key) => { +// if (!keys.includes(key)) +// throw new Error('Invalid Scope data') +// }) +// }) +// load(parsedFileDate) +// return true +// } catch (error) { +// $('#message').text('Invalid file format') +// return false +// } +// } + +// function receivedText(e) { +// // backUp data +// const backUp = JSON.parse( +// generateSaveData(escapeHtml($('#projectName').text()).trim(), false) +// ) +// const valid = ValidateData(e.target.result) // validate data +// if (!valid) { +// // fallback +// load(backUp) +// } else { +// $('#ImportCircuitFilesDialog').dialog('close') +// } +// } +// $('#CircuitDataFile').on('change', (event) => { +// var File = event.target.files[0] +// var fr = new FileReader() +// fr.onload = receivedText +// fr.readAsText(File) +// }) +// } + +// export default ImportCircuitFiles diff --git a/v1/src/simulator/src/file/SaveAs.js b/v1/src/simulator/src/file/SaveAs.js new file mode 100644 index 00000000..d94696a7 --- /dev/null +++ b/v1/src/simulator/src/file/SaveAs.js @@ -0,0 +1,54 @@ +/* **************************************************************************************************** */ +/* Implemented in ExportProject.vue Kept for reference in case any bugs occur */ +/* TODO: Remove this file after testing */ +/* **************************************************************************************************** */ + +// import { download, generateSaveData } from '../data/save' +// import { escapeHtml } from '../ux' + +// const GetDialogData = () => { +// const fileName = `${$('#projectName').text().trim()}.cv` +// const Input = document.createElement('input') +// Input.type = 'text' +// Input.name = 'fileName' +// Input.setAttribute('placeholder', fileName) +// Input.id = 'filename' +// Input.defaultValue = fileName +// const Label = document.createElement('label') +// Label.setAttribute('for', 'filename') +// Label.textContent = 'File Name' +// const container = document.createElement('div') +// container.appendChild(Label) +// container.appendChild(Input) +// return container +// } + +// /** +// * To Export Circuit Files +// */ +// const ExportCircuitFiles = () => { +// $('#ExportCircuitFilesDialog').empty() +// $('#ExportCircuitFilesDialog').append(GetDialogData()) +// $('#ExportCircuitFilesDialog').dialog({ +// resizable: false, +// buttons: [ +// { +// text: 'Save', +// click() { +// var fileName = +// escapeHtml($('#filename').val()) || 'untitled' +// const circuitData = generateSaveData( +// fileName.split('.')[0], +// false +// ) +// fileName = `${fileName.split('.')[0]}.cv` +// download(fileName, circuitData) +// $(this).dialog('close') +// }, +// }, +// ], +// }) +// $('#ExportCircuitFilesDialog').focus() +// } + +// export default ExportCircuitFiles diff --git a/v1/src/simulator/src/hotkey_binder/defaultKeys.js b/v1/src/simulator/src/hotkey_binder/defaultKeys.js new file mode 100644 index 00000000..0529ce5f --- /dev/null +++ b/v1/src/simulator/src/hotkey_binder/defaultKeys.js @@ -0,0 +1,29 @@ +/**Add more elements here, along with a valid value for key + * Elements keys must have the same name as their ID + **/ + +export const defaultKeys = { + 'New Circuit': 'Shift + N', + 'Save Online': 'Ctrl + S', + 'Save Offline': 'Ctrl + Alt + S', + 'Download as Image': 'Ctrl + D', + 'Open Offline': 'Ctrl + O', + 'Insert Sub-circuit': 'Shift + C', + 'Combinational Analysis': 'Shift + A', + // "Start Plot": "Ctrl + P", + 'Direction Up': 'Up', + 'Direction Down': 'Down', + 'Direction Left': 'Left', + 'Direction Right': 'Right', + 'Insert Label': 'Ctrl + L', + 'Label Direction Up': 'Alt + Up', + 'Label Direction Down': 'Alt + Down', + 'Label Direction Left': 'Alt + Left', + 'Label Direction Right': 'Alt + Right', + 'Move Element Up': 'Shift + Up', + 'Move Element Down': 'Shift + Down', + 'Move Element Left': 'Shift + Left', + 'Move Element Right': 'Shift + Right', + 'Hotkey Preference': 'F8', + 'Open Documentation': 'F1', +} diff --git a/v1/src/simulator/src/hotkey_binder/documentation.txt b/v1/src/simulator/src/hotkey_binder/documentation.txt new file mode 100644 index 00000000..f4af3be9 --- /dev/null +++ b/v1/src/simulator/src/hotkey_binder/documentation.txt @@ -0,0 +1,45 @@ +The feature includes two libraries: +1. shortcuts.plugin.js: It consists of a few function(s) + a. Add + b. Remove + c. RemoveAll + +a. function Add takes 3 arguments: + shortcut_combination: eg "Ctrl + X" + callback: reference to a function, which will be invoked when the above combination is detected + opt: some optional parameters such as propagation, type of keyevent, etc + +b. function Remove: removes a particular shortcut provided in the param. +c. function RemoveAll: takes no param removes all shortcuts. + +supported format: + * Modifier + * Modifier + key + * Single key + +2. normalizer.plugin.js: is used in keyBinder.js (line 73), to detect the shortcut_combination in string format + which is then passed to the shortcuts plugin. + +MAIN FILEs: + keyBinder.js is the controller, it performs operation that are defined in other files (model, view) + It consist of the keydown listener which detects while user is customizing the keys from the preference panel, + * checks for restrictions + * warns override + * restricts non-overridable combinations + * Others + +USAGE: + TO ADD SHORCUTS: + #1 add the shortcut in the defaultKeys.json file + #2 Assign the callback in addShorcut.js: + * the callback should be a func reference,if function to be invoked is already defined in the codebase just set it as it is. (for some func reference may not work, refer next step) + + *for callback not present in the codebase, or is present but doesn't work as intended, defined the function that will invoke the required callback in action.js, note that callback must be func reference hence define it as a higher-order func if necessary in action.js + TO REMOVE SHORCUTS: + #1. Remove the option from defaultKeys.json + #2. Remove the case from addShortcut.js + RESTRICTION: + To add restriction to any combination, mention it in utils.js -> checkRestricted func, which contains an array that consists of restricted combination, push to the array. + +*The feature make uses of localstorage for persistence, only defaultkeys else userkeys will be present in localstorage not both. +On load IFFE func on keyBinder.js (line 111) will check for userKeys, if found it it will be set, else defaultkeys will be set. \ No newline at end of file diff --git a/v1/src/simulator/src/hotkey_binder/keyBinder.js b/v1/src/simulator/src/hotkey_binder/keyBinder.js new file mode 100644 index 00000000..f4653fb7 --- /dev/null +++ b/v1/src/simulator/src/hotkey_binder/keyBinder.js @@ -0,0 +1,151 @@ +// import { +// editPanel, +// heading, +// markUp, +// closeEdit, +// submit, +// updateHTML, +// override, +// } from './view/panel.ui' +// import { setDefault, checkUpdate, addKeys, warnOverride } from './model/actions' +// import { KeyCode } from './model/normalize/normalizer.plugin.js' +// import { checkRestricted } from './model/utils.js' +// import { SimulatorStore } from '#/store/SimulatorStore/SimulatorStore' + +//** keyBinder dialog */ +// export function keyBinder() { + // const simulatorStore = SimulatorStore() + // simulatorStore.dialogBox.customshortcut_dialog = true + // $('#customShortcutDialog').append(editPanel) + // $('#customShortcutDialog').append(heading) + // $('#customShortcutDialog').append(markUp) + // $('#customShortcut').on('click', () => { + // closeEdit() + // $('#customShortcutDialog').dialog({ + // resizable: false, + // buttons: [ + // { + // text: 'Reset to default', + // click: () => { + // if ( + // confirm( + // 'Remove all custom keys & set the default keys?' + // ) + // ) + // setDefault() + // }, + // id: 'resetDefault', + // }, + // { + // text: 'Save', + // click: () => { + // submit() + // $('#customShortcutDialog').dialog('close') + // }, + // id: 'submitBtn', + // }, + // ], + // }) + // $('#customShortcutDialog').css('display', 'flex') + // }) + + // //** targetPref is assigned to the target key option to be edited */ + // let targetPref = null + // $('#preference').on('click', (e) => { + // $('#pressedKeys').text('') + // $('#warning').text('') + // $('#edit').css('border', 'none') + // $('#edit').css('display', 'block') + // $($('#edit')).focus() + // ;[, targetPref] = e.target.closest('div').children + // }) + + // //*** Modifiers restriction enabled here */ + // //*** below fn works in the edit panel where user enters key combo, + // //*** responsible for checking duplicate entries, overriding entries, checking restricted keys, arranging keys in + // //*** proper order, validating key combos */ + + // $('#edit').keydown((e) => { + // e = e || window.event + // e.stopPropagation() + // e.preventDefault() + // var k = KeyCode + // let modifiers = ['CTRL', 'ALT', 'SHIFT', 'META'] + // $('#edit').css('animation', 'none') + // $('#warning').text('') + // if (e.keyCode === 27) closeEdit() + // if (e.keyCode === 13) { + // if ($('#pressedKeys').text() === '') { + // $('#warning').text('Please enter some key(s)') + // $('#edit').css('animation', 'shake .3s linear') + // return + // } + // if (!checkRestricted($('#pressedKeys').text())) { + // override($('#pressedKeys').text()) + // targetPref.innerText = $('#pressedKeys').text() + // $('#pressedKeys').text('') + // $('#edit').css('display', 'none') + // } else { + // $('#warning').text('Please enter different key(s).') + // $('#edit').css('animation', 'shake .3s linear') + // $('#pressedKeys').text('') + // } + // } + // const currentKey = + // k.hot_key(k.translate_event(e)).split('+').join(' + ') !== 'Enter' + // ? k.hot_key(k.translate_event(e)).split('+').join(' + ') + // : '' + // if ( + // $('#pressedKeys').text().split(' + ').length === 2 && + // !modifiers.includes(currentKey) && + // modifiers.includes($('#pressedKeys').text().split(' + ')[1]) + // ) { + // $('#pressedKeys').append(` + ${currentKey}`) + // } else if (modifiers.includes($('#pressedKeys').text())) { + // modifiers = modifiers.filter( + // (mod) => mod === $('#pressedKeys').text() + // ) + // if (!modifiers.includes(currentKey)) { + // $('#pressedKeys').append(` + ${currentKey}`) + // } + // } else { + // $('#pressedKeys').text('') + // $('#pressedKeys').text(currentKey) + // } + // if (!$('#pressedKeys').text()) { + // $('#pressedKeys').text(currentKey) + // } + // if ( + // ($('#pressedKeys').text().split(' + ').length === 2 && + // ['Ctrl', 'Meta'].includes( + // $('#pressedKeys').text().split(' + ')[1] + // )) || + // ($('#pressedKeys').text().split(' + ')[0] === 'Alt' && + // $('#pressedKeys').text().split(' + ')[1] === 'Shift') + // ) { + // $('#pressedKeys').text( + // $('#pressedKeys').text().split(' + ').reverse().join(' + ') + // ) + // } + // warnOverride($('#pressedKeys').text(), targetPref) + // if (checkRestricted($('#pressedKeys').text())) { + // $('#warning').text( + // 'The above combination is a system default shortcut & cannot be set.' + // ) + // } + // }) + + // //** if users closes hotkey dialog by making changes & not saving them, fn will fallback to previous state */ + + // $('div#customShortcutDialog').on('dialogclose', function (event) { + // if (localStorage.userKeys) { + // updateHTML('user') + // } else updateHTML('default') + // }) + + // // Set up shortcuts + // if (localStorage.userKeys) { + // checkUpdate() + // addKeys('user') + // } else setDefault() +// } diff --git a/v1/src/simulator/src/hotkey_binder/model/actions.js b/v1/src/simulator/src/hotkey_binder/model/actions.js new file mode 100644 index 00000000..102209ee --- /dev/null +++ b/v1/src/simulator/src/hotkey_binder/model/actions.js @@ -0,0 +1,205 @@ +import { defaultKeys } from '../defaultKeys' +import { addShortcut } from './addShortcut' +import { updateHTML } from '../view/panel.ui' +import simulationArea from '../../simulationArea' +import { + scheduleUpdate, + update, + updateSelectionsAndPane, + wireToBeCheckedSet, + updatePositionSet, + updateSimulationSet, + updateCanvasSet, + gridUpdateSet, + errorDetectedSet, +} from '../../engine' + +import { getOS } from './utils.js' +import { shortcut } from './shortcuts.plugin.js' +/** + * Function used to add or change keys user or default + * grabs the keycombo from localstorage & + * calls the addShortcut function in a loop to bind them + * @param {string} mode - user custom keys or default keys + */ +export const addKeys = (mode) => { + shortcut.removeAll() + if (mode === 'user') { + localStorage.removeItem('defaultKeys') + let userKeys = localStorage.get('userKeys') + for (let pref in userKeys) { + let key = userKeys[pref] + key = key.split(' ').join('') + addShortcut(key, pref) + } + updateHTML('user') + } else if (mode == 'default') { + if (localStorage.userKeys) localStorage.removeItem('userKeys') + let defaultKeys = localStorage.get('defaultKeys') + for (let pref in defaultKeys) { + let key = defaultKeys[pref] + key = key.split(' ').join('') + addShortcut(key, pref) + } + updateHTML('default') + } +} +/** + * Function used to check if new keys are added, adds missing keys if added + */ +export const checkUpdate = () => { + const userK = localStorage.get('userKeys') + if (Object.size(userK) !== Object.size(defaultKeys)) { + for (const [key, value] of Object.entries(defaultKeys)) { + if (!Object.keys(userK).includes(key)) { + userK[key] = value + } + } + localStorage.set('userKeys', userK) + } else { + return + } +} +/** + * Function used to set userKeys, grabs the keycombo from the panel UI + * sets it to the localStorage & cals addKeys + * removes the defaultkeys from localStorage + */ +export const setUserKeys = () => { + if (localStorage.defaultKeys) localStorage.removeItem('defaultKeys') + let userKeys = {} + let x = 0 + while ($('#preference').children()[x]) { + userKeys[ + $('#preference').children()[x].children[1].children[0].innerText + ] = $('#preference').children()[x].children[1].children[1].innerText + x++ + } + localStorage.set('userKeys', userKeys) + addKeys('user') +} +/** + * Function used to set defaultKeys, grabs the keycombo from the defaultkeys metadata + * sets it to the localStorage & cals addKeys + * removes the userkeys from localStorage if present + * also checks for OS type + */ +export const setDefault = () => { + if (localStorage.userKeys) localStorage.removeItem('userKeys') + if (getOS() === 'MacOS') { + const macDefaultKeys = {} + for (let [key, value] of Object.entries(defaultKeys)) { + if (value.split(' + ')[0] == 'Ctrl'); + macDefaultKeys[key] = + value.split(' + ')[0] == 'Ctrl' + ? value.replace('Ctrl', 'Meta') + : value + localStorage.set('defaultKeys', macDefaultKeys) + } + } else { + localStorage.set('defaultKeys', defaultKeys) //TODO add a confirmation alert + } + addKeys('default') +} +/** + * function to check if user entered keys are already assigned to other key + * gives a warning message if keys already assigned + * @param {string} combo the key combo + * @param {string} target the target option of the panel + */ +export const warnOverride = (combo, target, warning) => { + let x = 0 + while ($('#preference').children()[x]) { + if ( + $('#preference').children()[x].children[1].children[1].innerText === + combo && + $('#preference').children()[x].children[1].children[0].innerText !== + target.previousElementSibling.innerText + ) { + const assignee = + $('#preference').children()[x].children[1].children[0].innerText + // $('#warning').text( + // `This key(s) is already assigned to: ${assignee}, press Enter to override.` + // ) + warning.value = `This key(s) is already assigned to: ${assignee}, press Enter to override.` + $('#edit').css('border', '1.5px solid #dc5656') + return + } else { + $('#edit').css('border', 'none') + } + x++ + } +} + +export const elementDirection = (direct) => () => { + if (simulationArea.lastSelected) { + simulationArea.lastSelected.newDirection(direct.toUpperCase()) + $("select[name |= 'newDirection']").val(direct.toUpperCase()) + updateSystem() + } +} + +export const labelDirection = (direct) => () => { + if ( + simulationArea.lastSelected && + !simulationArea.lastSelected.labelDirectionFixed + ) { + simulationArea.lastSelected.labelDirection = direct.toUpperCase() + $("select[name |= 'newLabelDirection']").val(direct.toUpperCase()) + updateSystem() + } +} + +export const insertLabel = () => { + if (simulationArea.lastSelected) { + $("input[name |= 'setLabel']").focus() + $("input[name |= 'setLabel']").val().length + ? null + : $("input[name |= 'setLabel']").val('Untitled') + $("input[name |= 'setLabel']").select() + updateSystem() + } +} + +export const moveElement = (direct) => () => { + if (simulationArea.lastSelected) { + switch (direct) { + case 'up': + simulationArea.lastSelected.y -= 10 + break + case 'down': + simulationArea.lastSelected.y += 10 + break + case 'left': + simulationArea.lastSelected.x -= 10 + break + case 'right': + simulationArea.lastSelected.x += 10 + break + } + updateSystem() + } +} + +export const openHotkey = () => $('#customShortcut').trigger('click') + +export const createNewCircuitScopeCall = () => + $('#createNewCircuitScope').trigger('click') // TODO: remove later + +export const openDocumentation = () => { + if ( + simulationArea.lastSelected == undefined || + simulationArea.lastSelected.helplink == undefined + ) { + // didn't select any element or documentation not found + window.open('https://docs.circuitverse.org/', '_blank') + } else { + window.open(simulationArea.lastSelected.helplink, '_blank') + } +} + +function updateSystem() { + updateCanvasSet(true) + wireToBeCheckedSet(1) + scheduleUpdate(1) +} diff --git a/v1/src/simulator/src/hotkey_binder/model/addShortcut.js b/v1/src/simulator/src/hotkey_binder/model/addShortcut.js new file mode 100644 index 00000000..27834ab1 --- /dev/null +++ b/v1/src/simulator/src/hotkey_binder/model/addShortcut.js @@ -0,0 +1,102 @@ +// import { shortcut } from './Shortcuts.plugin'; +// import createSaveAsImgPrompt from '../../data/saveImage'; +//Assign the callback func for the keymap here +import { + createNewCircuitScopeCall, + elementDirection, + insertLabel, + labelDirection, + openHotkey, + moveElement, + openDocumentation, +} from './actions' +import save from '../../data/save' +import { saveOffline, openOffline } from '../../data/project' +import createSaveAsImgPrompt from '../../data/saveImage' +import { createSubCircuitPrompt } from '../../subcircuit' +import { createCombinationalAnalysisPrompt } from '../../combinationalAnalysis' +import { shortcut } from './shortcuts.plugin.js' + +export const addShortcut = (keys, action) => { + let callback + switch (action) { + case 'New Circuit': + callback = createNewCircuitScopeCall // TODO: directly call rather than using dom click + break + case 'Save Online': + callback = save + break + case 'Save Offline': + callback = saveOffline + break + case 'Download as Image': + callback = createSaveAsImgPrompt + break + case 'Open Offline': + callback = openOffline + break + case 'Insert Sub-circuit': + callback = createSubCircuitPrompt + break + case 'Combinational Analysis': + callback = createCombinationalAnalysisPrompt + break //bug + // case "Start Plot": + // callback = startPlot; + // break; + case 'Direction Up': + callback = elementDirection('up') + break + case 'Direction Down': + callback = elementDirection('down') + break + case 'Direction Left': + callback = elementDirection('left') + break + case 'Direction Right': + callback = elementDirection('right') + break + case 'Insert Label': + callback = insertLabel + break + case 'Label Direction Up': + callback = labelDirection('up') + break + case 'Label Direction Down': + callback = labelDirection('down') + break + case 'Label Direction Left': + callback = labelDirection('left') + break + case 'Label Direction Right': + callback = labelDirection('right') + break + case 'Move Element Up': + callback = moveElement('up') + break + case 'Move Element Down': + callback = moveElement('down') + break + case 'Move Element Left': + callback = moveElement('left') + break + case 'Move Element Right': + callback = moveElement('right') + break + case 'Hotkey Preference': + callback = openHotkey + break + case 'Open Documentation': + callback = openDocumentation + break + default: + callback = () => console.log('No shortcut found..') + break + } + shortcut.add(keys, callback, { + type: 'keydown', + propagate: false, + target: document, + disable_in_input: true, + }) +} diff --git a/v1/src/simulator/src/hotkey_binder/model/normalize/normalizer.plugin.js b/v1/src/simulator/src/hotkey_binder/model/normalize/normalizer.plugin.js new file mode 100644 index 00000000..cc9a7def --- /dev/null +++ b/v1/src/simulator/src/hotkey_binder/model/normalize/normalizer.plugin.js @@ -0,0 +1,389 @@ +/** + * This plugin has been modified to support metakeys + */ + +/* + * Library to normalize key codes across browsers. This works with keydown + * events; keypress events are not fired for all keys, and the codes are + * different for them. It returns an object with the following fields: + * { int code, bool shift, bool alt, bool ctrl }. The normalized keycodes + * obey the following rules: + * + * For alphabetic characters, the ASCII code of the uppercase version + * + * For codes that are identical across all browsers (this includes all + * modifiers, esc, delete, arrows, etc.), the common keycode + * + * For numeric keypad keys, the value returned by numkey(). + * (Usually 96 + the number) + * + * For symbols, the ASCII code of the character that appears when shift + * is not held down, EXCEPT for '" => 222 (conflicts with right-arrow/pagedown), + * .> => 190 (conflicts with Delete) and `~ => 126 (conflicts with Num0). + * + * Basic usage: + * document.onkeydown = function(e) { + * do_something_with(KeyCode.translateEvent(e) + * }; + * + * The naming conventions for functions use 'code' to represent an integer + * keycode, 'key' to represent a key description (specified above), and 'e' + * to represent an event object. + * + * There's also functionality to track and detect which keys are currently + * being held down: install 'key_up' and 'key_down' on their respective event + * handlers, and then check with 'is_down'. + * + * @fileoverview + * @author Jonathan Tang + * @version 0.9 + * @license BSD + */ + +/* +Copyright (c) 2008 Jonathan Tang +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +var modifiers = ['ctrl', 'alt', 'shift', 'meta'], + KEY_MAP = {}, + shifted_symbols = { + 58: 59, // : -> ; + 43: 61, // = -> + + 60: 44, // < -> , + 95: 45, // _ -> - + 62: 46, // > -> . + 63: 47, // ? -> / + 96: 192, // ` -> ~ + 124: 92, // | -> \ + 39: 222, // ' -> 222 + 34: 222, // " -> 222 + 33: 49, // ! -> 1 + 64: 50, // @ -> 2 + 35: 51, // # -> 3 + 36: 52, // $ -> 4 + 37: 53, // % -> 5 + 94: 54, // ^ -> 6 + 38: 55, // & -> 7 + 42: 56, // * -> 8 + 40: 57, // ( -> 9 + 41: 58, // ) -> 0 + 123: 91, // { -> [ + 125: 93, // } -> ] + } + +function isLower(ascii) { + return ascii >= 97 && ascii <= 122 +} +function capitalize(str) { + return str.substr(0, 1).toUpperCase() + str.substr(1).toLowerCase() +} + +var is_gecko = navigator.userAgent.indexOf('Gecko') != -1, + is_ie = navigator.userAgent.indexOf('MSIE') != -1, + is_windows = navigator.platform.indexOf('Win') != -1, + is_opera = window.opera && window.opera.version() < 9.5, + is_konqueror = navigator.vendor && navigator.vendor.indexOf('KDE') != -1, + is_icab = navigator.vendor && navigator.vendor.indexOf('iCab') != -1 + +var GECKO_IE_KEYMAP = { + 186: 59, // ;: in IE + 187: 61, // =+ in IE + 188: 44, // ,< + 109: 95, // -_ in Mozilla + 107: 61, // =+ in Mozilla + 189: 95, // -_ in IE + 190: 62, // .> + 191: 47, // /? + 192: 126, // `~ + 219: 91, // {[ + 220: 92, // \| + 221: 93, // }] +} + +var OPERA_KEYMAP = {} + +// Browser detection taken from quirksmode.org +if (is_opera && is_windows) { + KEY_MAP = OPERA_KEYMAP +} else if (is_opera || is_konqueror || is_icab) { + var unshift = [ + 33, 64, 35, 36, 37, 94, 38, 42, 40, 41, 58, 43, 60, 95, 62, 63, 124, 34, + ] + KEY_MAP = OPERA_KEYMAP + for (var i = 0; i < unshift.length; ++i) { + KEY_MAP[unshift[i]] = shifted_symbols[unshift[i]] + } +} else { + // IE and Gecko are close enough that we can use the same map for both, + // and the rest of the world (eg. Opera 9.50) seems to be standardizing + // on them + KEY_MAP = GECKO_IE_KEYMAP +} + +if (is_konqueror) { + KEY_MAP[0] = 45 + KEY_MAP[127] = 46 + KEY_MAP[45] = 95 +} + +var key_names = { + 32: 'SPACE', + 13: 'ENTER', + 9: 'TAB', + 8: 'BACKSPACE', + 16: 'SHIFT', + 17: 'CTRL', + 18: 'ALT', + 20: 'CAPS_LOCK', + 144: 'NUM_LOCK', + 145: 'SCROLL_LOCK', + 37: 'LEFT', + 38: 'UP', + 39: 'RIGHT', + 40: 'DOWN', + 33: 'PAGE_UP', + 34: 'PAGE_DOWN', + 36: 'HOME', + 35: 'END', + 45: 'INSERT', + 46: 'DELETE', + 27: 'ESCAPE', + 19: 'PAUSE', + 222: "'", + 91: 'META', +} +function fn_name(code) { + if (code >= 112 && code <= 123) return 'F' + (code - 111) + return false +} +function num_name(code) { + if (code >= 96 && code < 106) return 'Num' + (code - 96) + switch (code) { + case 106: + return 'Num*' + case 111: + return 'Num/' + case 110: + return 'Num.' + default: + return false + } +} + +var current_keys = { + codes: {}, + ctrl: false, + alt: false, + shift: false, + meta: false, +} + +function update_current_modifiers(key) { + current_keys.ctrl = key.ctrl + current_keys.alt = key.alt + current_keys.shift = key.shift + current_keys.meta = key.meta +} + +function same_modifiers(key1, key2) { + return ( + key1.ctrl === key2.ctrl && + key1.alt === key2.alt && + key1.shift === key2.shift && + key1.meta === key2.meta + ) +} + +if (typeof window.KeyCode != 'undefined') { + var _KeyCode = window.KeyCode +} + +export const KeyCode = { + no_conflict: function () { + window.KeyCode = _KeyCode + return KeyCode + }, + + /** Generates a function key code from a number between 1 and 12 */ + fkey: function (num) { + return 111 + num + }, + + /** + * Generates a numeric keypad code from a number between 0 and 9. + * Also works for (some) arithmetic operators. The mappings are: + * + * *: 106, /: 111, .: 110 + * + * + and - are not supported because the keycodes generated by Mozilla + * conflict with the non-keypad codes. The same applies to all the + * arithmetic keypad keys on Konqueror and early Opera. + */ + numkey: function (num) { + switch (num) { + case '*': + return 106 + case '/': + return 111 + case '.': + return 110 + default: + return 96 + num + } + }, + + /** + * Generates a key code from the ASCII code of (the first character of) a + * string. + */ + key: function (str) { + var c = str.charCodeAt(0) + if (isLower(c)) return c - 32 + return shifted_symbols[c] || c + }, + + /** Checks if two key objects are equal. */ + key_equals: function (key1, key2) { + return key1.code == key2.code && same_modifiers(key1, key2) + }, + + /** Translates a keycode to its normalized value. */ + translate_key_code: function (code) { + return KEY_MAP[code] || code + }, + + /** + * Translates a keyDown event to a normalized key event object. The + * object has the following fields: + * { int code; boolean shift, boolean alt, boolean ctrl } + */ + translate_event: function (e) { + e = e || window.event + var code = e.which || e.keyCode + return { + code: KeyCode.translate_key_code(code), + shift: e.shiftKey, + alt: e.altKey, + ctrl: e.ctrlKey, + meta: e.metaKey, + } + }, + + /** + * Keydown event listener to update internal state of which keys are + * currently pressed. + */ + + key_down: function (e) { + var key = KeyCode.translate_event(e) + current_keys.codes[key.code] = key.code + update_current_modifiers(key) + }, + + /** + * Keyup event listener to update internal state. + */ + key_up: function (e) { + var key = KeyCode.translate_event(e) + delete current_keys.codes[key.code] + update_current_modifiers(key) + }, + + /** + * Returns true if the key spec (as returned by translate_event) is + * currently held down. + */ + is_down: function (key) { + var code = key.code + if (code == KeyCode.CTRL) return current_keys.ctrl + if (code == KeyCode.ALT) return current_keys.alt + if (code == KeyCode.SHIFT) return current_keys.shift + + return ( + current_keys.codes[code] !== undefined && + same_modifiers(key, current_keys) + ) + }, + + /** + * Returns a string representation of a key event suitable for the + * shortcut.js or JQuery HotKeys plugins. Also makes a decent UI display. + */ + hot_key: function (key) { + var pieces = [] + for (var i = 0; i < modifiers.length; ++i) { + var modifier = modifiers[i] + if ( + key[modifier] && + modifier.toUpperCase() != key_names[key.code] + ) { + pieces.push(capitalize(modifier)) + } + } + + var c = key.code + var key_name = + key_names[c] || fn_name(c) || num_name(c) || String.fromCharCode(c) + pieces.push(capitalize(key_name)) + return pieces.join('+') + }, +} + +// Add key constants +for (var code in key_names) { + KeyCode[key_names[code]] = code +} + +// var fields = ['charCode', 'keyCode', 'which', 'type', 'timeStamp', +// 'altKey', 'ctrlKey', 'shiftKey', 'metaKey']; +// var types = ['keydown', 'keypress', 'keyup']; + +// function show_event(type) { +// return function(e) { +// var lines = []; +// for(var i = 0; i < fields.length; ++i) { +// lines.push('' + fields[i] + ': ' + e[fields[i]] + '
'); +// } +// document.getElementByI(type).innerHTML = lines.join('\n'); +// return false; +// } +// }; + +// function show_is_key_down(id, code, ctrl, alt, shift) { +// document.getElementById(id + '_down').innerHTML = KeyCode.is_down({ +// code: code, +// ctrl: ctrl, +// alt: alt, +// shift: shift +// }); +// }; + +// function update_key_downs() { +// show_is_key_down('x', KeyCode.key('x'), false, false, false); +// show_is_key_down('shift_x', KeyCode.key('x'), false, false, true); +// show_is_key_down('shift_c', KeyCode.key('c'), false, false, true); +// show_is_key_down('ctrl_a', KeyCode.key('a'), true, false, false); +// }; diff --git a/v1/src/simulator/src/hotkey_binder/model/shortcuts.plugin.js b/v1/src/simulator/src/hotkey_binder/model/shortcuts.plugin.js new file mode 100644 index 00000000..f80d37bd --- /dev/null +++ b/v1/src/simulator/src/hotkey_binder/model/shortcuts.plugin.js @@ -0,0 +1,250 @@ +/** + * http://www.openjs.com/scripts/events/keyboard_shortcuts/ + * Version : 2.01.B + * By Binny V A + * License : BSD + */ + +/** + * Restrictions: + * The shortcut key combination should be specified in this format ... Modifier[+Modifier..]+Key. + * Can have a single key without Modifier .. Key, not Key + Key + * These restrictions must be be hardcoded to not let users input invalid key combo + * There is no way to override Ctrl+N, Ctrl+T, or Ctrl+W in Google Chrome since version 4 of Chrome (shipped in 2010). + * + **/ + +//*! This plugin has been modified + +export const shortcut = { + all_shortcuts: {}, //All the shortcuts are stored in this array ex. download : keycombo; + add: function (shortcut_combination, callback, opt) { + //Provide a set of default options + var default_options = { + type: 'keydown', + propagate: false, + disable_in_input: true, + target: document, + keycode: false, + } + + if (!opt) opt = default_options + else { + for (var dfo in default_options) { + if (typeof opt[dfo] == 'undefined') + opt[dfo] = default_options[dfo] + } + } + + var ele = opt.target + if (typeof opt.target == 'string') + ele = document.getElementById(opt.target) + var ths = this + shortcut_combination = shortcut_combination.toLowerCase() + + //The function to be called at keypress + var func = function (e) { + e = e || window.event + if (opt['disable_in_input']) { + //Don't enable shortcut keys in Input, Textarea fields + var element + if (e.target) element = e.target + else if (e.srcElement) element = e.srcElement + if (element.nodeType == 3) element = element.parentNode + + if (element.tagName == 'INPUT' || element.tagName == 'TEXTAREA') + return + } + + let code = '' + //Find Which key is pressed + if (e.keyCode) code = e.keyCode + else if (e.which) code = e.which + var character = String.fromCharCode(code).toLowerCase() + // e.preventDefault(); + + if (code == 188) character = ',' //If the user presses , when the type is onkeydown + if (code == 190) character = '.' //If the user presses , when the type is onkeydown + + var keys = shortcut_combination.split('+') + //Key Pressed - counts the number of valid keypresses - if it is same as the number of keys, the shortcut function is invoked + var kp = 0 + + //Work around for stupid Shift key bug created by using lowercase - as a result the shift+num combination was broken + var shift_nums = { + '`': '~', + 1: '!', + 2: '@', + 3: '#', + 4: '$', + 5: '%', + 6: '^', + 7: '&', + 8: '*', + 9: '(', + 0: ')', + '-': '_', + '=': '+', + ';': ':', + "'": '"', + ',': '<', + '.': '>', + '/': '?', + '\\': '|', + } + //Special Keys - and their codes + var special_keys = { + esc: 27, + escape: 27, + tab: 9, + space: 32, + return: 13, + enter: 13, + backspace: 8, + + scrolllock: 145, + scroll_lock: 145, + scroll: 145, + capslock: 20, + caps_lock: 20, + caps: 20, + numlock: 144, + num_lock: 144, + num: 144, + + pause: 19, + break: 19, + + insert: 45, + home: 36, + delete: 46, + end: 35, + + pageup: 33, + page_up: 33, + pu: 33, + + pagedown: 34, + page_down: 34, + pd: 34, + + left: 37, + up: 38, + right: 39, + down: 40, + + f1: 112, + f2: 113, + f3: 114, + f4: 115, + f5: 116, + f6: 117, + f7: 118, + f8: 119, + f9: 120, + f10: 121, + f11: 122, + f12: 123, + } + + var modifiers = { + shift: { wanted: false, pressed: false }, + ctrl: { wanted: false, pressed: false }, + alt: { wanted: false, pressed: false }, + meta: { wanted: false, pressed: false }, //Meta is Mac specific + } + + if (e.ctrlKey) modifiers.ctrl.pressed = true + if (e.shiftKey) modifiers.shift.pressed = true + if (e.altKey) modifiers.alt.pressed = true + if (e.metaKey) modifiers.meta.pressed = true + + let k + for (var i = 0; (k = keys[i]), i < keys.length; i++) { + //Modifiers + if (k == 'ctrl' || k == 'control') { + kp++ + modifiers.ctrl.wanted = true + } else if (k == 'shift') { + kp++ + modifiers.shift.wanted = true + } else if (k == 'alt') { + kp++ + modifiers.alt.wanted = true + } else if (k == 'meta') { + kp++ + modifiers.meta.wanted = true + } else if (k.length > 1) { + //If it is a special key + if (special_keys[k] == code) kp++ + } else if (opt['keycode']) { + if (opt['keycode'] == code) kp++ + } else { + //The special keys did not match + if (character == k) kp++ + else { + if (shift_nums[character] && e.shiftKey) { + //Stupid Shift key bug created by using lowercase + character = shift_nums[character] + if (character == k) kp++ + } + } + } + } + + if ( + kp == keys.length && + modifiers.ctrl.pressed == modifiers.ctrl.wanted && + modifiers.shift.pressed == modifiers.shift.wanted && + modifiers.alt.pressed == modifiers.alt.wanted && + modifiers.meta.pressed == modifiers.meta.wanted + ) { + callback(e) + + if (!opt['propagate']) { + //Stop the event + //e.cancelBubble is supported by IE - this will kill the bubbling process. + e.cancelBubble = true + e.returnValue = false + + //e.stopPropagation works in Firefox. + if (e.stopPropagation) { + e.stopPropagation() + e.preventDefault() + } + return false + } + } + } + this.all_shortcuts[shortcut_combination] = { + callback: func, + target: ele, + event: opt['type'], + } + //Attach the function with the event + if (ele.addEventListener) ele.addEventListener(opt['type'], func, false) + else if (ele.attachEvent) ele.attachEvent('on' + opt['type'], func) + else ele['on' + opt['type']] = func + }, + + //Remove the shortcut - just specify the shortcut and I will remove the binding + remove: function (shortcut_combination) { + shortcut_combination = shortcut_combination.toLowerCase() + var binding = this.all_shortcuts[shortcut_combination] + delete this.all_shortcuts[shortcut_combination] + if (!binding) return + var type = binding['event'] + var ele = binding['target'] + var callback = binding['callback'] + + if (ele.detachEvent) ele.detachEvent('on' + type, callback) + else if (ele.removeEventListener) + ele.removeEventListener(type, callback, false) + else ele['on' + type] = false + }, + removeAll: function () { + for (let x in this.all_shortcuts) { + this.remove(x) + } + }, +} diff --git a/v1/src/simulator/src/hotkey_binder/model/utils.js b/v1/src/simulator/src/hotkey_binder/model/utils.js new file mode 100644 index 00000000..8a2a8a8b --- /dev/null +++ b/v1/src/simulator/src/hotkey_binder/model/utils.js @@ -0,0 +1,67 @@ +Storage.prototype.set = function (key, obj) { + return this.setItem(key, JSON.stringify(obj)) +} + +Storage.prototype.get = function (key) { + return JSON.parse(this.getItem(key)) +} + +Object.size = function (obj) { + var size = 0, + key + for (key in obj) { + if (obj.hasOwnProperty(key)) size++ + } + return size +} + +export const getKey = (obj, val) => + Object.keys(obj).find((key) => obj[key] === val) + +export const getOS = () => { + let OSName = '' + if (navigator.appVersion.indexOf('Win') != -1) OSName = 'Windows' + if (navigator.appVersion.indexOf('Mac') != -1) OSName = 'MacOS' + if (navigator.appVersion.indexOf('X11') != -1) OSName = 'UNIX' + if (navigator.appVersion.indexOf('Linux') != -1) OSName = 'Linux' + return OSName +} + +export const checkRestricted = (key) => { + const restrictedKeys = [ + 'Ctrl + N', + 'Ctrl + W', + 'Ctrl + T', + 'Ctrl + C', + 'Ctrl + V', + 'Ctrl + Delete', + 'Ctrl + Backspace', + 'Ctrl + /', + 'Ctrl + \\', + 'Ctrl + ]', + "Ctrl + '", + 'Ctrl + `', + 'Ctrl + [', + 'Ctrl + ~', + 'Ctrl + Num1', + 'Ctrl + Num2', + 'Ctrl + Num3', + 'Ctrl + Num4', + 'Ctrl + Num5', + 'Ctrl + Num6', + 'Ctrl + Num*', + 'Ctrl + Num/', + 'Ctrl + Num.', + 'Ctrl + Num0', + ] + if (getOS == 'macOS') { + restrictedKeys.forEach((value, i) => { + if (value.split(' + ')[0] == 'Ctrl'); + restrictedKeys[i] = + value.split(' + ')[0] == 'Ctrl' + ? value.replace('Ctrl', 'Meta') + : value + }) + } + return restrictedKeys.includes(key) +} diff --git a/v1/src/simulator/src/hotkey_binder/view/panel.ui.js b/v1/src/simulator/src/hotkey_binder/view/panel.ui.js new file mode 100644 index 00000000..06222901 --- /dev/null +++ b/v1/src/simulator/src/hotkey_binder/view/panel.ui.js @@ -0,0 +1,60 @@ +import { setUserKeys } from '../model/actions' + +/** + * fn to update the htokey panel UI with the currently set configuration + * @param {string} mode user prefered if present, or default keys configuration + */ +export const updateHTML = (mode) => { + let x = 0 + if (mode == 'user') { + const userKeys = localStorage.get('userKeys') + while ($('#preference').children()[x]) { + $('#preference').children()[x].children[1].children[1].innerText = + userKeys[ + $('#preference').children()[ + x + ].children[1].children[0].innerText + ] + x++ + } + } else if (mode == 'default') { + while ($('#preference').children()[x]) { + const defaultKeys = localStorage.get('defaultKeys') + $('#preference').children()[x].children[1].children[1].innerText = + defaultKeys[ + $('#preference').children()[ + x + ].children[1].children[0].innerText + ] + x++ + } + } +} +/** + * fn to override key of duplicate entries + * old entry will be left blank & keys will be assigned to the new target + * @param {*} combo + */ +export const override = (combo) => { + let x = 0 + while ($('#preference').children()[x]) { + if ( + $('#preference').children()[x].children[1].children[1].innerText === + combo + ) + $('#preference').children()[x].children[1].children[1].innerText = + '' + x++ + } +} + +export const closeEdit = () => { + $('#pressedKeys').text('') + $('#edit').css('display', 'none') +} + +export const submit = () => { + $('#edit').css('display', 'none') + setUserKeys() + updateHTML('user') +} diff --git a/v1/src/simulator/src/i18n.js b/v1/src/simulator/src/i18n.js new file mode 100644 index 00000000..602f4c51 --- /dev/null +++ b/v1/src/simulator/src/i18n.js @@ -0,0 +1,17 @@ +import Banana from 'banana-i18n' + +const banana = new Banana() +banana.setLocale(window.locale) +const { locale } = banana +const finalFallback = 'en' +// object with default language preloaded +const messages = { + [finalFallback]: require(`./i18n/${finalFallback}.json`), +} +try { + messages[locale] = require(`./i18n/${locale}.json`) +} catch (err) { + // If Asynchronous loading for current locale failed, load default locale +} +banana.load(messages) +export default banana diff --git a/v1/src/simulator/src/i18n/en.json b/v1/src/simulator/src/i18n/en.json new file mode 100644 index 00000000..e9fe2e17 --- /dev/null +++ b/v1/src/simulator/src/i18n/en.json @@ -0,0 +1,8 @@ +{ + "@metadata": { + "authors": ["Pavan"], + "last-updated": "2021-07-06", + "locale": "en", + "message-documentation": "qqq" + } +} diff --git a/v1/src/simulator/src/i18n/hi.json b/v1/src/simulator/src/i18n/hi.json new file mode 100644 index 00000000..96f4543e --- /dev/null +++ b/v1/src/simulator/src/i18n/hi.json @@ -0,0 +1,8 @@ +{ + "@metadata": { + "authors": ["Pavan"], + "last-updated": "2021-07-06", + "locale": "hi", + "message-documentation": "qqq" + } +} diff --git a/v1/src/simulator/src/img/ALU.png b/v1/src/simulator/src/img/ALU.png new file mode 100644 index 00000000..fa809425 Binary files /dev/null and b/v1/src/simulator/src/img/ALU.png differ diff --git a/v1/src/simulator/src/img/ALU.svg b/v1/src/simulator/src/img/ALU.svg new file mode 100644 index 00000000..0db7c17a --- /dev/null +++ b/v1/src/simulator/src/img/ALU.svg @@ -0,0 +1 @@ +BACTRCarryAnsALU \ No newline at end of file diff --git a/v1/src/simulator/src/img/Adder.svg b/v1/src/simulator/src/img/Adder.svg new file mode 100644 index 00000000..9249cff0 --- /dev/null +++ b/v1/src/simulator/src/img/Adder.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/AndGate.svg b/v1/src/simulator/src/img/AndGate.svg new file mode 100644 index 00000000..6273b83b --- /dev/null +++ b/v1/src/simulator/src/img/AndGate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/Arrow.svg b/v1/src/simulator/src/img/Arrow.svg new file mode 100644 index 00000000..c25f20a1 --- /dev/null +++ b/v1/src/simulator/src/img/Arrow.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/AsyncCounter.jpeg b/v1/src/simulator/src/img/AsyncCounter.jpeg new file mode 100644 index 00000000..88a40590 Binary files /dev/null and b/v1/src/simulator/src/img/AsyncCounter.jpeg differ diff --git a/v1/src/simulator/src/img/BitSelector.svg b/v1/src/simulator/src/img/BitSelector.svg new file mode 100644 index 00000000..b0436573 --- /dev/null +++ b/v1/src/simulator/src/img/BitSelector.svg @@ -0,0 +1 @@ +x \ No newline at end of file diff --git a/v1/src/simulator/src/img/Buffer.svg b/v1/src/simulator/src/img/Buffer.svg new file mode 100644 index 00000000..bf867043 --- /dev/null +++ b/v1/src/simulator/src/img/Buffer.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/Button.svg b/v1/src/simulator/src/img/Button.svg new file mode 100644 index 00000000..6c16a24e --- /dev/null +++ b/v1/src/simulator/src/img/Button.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/Clock.svg b/v1/src/simulator/src/img/Clock.svg new file mode 100644 index 00000000..653d29e0 --- /dev/null +++ b/v1/src/simulator/src/img/Clock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/ConstantVal.svg b/v1/src/simulator/src/img/ConstantVal.svg new file mode 100644 index 00000000..dcc41214 --- /dev/null +++ b/v1/src/simulator/src/img/ConstantVal.svg @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/v1/src/simulator/src/img/Control Sequencer.png b/v1/src/simulator/src/img/Control Sequencer.png new file mode 100644 index 00000000..2189f0a7 Binary files /dev/null and b/v1/src/simulator/src/img/Control Sequencer.png differ diff --git a/v1/src/simulator/src/img/ControlledInverter.svg b/v1/src/simulator/src/img/ControlledInverter.svg new file mode 100644 index 00000000..927dc8c8 --- /dev/null +++ b/v1/src/simulator/src/img/ControlledInverter.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/Counter.svg b/v1/src/simulator/src/img/Counter.svg new file mode 100644 index 00000000..ee2aa77b --- /dev/null +++ b/v1/src/simulator/src/img/Counter.svg @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/v1/src/simulator/src/img/Decoder.svg b/v1/src/simulator/src/img/Decoder.svg new file mode 100644 index 00000000..9a48c394 --- /dev/null +++ b/v1/src/simulator/src/img/Decoder.svg @@ -0,0 +1 @@ +01 \ No newline at end of file diff --git a/v1/src/simulator/src/img/Demultiplexer.svg b/v1/src/simulator/src/img/Demultiplexer.svg new file mode 100644 index 00000000..61cad635 --- /dev/null +++ b/v1/src/simulator/src/img/Demultiplexer.svg @@ -0,0 +1 @@ +01 \ No newline at end of file diff --git a/v1/src/simulator/src/img/DflipFlop.svg b/v1/src/simulator/src/img/DflipFlop.svg new file mode 100644 index 00000000..49f4ab5f --- /dev/null +++ b/v1/src/simulator/src/img/DflipFlop.svg @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/v1/src/simulator/src/img/DigitalLed.svg b/v1/src/simulator/src/img/DigitalLed.svg new file mode 100644 index 00000000..a9259c2f --- /dev/null +++ b/v1/src/simulator/src/img/DigitalLed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/Dlatch.svg b/v1/src/simulator/src/img/Dlatch.svg new file mode 100644 index 00000000..8aef7671 --- /dev/null +++ b/v1/src/simulator/src/img/Dlatch.svg @@ -0,0 +1 @@ +3 \ No newline at end of file diff --git a/v1/src/simulator/src/img/EEPROM.svg b/v1/src/simulator/src/img/EEPROM.svg new file mode 100644 index 00000000..504f61b0 --- /dev/null +++ b/v1/src/simulator/src/img/EEPROM.svg @@ -0,0 +1 @@ +EPROMADIWDO \ No newline at end of file diff --git a/v1/src/simulator/src/img/Flag.svg b/v1/src/simulator/src/img/Flag.svg new file mode 100644 index 00000000..87ef27f4 --- /dev/null +++ b/v1/src/simulator/src/img/Flag.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/FlipFlop.jpeg b/v1/src/simulator/src/img/FlipFlop.jpeg new file mode 100644 index 00000000..5c3d87ce Binary files /dev/null and b/v1/src/simulator/src/img/FlipFlop.jpeg differ diff --git a/v1/src/simulator/src/img/ForceGate.svg b/v1/src/simulator/src/img/ForceGate.svg new file mode 100644 index 00000000..28b50f3f --- /dev/null +++ b/v1/src/simulator/src/img/ForceGate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/Ground.svg b/v1/src/simulator/src/img/Ground.svg new file mode 100644 index 00000000..70f453f6 --- /dev/null +++ b/v1/src/simulator/src/img/Ground.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/HexDisplay.svg b/v1/src/simulator/src/img/HexDisplay.svg new file mode 100644 index 00000000..10c89e13 --- /dev/null +++ b/v1/src/simulator/src/img/HexDisplay.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/ImageAnnotation.svg b/v1/src/simulator/src/img/ImageAnnotation.svg new file mode 100644 index 00000000..8fbd9f75 --- /dev/null +++ b/v1/src/simulator/src/img/ImageAnnotation.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/Input.svg b/v1/src/simulator/src/img/Input.svg new file mode 100644 index 00000000..42a626ff --- /dev/null +++ b/v1/src/simulator/src/img/Input.svg @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/v1/src/simulator/src/img/JKflipFlop.svg b/v1/src/simulator/src/img/JKflipFlop.svg new file mode 100644 index 00000000..9e723197 --- /dev/null +++ b/v1/src/simulator/src/img/JKflipFlop.svg @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/v1/src/simulator/src/img/Keyboard.jpeg b/v1/src/simulator/src/img/Keyboard.jpeg new file mode 100644 index 00000000..9922daff Binary files /dev/null and b/v1/src/simulator/src/img/Keyboard.jpeg differ diff --git a/v1/src/simulator/src/img/Keyboard.svg b/v1/src/simulator/src/img/Keyboard.svg new file mode 100644 index 00000000..6e215f6f --- /dev/null +++ b/v1/src/simulator/src/img/Keyboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/LSB.svg b/v1/src/simulator/src/img/LSB.svg new file mode 100644 index 00000000..31148b47 --- /dev/null +++ b/v1/src/simulator/src/img/LSB.svg @@ -0,0 +1 @@ +LSBEN \ No newline at end of file diff --git a/v1/src/simulator/src/img/MSB.svg b/v1/src/simulator/src/img/MSB.svg new file mode 100644 index 00000000..21997b94 --- /dev/null +++ b/v1/src/simulator/src/img/MSB.svg @@ -0,0 +1 @@ +MSBEN \ No newline at end of file diff --git a/v1/src/simulator/src/img/Main.png b/v1/src/simulator/src/img/Main.png new file mode 100644 index 00000000..3bb25dac Binary files /dev/null and b/v1/src/simulator/src/img/Main.png differ diff --git a/v1/src/simulator/src/img/Multiplexer.svg b/v1/src/simulator/src/img/Multiplexer.svg new file mode 100644 index 00000000..b1b8472d --- /dev/null +++ b/v1/src/simulator/src/img/Multiplexer.svg @@ -0,0 +1 @@ +01 \ No newline at end of file diff --git a/v1/src/simulator/src/img/NandGate.svg b/v1/src/simulator/src/img/NandGate.svg new file mode 100644 index 00000000..04cddce2 --- /dev/null +++ b/v1/src/simulator/src/img/NandGate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/NorGate.svg b/v1/src/simulator/src/img/NorGate.svg new file mode 100644 index 00000000..55e9abc2 --- /dev/null +++ b/v1/src/simulator/src/img/NorGate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/NotGate.svg b/v1/src/simulator/src/img/NotGate.svg new file mode 100644 index 00000000..a0d40ad4 --- /dev/null +++ b/v1/src/simulator/src/img/NotGate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/OrGate.svg b/v1/src/simulator/src/img/OrGate.svg new file mode 100644 index 00000000..741ba9fe --- /dev/null +++ b/v1/src/simulator/src/img/OrGate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/Output.svg b/v1/src/simulator/src/img/Output.svg new file mode 100644 index 00000000..7d4298f6 --- /dev/null +++ b/v1/src/simulator/src/img/Output.svg @@ -0,0 +1 @@ +x \ No newline at end of file diff --git a/v1/src/simulator/src/img/Power.svg b/v1/src/simulator/src/img/Power.svg new file mode 100644 index 00000000..a4619920 --- /dev/null +++ b/v1/src/simulator/src/img/Power.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/PriorityEncoder.svg b/v1/src/simulator/src/img/PriorityEncoder.svg new file mode 100644 index 00000000..417325c6 --- /dev/null +++ b/v1/src/simulator/src/img/PriorityEncoder.svg @@ -0,0 +1 @@ +010EN \ No newline at end of file diff --git a/v1/src/simulator/src/img/RAM.svg b/v1/src/simulator/src/img/RAM.svg new file mode 100644 index 00000000..92fd8293 --- /dev/null +++ b/v1/src/simulator/src/img/RAM.svg @@ -0,0 +1 @@ +RAMADIWDO \ No newline at end of file diff --git a/v1/src/simulator/src/img/RGBLed.svg b/v1/src/simulator/src/img/RGBLed.svg new file mode 100644 index 00000000..299657c8 --- /dev/null +++ b/v1/src/simulator/src/img/RGBLed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/RGBLedMatrix.svg b/v1/src/simulator/src/img/RGBLedMatrix.svg new file mode 100644 index 00000000..cbb52e83 --- /dev/null +++ b/v1/src/simulator/src/img/RGBLedMatrix.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/Random.svg b/v1/src/simulator/src/img/Random.svg new file mode 100644 index 00000000..4da0fa99 --- /dev/null +++ b/v1/src/simulator/src/img/Random.svg @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/v1/src/simulator/src/img/Rectangle.svg b/v1/src/simulator/src/img/Rectangle.svg new file mode 100644 index 00000000..f2ac9773 --- /dev/null +++ b/v1/src/simulator/src/img/Rectangle.svg @@ -0,0 +1 @@ +Rectangle \ No newline at end of file diff --git a/v1/src/simulator/src/img/RippleCarry.jpeg b/v1/src/simulator/src/img/RippleCarry.jpeg new file mode 100644 index 00000000..c1fbf61f Binary files /dev/null and b/v1/src/simulator/src/img/RippleCarry.jpeg differ diff --git a/v1/src/simulator/src/img/Rom.svg b/v1/src/simulator/src/img/Rom.svg new file mode 100644 index 00000000..b72c1c26 --- /dev/null +++ b/v1/src/simulator/src/img/Rom.svg @@ -0,0 +1 @@ +ADEn000000000000000000000000000000000004080c \ No newline at end of file diff --git a/v1/src/simulator/src/img/SAP.jpeg b/v1/src/simulator/src/img/SAP.jpeg new file mode 100644 index 00000000..79b76da0 Binary files /dev/null and b/v1/src/simulator/src/img/SAP.jpeg differ diff --git a/v1/src/simulator/src/img/SRflipFlop.svg b/v1/src/simulator/src/img/SRflipFlop.svg new file mode 100644 index 00000000..c41ff746 --- /dev/null +++ b/v1/src/simulator/src/img/SRflipFlop.svg @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/v1/src/simulator/src/img/SevenSegDisplay.svg b/v1/src/simulator/src/img/SevenSegDisplay.svg new file mode 100644 index 00000000..bb3d2ae8 --- /dev/null +++ b/v1/src/simulator/src/img/SevenSegDisplay.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/SixteenSegDisplay.svg b/v1/src/simulator/src/img/SixteenSegDisplay.svg new file mode 100644 index 00000000..ad05274b --- /dev/null +++ b/v1/src/simulator/src/img/SixteenSegDisplay.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/Splitter.svg b/v1/src/simulator/src/img/Splitter.svg new file mode 100644 index 00000000..fa4a969d --- /dev/null +++ b/v1/src/simulator/src/img/Splitter.svg @@ -0,0 +1 @@ +0:11:2 \ No newline at end of file diff --git a/v1/src/simulator/src/img/SquareRGBLed.svg b/v1/src/simulator/src/img/SquareRGBLed.svg new file mode 100644 index 00000000..7900e614 --- /dev/null +++ b/v1/src/simulator/src/img/SquareRGBLed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/Stepper.svg b/v1/src/simulator/src/img/Stepper.svg new file mode 100644 index 00000000..f18aa4fa --- /dev/null +++ b/v1/src/simulator/src/img/Stepper.svg @@ -0,0 +1 @@ +5f \ No newline at end of file diff --git a/v1/src/simulator/src/img/T-clock.png b/v1/src/simulator/src/img/T-clock.png new file mode 100644 index 00000000..66442d9e Binary files /dev/null and b/v1/src/simulator/src/img/T-clock.png differ diff --git a/v1/src/simulator/src/img/TB_Input.svg b/v1/src/simulator/src/img/TB_Input.svg new file mode 100644 index 00000000..c1c42ead --- /dev/null +++ b/v1/src/simulator/src/img/TB_Input.svg @@ -0,0 +1 @@ +Test1 [INPUT] Case:0 \ No newline at end of file diff --git a/v1/src/simulator/src/img/TB_Output.svg b/v1/src/simulator/src/img/TB_Output.svg new file mode 100644 index 00000000..b62e1993 --- /dev/null +++ b/v1/src/simulator/src/img/TB_Output.svg @@ -0,0 +1 @@ +Test1 [OUTPUT] Paired \ No newline at end of file diff --git a/v1/src/simulator/src/img/TTY.svg b/v1/src/simulator/src/img/TTY.svg new file mode 100644 index 00000000..208e42b9 --- /dev/null +++ b/v1/src/simulator/src/img/TTY.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/Text.svg b/v1/src/simulator/src/img/Text.svg new file mode 100644 index 00000000..db86087a --- /dev/null +++ b/v1/src/simulator/src/img/Text.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/TflipFlop.svg b/v1/src/simulator/src/img/TflipFlop.svg new file mode 100644 index 00000000..50925f80 --- /dev/null +++ b/v1/src/simulator/src/img/TflipFlop.svg @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/v1/src/simulator/src/img/TriState.svg b/v1/src/simulator/src/img/TriState.svg new file mode 100644 index 00000000..d251882c --- /dev/null +++ b/v1/src/simulator/src/img/TriState.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/Tunnel.svg b/v1/src/simulator/src/img/Tunnel.svg new file mode 100644 index 00000000..ff5b95bb --- /dev/null +++ b/v1/src/simulator/src/img/Tunnel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/TwoComplement.svg b/v1/src/simulator/src/img/TwoComplement.svg new file mode 100644 index 00000000..6baa7056 --- /dev/null +++ b/v1/src/simulator/src/img/TwoComplement.svg @@ -0,0 +1 @@ +2' \ No newline at end of file diff --git a/v1/src/simulator/src/img/VariableLed.svg b/v1/src/simulator/src/img/VariableLed.svg new file mode 100644 index 00000000..e2829407 --- /dev/null +++ b/v1/src/simulator/src/img/VariableLed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/XnorGate.svg b/v1/src/simulator/src/img/XnorGate.svg new file mode 100644 index 00000000..4a8bae40 --- /dev/null +++ b/v1/src/simulator/src/img/XnorGate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/XorGate.svg b/v1/src/simulator/src/img/XorGate.svg new file mode 100644 index 00000000..639b4c0b --- /dev/null +++ b/v1/src/simulator/src/img/XorGate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/assignment.png b/v1/src/simulator/src/img/assignment.png new file mode 100644 index 00000000..c27df5bc Binary files /dev/null and b/v1/src/simulator/src/img/assignment.png differ diff --git a/v1/src/simulator/src/img/bus.png b/v1/src/simulator/src/img/bus.png new file mode 100644 index 00000000..d37b30aa Binary files /dev/null and b/v1/src/simulator/src/img/bus.png differ diff --git a/v1/src/simulator/src/img/caret.jpg b/v1/src/simulator/src/img/caret.jpg new file mode 100644 index 00000000..a382f8da Binary files /dev/null and b/v1/src/simulator/src/img/caret.jpg differ diff --git a/v1/src/simulator/src/img/circuitverse2.svg b/v1/src/simulator/src/img/circuitverse2.svg new file mode 100644 index 00000000..23dd6118 --- /dev/null +++ b/v1/src/simulator/src/img/circuitverse2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/circuitverse_black.svg b/v1/src/simulator/src/img/circuitverse_black.svg new file mode 100644 index 00000000..eba77c2a --- /dev/null +++ b/v1/src/simulator/src/img/circuitverse_black.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/circuitverse_logo.svg b/v1/src/simulator/src/img/circuitverse_logo.svg new file mode 100644 index 00000000..27202ecb --- /dev/null +++ b/v1/src/simulator/src/img/circuitverse_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/cross.png b/v1/src/simulator/src/img/cross.png new file mode 100644 index 00000000..bb7126e2 Binary files /dev/null and b/v1/src/simulator/src/img/cross.png differ diff --git a/v1/src/simulator/src/img/cvlogo.svg b/v1/src/simulator/src/img/cvlogo.svg new file mode 100644 index 00000000..ce4f7e59 --- /dev/null +++ b/v1/src/simulator/src/img/cvlogo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/simulator/src/img/default.png b/v1/src/simulator/src/img/default.png new file mode 100644 index 00000000..8e183a8a Binary files /dev/null and b/v1/src/simulator/src/img/default.png differ diff --git a/v1/src/simulator/src/img/drag.mp4 b/v1/src/simulator/src/img/drag.mp4 new file mode 100644 index 00000000..b211439c Binary files /dev/null and b/v1/src/simulator/src/img/drag.mp4 differ diff --git a/v1/src/simulator/src/img/edit_icon.png b/v1/src/simulator/src/img/edit_icon.png new file mode 100644 index 00000000..32f99fb1 Binary files /dev/null and b/v1/src/simulator/src/img/edit_icon.png differ diff --git a/v1/src/simulator/src/img/embed.png b/v1/src/simulator/src/img/embed.png new file mode 100644 index 00000000..54a7d78d Binary files /dev/null and b/v1/src/simulator/src/img/embed.png differ diff --git a/v1/src/simulator/src/img/facebook.png b/v1/src/simulator/src/img/facebook.png new file mode 100644 index 00000000..fc961175 Binary files /dev/null and b/v1/src/simulator/src/img/facebook.png differ diff --git a/v1/src/simulator/src/img/facebook_signin.png b/v1/src/simulator/src/img/facebook_signin.png new file mode 100644 index 00000000..e78185f0 Binary files /dev/null and b/v1/src/simulator/src/img/facebook_signin.png differ diff --git a/v1/src/simulator/src/img/fullAdder.png b/v1/src/simulator/src/img/fullAdder.png new file mode 100644 index 00000000..1150d9c6 Binary files /dev/null and b/v1/src/simulator/src/img/fullAdder.png differ diff --git a/v1/src/simulator/src/img/google.png b/v1/src/simulator/src/img/google.png new file mode 100644 index 00000000..2accb4e6 Binary files /dev/null and b/v1/src/simulator/src/img/google.png differ diff --git a/v1/src/simulator/src/img/google_signin.png b/v1/src/simulator/src/img/google_signin.png new file mode 100644 index 00000000..18bbd0b7 Binary files /dev/null and b/v1/src/simulator/src/img/google_signin.png differ diff --git a/v1/src/simulator/src/img/grading.png b/v1/src/simulator/src/img/grading.png new file mode 100644 index 00000000..166ee57e Binary files /dev/null and b/v1/src/simulator/src/img/grading.png differ diff --git a/v1/src/simulator/src/img/groups.png b/v1/src/simulator/src/img/groups.png new file mode 100644 index 00000000..e3619597 Binary files /dev/null and b/v1/src/simulator/src/img/groups.png differ diff --git a/v1/src/simulator/src/img/halfAdder.png b/v1/src/simulator/src/img/halfAdder.png new file mode 100644 index 00000000..7c9f3f0c Binary files /dev/null and b/v1/src/simulator/src/img/halfAdder.png differ diff --git a/v1/src/simulator/src/img/help.png b/v1/src/simulator/src/img/help.png new file mode 100644 index 00000000..5c72dbb7 Binary files /dev/null and b/v1/src/simulator/src/img/help.png differ diff --git a/v1/src/simulator/src/img/iDecoder.png b/v1/src/simulator/src/img/iDecoder.png new file mode 100644 index 00000000..d55e9bfa Binary files /dev/null and b/v1/src/simulator/src/img/iDecoder.png differ diff --git a/v1/src/simulator/src/img/iiitb.png b/v1/src/simulator/src/img/iiitb.png new file mode 100644 index 00000000..5a1c2fe3 Binary files /dev/null and b/v1/src/simulator/src/img/iiitb.png differ diff --git a/v1/src/simulator/src/img/implemented.png b/v1/src/simulator/src/img/implemented.png new file mode 100644 index 00000000..6766aaf6 Binary files /dev/null and b/v1/src/simulator/src/img/implemented.png differ diff --git a/v1/src/simulator/src/img/logix.png b/v1/src/simulator/src/img/logix.png new file mode 100644 index 00000000..2f6daac5 Binary files /dev/null and b/v1/src/simulator/src/img/logix.png differ diff --git a/v1/src/simulator/src/img/logixBanner.png b/v1/src/simulator/src/img/logixBanner.png new file mode 100644 index 00000000..b9dad5d5 Binary files /dev/null and b/v1/src/simulator/src/img/logixBanner.png differ diff --git a/v1/src/simulator/src/img/logixBanner2.png b/v1/src/simulator/src/img/logixBanner2.png new file mode 100644 index 00000000..9e7b044a Binary files /dev/null and b/v1/src/simulator/src/img/logixBanner2.png differ diff --git a/v1/src/simulator/src/img/logix_banner_new.png b/v1/src/simulator/src/img/logix_banner_new.png new file mode 100644 index 00000000..13d7fc63 Binary files /dev/null and b/v1/src/simulator/src/img/logix_banner_new.png differ diff --git a/v1/src/simulator/src/img/multiselectionDrag.mp4 b/v1/src/simulator/src/img/multiselectionDrag.mp4 new file mode 100644 index 00000000..6a193d8e Binary files /dev/null and b/v1/src/simulator/src/img/multiselectionDrag.mp4 differ diff --git a/v1/src/simulator/src/img/properties.mp4 b/v1/src/simulator/src/img/properties.mp4 new file mode 100644 index 00000000..754f6c58 Binary files /dev/null and b/v1/src/simulator/src/img/properties.mp4 differ diff --git a/v1/src/simulator/src/img/properties.png b/v1/src/simulator/src/img/properties.png new file mode 100644 index 00000000..167810f5 Binary files /dev/null and b/v1/src/simulator/src/img/properties.png differ diff --git a/v1/src/simulator/src/img/stats.png b/v1/src/simulator/src/img/stats.png new file mode 100644 index 00000000..700bd7fb Binary files /dev/null and b/v1/src/simulator/src/img/stats.png differ diff --git a/v1/src/simulator/src/img/students.png b/v1/src/simulator/src/img/students.png new file mode 100644 index 00000000..0be22e0e Binary files /dev/null and b/v1/src/simulator/src/img/students.png differ diff --git a/v1/src/simulator/src/img/super.png b/v1/src/simulator/src/img/super.png new file mode 100644 index 00000000..4e3a0996 Binary files /dev/null and b/v1/src/simulator/src/img/super.png differ diff --git a/v1/src/simulator/src/img/wire.mp4 b/v1/src/simulator/src/img/wire.mp4 new file mode 100644 index 00000000..8b246df9 Binary files /dev/null and b/v1/src/simulator/src/img/wire.mp4 differ diff --git a/v1/src/simulator/src/layout/layoutBuffer.js b/v1/src/simulator/src/layout/layoutBuffer.js new file mode 100644 index 00000000..1c339d28 --- /dev/null +++ b/v1/src/simulator/src/layout/layoutBuffer.js @@ -0,0 +1,88 @@ +import LayoutNode from './layoutNode' +/** + * Buffer object to store changes so that you can reset changes + * @class + * @param {Scope=} scope + * @category layout + */ +export default class LayoutBuffer { + constructor(scope = globalScope) { + var w = 300 * DPR + var h = 50 * DPR + + globalScope.ox = w + globalScope.oy = h + + // Assign layout if exist or create new one + this.layout = { ...scope.layout } // Object.create(scope.layout); + + // Push Input Nodes + this.Input = [] + for (let i = 0; i < scope.Input.length; i++) + this.Input.push( + new LayoutNode( + scope.Input[i].layoutProperties.x, + scope.Input[i].layoutProperties.y, + scope.Input[i].layoutProperties.id, + scope.Input[i].label, + scope.Input[i].type, + scope.Input[i] + ) + ) + + // Push Output Nodes + this.Output = [] + for (let i = 0; i < scope.Output.length; i++) + this.Output.push( + new LayoutNode( + scope.Output[i].layoutProperties.x, + scope.Output[i].layoutProperties.y, + scope.Output[i].layoutProperties.id, + scope.Output[i].label, + scope.Output[i].type, + scope.Output[i] + ) + ) + + // holds subcircuit elements + this.subElements = [] + } + + /** + * @memberof layoutBuffer + * Check if position is on the boundaries of subcircuit + * if the desired width and heiht is allowed + */ + isAllowed(x, y) { + if (x < 0 || x > this.layout.width || y < 0 || y > this.layout.height) + return false + if (x > 0 && x < this.layout.width && y > 0 && y < this.layout.height) + return false + + if ( + (x === 0 && y === 0) || + (x === 0 && y === this.layout.height) || + (x === this.layout.width && y === 0) || + (x === this.layout.width && y === this.layout.height) + ) + return false + + return true + } + + /** + * @memberof layoutBuffer + * Check if node is already at a position + * Function is called while decreasing height to + * check if it is possible without moving other node + */ + isNodeAt(x, y) { + for (let i = 0; i < this.Input.length; i++) { + if (this.Input[i].x === x && this.Input[i].y === y) return true + } + for (let i = 0; i < this.Output.length; i++) { + if (this.Output[i].x === x && this.Output[i].y === y) return true + } + return false + } +} diff --git a/v1/src/simulator/src/layout/layoutNode.js b/v1/src/simulator/src/layout/layoutNode.js new file mode 100644 index 00000000..ddf199b3 --- /dev/null +++ b/v1/src/simulator/src/layout/layoutNode.js @@ -0,0 +1,124 @@ +import { drawCircle } from '../canvasApi' +import simulationArea from '../simulationArea' +import { tempBuffer } from '../layoutMode' + +/** + * @class + * @param {number} x - x coord of node + * @param {number} y - y coord of node + * @param {strng} id - id for node + * @param {string=} label - label for the node + * @param {number} xx - parent x + * @param {number} yy - parent y + * @param {number} type - input or output node + * @param {CircuitElement} parent parent of the node + * @category layout + */ +export default class LayoutNode { + constructor(x, y, id, label = '', type, parent) { + this.type = type + this.id = id + + this.label = label + + this.prevx = undefined + this.prevy = undefined + this.x = x // Position of node wrt to parent + this.y = y // Position of node wrt to parent + + this.radius = 5 + this.clicked = false + this.hover = false + this.wasClicked = false + this.prev = 'a' + this.count = 0 + this.parent = parent + this.objectType = 'Layout Node' + } + + absX() { + return this.x + } + + absY() { + return this.y + } + + update() { + // Code copied from node.update() - Some code is redundant - needs to be removed + + if (this === simulationArea.hover) simulationArea.hover = undefined + this.hover = this.isHover() + + if (!simulationArea.mouseDown) { + if (this.absX() !== this.prevx || this.absY() !== this.prevy) { + // Store position before clicked + this.prevx = this.absX() + this.prevy = this.absY() + } + } + + if (this.hover) { + simulationArea.hover = this + } + + if ( + simulationArea.mouseDown && + ((this.hover && !simulationArea.selected) || + simulationArea.lastSelected === this) + ) { + simulationArea.selected = true + simulationArea.lastSelected = this + this.clicked = true + } else { + this.clicked = false + } + + if (!this.wasClicked && this.clicked) { + this.wasClicked = true + this.prev = 'a' + simulationArea.lastSelected = this + } else if (this.wasClicked && this.clicked) { + // Check if valid position and update accordingly + if ( + tempBuffer.isAllowed( + simulationArea.mouseX, + simulationArea.mouseY + ) && + !tempBuffer.isNodeAt( + simulationArea.mouseX, + simulationArea.mouseY + ) + ) { + this.x = simulationArea.mouseX + this.y = simulationArea.mouseY + } + } + } + + /** + * @memberof layoutNode + * this function is used to draw the nodes + */ + draw() { + var ctx = simulationArea.context + drawCircle( + ctx, + this.absX(), + this.absY(), + 3, + ['green', 'red'][+(simulationArea.lastSelected === this)] + ) + } + + /** + * @memberof layoutNode + * this function is used to check if hover + */ + isHover() { + return ( + this.absX() === simulationArea.mouseX && + this.absY() === simulationArea.mouseY + ) + } +} diff --git a/v1/src/simulator/src/layoutMode.js b/v1/src/simulator/src/layoutMode.js new file mode 100644 index 00000000..c400cb71 --- /dev/null +++ b/v1/src/simulator/src/layoutMode.js @@ -0,0 +1,544 @@ +/* eslint-disable import/no-cycle */ +/* eslint-disable no-continue */ +import { dots, correctWidth, fillText, rect2 } from './canvasApi' +import LayoutBuffer from './layout/layoutBuffer' +import simulationArea from './simulationArea' +import { + hideProperties, + fillSubcircuitElements, + prevPropertyObjGet, + prevPropertyObjSet, + showProperties, +} from './ux' +import { + update, + scheduleUpdate, + willBeUpdatedSet, + gridUpdateSet, + gridUpdateGet, +} from './engine' +import miniMapArea from './minimap' +import { showMessage } from './utils' +import * as metadata from './metadata.json' +import { verilogModeGet, verilogModeSet } from './Verilog2CV' + +/** + * Layout.js - all subcircuit layout related code is here + * You can edit how your subcircuit for a circuit will look by + * clicking edit layout in properties for a ciruit + * @category layoutMode + */ + +var layoutMode = false + +export function layoutModeSet(param) { + layoutMode = param +} + +export function layoutModeGet(param) { + return layoutMode +} + +/** + * @type {LayoutBuffer} - used to temporartily store all changes. + * @category layoutMode + */ +export var tempBuffer + +/** + * Helper function to determine alignment and position of nodes for rendering + * @param {number} x - width of label + * @param {number} y - height of label + * @category layoutMode + */ +export function determineLabel(x, y) { + if (x === 0) return ['left', 5, 5] + if (x === tempBuffer.layout.width) return ['right', -5, 5] + if (y === 0) return ['center', 0, 13] + return ['center', 0, -6] +} + +/** + * Used to move the grid in the layout mode + * @param {Scope} scope - the circuit whose subcircuit we are editing + * @category layoutMode + */ +export function paneLayout(scope = globalScope) { + if (!simulationArea.selected && simulationArea.mouseDown) { + simulationArea.selected = true + simulationArea.lastSelected = scope.root + simulationArea.hover = scope.root + } else if ( + simulationArea.lastSelected === scope.root && + simulationArea.mouseDown + ) { + // pane canvas + if (true) { + globalScope.ox = + simulationArea.mouseRawX - + simulationArea.mouseDownRawX + + simulationArea.oldx + globalScope.oy = + simulationArea.mouseRawY - + simulationArea.mouseDownRawY + + simulationArea.oldy + globalScope.ox = Math.round(globalScope.ox) + globalScope.oy = Math.round(globalScope.oy) + gridUpdateSet(true) + if (!embed && !lightMode) miniMapArea.setup() + } + } else if (simulationArea.lastSelected === scope.root) { + // Select multiple objects + + simulationArea.lastSelected = undefined + simulationArea.selected = false + simulationArea.hover = undefined + } +} + +/** + * Function to render layout on screen in layoutMode + * @param {Scope=} scope + * @category layoutMode + */ +export function renderLayout(scope = globalScope) { + if (!layoutModeGet()) return + var ctx = simulationArea.context + simulationArea.clear() + ctx.strokeStyle = 'black' + ctx.fillStyle = 'white' + ctx.lineWidth = correctWidth(3) + // Draw base rectangle + ctx.beginPath() + rect2( + ctx, + 0, + 0, + tempBuffer.layout.width, + tempBuffer.layout.height, + 0, + 0, + 'RIGHT' + ) + ctx.fill() + ctx.stroke() + ctx.beginPath() + ctx.textAlign = 'center' + ctx.fillStyle = 'black' + if (tempBuffer.layout.titleEnabled) { + fillText( + ctx, + scope.name, + tempBuffer.layout.title_x, + tempBuffer.layout.title_y, + 11 + ) + } + + // Draw labels + var info + for (let i = 0; i < tempBuffer.Input.length; i++) { + if (!tempBuffer.Input[i].label) continue + info = determineLabel( + tempBuffer.Input[i].x, + tempBuffer.Input[i].y, + scope + ) + ;[ctx.textAlign] = info + fillText( + ctx, + tempBuffer.Input[i].label, + tempBuffer.Input[i].x + info[1], + tempBuffer.Input[i].y + info[2], + 12 + ) + } + for (let i = 0; i < tempBuffer.Output.length; i++) { + if (!tempBuffer.Output[i].label) continue + info = determineLabel( + tempBuffer.Output[i].x, + tempBuffer.Output[i].y, + scope + ) + ;[ctx.textAlign] = info + fillText( + ctx, + tempBuffer.Output[i].label, + tempBuffer.Output[i].x + info[1], + tempBuffer.Output[i].y + info[2], + 12 + ) + } + ctx.fill() + + // Draw points + for (let i = 0; i < tempBuffer.Input.length; i++) { + tempBuffer.Input[i].draw() + } + for (let i = 0; i < tempBuffer.Output.length; i++) { + tempBuffer.Output[i].draw() + } + + if (gridUpdateGet()) { + dots() + } + + // Update UI position + for (let i = 0; i < tempBuffer.subElements.length; i++) { + tempBuffer.subElements[i].update() + + // element nodes + for (let j = 0; j < tempBuffer.subElements[i].nodeList.length; j++) + tempBuffer.subElements[i].nodeList[j].update() + } + + // Show properties of selected element + if (!embed && prevPropertyObjGet() != simulationArea.lastSelected) { + if (simulationArea.lastSelected) { + showProperties(simulationArea.lastSelected) + } + } + // Render objects + for (let i = 0; i < circuitElementList.length; i++) { + if (globalScope[circuitElementList[i]].length === 0) continue + if (!globalScope[circuitElementList[i]][0].canShowInSubcircuit) continue + + let elementName = circuitElementList[i] + + for (let j = 0; j < globalScope[elementName].length; j++) { + if ( + globalScope[elementName][j].subcircuitMetadata.showInSubcircuit + ) { + globalScope[elementName][j].drawLayoutMode() + } + } + } +} + +/** + * Update UI, positions of inputs and outputs + * @param {Scope} scope - the circuit whose subcircuit we are editing + * @category layoutMode + */ +export function layoutUpdate(scope = globalScope) { + if (!layoutModeGet()) return + willBeUpdatedSet(false) + for (let i = 0; i < tempBuffer.Input.length; i++) { + tempBuffer.Input[i].update() + } + for (let i = 0; i < tempBuffer.Output.length; i++) { + tempBuffer.Output[i].update() + } + + for (let i = 0; i < circuitElementList.length; i++) { + if (globalScope[circuitElementList[i]].length === 0) continue + if (!globalScope[circuitElementList[i]][0].canShowInSubcircuit) continue + let elementName = circuitElementList[i] + + for (let j = 0; j < globalScope[elementName].length; j++) { + if ( + globalScope[elementName][j].subcircuitMetadata.showInSubcircuit + ) { + globalScope[elementName][j].layoutUpdate() + } + } + } + paneLayout(scope) + renderLayout(scope) +} + +/** + * Helper function to reset all nodes to original default positions + * @category layoutMode + */ +export function layoutResetNodes() { + tempBuffer.layout.width = 100 + tempBuffer.layout.height = + Math.max(tempBuffer.Input.length, tempBuffer.Output.length) * 20 + 20 + for (let i = 0; i < tempBuffer.Input.length; i++) { + tempBuffer.Input[i].x = 0 + tempBuffer.Input[i].y = i * 20 + 20 + } + for (let i = 0; i < tempBuffer.Output.length; i++) { + tempBuffer.Output[i].x = tempBuffer.layout.width + tempBuffer.Output[i].y = i * 20 + 20 + } +} + +/** + * Increase width, and move all nodes + * @category layoutMode + */ +export function increaseLayoutWidth() { + for (let i = 0; i < tempBuffer.Input.length; i++) { + if (tempBuffer.Input[i].x === tempBuffer.layout.width) { + tempBuffer.Input[i].x += 10 + } + } + for (let i = 0; i < tempBuffer.Output.length; i++) { + if (tempBuffer.Output[i].x === tempBuffer.layout.width) { + tempBuffer.Output[i].x += 10 + } + } + tempBuffer.layout.width += 10 +} + +/** + * Increase Height, and move all nodes + * @category layoutMode + */ +export function increaseLayoutHeight() { + for (let i = 0; i < tempBuffer.Input.length; i++) { + if (tempBuffer.Input[i].y === tempBuffer.layout.height) { + tempBuffer.Input[i].y += 10 + } + } + for (let i = 0; i < tempBuffer.Output.length; i++) { + if (tempBuffer.Output[i].y === tempBuffer.layout.height) { + tempBuffer.Output[i].y += 10 + } + } + tempBuffer.layout.height += 10 +} + +/** + * Decrease Width, and move all nodes, check if space is there + * @category layoutMode + */ +export function decreaseLayoutWidth() { + if (tempBuffer.layout.width < 30) return + for (let i = 0; i < tempBuffer.Input.length; i++) { + if (tempBuffer.Input[i].x === tempBuffer.layout.width - 10) { + showMessage('No space. Move or delete some nodes to make space.') + return + } + } + for (let i = 0; i < tempBuffer.Output.length; i++) { + if (tempBuffer.Output[i].x === tempBuffer.layout.width - 10) { + showMessage('No space. Move or delete some nodes to make space.') + return + } + } + + for (let i = 0; i < tempBuffer.Input.length; i++) { + if (tempBuffer.Input[i].x === tempBuffer.layout.width) { + tempBuffer.Input[i].x -= 10 + } + } + for (let i = 0; i < tempBuffer.Output.length; i++) { + if (tempBuffer.Output[i].x === tempBuffer.layout.width) { + tempBuffer.Output[i].x -= 10 + } + } + tempBuffer.layout.width -= 10 +} + +/** + * Decrease Height, and move all nodes, check if space is there + * @category layoutMode + */ +export function decreaseLayoutHeight() { + if (tempBuffer.layout.height < 30) return + for (let i = 0; i < tempBuffer.Input.length; i++) { + if (tempBuffer.Input[i].y === tempBuffer.layout.height - 10) { + showMessage('No space. Move or delete some nodes to make space.') + return + } + } + for (let i = 0; i < tempBuffer.Output.length; i++) { + if (tempBuffer.Output[i].y === tempBuffer.layout.height - 10) { + showMessage('No space. Move or delete some nodes to make space.') + return + } + } + + for (let i = 0; i < tempBuffer.Input.length; i++) { + if (tempBuffer.Input[i].y === tempBuffer.layout.height) { + tempBuffer.Input[i].y -= 10 + } + } + for (let i = 0; i < tempBuffer.Output.length; i++) { + if (tempBuffer.Output[i].y === tempBuffer.layout.height) { + tempBuffer.Output[i].y -= 10 + } + } + tempBuffer.layout.height -= 10 +} + +/** + * Helper functions to move the titles + * @category layoutMode + */ +export function layoutTitleUp() { + tempBuffer.layout.title_y -= 5 +} + +/** + * Helper functions to move the titles + * @category layoutMode + */ +export function layoutTitleDown() { + tempBuffer.layout.title_y += 5 +} + +/** + * Helper functions to move the titles + * @category layoutMode + */ +export function layoutTitleRight() { + tempBuffer.layout.title_x += 5 +} + +/** + * Helper functions to move the titles + * @category layoutMode + */ +export function layoutTitleLeft() { + tempBuffer.layout.title_x -= 5 +} + +/** + * Helper functions to move the titles + * @category layoutMode + */ +export function toggleLayoutTitle() { + tempBuffer.layout.titleEnabled = !tempBuffer.layout.titleEnabled +} + +/** + * just toggles back to normal mode + * @category layoutMode + */ +export function cancelLayout() { + if (layoutModeGet()) { + // eslint-disable-next-line no-use-before-define + toggleLayoutMode() + } +} + +/** + * Store all data into layout and exit + * @category layoutMode + */ +export function saveLayout() { + if (layoutModeGet()) { + for (let i = 0; i < tempBuffer.Input.length; i++) { + tempBuffer.Input[i].parent.layoutProperties.x = + tempBuffer.Input[i].x + tempBuffer.Input[i].parent.layoutProperties.y = + tempBuffer.Input[i].y + } + for (let i = 0; i < tempBuffer.Output.length; i++) { + tempBuffer.Output[i].parent.layoutProperties.x = + tempBuffer.Output[i].x + tempBuffer.Output[i].parent.layoutProperties.y = + tempBuffer.Output[i].y + } + globalScope.layout = { ...tempBuffer.layout } + // eslint-disable-next-line no-use-before-define + toggleLayoutMode() + } +} + +/** + * Function to toggle between layoutMode and normal Mode + * the sidebar is disabled and n properties are shown. + * @category layoutMode + */ +export function toggleLayoutMode() { + // hideProperties() + // lines from hideProperty function() <--- + prevPropertyObjSet(undefined) + $('.objectPropertyAttribute').unbind('change keyup paste click') + + if (layoutModeGet()) { + layoutModeSet(false) + $('#layoutDialog').fadeOut() + $('.layoutElementPanel').fadeOut() + $('.elementPanel').fadeIn() + $('.timing-diagram-panel').fadeIn() + $('.testbench-manual-panel').fadeIn() + globalScope.centerFocus(false) + if (globalScope.verilogMetadata.isVerilogCircuit) verilogModeSet(true) + dots() + } else { + layoutModeSet(true) + verilogModeSet(false) + $('#layoutDialog').fadeIn() + $('.layoutElementPanel').fadeIn() + $('.elementPanel').fadeOut() + $('.timing-diagram-panel').fadeOut() + $('.testbench-manual-panel').fadeOut() + fillSubcircuitElements() + + globalScope.ox = 0 + globalScope.oy = 0 + globalScope.scale = DPR * 1.3 + dots() + tempBuffer = new LayoutBuffer() + // $('#toggleLayoutTitle')[0].checked = tempBuffer.layout.titleEnabled + } + update(globalScope, true) + scheduleUpdate() +} + +export const layoutFunctions = { + decreaseLayoutWidth, + increaseLayoutWidth, + decreaseLayoutHeight, + increaseLayoutHeight, + layoutResetNodes, + layoutTitleUp, + layoutTitleDown, + layoutTitleLeft, + layoutTitleRight, + toggleLayoutTitle, + cancelLayout, + saveLayout, + toggleLayoutMode, +} + +// export function setupLayoutModePanelListeners() { +// $('#decreaseLayoutWidth').on('click', () => { +// decreaseLayoutWidth() +// }) +// $('#increaseLayoutWidth').on('click', () => { +// increaseLayoutWidth() +// }) +// $('#decreaseLayoutHeight').on('click', () => { +// decreaseLayoutHeight() +// }) +// $('#increaseLayoutHeight').on('click', () => { +// increaseLayoutHeight() +// }) +// $('#layoutResetNodes').on('click', () => { +// layoutResetNodes() +// }) +// $('#layoutTitleUp').on('click', () => { +// layoutTitleUp() +// }) +// $('#layoutTitleDown').on('click', () => { +// layoutTitleDown() +// }) +// $('#layoutTitleLeft').on('click', () => { +// layoutTitleLeft() +// }) +// $('#layoutTitleRight').on('click', () => { +// layoutTitleRight() +// }) +// $('#toggleLayoutTitle').on('click', () => { +// toggleLayoutTitle() +// }) +// $('#saveLayout').on('click', () => { +// saveLayout() +// }) +// $('#cancelLayout').on('click', () => { +// cancelLayout() +// }) +// $('#layoutDialog button').on('click', () => { +// scheduleUpdate() +// }) +// $('#layoutDialog input').on('click', () => { +// scheduleUpdate() +// }) +// } diff --git a/v1/src/simulator/src/listeners.js b/v1/src/simulator/src/listeners.js new file mode 100644 index 00000000..460be076 --- /dev/null +++ b/v1/src/simulator/src/listeners.js @@ -0,0 +1,761 @@ +// Most Listeners are stored here +import { + layoutModeGet, + tempBuffer, + layoutUpdate, + // setupLayoutModePanelListeners, +} from './layoutMode' +import simulationArea from './simulationArea' +import { + scheduleUpdate, + update, + updateSelectionsAndPane, + wireToBeCheckedSet, + updatePositionSet, + updateSimulationSet, + updateCanvasSet, + gridUpdateSet, + errorDetectedSet, +} from './engine' +import { changeScale } from './canvasApi' +import { scheduleBackup } from './data/backupCircuit' +import { + hideProperties, + deleteSelected, + uxvar, + fullView, + exitFullView, +} from './ux' +import { + updateRestrictedElementsList, + updateRestrictedElementsInScope, + hideRestricted, + showRestricted, +} from './restrictedElementDiv' +import { removeMiniMap, updatelastMinimapShown } from './minimap' +import undo from './data/undo' +import redo from './data/redo' +import { copy, paste, selectAll } from './events' +import save from './data/save' +import { verilogModeGet } from './Verilog2CV' +import { setupTimingListeners } from './plotArea' + +var unit = 10 +var listenToSimulator = true + +export default function startListeners() { + // added the below functionalities in QuickButton.vue component local script tag part + + // $('#deleteSelected').on('click', () => { + // deleteSelected() + // }) + + // $('#zoomIn').on('click', () => { + // changeScale(0.2, 'zoomButton', 'zoomButton', 2) + // }) + + // $('#zoomOut').on('click', () => { + // changeScale(-0.2, 'zoomButton', 'zoomButton', 2) + // }) + + // $('#undoButton').on('click', () => { + // undo() + // }) + // $('#redoButton').on('click', () => { + // redo() + // }) + // $('#viewButton').on('click', () => { + // fullView() + // }) + + $(document).on('keyup', (e) => { + if (e.key === 'Escape') exitFullView() + }) + + $('#projectName').on('click', () => { + simulationArea.lastSelected = globalScope.root + setTimeout(() => { + document.getElementById('projname').select() + }, 100) + }) + /* Makes tabs reordering possible by making them sortable */ + // $("#tabsBar").sortable({ + // containment: 'parent', + // items: '> div', + // revert: false, + // opacity: 0.5, + // tolerance: 'pointer', + // placeholder: 'placeholder', + // forcePlaceholderSize: true, + // }); + + document + .getElementById('simulationArea') + .addEventListener('mousedown', (e) => { + simulationArea.mouseDown = true + + // Deselect Input + if (document.activeElement instanceof HTMLElement) + document.activeElement.blur() + + errorDetectedSet(false) + updateSimulationSet(true) + updatePositionSet(true) + updateCanvasSet(true) + + simulationArea.lastSelected = undefined + simulationArea.selected = false + simulationArea.hover = undefined + var rect = simulationArea.canvas.getBoundingClientRect() + simulationArea.mouseDownRawX = (e.clientX - rect.left) * DPR + simulationArea.mouseDownRawY = (e.clientY - rect.top) * DPR + simulationArea.mouseDownX = + Math.round( + (simulationArea.mouseDownRawX - globalScope.ox) / + globalScope.scale / + unit + ) * unit + simulationArea.mouseDownY = + Math.round( + (simulationArea.mouseDownRawY - globalScope.oy) / + globalScope.scale / + unit + ) * unit + simulationArea.oldx = globalScope.ox + simulationArea.oldy = globalScope.oy + + e.preventDefault() + scheduleBackup() + scheduleUpdate(1) + $('.dropdown.open').removeClass('open') + }) + document + .getElementById('simulationArea') + .addEventListener('mouseup', (e) => { + if (simulationArea.lastSelected) + simulationArea.lastSelected.newElement = false + /* + handling restricted circuit elements + */ + + if ( + simulationArea.lastSelected && + restrictedElements.includes( + simulationArea.lastSelected.objectType + ) && + !globalScope.restrictedCircuitElementsUsed.includes( + simulationArea.lastSelected.objectType + ) + ) { + globalScope.restrictedCircuitElementsUsed.push( + simulationArea.lastSelected.objectType + ) + updateRestrictedElementsList() + } + + // deselect multible elements with click + if ( + !simulationArea.shiftDown && + simulationArea.multipleObjectSelections.length > 0 + ) { + if ( + !simulationArea.multipleObjectSelections.includes( + simulationArea.lastSelected + ) + ) { + simulationArea.multipleObjectSelections = [] + } + } + }) + document + .getElementById('simulationArea') + .addEventListener('mousemove', onMouseMove) + + window.addEventListener('keyup', (e) => { + scheduleUpdate(1) + simulationArea.shiftDown = e.shiftKey + if (e.keyCode == 16) { + simulationArea.shiftDown = false + } + if (e.key == 'Meta' || e.key == 'Control') { + simulationArea.controlDown = false + } + }) + + window.addEventListener( + 'keydown', + (e) => { + if (document.activeElement.tagName == 'INPUT') return + if (document.activeElement != document.body) return + + simulationArea.shiftDown = e.shiftKey + if (e.key == 'Meta' || e.key == 'Control') { + simulationArea.controlDown = true + } + + if ( + simulationArea.controlDown && + e.key.charCodeAt(0) == 122 && + !simulationArea.shiftDown + ) { + // detect the special CTRL-Z code + undo() + } + if ( + simulationArea.controlDown && + e.key.charCodeAt(0) == 122 && + simulationArea.shiftDown + ) { + // detect the special Cmd + shift + z code (macOs) + redo() + } + if ( + simulationArea.controlDown && + e.key.charCodeAt(0) == 121 && + !simulationArea.shiftDown + ) { + // detect the special ctrl + Y code (windows) + redo() + } + + if (listenToSimulator) { + // If mouse is focusing on input element, then override any action + // if($(':focus').length){ + // return; + // } + + if ( + document.activeElement.tagName == 'INPUT' || + simulationArea.mouseRawX < 0 || + simulationArea.mouseRawY < 0 || + simulationArea.mouseRawX > width || + simulationArea.mouseRawY > height + ) { + return + } + // HACK TO REMOVE FOCUS ON PROPERTIES + if (document.activeElement.type == 'number') { + hideProperties() + showProperties(simulationArea.lastSelected) + } + + errorDetectedSet(false) + updateSimulationSet(true) + updatePositionSet(true) + simulationArea.shiftDown = e.shiftKey + + if (e.key == 'Meta' || e.key == 'Control') { + simulationArea.controlDown = true + } + + // zoom in (+) + if ( + (simulationArea.controlDown && + (e.keyCode == 187 || e.keyCode == 171)) || + e.keyCode == 107 + ) { + e.preventDefault() + ZoomIn() + } + // zoom out (-) + if ( + (simulationArea.controlDown && + (e.keyCode == 189 || e.keyCode == 173)) || + e.keyCode == 109 + ) { + e.preventDefault() + ZoomOut() + } + + if ( + simulationArea.mouseRawX < 0 || + simulationArea.mouseRawY < 0 || + simulationArea.mouseRawX > width || + simulationArea.mouseRawY > height + ) + return + + scheduleUpdate(1) + updateCanvasSet(true) + wireToBeCheckedSet(1) + + // Needs to be deprecated, moved to more recent listeners + if ( + simulationArea.controlDown && + (e.key == 'C' || e.key == 'c') + ) { + // simulationArea.copyList=simulationArea.multipleObjectSelections.slice(); + // if(simulationArea.lastSelected&&simulationArea.lastSelected!==simulationArea.root&&!simulationArea.copyList.contains(simulationArea.lastSelected)){ + // simulationArea.copyList.push(simulationArea.lastSelected); + // } + // copy(simulationArea.copyList); + } + + if ( + simulationArea.lastSelected && + simulationArea.lastSelected.keyDown + ) { + if ( + e.key.toString().length == 1 || + e.key.toString() == 'Backspace' || + e.key.toString() == 'Enter' + ) { + simulationArea.lastSelected.keyDown(e.key.toString()) + e.cancelBubble = true + e.returnValue = false + + //e.stopPropagation works in Firefox. + if (e.stopPropagation) { + e.stopPropagation() + e.preventDefault() + } + return + } + } + + if ( + simulationArea.lastSelected && + simulationArea.lastSelected.keyDown2 + ) { + if (e.key.toString().length == 1) { + simulationArea.lastSelected.keyDown2(e.key.toString()) + return + } + } + + if ( + simulationArea.lastSelected && + simulationArea.lastSelected.keyDown3 + ) { + if ( + e.key.toString() != 'Backspace' && + e.key.toString() != 'Delete' + ) { + simulationArea.lastSelected.keyDown3(e.key.toString()) + return + } + } + + if (e.keyCode == 16) { + simulationArea.shiftDown = true + if ( + simulationArea.lastSelected && + !simulationArea.lastSelected.keyDown && + simulationArea.lastSelected.objectType != 'Wire' && + simulationArea.lastSelected.objectType != + 'CircuitElement' && + !simulationArea.multipleObjectSelections.contains( + simulationArea.lastSelected + ) + ) { + simulationArea.multipleObjectSelections.push( + simulationArea.lastSelected + ) + } + } + + // Detect offline save shortcut (CTRL+SHIFT+S) + if ( + simulationArea.controlDown && + e.keyCode == 83 && + simulationArea.shiftDown + ) { + saveOffline() + e.preventDefault() + } + + // Detect Select all Shortcut + if ( + simulationArea.controlDown && + (e.keyCode == 65 || e.keyCode == 97) + ) { + selectAll() + e.preventDefault() + } + + // deselect all Shortcut + if (e.keyCode == 27) { + simulationArea.multipleObjectSelections = [] + simulationArea.lastSelected = undefined + e.preventDefault() + } + + if ( + (e.keyCode == 113 || e.keyCode == 81) && + simulationArea.lastSelected != undefined + ) { + if (simulationArea.lastSelected.bitWidth !== undefined) { + simulationArea.lastSelected.newBitWidth( + parseInt(prompt('Enter new bitWidth'), 10) + ) + } + } + + if ( + simulationArea.controlDown && + (e.key == 'T' || e.key == 't') + ) { + // e.preventDefault(); //browsers normally open a new tab + simulationArea.changeClockTime(prompt('Enter Time:')) + } + } + + if (e.keyCode == 8 || e.key == 'Delete') { + deleteSelected() + } + }, + true + ) + + document + .getElementById('simulationArea') + .addEventListener('dblclick', (e) => { + updateCanvasSet(true) + if ( + simulationArea.lastSelected && + simulationArea.lastSelected.dblclick !== undefined + ) { + simulationArea.lastSelected.dblclick() + } else if (!simulationArea.shiftDown) { + simulationArea.multipleObjectSelections = [] + } + scheduleUpdate(2) + }) + + document + .getElementById('simulationArea') + .addEventListener('mouseup', onMouseUp) + + document + .getElementById('simulationArea') + .addEventListener('mousewheel', MouseScroll) + document + .getElementById('simulationArea') + .addEventListener('DOMMouseScroll', MouseScroll) + + function MouseScroll(event) { + updateCanvasSet(true) + event.preventDefault() + var deltaY = event.wheelDelta ? event.wheelDelta : -event.detail + event.preventDefault() + var deltaY = event.wheelDelta ? event.wheelDelta : -event.detail + const direction = deltaY > 0 ? 1 : -1 + handleZoom(direction) + updateCanvasSet(true) + gridUpdateSet(true) + + if (layoutModeGet()) layoutUpdate() + else update() // Schedule update not working, this is INEFFICIENT + } + + document.addEventListener('cut', (e) => { + if (verilogModeGet()) return + if (document.activeElement.tagName == 'INPUT') return + if (document.activeElement.tagName != 'BODY') return + + if (listenToSimulator) { + simulationArea.copyList = + simulationArea.multipleObjectSelections.slice() + if ( + simulationArea.lastSelected && + simulationArea.lastSelected !== simulationArea.root && + !simulationArea.copyList.contains(simulationArea.lastSelected) + ) { + simulationArea.copyList.push(simulationArea.lastSelected) + } + + var textToPutOnClipboard = copy(simulationArea.copyList, true) + + // Updated restricted elements + updateRestrictedElementsInScope() + localStorage.setItem('clipboardData', textToPutOnClipboard) + e.preventDefault() + if (textToPutOnClipboard == undefined) return + if (isIe) { + window.clipboardData.setData('Text', textToPutOnClipboard) + } else { + e.clipboardData.setData('text/plain', textToPutOnClipboard) + } + } + }) + + document.addEventListener('copy', (e) => { + if (verilogModeGet()) return + if (document.activeElement.tagName == 'INPUT') return + if (document.activeElement.tagName != 'BODY') return + + if (listenToSimulator) { + simulationArea.copyList = + simulationArea.multipleObjectSelections.slice() + if ( + simulationArea.lastSelected && + simulationArea.lastSelected !== simulationArea.root && + !simulationArea.copyList.contains(simulationArea.lastSelected) + ) { + simulationArea.copyList.push(simulationArea.lastSelected) + } + + var textToPutOnClipboard = copy(simulationArea.copyList) + + // Updated restricted elements + updateRestrictedElementsInScope() + localStorage.setItem('clipboardData', textToPutOnClipboard) + e.preventDefault() + if (textToPutOnClipboard == undefined) return + if (isIe) { + window.clipboardData.setData('Text', textToPutOnClipboard) + } else { + e.clipboardData.setData('text/plain', textToPutOnClipboard) + } + } + }) + + document.addEventListener('paste', (e) => { + if (document.activeElement.tagName == 'INPUT') return + if (document.activeElement.tagName != 'BODY') return + + if (listenToSimulator) { + var data + if (isIe) { + data = window.clipboardData.getData('Text') + } else { + data = e.clipboardData.getData('text/plain') + } + + paste(data) + + // Updated restricted elements + updateRestrictedElementsInScope() + + e.preventDefault() + } + }) + + // 'drag and drop' event listener for subcircuit elements in layout mode + $('#subcircuitMenu').on( + 'dragstop', + '.draggableSubcircuitElement', + function (event, ui) { + const sideBarWidth = $('#guide_1')[0].clientWidth + let tempElement + + if (ui.position.top > 10 && ui.position.left > sideBarWidth) { + // make a shallow copy of the element with the new coordinates + tempElement = + globalScope[this.dataset.elementName][ + this.dataset.elementId + ] + + // Changing the coordinate doesn't work yet, nodes get far from element + tempElement.x = ui.position.left - sideBarWidth + tempElement.y = ui.position.top + for (let node of tempElement.nodeList) { + node.x = ui.position.left - sideBarWidth + node.y = ui.position.top + } + + tempBuffer.subElements.push(tempElement) + this.parentElement.removeChild(this) + } + } + ) + + restrictedElements.forEach((element) => { + $(`#${element}`).mouseover(() => { + showRestricted() + }) + + $(`#${element}`).mouseout(() => { + hideRestricted() + }) + }) + + zoomSliderListeners() + // setupLayoutModePanelListeners() + if (!embed) { + setupTimingListeners() + } +} + +var isIe = + navigator.userAgent.toLowerCase().indexOf('msie') != -1 || + navigator.userAgent.toLowerCase().indexOf('trident') != -1 + +function onMouseMove(e) { + var rect = simulationArea.canvas.getBoundingClientRect() + simulationArea.mouseRawX = (e.clientX - rect.left) * DPR + simulationArea.mouseRawY = (e.clientY - rect.top) * DPR + simulationArea.mouseXf = + (simulationArea.mouseRawX - globalScope.ox) / globalScope.scale + simulationArea.mouseYf = + (simulationArea.mouseRawY - globalScope.oy) / globalScope.scale + simulationArea.mouseX = Math.round(simulationArea.mouseXf / unit) * unit + simulationArea.mouseY = Math.round(simulationArea.mouseYf / unit) * unit + + updateCanvasSet(true) + + if ( + simulationArea.lastSelected && + (simulationArea.mouseDown || simulationArea.lastSelected.newElement) + ) { + updateCanvasSet(true) + var fn + + if (simulationArea.lastSelected == globalScope.root) { + fn = function () { + updateSelectionsAndPane() + } + } else { + fn = function () { + if (simulationArea.lastSelected) { + simulationArea.lastSelected.update() + } + } + } + scheduleUpdate(0, 20, fn) + } else { + scheduleUpdate(0, 200) + } +} + +function onMouseUp(e) { + simulationArea.mouseDown = false + if (!lightMode) { + updatelastMinimapShown() + setTimeout(removeMiniMap, 2000) + } + + errorDetectedSet(false) + updateSimulationSet(true) + updatePositionSet(true) + updateCanvasSet(true) + gridUpdateSet(true) + wireToBeCheckedSet(1) + + scheduleUpdate(1) + simulationArea.mouseDown = false + + for (var i = 0; i < 2; i++) { + updatePositionSet(true) + wireToBeCheckedSet(1) + update() + } + errorDetectedSet(false) + updateSimulationSet(true) + updatePositionSet(true) + updateCanvasSet(true) + gridUpdateSet(true) + wireToBeCheckedSet(1) + + scheduleUpdate(1) + var rect = simulationArea.canvas.getBoundingClientRect() + + if ( + !( + simulationArea.mouseRawX < 0 || + simulationArea.mouseRawY < 0 || + simulationArea.mouseRawX > width || + simulationArea.mouseRawY > height + ) + ) { + uxvar.smartDropXX = simulationArea.mouseX + 100 // Math.round(((simulationArea.mouseRawX - globalScope.ox+100) / globalScope.scale) / unit) * unit; + uxvar.smartDropYY = simulationArea.mouseY - 50 // Math.round(((simulationArea.mouseRawY - globalScope.oy+100) / globalScope.scale) / unit) * unit; + } +} + +function resizeTabs() { + var $windowsize = $('body').width() + var $sideBarsize = $('.side').width() + var $maxwidth = $windowsize - $sideBarsize + $('#tabsBar div').each(function (e) { + $(this).css({ 'max-width': $maxwidth - 30 }) + }) +} + +window.addEventListener('resize', resizeTabs) +resizeTabs() + +// $(() => { +// $('[data-toggle="tooltip"]').tooltip() +// }) + +// direction is only 1 or -1 +function handleZoom(direction) { + var zoomSlider = $('#customRange1') + var currentSliderValue = parseInt(zoomSlider.val(), 10) + currentSliderValue += direction + + if (globalScope.scale > 0.5 * DPR) { + zoomSlider.val(currentSliderValue).change() + } else if (globalScope.scale < 4 * DPR) { + zoomSlider.val(currentSliderValue).change() + } + + gridUpdateSet(true) + scheduleUpdate() +} + +export function ZoomIn() { + handleZoom(1) +} + +export function ZoomOut() { + handleZoom(-1) +} + +function zoomSliderListeners() { + document.getElementById('customRange1').value = 5 + document + .getElementById('simulationArea') + .addEventListener('DOMMouseScroll', zoomSliderScroll) + document + .getElementById('simulationArea') + .addEventListener('mousewheel', zoomSliderScroll) + let curLevel = document.getElementById('customRange1').value + $(document).on('input change', '#customRange1', function (e) { + let newValue = $(this).val() + let changeInScale = newValue - curLevel + updateCanvasSet(true) + changeScale(changeInScale * 0.1, 'zoomButton', 'zoomButton', 3) + gridUpdateSet(true) + curLevel = newValue + }) + function zoomSliderScroll(e) { + let zoomLevel = document.getElementById('customRange1').value + let deltaY = e.wheelDelta ? e.wheelDelta : -e.detail + const directionY = deltaY > 0 ? 1 : -1 + if (directionY > 0) zoomLevel++ + else zoomLevel-- + if (zoomLevel >= 45) { + zoomLevel = 45 + document.getElementById('customRange1').value = 45 + } else if (zoomLevel <= 0) { + zoomLevel = 0 + document.getElementById('customRange1').value = 0 + } else { + document.getElementById('customRange1').value = zoomLevel + curLevel = zoomLevel + } + } + + // previously used for the + and - zoom buttons in quickButtons + + // function sliderZoomButton(direction) { + // var zoomSlider = $('#customRange1') + // var currentSliderValue = parseInt(zoomSlider.val(), 10) + // if (direction === -1) { + // currentSliderValue-- + // } else { + // currentSliderValue++ + // } + // zoomSlider.val(currentSliderValue).change() + // } + + // $('#decrement').click(() => { + // sliderZoomButton(-1) + // }) + + // $('#increment').click(() => { + // sliderZoomButton(1) + // }) +} diff --git a/v1/src/simulator/src/metadata.json b/v1/src/simulator/src/metadata.json new file mode 100644 index 00000000..8ed4f225 --- /dev/null +++ b/v1/src/simulator/src/metadata.json @@ -0,0 +1,179 @@ +{ + "circuitElementList": [ + "Input", + "Output", + "NotGate", + "OrGate", + "AndGate", + "NorGate", + "NandGate", + "XorGate", + "XnorGate", + "SevenSegDisplay", + "SixteenSegDisplay", + "HexDisplay", + "Multiplexer", + "BitSelector", + "Splitter", + "Power", + "Ground", + "ConstantVal", + "ControlledInverter", + "TriState", + "Adder", + "verilogMultiplier", + "verilogDivider", + "verilogPower", + "verilogShiftLeft", + "TwoComplement", + "verilogShiftRight", + "Rom", + "RAM", + "verilogRAM", + "EEPROM", + "TflipFlop", + "JKflipFlop", + "SRflipFlop", + "DflipFlop", + "TTY", + "Keyboard", + "Clock", + "DigitalLed", + "Stepper", + "VariableLed", + "RGBLed", + "SquareRGBLed", + "RGBLedMatrix", + "Button", + "Demultiplexer", + "Buffer", + "SubCircuit", + "Flag", + "MSB", + "LSB", + "PriorityEncoder", + "Tunnel", + "ALU", + "Decoder", + "Random", + "Counter", + "Dlatch", + "TB_Input", + "TB_Output", + "ForceGate" + ], + "annotationList": ["Text", "Rectangle", "Arrow", "ImageAnnotation"], + "inputList": [ + "Random", + "Dlatch", + "JKflipFlop", + "TflipFlop", + "SRflipFlop", + "DflipFlop", + "Buffer", + "Stepper", + "Ground", + "Power", + "ConstantVal", + "Input", + "Clock", + "Button", + "Counter" + ], + "subCircuitInputList": [ + "Random", + "Dlatch", + "JKflipFlop", + "TflipFlop", + "SRflipFlop", + "DflipFlop", + "Buffer", + "Stepper", + "Ground", + "Power", + "ConstantVal", + "Clock", + "Button", + "Counter" + ], + "elementHierarchy": { + "Input": [ + { "name": "Input", "label": "Input" }, + { "name": "Button", "label": "Button" }, + { "name": "Power", "label": "Power" }, + { "name": "Ground", "label": "Ground" }, + { "name": "ConstantVal", "label": "Constant Value" }, + { "name": "Stepper", "label": "Stepper" }, + { "name": "Random", "label": "Random" }, + { "name": "Counter", "label": "Counter" } + ], + "Output": [ + { "name": "Output", "label": "Output" }, + { "name": "RGBLed", "label": "RGB Led" }, + { "name": "DigitalLed", "label": "Digital Led" }, + { "name": "VariableLed", "label": "Variable Led" }, + { "name": "HexDisplay", "label": "Hex Display" }, + { "name": "SevenSegDisplay", "label": "Seven Segment Display" }, + { "name": "SixteenSegDisplay", "label": "Sixteen Segment Display" }, + { "name": "SquareRGBLed", "label": "Square RGB Led" }, + { "name": "RGBLedMatrix", "label": "RGB Led Matrix" } + ], + "Gates": [ + { "name": "AndGate", "label": "And Gate" }, + { "name": "OrGate", "label": "Or Gate" }, + { "name": "NotGate", "label": "Not Gate" }, + { "name": "XorGate", "label": "Xor Gate" }, + { "name": "NandGate", "label": "Nand Gate" }, + { "name": "NorGate", "label": "Nor Gate" }, + { "name": "XnorGate", "label": "Xnor Gate" } + ], + "Decoders & Plexers": [ + { "name": "Multiplexer", "label": "Multiplexer" }, + { "name": "Demultiplexer", "label": "Demultiplexer" }, + { "name": "BitSelector", "label": "Bit Selector" }, + { "name": "MSB", "label": "MSB(Most Significant Bit)" }, + { "name": "LSB", "label": "LSB(Least Significant Bit)" }, + { "name": "PriorityEncoder", "label": "Priority Encoder" }, + { "name": "Decoder", "label": "Decoder" } + ], + "Sequential Elements": [ + { "name": "DflipFlop", "label": "D flip Flop" }, + { "name": "Dlatch", "label": "D latch" }, + { "name": "TflipFlop", "label": "T flip Flop" }, + { "name": "JKflipFlop", "label": "JK flip Flop" }, + { "name": "SRflipFlop", "label": "SR flip Flop" }, + { "name": "TTY", "label": "TTY" }, + { "name": "Keyboard", "label": "Keyboard" }, + { "name": "Clock", "label": "Clock" }, + { "name": "Rom", "label": "ROM" }, + { "name": "RAM", "label": "RAM" }, + { "name": "verilogRAM", "label": "Verilog RAM" }, + { "name": "EEPROM", "label": "EEPROM" } + ], + "Annotation": [ + { "name": "Rectangle", "label": "Rectangle" }, + { "name": "Arrow", "label": "Arrow" }, + { "name": "ImageAnnotation", "label": "Image Annotation" }, + { "name": "Text", "label": "Text" } + ], + "Misc": [ + { "name": "TwoComplement", "label": "Two Complement" }, + { "name": "Flag", "label": "Flag" }, + { "name": "Splitter", "label": "Splitter" }, + { "name": "Adder", "label": "Adder" }, + { "name": "ALU", "label": "ALU(Arithmetic and Logical Unit)" }, + { "name": "TriState", "label": "TriState Flip Flop" }, + { "name": "Tunnel", "label": "Tunnel" }, + { "name": "verilogMultiplier", "label": "Verilog Multiplier" }, + { "name": "verilogDivider", "label": "Verilog Divider" }, + { "name": "verilogPower", "label": "Verilog Power" }, + { "name": "verilogShiftLeft", "label": "Verilog Shift Left" }, + { "name": "verilogShiftRight", "label": "Verilog Shift Right" }, + { "name": "Buffer", "label": "Buffer" }, + { "name": "ControlledInverter", "label": "Controlled Inverter" }, + { "name": "TB_Input", "label": "TB Input" }, + { "name": "TB_Output", "label": "TB Output" }, + { "name": "ForceGate", "label": "Force Gate" } + ] + } +} diff --git a/v1/src/simulator/src/minimap.js b/v1/src/simulator/src/minimap.js new file mode 100644 index 00000000..6cfcf49d --- /dev/null +++ b/v1/src/simulator/src/minimap.js @@ -0,0 +1,193 @@ +import simulationArea from './simulationArea' +import { colors } from './themer/themer' +import { layoutModeGet } from './layoutMode' + +/** + * @type {Object} miniMapArea + * This object is used to draw the miniMap. + * @property {number} pageY + * @property {number} pageX + * @property {HTMLCanvasObject} canvas - the canvas object + * @property {function} setup - used to setup the parameters and dimensions + * @property {function} play - used to draw outline of minimap and call resolve + * @property {function} resolve - used to resolve all objects and draw them on minimap + * @property {function} clear - used to clear minimap + * @category minimap + */ +var miniMapArea +export default miniMapArea = { + canvas: document.getElementById('miniMapArea'), + setup() { + if (lightMode) return + this.canvas = document.getElementById('miniMapArea') + this.pageHeight = height // Math.round(((parseInt($("#simulationArea").height())))/ratio)*ratio-50; // -50 for tool bar? Check again + this.pageWidth = width // Math.round(((parseInt($("#simulationArea").width())))/ratio)*ratio; + this.pageY = this.pageHeight - globalScope.oy + this.pageX = this.pageWidth - globalScope.ox + + if (simulationArea.minHeight != undefined) { + this.minY = Math.min( + simulationArea.minHeight, + -globalScope.oy / globalScope.scale + ) + } else { + this.minY = -globalScope.oy / globalScope.scale + } + if (simulationArea.maxHeight != undefined) { + this.maxY = Math.max( + simulationArea.maxHeight, + this.pageY / globalScope.scale + ) + } else { + this.maxY = this.pageY / globalScope.scale + } + if (simulationArea.minWidth != undefined) { + this.minX = Math.min( + simulationArea.minWidth, + -globalScope.ox / globalScope.scale + ) + } else { + this.minX = -globalScope.ox / globalScope.scale + } + if (simulationArea.maxWidth != undefined) { + this.maxX = Math.max( + simulationArea.maxWidth, + this.pageX / globalScope.scale + ) + } else { + this.maxX = this.pageX / globalScope.scale + } + + var h = this.maxY - this.minY + var w = this.maxX - this.minX + + var ratio = Math.min(250 / h, 250 / w) + if (h > w) { + this.canvas.height = 250.0 + this.canvas.width = (250.0 * w) / h + } else { + this.canvas.width = 250.0 + this.canvas.height = (250.0 * h) / w + } + + this.canvas.height += 5 + this.canvas.width += 5 + + document.getElementById('miniMap').style.height = this.canvas.height + document.getElementById('miniMap').style.width = this.canvas.width + this.ctx = this.canvas.getContext('2d') + this.play(ratio) + }, + + play(ratio) { + if (lightMode || layoutModeGet()) return + + this.ctx.fillStyle = '#bbb' + this.ctx.rect(0, 0, this.canvas.width, this.canvas.height) + this.ctx.fill() + this.resolve(ratio) + }, + resolve(ratio) { + if (lightMode) return + + this.ctx.fillStyle = '#ddd' + this.ctx.beginPath() + this.ctx.rect( + 2.5 + + ((this.pageX - this.pageWidth) / globalScope.scale - + this.minX) * + ratio, + 2.5 + + ((this.pageY - this.pageHeight) / globalScope.scale - + this.minY) * + ratio, + (this.pageWidth * ratio) / globalScope.scale, + (this.pageHeight * ratio) / globalScope.scale + ) + this.ctx.fill() + + // to show the area of current canvas + var lst = updateOrder + const miniFill = colors['mini_fill'] + const miniStroke = colors['mini_stroke'] + + this.ctx.strokeStyle = miniStroke + this.ctx.fillStyle = miniFill + for (var i = 0; i < lst.length; i++) { + if (lst[i] === 'wires') { + for (var j = 0; j < globalScope[lst[i]].length; j++) { + this.ctx.beginPath() + this.ctx.moveTo( + 2.5 + + (globalScope[lst[i]][j].node1.absX() - this.minX) * + ratio, + 2.5 + + (globalScope[lst[i]][j].node1.absY() - this.minY) * + ratio + ) + this.ctx.lineTo( + 2.5 + + (globalScope[lst[i]][j].node2.absX() - this.minX) * + ratio, + 2.5 + + (globalScope[lst[i]][j].node2.absY() - this.minY) * + ratio + ) + this.ctx.stroke() + } + } else if (lst[i] != 'nodes') { + // Don't include SquareRGBLed here; it has correct size. + var ledY = 0 + if ( + lst[i] == 'DigitalLed' || + lst[i] == 'VariableLed' || + lst[i] == 'RGBLed' + ) { + ledY = 20 + } + + for (var j = 0; j < globalScope[lst[i]].length; j++) { + var xx = globalScope[lst[i]][j].x - simulationArea.minWidth + var yy = globalScope[lst[i]][j].y - simulationArea.minHeight + this.ctx.beginPath() + var obj = globalScope[lst[i]][j] + this.ctx.rect( + 2.5 + (obj.x - obj.leftDimensionX - this.minX) * ratio, + 2.5 + (obj.y - obj.upDimensionY - this.minY) * ratio, + (obj.rightDimensionX + obj.leftDimensionX) * ratio, + (obj.downDimensionY + obj.upDimensionY + ledY) * ratio + ) + + this.ctx.fill() + this.ctx.stroke() + } + } + } + }, + clear() { + if (lightMode) return + $('#miniMapArea').css('z-index', '-1') + this.context.clearRect(0, 0, this.canvas.width, this.canvas.height) + }, +} +var lastMiniMapShown +export function updatelastMinimapShown() { + lastMiniMapShown = new Date().getTime() +} +export function removeMiniMap() { + if (lightMode) return + + if ( + simulationArea.lastSelected == globalScope.root && + simulationArea.mouseDown + ) + return + if (lastMiniMapShown + 2000 >= new Date().getTime()) { + setTimeout( + removeMiniMap, + lastMiniMapShown + 2000 - new Date().getTime() + ) + return + } + $('#miniMap').fadeOut('fast') +} diff --git a/v1/src/simulator/src/moduleSetup.js b/v1/src/simulator/src/moduleSetup.js new file mode 100644 index 00000000..5002bc69 --- /dev/null +++ b/v1/src/simulator/src/moduleSetup.js @@ -0,0 +1,135 @@ +import modules from './modules' +import Adder from './modules/Adder' +import ALU from './modules/ALU' +import AndGate from './modules/AndGate' +import Arrow from './modules/Arrow' +import ImageAnnotation from './modules/ImageAnnotation' +import BitSelector from './modules/BitSelector' +import Buffer from './modules/Buffer' +import Button from './modules/Button' +import ConstantVal from './modules/ConstantVal' +import ControlledInverter from './modules/ControlledInverter' +import Counter from './modules/Counter' +import Decoder from './modules/Decoder' +import Demultiplexer from './modules/Demultiplexer' +import DigitalLed from './modules/DigitalLed' +import Flag from './modules/Flag' +import Ground from './modules/Ground' +import HexDisplay from './modules/HexDisplay' +import Input from './modules/Input' +import LSB from './modules/LSB' +import MSB from './modules/MSB' +import Multiplexer from './modules/Multiplexer' +import NandGate from './modules/NandGate' +import NorGate from './modules/NorGate' +import NotGate from './modules/NotGate' +import OrGate from './modules/OrGate' +import Output from './modules/Output' +import Power from './modules/Power' +import PriorityEncoder from './modules/PriorityEncoder' +import Random from './modules/Random' +import Rectangle from './modules/Rectangle' +import RGBLed from './modules/RGBLed' +import RGBLedMatrix from './modules/RGBLedMatrix' +import SevenSegDisplay from './modules/SevenSegDisplay' +import SixteenSegDisplay from './modules/SixteenSegDisplay' +import Splitter from './modules/Splitter' +import SquareRGBLed from './modules/SquareRGBLed' +import Stepper from './modules/Stepper' +import Text from './modules/Text' +import TriState from './modules/TriState' +import Tunnel from './modules/Tunnel' +import TwoComplement from './modules/TwoComplement' +import VariableLed from './modules/VariableLed' +import XnorGate from './modules/XnorGate' +import XorGate from './modules/XorGate' +import Clock from './sequential/Clock' +import DflipFlop from './sequential/DflipFlop' +import Dlatch from './sequential/Dlatch' +import EEPROM from './sequential/EEPROM' +import JKflipFlop from './sequential/JKflipFlop' +import Keyboard from './sequential/Keyboard' +import RAM from './sequential/RAM' +import Rom from './sequential/Rom' +import SRflipFlop from './sequential/SRflipFlop' +import TflipFlop from './sequential/TflipFlop' +import TTY from './sequential/TTY' +import ForceGate from './testbench/ForceGate' +import TB_Input from './testbench/testbenchInput' +import TB_Output from './testbench/testbenchOutput' +import verilogMultiplier from './modules/verilogMultiplier' +import verilogDivider from './modules/verilogDivider' +import verilogPower from './modules/verilogPower' +import verilogShiftLeft from './modules/verilogShiftLeft' +import verilogShiftRight from './modules/verilogShiftRight' +import verilogRAM from './sequential/verilogRAM' + +export default function setupModules() { + var moduleSet = { + AndGate, + Random, + NandGate, + Counter, + Multiplexer, + XorGate, + XnorGate, + SevenSegDisplay, + SixteenSegDisplay, + HexDisplay, + OrGate, + Stepper, + NotGate, + Text, + TriState, + Buffer, + ControlledInverter, + Adder, + verilogMultiplier, + verilogDivider, + verilogPower, + verilogShiftLeft, + verilogShiftRight, + TwoComplement, + Splitter, + Ground, + Power, + Input, + Output, + BitSelector, + ConstantVal, + NorGate, + DigitalLed, + VariableLed, + Button, + RGBLed, + SquareRGBLed, + Demultiplexer, + Decoder, + Flag, + MSB, + LSB, + PriorityEncoder, + Tunnel, + ALU, + Rectangle, + Arrow, + ImageAnnotation, + RGBLedMatrix, + TflipFlop, + DflipFlop, + Dlatch, + SRflipFlop, + JKflipFlop, + TTY, + Keyboard, + Clock, + Rom, + EEPROM, + RAM, + verilogRAM, + TB_Input, + TB_Output, + ForceGate, + } + Object.assign(modules, moduleSet) +} diff --git a/v1/src/simulator/src/modules.js b/v1/src/simulator/src/modules.js new file mode 100644 index 00000000..41bacb6f --- /dev/null +++ b/v1/src/simulator/src/modules.js @@ -0,0 +1,60 @@ +/* eslint-disable import/no-cycle */ +import simulationArea from './simulationArea' + +export function getNextPosition(x = 0, scope = globalScope) { + let possibleY = 20 + const done = {} + for (let i = 0; i < scope.Input.length - 1; i++) { + if (scope.Input[i].layoutProperties.x === x) { + done[scope.Input[i].layoutProperties.y] = 1 + } + } + for (let i = 0; i < scope.Output.length; i++) { + if (scope.Output[i].layoutProperties.x === x) { + done[scope.Output[i].layoutProperties.y] = 1 + } + } + while (done[possibleY] || done[possibleY + 10] || done[possibleY - 10]) { + possibleY += 10 + } + const height = possibleY + 20 + if (height > scope.layout.height) { + const oldHeight = scope.layout.height + scope.layout.height = height + for (let i = 0; i < scope.Input.length; i++) { + if (scope.Input[i].layoutProperties.y === oldHeight) { + scope.Input[i].layoutProperties.y = scope.layout.height + } + } + for (let i = 0; i < scope.Output.length; i++) { + if (scope.Output[i].layoutProperties.y === oldHeight) { + scope.Output[i].layoutProperties.y = scope.layout.height + } + } + } + return possibleY +} + +/** + * Global + */ +var modules = {} + +export default modules + +export function changeInputSize(size) { + if (size == undefined || size < 2 || size > 10) return + if (this.inputSize == size) return + size = parseInt(size, 10) + var obj = new modules[this.objectType]( + this.x, + this.y, + this.scope, + this.direction, + size, + this.bitWidth + ) + this.delete() + simulationArea.lastSelected = obj + return obj +} diff --git a/v1/src/simulator/src/modules/ALU.js b/v1/src/simulator/src/modules/ALU.js new file mode 100644 index 00000000..19cda667 --- /dev/null +++ b/v1/src/simulator/src/modules/ALU.js @@ -0,0 +1,200 @@ +/* eslint-disable no-bitwise */ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, fillText4 } from '../canvasApi' +import { colors } from '../themer/themer' + +/** + * @class + * ALU + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +export default class ALU extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT', bitWidth = 1) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['ALU'].push(this); + */ + this.message = 'ALU' + + this.setDimensions(30, 40) + this.rectangleObject = false + + this.inp1 = new Node(-30, -30, 0, this, this.bitwidth, 'A') + this.inp2 = new Node(-30, 30, 0, this, this.bitwidth, 'B') + + this.controlSignalInput = new Node(-10, -40, 0, this, 3, 'Ctrl') + this.carryOut = new Node(-10, 40, 1, this, 1, 'Cout') + this.output = new Node(30, 0, 1, this, this.bitwidth, 'Out') + } + + /** + * @memberof ALU + * function to change bitwidth of the element + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + this.bitWidth = bitWidth + this.inp1.bitWidth = bitWidth + this.inp2.bitWidth = bitWidth + this.output.bitWidth = bitWidth + } + + /** + * @memberof ALU + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.direction, this.bitWidth], + nodes: { + inp1: findNode(this.inp1), + inp2: findNode(this.inp2), + output: findNode(this.output), + carryOut: findNode(this.carryOut), + controlSignalInput: findNode(this.controlSignalInput), + }, + } + return data + } + + /** + * @memberof ALU + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + const xx = this.x + const yy = this.y + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.lineWidth = correctWidth(3) + ctx.beginPath() + moveTo(ctx, 30, 10, xx, yy, this.direction) + lineTo(ctx, 30, -10, xx, yy, this.direction) + lineTo(ctx, 10, -40, xx, yy, this.direction) + lineTo(ctx, -30, -40, xx, yy, this.direction) + lineTo(ctx, -30, -20, xx, yy, this.direction) + lineTo(ctx, -20, -10, xx, yy, this.direction) + lineTo(ctx, -20, 10, xx, yy, this.direction) + lineTo(ctx, -30, 20, xx, yy, this.direction) + lineTo(ctx, -30, 40, xx, yy, this.direction) + lineTo(ctx, 10, 40, xx, yy, this.direction) + lineTo(ctx, 30, 10, xx, yy, this.direction) + ctx.closePath() + ctx.stroke() + + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.fillStyle = colors['hover_select'] + } + ctx.fill() + ctx.stroke() + + ctx.beginPath() + ctx.fillStyle = 'Black' + ctx.textAlign = 'center' + + fillText4(ctx, 'B', -23, 30, xx, yy, this.direction, 6) + fillText4(ctx, 'A', -23, -30, xx, yy, this.direction, 6) + fillText4(ctx, 'CTR', -10, -30, xx, yy, this.direction, 6) + fillText4(ctx, 'Carry', -10, 30, xx, yy, this.direction, 6) + fillText4(ctx, 'Ans', 20, 0, xx, yy, this.direction, 6) + ctx.fill() + ctx.beginPath() + ctx.fillStyle = 'DarkGreen' + fillText4(ctx, this.message, 0, 0, xx, yy, this.direction, 12) + ctx.fill() + } + + /** + * @memberof ALU + * resolve output values based on inputData + */ + resolve() { + if (this.controlSignalInput.value === 0) { + this.output.value = this.inp1.value & this.inp2.value + simulationArea.simulationQueue.add(this.output) + this.carryOut.value = 0 + simulationArea.simulationQueue.add(this.carryOut) + this.message = 'A&B' + } else if (this.controlSignalInput.value === 1) { + this.output.value = this.inp1.value | this.inp2.value + + simulationArea.simulationQueue.add(this.output) + this.carryOut.value = 0 + simulationArea.simulationQueue.add(this.carryOut) + this.message = 'A|B' + } else if (this.controlSignalInput.value === 2) { + const sum = this.inp1.value + this.inp2.value + this.output.value = + (sum << (32 - this.bitWidth)) >>> (32 - this.bitWidth) + this.carryOut.value = +(sum >>> this.bitWidth !== 0) + simulationArea.simulationQueue.add(this.carryOut) + simulationArea.simulationQueue.add(this.output) + this.message = 'A+B' + } else if (this.controlSignalInput.value === 3) { + this.message = 'ALU' + } else if (this.controlSignalInput.value === 4) { + this.message = 'A&~B' + this.output.value = this.inp1.value & this.flipBits(this.inp2.value) + simulationArea.simulationQueue.add(this.output) + this.carryOut.value = 0 + simulationArea.simulationQueue.add(this.carryOut) + } else if (this.controlSignalInput.value === 5) { + this.message = 'A|~B' + this.output.value = this.inp1.value | this.flipBits(this.inp2.value) + simulationArea.simulationQueue.add(this.output) + this.carryOut.value = 0 + simulationArea.simulationQueue.add(this.carryOut) + } else if (this.controlSignalInput.value === 6) { + this.message = 'A-B' + this.output.value = + ((this.inp1.value - this.inp2.value) << + (32 - this.bitWidth)) >>> + (32 - this.bitWidth) + simulationArea.simulationQueue.add(this.output) + this.carryOut.value = 0 + simulationArea.simulationQueue.add(this.carryOut) + } else if (this.controlSignalInput.value === 7) { + this.message = 'A>> (32 - this.bitWidth) + this.carryOut.value = +(sum >>> this.bitWidth !== 0) + simulationArea.simulationQueue.add(this.carryOut) + simulationArea.simulationQueue.add(this.sum) + } + + generateVerilog() { + if (this.carryIn.verilogLabel) { + return `assign ${this.sum.verilogLabel} = ${this.inpA.verilogLabel} + ${this.inpB.verilogLabel} + ${this.carryIn.verilogLabel};` + } + return `assign ${this.sum.verilogLabel} = ${this.inpA.verilogLabel} + ${this.inpB.verilogLabel};` + } +} + +/** + * @memberof Adder + * Help Tip + * @type {string} + * @category modules + */ +Adder.prototype.tooltipText = 'Adder ToolTip : Performs addition of numbers.' +Adder.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/8misc?id=adder' +Adder.prototype.objectType = 'Adder' diff --git a/v1/src/simulator/src/modules/AndGate.js b/v1/src/simulator/src/modules/AndGate.js new file mode 100644 index 00000000..43c3fced --- /dev/null +++ b/v1/src/simulator/src/modules/AndGate.js @@ -0,0 +1,168 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, arc } from '../canvasApi' +import { changeInputSize } from '../modules' +import { colors } from '../themer/themer' +import { gateGenerateVerilog } from '../utils' + +/** + * @class + * AndGate + * @extends CircuitElement + * @param {number} x - x coordinate of And Gate. + * @param {number} y - y coordinate of And Gate. + * @param {Scope=} scope - Cirucit on which and gate is drawn + * @param {string=} dir - direction of And Gate + * @param {number=} inputLength - number of input nodes + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +export default class AndGate extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + inputLength = 2, + bitWidth = 1 + ) { + /** + * super call + */ + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['AndGate'].push(this); + */ + this.rectangleObject = false + this.setDimensions(15, 20) + this.inp = [] + this.inputSize = inputLength + + // variable inputLength , node creation + if (inputLength % 2 === 1) { + for (let i = 0; i < inputLength / 2 - 1; i++) { + const a = new Node(-10, -10 * (i + 1), 0, this) + this.inp.push(a) + } + let a = new Node(-10, 0, 0, this) + this.inp.push(a) + for (let i = inputLength / 2 + 1; i < inputLength; i++) { + a = new Node(-10, 10 * (i + 1 - inputLength / 2 - 1), 0, this) + this.inp.push(a) + } + } else { + for (let i = 0; i < inputLength / 2; i++) { + const a = new Node(-10, -10 * (i + 1), 0, this) + this.inp.push(a) + } + for (let i = inputLength / 2; i < inputLength; i++) { + const a = new Node(-10, 10 * (i + 1 - inputLength / 2), 0, this) + this.inp.push(a) + } + } + + this.output1 = new Node(20, 0, 1, this) + } + + /** + * @memberof AndGate + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [ + this.direction, + this.inputSize, + this.bitWidth, + ], + nodes: { + inp: this.inp.map(findNode), + output1: findNode(this.output1), + }, + } + return data + } + + /** + * @memberof AndGate + * resolve output values based on inputData + */ + resolve() { + let result = this.inp[0].value || 0 + if (this.isResolvable() === false) { + return + } + for (let i = 1; i < this.inputSize; i++) + result &= this.inp[i].value || 0 + this.output1.value = result >>> 0 + simulationArea.simulationQueue.add(this.output1) + } + + /** + * @memberof AndGate + * function to draw And Gate + */ + customDraw() { + var ctx = simulationArea.context + ctx.beginPath() + ctx.lineWidth = correctWidth(3) + ctx.strokeStyle = colors['stroke'] // ("rgba(0,0,0,1)"); + ctx.fillStyle = colors['fill'] + const xx = this.x + const yy = this.y + + moveTo(ctx, -10, -20, xx, yy, this.direction) + lineTo(ctx, 0, -20, xx, yy, this.direction) + arc(ctx, 0, 0, 20, -Math.PI / 2, Math.PI / 2, xx, yy, this.direction) + lineTo(ctx, -10, 20, xx, yy, this.direction) + lineTo(ctx, -10, -20, xx, yy, this.direction) + ctx.closePath() + + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + } + + generateVerilog() { + return gateGenerateVerilog.call(this, '&') + } +} + +/** + * @memberof AndGate + * Help Tip + * @type {string} + * @category modules + */ +AndGate.prototype.tooltipText = + 'And Gate Tooltip : Implements logical conjunction' + +/** + * @memberof AndGate + * @type {boolean} + * @category modules + */ +AndGate.prototype.alwaysResolve = true + +/** + * @memberof AndGate + * @type {string} + * @category modules + */ +AndGate.prototype.verilogType = 'and' + +/** + * @memberof AndGate + * function to change input nodes of the gate + * @category modules + */ +AndGate.prototype.changeInputSize = changeInputSize +AndGate.prototype.helplink = 'https://docs.circuitverse.org/#/chapter4/4gates?id=and-gate' +AndGate.prototype.objectType = 'AndGate' diff --git a/v1/src/simulator/src/modules/Arrow.js b/v1/src/simulator/src/modules/Arrow.js new file mode 100644 index 00000000..24eeddfd --- /dev/null +++ b/v1/src/simulator/src/modules/Arrow.js @@ -0,0 +1,83 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, arc } from '../canvasApi' +import { changeInputSize } from '../modules' +/** + * @class + * Arrow + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @category modules + */ +import { colors } from '../themer/themer' + +export default class Arrow extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT') { + super(x, y, scope, dir, 8) + /* this is done in this.baseSetup() now + this.scope['Arrow'].push(this); + */ + this.rectangleObject = false + this.fixedBitWidth = true + this.setDimensions(30, 20) + } + + /** + * @memberof Arrow + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.direction], + } + return data + } + + /** + * @memberof Arrow + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.lineWidth = correctWidth(3) + const xx = this.x + const yy = this.y + ctx.strokeStyle = colors['stroke_alt'] + ctx.fillStyle = colors['fill'] + + ctx.beginPath() + + moveTo(ctx, -30, -3, xx, yy, this.direction) + lineTo(ctx, 10, -3, xx, yy, this.direction) + lineTo(ctx, 10, -15, xx, yy, this.direction) + lineTo(ctx, 30, 0, xx, yy, this.direction) + lineTo(ctx, 10, 15, xx, yy, this.direction) + lineTo(ctx, 10, 3, xx, yy, this.direction) + lineTo(ctx, -30, 3, xx, yy, this.direction) + ctx.closePath() + ctx.stroke() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + } +} + +/** + * @memberof Arrow + * Help Tip + * @type {string} + * @category modules + */ +Arrow.prototype.tooltipText = 'Arrow ToolTip : Arrow Selected.' +Arrow.prototype.propagationDelayFixed = true +Arrow.prototype.helplink = 'https://docs.circuitverse.org/#/chapter4/7annotation?id=arrow' +Arrow.prototype.objectType = 'Arrow' diff --git a/v1/src/simulator/src/modules/BitSelector.js b/v1/src/simulator/src/modules/BitSelector.js new file mode 100644 index 00000000..8c91cee4 --- /dev/null +++ b/v1/src/simulator/src/modules/BitSelector.js @@ -0,0 +1,173 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode, extractBits } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, rect, fillText } from '../canvasApi' +/** + * @class + * BitSelector + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @param {number=} selectorBitWidth - 1 by default + * @category modules + */ +import { colors } from '../themer/themer' + +export default class BitSelector extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + bitWidth = 2, + selectorBitWidth = 1 + ) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['BitSelector'].push(this); + */ + this.setDimensions(20, 20) + this.selectorBitWidth = + selectorBitWidth || parseInt(prompt('Enter Selector bitWidth'), 10) + this.rectangleObject = false + this.inp1 = new Node(-20, 0, 0, this, this.bitWidth, 'Input') + this.output1 = new Node(20, 0, 1, this, 1, 'Output') + this.bitSelectorInp = new Node( + 0, + 20, + 0, + this, + this.selectorBitWidth, + 'Bit Selector' + ) + } + + /** + * @memberof BitSelector + * Function to change selector Bitwidth + * @param {size} + */ + changeSelectorBitWidth(size) { + if (size === undefined || size < 1 || size > 32) return + this.selectorBitWidth = size + this.bitSelectorInp.bitWidth = size + } + + /** + * @memberof BitSelector + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + nodes: { + inp1: findNode(this.inp1), + output1: findNode(this.output1), + bitSelectorInp: findNode(this.bitSelectorInp), + }, + constructorParamaters: [ + this.direction, + this.bitWidth, + this.selectorBitWidth, + ], + } + return data + } + + /** + * @memberof BitSelector + * function to change bitwidth of the element + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + this.inp1.bitWidth = bitWidth + this.bitWidth = bitWidth + } + + /** + * @memberof BitSelector + * resolve output values based on inputData + */ + resolve() { + this.output1.value = extractBits( + this.inp1.value, + this.bitSelectorInp.value + 1, + this.bitSelectorInp.value + 1 + ) // (this.inp1.value^(1<> ${this.bitSelectorInp.verilogLabel};` + } +} + +/** + * @memberof BitSelector + * Help Tip + * @type {string} + * @category modules + */ +BitSelector.prototype.tooltipText = + 'BitSelector ToolTip : Divides input bits into several equal-sized groups.' +BitSelector.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/5muxandplex?id=bitselector' + +/** + * @memberof BitSelector + * Mutable properties of the element + * @type {JSON} + * @category modules + */ +BitSelector.prototype.mutableProperties = { + selectorBitWidth: { + name: 'Selector Bit Width: ', + type: 'number', + max: '32', + min: '1', + func: 'changeSelectorBitWidth', + }, +} +BitSelector.prototype.objectType = 'BitSelector' diff --git a/v1/src/simulator/src/modules/Buffer.js b/v1/src/simulator/src/modules/Buffer.js new file mode 100644 index 00000000..f3e9cd69 --- /dev/null +++ b/v1/src/simulator/src/modules/Buffer.js @@ -0,0 +1,134 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, arc } from '../canvasApi' +import { changeInputSize } from '../modules' +/** + * @class + * Buffer + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +import { colors } from '../themer/themer' + +export default class Buffer extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT', bitWidth = 1) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['Buffer'].push(this); + */ + this.rectangleObject = false + this.setDimensions(15, 15) + this.state = 0 + this.preState = 0 + this.inp1 = new Node(-10, 0, 0, this) + this.reset = new Node(0, 0, 0, this, 1, 'reset') + this.output1 = new Node(20, 0, 1, this) + } + + /** + * @memberof Buffer + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.direction, this.bitWidth], + nodes: { + output1: findNode(this.output1), + inp1: findNode(this.inp1), + reset: findNode(this.reset), + }, + } + return data + } + + /** + * @memberof Buffer + * function to change bitwidth of the element + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + this.inp1.bitWidth = bitWidth + this.output1.bitWidth = bitWidth + this.bitWidth = bitWidth + } + + /** + * @memberof Buffer + * Checks if the element is resolvable + * @return {boolean} + */ + isResolvable() { + return true + } + + /** + * @memberof Buffer + * resolve output values based on inputData + */ + resolve() { + if (this.reset.value === 1) { + this.state = this.preState + } + if (this.inp1.value !== undefined) { + this.state = this.inp1.value + } + + this.output1.value = this.state + simulationArea.simulationQueue.add(this.output1) + } + + /** + * @memberof Buffer + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.strokeStyle = colors['stroke_alt'] + ctx.lineWidth = correctWidth(3) + const xx = this.x + const yy = this.y + ctx.beginPath() + ctx.fillStyle = colors['fill'] + moveTo(ctx, -10, -15, xx, yy, this.direction) + lineTo(ctx, 20, 0, xx, yy, this.direction) + lineTo(ctx, -10, 15, xx, yy, this.direction) + ctx.closePath() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + } + + generateVerilog() { + return ( + 'assign ' + + this.output1.verilogLabel + + ' = ' + + this.inp1.verilogLabel + + ';' + ) + } +} + +/** + * @memberof Buffer + * Help Tip + * @type {string} + * @category modules + */ +Buffer.prototype.tooltipText = + 'Buffer ToolTip : Isolate the input from the output.' +Buffer.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/8misc?id=buffer' +Buffer.prototype.objectType = 'Buffer' diff --git a/v1/src/simulator/src/modules/Button.js b/v1/src/simulator/src/modules/Button.js new file mode 100644 index 00000000..b108afaa --- /dev/null +++ b/v1/src/simulator/src/modules/Button.js @@ -0,0 +1,184 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, drawCircle2 } from '../canvasApi' + +/** + * @class + * Button + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @category modules + */ +export default class Button extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT') { + super(x, y, scope, dir, 1) + /* this is done in this.baseSetup() now + this.scope['Button'].push(this); + */ + this.state = 0 + this.output1 = new Node(30, 0, 1, this) + this.wasClicked = false + this.rectangleObject = false + this.setDimensions(10, 10) + } + + /** + * @memberof Button + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + nodes: { + output1: findNode(this.output1), + }, + values: { + state: this.state, + }, + constructorParamaters: [this.direction, this.bitWidth], + } + return data + } + + /** + * @memberof Button + * resolve output values based on inputData + */ + resolve() { + if (this.wasClicked) { + this.state = 1 + this.output1.value = this.state + } else { + this.state = 0 + this.output1.value = this.state + } + simulationArea.simulationQueue.add(this.output1) + } + + /** + * @memberof Button + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + const xx = this.x + const yy = this.y + ctx.fillStyle = '#ddd' + + ctx.strokeStyle = '#353535' + ctx.lineWidth = correctWidth(5) + + ctx.beginPath() + + moveTo(ctx, 10, 0, xx, yy, this.direction) + lineTo(ctx, 30, 0, xx, yy, this.direction) + ctx.stroke() + + ctx.beginPath() + + drawCircle2(ctx, 0, 0, 12, xx, yy, this.direction) + ctx.stroke() + + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.fillStyle = 'rgba(232, 13, 13,0.6)' + } + + if (this.wasClicked) { + ctx.fillStyle = 'rgba(232, 13, 13,0.8)' + } + ctx.fill() + } + + subcircuitDraw(xOffset = 0, yOffset = 0) { + var ctx = simulationArea.context + var xx = this.subcircuitMetadata.x + xOffset + var yy = this.subcircuitMetadata.y + yOffset + ctx.fillStyle = '#ddd' + + ctx.strokeStyle = '#353535' + ctx.lineWidth = correctWidth(3) + ctx.beginPath() + drawCircle2(ctx, 0, 0, 6, xx, yy, this.direction) + ctx.stroke() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected == this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = 'rgba(232, 13, 13,0.6)' + if (this.wasClicked) ctx.fillStyle = 'rgba(232, 13, 13,0.8)' + ctx.fill() + } + static verilogInstructions() { + return `Button - Buttons are not natively supported in verilog, consider using Inputs instead\n` + } + verilogBaseType() { + return this.verilogName() + (Button.selSizes.length - 1) + } + + //this code to generate Verilog + generateVerilog() { + Button.selSizes.push(this.data) + return CircuitElement.prototype.generateVerilog.call(this) + } + + static moduleVerilog() { + var output = '' + + for (var i = 0; i < Button.selSizes.length; i++) { + output += `// Skeleton for Button${i} + /* + module Button${i}(out); + output reg out; + + initial begin + //do something with the button here + end + endmodule + */ + ` + } + + return output + } + + //reset the sized before Verilog generation + static resetVerilog() { + Button.selSizes = [] + } +} + +/** + * @memberof Button + * Help Tip + * @type {string} + * @category modules + */ +Button.prototype.tooltipText = + 'Button ToolTip: High(1) when pressed and Low(0) when released.' + +/** + * @memberof Button + * Help URL + * @type {string} + * @category modules + */ +Button.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/2input?id=button' + +/** + * @memberof Button + * @type {number} + * @category modules + */ +Button.prototype.propagationDelay = 0 +Button.prototype.objectType = 'Button' +Button.prototype.canShowInSubcircuit = true diff --git a/v1/src/simulator/src/modules/ConstantVal.js b/v1/src/simulator/src/modules/ConstantVal.js new file mode 100644 index 00000000..d5f9028a --- /dev/null +++ b/v1/src/simulator/src/modules/ConstantVal.js @@ -0,0 +1,209 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, rect2, fillText, oppositeDirection } from '../canvasApi' +import { colors } from '../themer/themer' + +function bin2dec(binString) { + return parseInt(binString, 2) +} + +function dec2bin(dec, bitWidth = undefined) { + // only for positive nos + var bin = dec.toString(2) + if (bitWidth == undefined) return bin + return '0'.repeat(bitWidth - bin.length) + bin +} + +/** + * @class + * ConstantVal + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @param {string=} state - The state of element + * @category modules + */ +export default class ConstantVal extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + bitWidth = 1, + state = '0' + ) { + // state = state || prompt('Enter value'); + super(x, y, scope, dir, state.length) + /* this is done in this.baseSetup() now + this.scope['ConstantVal'].push(this); + */ + this.state = state + this.setDimensions(10 * this.state.length, 10) + this.bitWidth = bitWidth || this.state.length + this.directionFixed = true + this.orientationFixed = false + this.rectangleObject = false + + this.output1 = new Node(this.bitWidth * 10, 0, 1, this) + this.wasClicked = false + this.label = '' + } + + generateVerilog() { + return `localparam [${this.bitWidth - 1}:0] ${this.verilogLabel}=${ + this.bitWidth + }b'${this.state};` + } + + /** + * @memberof ConstantVal + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + nodes: { + output1: findNode(this.output1), + }, + constructorParamaters: [this.direction, this.bitWidth, this.state], + } + return data + } + + /** + * @memberof ConstantVal + * resolve output values based on inputData + */ + resolve() { + this.output1.value = bin2dec(this.state) + simulationArea.simulationQueue.add(this.output1) + } + + /** + * @memberof ConstantVal + * updates state using a prompt when dbl clicked + */ + dblclick() { + this.state = prompt('Re enter the value') || '0' + this.newBitWidth(this.state.toString().length) + } + + /** + * @memberof ConstantVal + * function to change bitwidth of the element + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + if (bitWidth > this.state.length) + this.state = '0'.repeat(bitWidth - this.state.length) + this.state + else if (bitWidth < this.state.length) + this.state = this.state.slice(this.bitWidth - bitWidth) + this.bitWidth = bitWidth // ||parseInt(prompt("Enter bitWidth"),10); + this.output1.bitWidth = bitWidth + this.setDimensions(10 * this.bitWidth, 10) + if (this.direction === 'RIGHT') { + this.output1.x = 10 * this.bitWidth + this.output1.leftx = 10 * this.bitWidth + } else if (this.direction === 'LEFT') { + this.output1.x = -10 * this.bitWidth + this.output1.leftx = 10 * this.bitWidth + } + } + + /** + * @memberof ConstantVal + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.lineWidth = correctWidth(1) + const xx = this.x + const yy = this.y + + rect2( + ctx, + -10 * this.bitWidth, + -10, + 20 * this.bitWidth, + 20, + xx, + yy, + 'RIGHT' + ) + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + + ctx.beginPath() + ctx.fillStyle = colors['input_text'] + ctx.textAlign = 'center' + const bin = this.state // dec2bin(this.state,this.bitWidth); + for (let k = 0; k < this.bitWidth; k++) { + fillText(ctx, bin[k], xx - 10 * this.bitWidth + 10 + k * 20, yy + 5) + } + ctx.fill() + } + + /** + * @memberof ConstantVal + * function to change direction of ConstantVal + * @param {string} dir - new direction + */ + newDirection(dir) { + if (dir === this.direction) return + this.direction = dir + this.output1.refresh() + if (dir === 'RIGHT' || dir === 'LEFT') { + this.output1.leftx = 10 * this.bitWidth + this.output1.lefty = 0 + } else { + this.output1.leftx = 10 // 10*this.bitWidth; + this.output1.lefty = 0 + } + + this.output1.refresh() + this.labelDirection = oppositeDirection[this.direction] + } + + generateVerilog() { + return `assign ${this.output1.verilogLabel} = ${this.bitWidth}'b${this.state};` + } +} + +/** + * @memberof ConstantVal + * Help Tip + * @type {string} + * @category modules + */ +ConstantVal.prototype.tooltipText = + 'Constant ToolTip: Bits are fixed. Double click element to change the bits.' + +/** + * @memberof ConstantVal + * Help URL + * @type {string} + * @category modules + */ +ConstantVal.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/2input?id=constantval' + +/** + * @memberof ConstantVal + * @type {number} + * @category modules + */ +ConstantVal.prototype.propagationDelay = 0 +ConstantVal.prototype.objectType = 'ConstantVal' diff --git a/v1/src/simulator/src/modules/ControlledInverter.js b/v1/src/simulator/src/modules/ControlledInverter.js new file mode 100644 index 00000000..c846abec --- /dev/null +++ b/v1/src/simulator/src/modules/ControlledInverter.js @@ -0,0 +1,122 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, drawCircle2 } from '../canvasApi' +import { changeInputSize } from '../modules' +/** + * @class + * ControlledInverter + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +import { colors } from '../themer/themer' + +export default class ControlledInverter extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT', bitWidth = 1) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['ControlledInverter'].push(this); + */ + this.rectangleObject = false + this.setDimensions(15, 15) + + this.inp1 = new Node(-10, 0, 0, this) + this.output1 = new Node(30, 0, 1, this) + this.state = new Node(0, 0, 0, this, 1, 'Enable') + } + + /** + * @memberof ControlledInverter + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.direction, this.bitWidth], + nodes: { + output1: findNode(this.output1), + inp1: findNode(this.inp1), + state: findNode(this.state), + }, + } + return data + } + + /** + * @memberof ControlledInverter + * function to change bitwidth of the element + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + this.inp1.bitWidth = bitWidth + this.output1.bitWidth = bitWidth + this.bitWidth = bitWidth + } + + /** + * @memberof ControlledInverter + * resolve output values based on inputData + */ + resolve() { + if (this.isResolvable() === false) { + return + } + if (this.state.value === 1) { + this.output1.value = + ((~this.inp1.value >>> 0) << (32 - this.bitWidth)) >>> + (32 - this.bitWidth) + simulationArea.simulationQueue.add(this.output1) + } + if (this.state.value === 0) { + this.output1.value = undefined + } + } + + /** + * @memberof ControlledInverter + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.strokeStyle = colors['stroke'] + ctx.lineWidth = correctWidth(3) + const xx = this.x + const yy = this.y + ctx.beginPath() + ctx.fillStyle = colors['fill'] + moveTo(ctx, -10, -15, xx, yy, this.direction) + lineTo(ctx, 20, 0, xx, yy, this.direction) + lineTo(ctx, -10, 15, xx, yy, this.direction) + ctx.closePath() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + ctx.beginPath() + drawCircle2(ctx, 25, 0, 5, xx, yy, this.direction) + ctx.stroke() + } + + generateVerilog() { + return `assign ${this.output1.verilogLabel} = (${this.state.verilogLabel}!=0) ? ~${this.inp1.verilogLabel} : ${this.inp1.verilogLabel};` + } +} + +/** + * @memberof ControlledInverter + * Help Tip + * @type {string} + * @category modules + */ +ControlledInverter.prototype.tooltipText = + 'Controlled Inverter ToolTip : Controlled buffer and NOT gate.' +ControlledInverter.prototype.objectType = 'ControlledInverter' diff --git a/v1/src/simulator/src/modules/Counter.js b/v1/src/simulator/src/modules/Counter.js new file mode 100644 index 00000000..7ffb23fc --- /dev/null +++ b/v1/src/simulator/src/modules/Counter.js @@ -0,0 +1,194 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { lineTo, moveTo, fillText, correctWidth, rect2 } from '../canvasApi' +import { colors } from '../themer/themer' + +/** + * @class + * Counter component. + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {number=} rows - number of rows + * @param {number=} cols - number of columns. + * Counts from zero to a particular maximum value, which is either + * specified by an input pin or determined by the Counter's bitWidth. + * The counter outputs its current value and a flag that indicates + * when the output value is zero and the clock is 1. + * The counter can be reset to zero at any point using the RESET pin. + * @category modules + */ +export default class Counter extends CircuitElement { + constructor(x, y, scope = globalScope, bitWidth = 8) { + super(x, y, scope, 'RIGHT', bitWidth) + /* this is done in this.baseSetup() now + this.scope['Counter'].push(this); + */ + this.directionFixed = true + this.rectangleObject = true + + this.setDimensions(20, 20) + + this.maxValue = new Node(-20, -10, 0, this, this.bitWidth, 'MaxValue') + this.clock = new Node(-20, +10, 0, this, 1, 'Clock') + this.reset = new Node(0, 20, 0, this, 1, 'Reset') + this.output = new Node(20, -10, 1, this, this.bitWidth, 'Value') + this.zero = new Node(20, 10, 1, this, 1, 'Zero') + + this.value = 0 + this.prevClockState = undefined + } + + customSave() { + return { + nodes: { + maxValue: findNode(this.maxValue), + clock: findNode(this.clock), + reset: findNode(this.reset), + output: findNode(this.output), + zero: findNode(this.zero), + }, + constructorParamaters: [this.bitWidth], + } + } + + newBitWidth(bitWidth) { + this.bitWidth = bitWidth + this.maxValue.bitWidth = bitWidth + this.output.bitWidth = bitWidth + } + + isResolvable() { + return true + } + + resolve() { + // Max value is either the value in the input pin or the max allowed by the bitWidth. + var maxValue = + this.maxValue.value != undefined + ? this.maxValue.value + : (1 << this.bitWidth) - 1 + var outputValue = this.value + + // Increase value when clock is raised + if (this.clock.value != this.prevClockState && this.clock.value == 1) { + outputValue++ + } + this.prevClockState = this.clock.value + + // Limit to the effective maximum value; this also accounts for bitWidth changes. + outputValue = outputValue % (maxValue + 1) + + // Reset to zero if RESET pin is on + if (this.reset.value == 1) { + outputValue = 0 + } + + // Output the new value + this.value = outputValue + if (this.output.value != outputValue) { + this.output.value = outputValue + simulationArea.simulationQueue.add(this.output) + } + + // Output the zero signal + var zeroValue = this.clock.value == 1 && outputValue == 0 ? 1 : 0 + if (this.zero.value != zeroValue) { + this.zero.value = zeroValue + simulationArea.simulationQueue.add(this.zero) + } + } + + customDraw() { + var ctx = simulationArea.context + var xx = this.x + var yy = this.y + + ctx.beginPath() + ctx.font = '20px Raleway' + ctx.fillStyle = colors['input_text'] + ctx.textAlign = 'center' + fillText(ctx, this.value.toString(16), this.x, this.y + 5) + ctx.fill() + + ctx.strokeStyle = colors['stroke'] + ctx.beginPath() + moveTo(ctx, -20, 5, xx, yy, this.direction) + lineTo(ctx, -15, 10, xx, yy, this.direction) + lineTo(ctx, -20, 15, xx, yy, this.direction) + ctx.stroke() + } + + // Draws the element in the subcuircuit. Used in layout mode + subcircuitDraw(xOffset = 0, yOffset = 0) { + var ctx = simulationArea.context + var xx = this.subcircuitMetadata.x + xOffset + var yy = this.subcircuitMetadata.y + yOffset + + ctx.beginPath() + ctx.font = '20px Raleway' + ctx.fillStyle = 'green' + ctx.textAlign = 'center' + fillText(ctx, this.value.toString(16), xx + 10, yy + 17) + ctx.fill() + + ctx.beginPath() + ctx.lineWidth = correctWidth(1) + rect2(ctx, 0, 0, 20, 20, xx, yy, this.direction) + ctx.stroke() + + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected == this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.fillStyle = 'rgba(255, 255, 32,0.6)' + ctx.fill() + } + } + static moduleVerilog() { + return ` + module Counter(val, zero, max, clk, rst); + parameter WIDTH = 1; + output reg [WIDTH-1:0] val; + output reg zero; + input [WIDTH-1:0] max; + input clk, rst; + + initial + val = 0; + + always @ (val) + if (val == 0) + zero = 1; + else + zero = 0; + + always @ (posedge clk or posedge rst) begin + if (rst) + val <= 0; + else + if (val == max) + val <= 0; + else + val <= val + 1; + end + endmodule` + } +} + +Counter.prototype.tooltipText = + 'Counter: a binary counter from zero to a given maximum value' +Counter.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/2input?id=counter' +Counter.prototype.objectType = 'Counter' +Counter.prototype.objectType = 'Counter' +Counter.prototype.canShowInSubcircuit = true +Counter.prototype.layoutProperties = { + rightDimensionX: 20, + leftDimensionX: 0, + upDimensionY: 0, + downDimensionY: 20, +} diff --git a/v1/src/simulator/src/modules/Decoder.js b/v1/src/simulator/src/modules/Decoder.js new file mode 100644 index 00000000..6b6d9278 --- /dev/null +++ b/v1/src/simulator/src/modules/Decoder.js @@ -0,0 +1,291 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, rect, fillText } from '../canvasApi' +/** + * @class + * Decoder + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +import { colors } from '../themer/themer' + +export default class Decoder extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'LEFT', bitWidth = 1) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['Decoder'].push(this); + */ + // this.controlSignalSize = controlSignalSize || parseInt(prompt("Enter control signal bitWidth"), 10); + this.outputsize = 1 << this.bitWidth + this.xOff = 0 + this.yOff = 1 + if (this.bitWidth === 1) { + this.xOff = 10 + } + if (this.bitWidth <= 3) { + this.yOff = 2 + } + + // this.changeControlSignalSize = function(size) { + // if (size === undefined || size < 1 || size > 32) return; + // if (this.controlSignalSize === size) return; + // let obj = new window[this.objectType](this.x, this.y, this.scope, this.direction, this.bitWidth, size); + // this.cleanDelete(); + // simulationArea.lastSelected = obj; + // return obj; + // } + // this.mutableProperties = { + // "controlSignalSize": { + // name: "Control Signal Size", + // type: "number", + // max: "32", + // min: "1", + // func: "changeControlSignalSize", + // }, + // } + // eslint-disable-next-line no-shadow + this.newBitWidth = function (bitWidth) { + // this.bitWidth = bitWidth; + // for (let i = 0; i < this.inputSize; i++) { + // this.outputs1[i].bitWidth = bitWidth + // } + // this.input.bitWidth = bitWidth; + if (bitWidth === undefined || bitWidth < 1 || bitWidth > 32) return + if (this.bitWidth === bitWidth) return + const obj = new Decoder( + this.x, + this.y, + this.scope, + this.direction, + bitWidth + ) + this.cleanDelete() + simulationArea.lastSelected = obj + return obj + } + + this.setDimensions(20 - this.xOff, this.yOff * 5 * this.outputsize) + this.rectangleObject = false + this.input = new Node(20 - this.xOff, 0, 0, this) + + this.output1 = [] + for (let i = 0; i < this.outputsize; i++) { + const a = new Node( + -20 + this.xOff, + +this.yOff * 10 * (i - this.outputsize / 2) + 10, + 1, + this, + 1 + ) + this.output1.push(a) + } + + // this.controlSignalInput = new Node(0,this.yOff * 10 * (this.outputsize / 2 - 1) +this.xOff + 10, 0, this, this.controlSignalSize,"Control Signal"); + } + + /** + * @memberof Decoder + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.direction, this.bitWidth], + nodes: { + output1: this.output1.map(findNode), + input: findNode(this.input), + }, + } + return data + } + + /** + * @memberof Decoder + * resolve output values based on inputData + */ + resolve() { + for (let i = 0; i < this.output1.length; i++) { + this.output1[i].value = 0 + } + if (this.input.value !== undefined) + this.output1[this.input.value].value = 1 // if input is undefined, don't change output + for (let i = 0; i < this.output1.length; i++) { + simulationArea.simulationQueue.add(this.output1[i]) + } + } + + /** + * @memberof Decoder + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + + const xx = this.x + const yy = this.y + + // ctx.beginPath(); + // moveTo(ctx, 0,this.yOff * 10 * (this.outputsize / 2 - 1) + 10 + 0.5 *this.xOff, xx, yy, this.direction); + // lineTo(ctx, 0,this.yOff * 5 * (this.outputsize - 1) +this.xOff, xx, yy, this.direction); + // ctx.stroke(); + + ctx.beginPath() + ctx.strokeStyle = colors['stroke'] + ctx.lineWidth = correctWidth(4) + ctx.fillStyle = colors['fill'] + moveTo( + ctx, + -20 + this.xOff, + -this.yOff * 10 * (this.outputsize / 2), + xx, + yy, + this.direction + ) + lineTo( + ctx, + -20 + this.xOff, + 20 + this.yOff * 10 * (this.outputsize / 2 - 1), + xx, + yy, + this.direction + ) + lineTo( + ctx, + 20 - this.xOff, + +this.yOff * 10 * (this.outputsize / 2 - 1) + this.xOff, + xx, + yy, + this.direction + ) + lineTo( + ctx, + 20 - this.xOff, + -this.yOff * 10 * (this.outputsize / 2) - this.xOff + 20, + xx, + yy, + this.direction + ) + + ctx.closePath() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.fillStyle = colors['hover_select'] + } + ctx.fill() + ctx.stroke() + + ctx.beginPath() + ctx.fillStyle = 'black' + ctx.textAlign = 'center' + // [xFill,yFill] = rotate(xx + this.output1[i].x - 7, yy + this.output1[i].y + 2); + for (let i = 0; i < this.outputsize; i++) { + if (this.direction === 'LEFT') + fillText( + ctx, + String(i), + xx + this.output1[i].x - 7, + yy + this.output1[i].y + 2, + 10 + ) + else if (this.direction === 'RIGHT') + fillText( + ctx, + String(i), + xx + this.output1[i].x + 7, + yy + this.output1[i].y + 2, + 10 + ) + else if (this.direction === 'UP') + fillText( + ctx, + String(i), + xx + this.output1[i].x, + yy + this.output1[i].y - 5, + 10 + ) + else + fillText( + ctx, + String(i), + xx + this.output1[i].x, + yy + this.output1[i].y + 10, + 10 + ) + } + ctx.fill() + } + + verilogBaseType() { + return this.verilogName() + this.output1.length + } + + //this code to generate Verilog + generateVerilog() { + Decoder.selSizes.add(this.bitWidth) + return CircuitElement.prototype.generateVerilog.call(this) + } + + static moduleVerilog() { + var output = '' + + for (var size of Decoder.selSizes) { + var numOutput = 1 << size + output += '\n' + output += 'module Decoder' + numOutput + output += '(' + for (var j = 0; j < numOutput; j++) { + output += 'out' + j + ', ' + } + output += 'sel);\n' + + output += ' output reg ' + for (var j = 0; j < numOutput - 1; j++) { + output += 'out' + j + ', ' + } + output += 'out' + (numOutput - 1) + ';\n' + + output += ' input [' + (size - 1) + ':0] sel;\n' + output += ' \n' + + output += ' always @ (*) begin\n' + for (var j = 0; j < numOutput; j++) { + output += ' out' + j + ' = 0;\n' + } + output += ' case (sel)\n' + for (var j = 0; j < numOutput; j++) { + output += ' ' + j + ' : out' + j + ' = 1;\n' + } + output += ' endcase\n' + output += ' end\n' + output += 'endmodule\n' + } + + return output + } + + //reset the sized before Verilog generation + static resetVerilog() { + Decoder.selSizes = new Set() + } +} + +/** + * @memberof Decoder + * Help Tip + * @type {string} + * @category modules + */ +Decoder.prototype.tooltipText = + 'Decoder ToolTip : Converts coded inputs into coded outputs.' +Decoder.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/5muxandplex?id=decoder' +Decoder.prototype.objectType = 'Decoder' diff --git a/v1/src/simulator/src/modules/Demultiplexer.js b/v1/src/simulator/src/modules/Demultiplexer.js new file mode 100644 index 00000000..feb21cfa --- /dev/null +++ b/v1/src/simulator/src/modules/Demultiplexer.js @@ -0,0 +1,324 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, fillText } from '../canvasApi' +/** + * @class + * Demultiplexer + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @param {number=} controlSignalSize - 1 by default + * @category modules + */ +import { colors } from '../themer/themer' + +export default class Demultiplexer extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'LEFT', + bitWidth = 1, + controlSignalSize = 1 + ) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['Demultiplexer'].push(this); + */ + this.controlSignalSize = + controlSignalSize || + parseInt(prompt('Enter control signal bitWidth'), 10) + this.outputsize = 1 << this.controlSignalSize + this.xOff = 0 + this.yOff = 1 + if (this.controlSignalSize === 1) { + this.xOff = 10 + } + if (this.controlSignalSize <= 3) { + this.yOff = 2 + } + + this.changeControlSignalSize = function (size) { + if (size === undefined || size < 1 || size > 32) return + if (this.controlSignalSize === size) return + const obj = new Demultiplexer( + this.x, + this.y, + this.scope, + this.direction, + this.bitWidth, + size + ) + this.cleanDelete() + simulationArea.lastSelected = obj + return obj + } + this.mutableProperties = { + controlSignalSize: { + name: 'Control Signal Size', + type: 'number', + max: '10', + min: '1', + func: 'changeControlSignalSize', + }, + } + // eslint-disable-next-line no-shadow + this.newBitWidth = function (bitWidth) { + this.bitWidth = bitWidth + for (let i = 0; i < this.outputsize; i++) { + this.output1[i].bitWidth = bitWidth + } + this.input.bitWidth = bitWidth + } + + this.setDimensions(20 - this.xOff, this.yOff * 5 * this.outputsize) + this.rectangleObject = false + this.input = new Node(20 - this.xOff, 0, 0, this) + + this.output1 = [] + for (let i = 0; i < this.outputsize; i++) { + const a = new Node( + -20 + this.xOff, + +this.yOff * 10 * (i - this.outputsize / 2) + 10, + 1, + this + ) + this.output1.push(a) + } + + this.controlSignalInput = new Node( + 0, + this.yOff * 10 * (this.outputsize / 2 - 1) + this.xOff + 10, + 0, + this, + this.controlSignalSize, + 'Control Signal' + ) + } + + /** + * @memberof Demultiplexer + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [ + this.direction, + this.bitWidth, + this.controlSignalSize, + ], + nodes: { + output1: this.output1.map(findNode), + input: findNode(this.input), + controlSignalInput: findNode(this.controlSignalInput), + }, + } + return data + } + + /** + * @memberof Demultiplexer + * resolve output values based on inputData + */ + resolve() { + for (let i = 0; i < this.output1.length; i++) { + this.output1[i].value = 0 + } + + this.output1[this.controlSignalInput.value].value = this.input.value + + for (let i = 0; i < this.output1.length; i++) { + simulationArea.simulationQueue.add(this.output1[i]) + } + } + + /** + * @memberof Demultiplexer + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + + const xx = this.x + const yy = this.y + + ctx.beginPath() + moveTo( + ctx, + 0, + this.yOff * 10 * (this.outputsize / 2 - 1) + 10 + 0.5 * this.xOff, + xx, + yy, + this.direction + ) + lineTo( + ctx, + 0, + this.yOff * 5 * (this.outputsize - 1) + this.xOff, + xx, + yy, + this.direction + ) + ctx.stroke() + + ctx.beginPath() + ctx.strokeStyle = colors['stroke'] + ctx.lineWidth = correctWidth(4) + ctx.fillStyle = colors['fill'] + moveTo( + ctx, + -20 + this.xOff, + -this.yOff * 10 * (this.outputsize / 2), + xx, + yy, + this.direction + ) + lineTo( + ctx, + -20 + this.xOff, + 20 + this.yOff * 10 * (this.outputsize / 2 - 1), + xx, + yy, + this.direction + ) + lineTo( + ctx, + 20 - this.xOff, + +this.yOff * 10 * (this.outputsize / 2 - 1) + this.xOff, + xx, + yy, + this.direction + ) + lineTo( + ctx, + 20 - this.xOff, + -this.yOff * 10 * (this.outputsize / 2) - this.xOff + 20, + xx, + yy, + this.direction + ) + ctx.closePath() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.fillStyle = colors['hover_select'] + } + ctx.fill() + ctx.stroke() + + ctx.beginPath() + ctx.fillStyle = 'black' + ctx.textAlign = 'center' + // [xFill,yFill] = rotate(xx + this.output1[i].x - 7, yy + this.output1[i].y + 2); + for (let i = 0; i < this.outputsize; i++) { + if (this.direction === 'LEFT') + fillText( + ctx, + String(i), + xx + this.output1[i].x - 7, + yy + this.output1[i].y + 2, + 10 + ) + else if (this.direction === 'RIGHT') + fillText( + ctx, + String(i), + xx + this.output1[i].x + 7, + yy + this.output1[i].y + 2, + 10 + ) + else if (this.direction === 'UP') + fillText( + ctx, + String(i), + xx + this.output1[i].x, + yy + this.output1[i].y - 5, + 10 + ) + else + fillText( + ctx, + String(i), + xx + this.output1[i].x, + yy + this.output1[i].y + 10, + 10 + ) + } + ctx.fill() + } + + verilogBaseType() { + return this.verilogName() + this.output1.length + } + + //this code to generate Verilog + generateVerilog() { + Demultiplexer.selSizes.add(this.controlSignalSize) + return CircuitElement.prototype.generateVerilog.call(this) + } + + //generate the needed modules + static moduleVerilog() { + var output = '' + + for (var size of Demultiplexer.selSizes) { + var numOutput = 1 << size + output += '\n' + output += 'module Demultiplexer' + numOutput + output += '(' + for (var j = 0; j < numOutput; j++) { + output += 'out' + j + ', ' + } + output += 'in, sel);\n' + + output += ' parameter WIDTH = 1;\n' + output += ' output reg [WIDTH-1:0] ' + for (var j = 0; j < numOutput - 1; j++) { + output += 'out' + j + ', ' + } + output += 'out' + (numOutput - 1) + ';\n' + + output += ' input [WIDTH-1:0] in;\n' + output += ' input [' + (size - 1) + ':0] sel;\n' + output += ' \n' + + output += ' always @ (*) begin\n' + for (var j = 0; j < numOutput; j++) { + output += ' out' + j + ' = 0;\n' + } + output += ' case (sel)\n' + for (var j = 0; j < numOutput; j++) { + output += ' ' + j + ' : out' + j + ' = in;\n' + } + output += ' endcase\n' + output += ' end\n' + output += 'endmodule\n' + } + + return output + } + + //reset the sized before Verilog generation + static resetVerilog() { + Demultiplexer.selSizes = new Set() + } +} + +/** + * @memberof Demultiplexer + * Help Tip + * @type {string} + * @category modules + */ +Demultiplexer.prototype.tooltipText = + 'DeMultiplexer ToolTip : Multiple outputs and a single line input.' +Demultiplexer.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/5muxandplex?id=demultiplexer' +Demultiplexer.prototype.objectType = 'Demultiplexer' diff --git a/v1/src/simulator/src/modules/DigitalLed.js b/v1/src/simulator/src/modules/DigitalLed.js new file mode 100644 index 00000000..df17fbfa --- /dev/null +++ b/v1/src/simulator/src/modules/DigitalLed.js @@ -0,0 +1,188 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { + correctWidth, + lineTo, + moveTo, + arc, + colorToRGBA, + drawCircle2, + validColor, +} from '../canvasApi' +import { changeInputSize } from '../modules' +/** + * @class + * DigitalLed + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} color - color of led + * @category modules + */ +import { colors } from '../themer/themer' + +export default class DigitalLed extends CircuitElement { + constructor(x, y, scope = globalScope, color = 'Red') { + // Calling base class constructor + + super(x, y, scope, 'UP', 1) + /* this is done in this.baseSetup() now + this.scope['DigitalLed'].push(this); + */ + this.rectangleObject = false + this.setDimensions(10, 20) + this.inp1 = new Node(-40, 0, 0, this, 1) + this.directionFixed = true + this.fixedBitWidth = true + this.color = color + const temp = colorToRGBA(this.color) + this.actualColor = `rgba(${temp[0]},${temp[1]},${temp[2]},${0.8})` + } + + /** + * @memberof DigitalLed + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.color], + nodes: { + inp1: findNode(this.inp1), + }, + } + return data + } + + /** + * @memberof DigitalLed + * function to change color of the led + */ + changeColor(value) { + if (validColor(value)) { + this.color = value + const temp = colorToRGBA(this.color) + this.actualColor = `rgba(${temp[0]},${temp[1]},${temp[2]},${0.8})` + } + } + + /** + * @memberof DigitalLed + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + + const xx = this.x + const yy = this.y + + ctx.strokeStyle = '#e3e4e5' + ctx.lineWidth = correctWidth(3) + ctx.beginPath() + moveTo(ctx, -20, 0, xx, yy, this.direction) + lineTo(ctx, -40, 0, xx, yy, this.direction) + ctx.stroke() + + ctx.strokeStyle = '#d3d4d5' + ctx.fillStyle = ['rgba(227,228,229,0.8)', this.actualColor][ + this.inp1.value || 0 + ] + ctx.lineWidth = correctWidth(1) + + ctx.beginPath() + + moveTo(ctx, -15, -9, xx, yy, this.direction) + lineTo(ctx, 0, -9, xx, yy, this.direction) + arc(ctx, 0, 0, 9, -Math.PI / 2, Math.PI / 2, xx, yy, this.direction) + lineTo(ctx, -15, 9, xx, yy, this.direction) + lineTo(ctx, -18, 12, xx, yy, this.direction) + arc( + ctx, + 0, + 0, + Math.sqrt(468), + Math.PI / 2 + Math.acos(12 / Math.sqrt(468)), + -Math.PI / 2 - Math.asin(18 / Math.sqrt(468)), + xx, + yy, + this.direction + ) + lineTo(ctx, -15, -9, xx, yy, this.direction) + ctx.stroke() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + } + + // Draws the element in the subcircuit. Used in layout mode + subcircuitDraw(xOffset = 0, yOffset = 0) { + var ctx = simulationArea.context + + var xx = this.subcircuitMetadata.x + xOffset + var yy = this.subcircuitMetadata.y + yOffset + + ctx.strokeStyle = '#090a0a' + ctx.fillStyle = ['rgba(227,228,229,0.8)', this.actualColor][ + this.inp1.value || 0 + ] + ctx.lineWidth = correctWidth(1) + + ctx.beginPath() + drawCircle2(ctx, 0, 0, 6, xx, yy, this.direction) + ctx.stroke() + + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected == this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = 'rgba(255, 255, 32,0.8)' + ctx.fill() + } + generateVerilog() { + var label = this.label ? this.verilogLabel : this.inp1.verilogLabel + return ` + always @ (*) + $display("DigitalLed:${label}=%d", ${this.inp1.verilogLabel});` + } +} + +/** + * @memberof DigitalLed + * Help Tip + * @type {string} + * @category modules + */ +DigitalLed.prototype.tooltipText = + 'Digital Led ToolTip: Digital LED glows high when input is High(1).' + +/** + * @memberof DigitalLed + * Help URL + * @type {string} + * @category modules + */ +DigitalLed.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/3output?id=digital-led' + +/** + * @memberof DigitalLed + * Mutable properties of the element + * @type {JSON} + * @category modules + */ +DigitalLed.prototype.mutableProperties = { + color: { + name: 'Color: ', + type: 'text', + func: 'changeColor', + }, +} +DigitalLed.prototype.objectType = 'DigitalLed' +DigitalLed.prototype.canShowInSubcircuit = true diff --git a/v1/src/simulator/src/modules/Flag.js b/v1/src/simulator/src/modules/Flag.js new file mode 100644 index 00000000..f332622b --- /dev/null +++ b/v1/src/simulator/src/modules/Flag.js @@ -0,0 +1,234 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, rect2, fillText } from '../canvasApi' +import plotArea from '../plotArea' +import EventQueue from '../eventQueue' +/** + * @class + * Flag + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @param {string} identifier - id + * @category modules + */ +import { colors } from '../themer/themer' + +export default class Flag extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + bitWidth = 1, + identifier + ) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['Flag'].push(this); + */ + this.setWidth(60) + this.setHeight(20) + this.rectangleObject = false + this.directionFixed = true + this.orientationFixed = false + this.identifier = identifier || `F${this.scope.Flag.length}` + this.plotValues = [] + + this.xSize = 10 + this.flagTimeUnit = 0 + + this.inp1 = new Node(40, 0, 0, this) + } + + resolve() { + this.flagTimeUnit = simulationArea.simulationQueue.time + const time = plotArea.getPlotTime(this.flagTimeUnit) + + if ( + this.plotValues.length && + this.plotValues[this.plotValues.length - 1][0] === time + ) { + this.plotValues.pop() + } + + if (this.plotValues.length === 0) { + this.plotValues.push([time, this.inp1.value]) + return + } + + if ( + this.plotValues[this.plotValues.length - 1][1] === this.inp1.value + ) { + return + } + this.plotValues.push([time, this.inp1.value]) + } + + /** + * @memberof Flag + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.direction, this.bitWidth], + nodes: { + inp1: findNode(this.inp1), + }, + values: { + identifier: this.identifier, + }, + } + return data + } + + /** + * @memberof Flag + * set the flag id + * @param {number} id - identifier for flag + */ + setIdentifier(id = '') { + if (id.length === 0) return + this.identifier = id + const len = this.identifier.length + if (len === 1) this.xSize = 20 + else if (len > 1 && len < 4) this.xSize = 10 + else this.xSize = 0 + } + + /** + * @memberof Flag + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.lineWidth = correctWidth(1) + const xx = this.x + const yy = this.y + + rect2( + ctx, + -50 + this.xSize, + -20, + 100 - 2 * this.xSize, + 40, + xx, + yy, + 'RIGHT' + ) + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + + ctx.font = '14px Raleway' + this.xOff = ctx.measureText(this.identifier).width + + ctx.beginPath() + rect2(ctx, -40 + this.xSize, -12, this.xOff + 10, 25, xx, yy, 'RIGHT') + ctx.fillStyle = '#eee' + ctx.strokeStyle = '#ccc' + ctx.fill() + ctx.stroke() + + ctx.beginPath() + ctx.textAlign = 'center' + ctx.fillStyle = 'black' + fillText( + ctx, + this.identifier, + xx - 35 + this.xOff / 2 + this.xSize, + yy + 5, + 14 + ) + ctx.fill() + + ctx.beginPath() + ctx.font = '30px Raleway' + ctx.textAlign = 'center' + ctx.fillStyle = ['blue', 'red'][+(this.inp1.value === undefined)] + if (this.inp1.value !== undefined) { + fillText( + ctx, + this.inp1.value.toString(16), + xx + 35 - this.xSize, + yy + 8, + 25 + ) + } else { + fillText(ctx, 'x', xx + 35 - this.xSize, yy + 8, 25) + } + ctx.fill() + } + + /** + * @memberof Flag + * function to change direction of Flag + * @param {string} dir - new direction + */ + newDirection(dir) { + if (dir === this.direction) return + this.direction = dir + this.inp1.refresh() + if (dir === 'RIGHT' || dir === 'LEFT') { + this.inp1.leftx = 50 - this.xSize + } else if (dir === 'UP') { + this.inp1.leftx = 20 + } else { + this.inp1.leftx = 20 + } + // if(this.direction=="LEFT" || this.direction=="RIGHT") this.inp1.leftx=50-this.xSize; + // this.inp1.refresh(); + + this.inp1.refresh() + } +} + +/** + * @memberof Flag + * Help Tip + * @type {string} + * @category modules + */ +Flag.prototype.tooltipText = + 'FLag ToolTip: Use this for debugging and plotting.' +Flag.prototype.helplink = + 'https://docs.circuitverse.org/#/timing_diagrams?id=using-flags' + +/** + * @memberof Flag + * Help URL + * @type {string} + * @category modules + */ +Flag.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/8misc?id=tunnel' + +/** + * @memberof Flag + * Mutable properties of the element + * @type {JSON} + * @category modules + */ +Flag.prototype.mutableProperties = { + identifier: { + name: 'Debug Flag identifier', + type: 'text', + maxlength: '5', + func: 'setIdentifier', + }, +} +Flag.prototype.objectType = 'Flag' +Flag.prototype.propagationDelay = 0 diff --git a/v1/src/simulator/src/modules/Ground.js b/v1/src/simulator/src/modules/Ground.js new file mode 100644 index 00000000..f1c88932 --- /dev/null +++ b/v1/src/simulator/src/modules/Ground.js @@ -0,0 +1,129 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, arc } from '../canvasApi' +import { changeInputSize } from '../modules' +/** + * @class + * Ground + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +import { colors } from '../themer/themer' + +export default class Ground extends CircuitElement { + constructor(x, y, scope = globalScope, bitWidth = 1) { + super(x, y, scope, 'RIGHT', bitWidth) + /* this is done in this.baseSetup() now + this.scope['Ground'].push(this); + */ + this.rectangleObject = false + this.setDimensions(10, 10) + this.directionFixed = true + this.output1 = new Node(0, -10, 1, this) + } + + /** + * @memberof Ground + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + nodes: { + output1: findNode(this.output1), + }, + values: { + state: this.state, + }, + constructorParamaters: [this.direction, this.bitWidth], + } + return data + } + + /** + * @memberof Ground + * resolve output values based on inputData + */ + resolve() { + this.output1.value = 0 + simulationArea.simulationQueue.add(this.output1) + } + + /** + * @memberof Ground + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + nodes: { + output1: findNode(this.output1), + }, + constructorParamaters: [this.bitWidth], + } + return data + } + + /** + * @memberof Ground + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + // + ctx.beginPath() + ctx.strokeStyle = [colors['stroke'], 'brown'][ + ((this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this)) + 0 + ] + ctx.lineWidth = correctWidth(3) + + const xx = this.x + const yy = this.y + + moveTo(ctx, 0, -10, xx, yy, this.direction) + lineTo(ctx, 0, 0, xx, yy, this.direction) + moveTo(ctx, -10, 0, xx, yy, this.direction) + lineTo(ctx, 10, 0, xx, yy, this.direction) + moveTo(ctx, -6, 5, xx, yy, this.direction) + lineTo(ctx, 6, 5, xx, yy, this.direction) + moveTo(ctx, -2.5, 10, xx, yy, this.direction) + lineTo(ctx, 2.5, 10, xx, yy, this.direction) + ctx.stroke() + } + + generateVerilog() { + return `assign ${this.output1.verilogLabel} = ${this.bitWidth}'b0;` + } +} + +/** + * @memberof Ground + * Help Tip + * @type {string} + * @category modules + */ +Ground.prototype.tooltipText = 'Ground: All bits are Low(0).' + +/** + * @memberof Ground + * Help URL + * @type {string} + * @category modules + */ +Ground.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/2input?id=ground' + +/** + * @memberof Ground + * @type {number} + * @category modules + */ +Ground.prototype.propagationDelay = 0 +Ground.prototype.objectType = 'Ground' diff --git a/v1/src/simulator/src/modules/HexDisplay.js b/v1/src/simulator/src/modules/HexDisplay.js new file mode 100644 index 00000000..b96cd096 --- /dev/null +++ b/v1/src/simulator/src/modules/HexDisplay.js @@ -0,0 +1,420 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { + correctWidth, + lineTo, + moveTo, + arc, + rect2, + validColor, + colorToRGBA, +} from '../canvasApi' +import { changeInputSize } from '../modules' +/** + * @class + * HexDisplay + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @category modules + */ +import { colors } from '../themer/themer' + +export default class HexDisplay extends CircuitElement { + constructor(x, y, scope = globalScope, color = 'Red') { + super(x, y, scope, 'RIGHT', 4) + /* this is done in this.baseSetup() now + this.scope['HexDisplay'].push(this); + */ + this.directionFixed = true + this.fixedBitWidth = true + this.setDimensions(30, 50) + this.inp = new Node(0, -50, 0, this, 4) + this.direction = 'RIGHT' + this.color = color + this.actualColor = color + } + + /** + * @memberof HexDisplay + * fn to change the color of HexDisplay + * @return {JSON} + */ + changeColor(value) { + if (validColor(value)) { + if (value.trim() === '') { + this.color = 'Red' + this.actualColor = 'rgba(255, 0, 0, 1)' + } else { + this.color = value + const temp = colorToRGBA(value) + this.actualColor = `rgba(${temp[0]},${temp[1]},${temp[2]}, ${temp[3]})` + } + } + } + + /** + * @memberof HexDisplay + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.color], + nodes: { + inp: findNode(this.inp), + }, + } + return data + } + + /** + * @memberof HexDisplay + * function to draw element + */ + customDrawSegment(x1, y1, x2, y2, color) { + if (color === undefined) color = 'lightgrey' + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = color + ctx.lineWidth = correctWidth(5) + const xx = this.x + const yy = this.y + + moveTo(ctx, x1, y1, xx, yy, this.direction) + lineTo(ctx, x2, y2, xx, yy, this.direction) + ctx.closePath() + ctx.stroke() + } + + /** + * @memberof HexDisplay + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + + const xx = this.x + const yy = this.y + + ctx.strokeStyle = colors['stroke'] + ctx.lineWidth = correctWidth(3) + + let a = 0, + b = 0, + c = 0, + d = 0, + e = 0, + f = 0, + g = 0 + switch (this.inp.value) { + case 0: + a = b = c = d = e = f = 1 + break + case 1: + b = c = 1 + break + case 2: + a = b = g = e = d = 1 + break + case 3: + a = b = g = c = d = 1 + break + case 4: + f = g = b = c = 1 + break + case 5: + a = f = g = c = d = 1 + break + case 6: + a = f = g = e = c = d = 1 + break + case 7: + a = b = c = 1 + break + case 8: + a = b = c = d = e = g = f = 1 + break + case 9: + a = f = g = b = c = 1 + break + case 0xa: + a = f = b = c = g = e = 1 + break + case 0xb: + f = e = g = c = d = 1 + break + case 0xc: + a = f = e = d = 1 + break + case 0xd: + b = c = g = e = d = 1 + break + case 0xe: + a = f = g = e = d = 1 + break + case 0xf: + a = f = g = e = 1 + break + default: + } + this.customDrawSegment( + 18, + -3, + 18, + -38, + ['lightgrey', this.actualColor][b] + ) + this.customDrawSegment( + 18, + 3, + 18, + 38, + ['lightgrey', this.actualColor][c] + ) + this.customDrawSegment( + -18, + -3, + -18, + -38, + ['lightgrey', this.actualColor][f] + ) + this.customDrawSegment( + -18, + 3, + -18, + 38, + ['lightgrey', this.actualColor][e] + ) + this.customDrawSegment( + -17, + -38, + 17, + -38, + ['lightgrey', this.actualColor][a] + ) + this.customDrawSegment( + -17, + 0, + 17, + 0, + ['lightgrey', this.actualColor][g] + ) + this.customDrawSegment( + -15, + 38, + 17, + 38, + ['lightgrey', this.actualColor][d] + ) + } + + subcircuitDrawSegment(x1, y1, x2, y2, color, xxSegment, yySegment) { + if (color == undefined) color = 'lightgrey' + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = color + ctx.lineWidth = correctWidth(3) + var xx = xxSegment + var yy = yySegment + + moveTo(ctx, x1, y1, xx, yy, this.direction) + lineTo(ctx, x2, y2, xx, yy, this.direction) + ctx.closePath() + ctx.stroke() + } + // Draws the element in the subcircuit. Used in layout mode + subcircuitDraw(xOffset = 0, yOffset = 0) { + var ctx = simulationArea.context + + var xx = this.subcircuitMetadata.x + xOffset + var yy = this.subcircuitMetadata.y + yOffset + + ctx.strokeStyle = 'black' + ctx.lineWidth = correctWidth(3) + let a = 0, + b = 0, + c = 0, + d = 0, + e = 0, + f = 0, + g = 0 + + switch (this.inp.value) { + case 0: + a = b = c = d = e = f = 1 + break + case 1: + b = c = 1 + break + case 2: + a = b = g = e = d = 1 + break + case 3: + a = b = g = c = d = 1 + break + case 4: + f = g = b = c = 1 + break + case 5: + a = f = g = c = d = 1 + break + case 6: + a = f = g = e = c = d = 1 + break + case 7: + a = b = c = 1 + break + case 8: + a = b = c = d = e = g = f = 1 + break + case 9: + a = f = g = b = c = 1 + break + case 0xa: + a = f = b = c = g = e = 1 + break + case 0xb: + f = e = g = c = d = 1 + break + case 0xc: + a = f = e = d = 1 + break + case 0xd: + b = c = g = e = d = 1 + break + case 0xe: + a = f = g = e = d = 1 + break + case 0xf: + a = f = g = e = 1 + break + default: + } + this.subcircuitDrawSegment( + 10, + -20, + 10, + -38, + ['lightgrey', this.actualColor][b], + xx, + yy + ) + this.subcircuitDrawSegment( + 10, + -17, + 10, + 1, + ['lightgrey', this.actualColor][c], + xx, + yy + ) + this.subcircuitDrawSegment( + -10, + -20, + -10, + -38, + ['lightgrey', this.actualColor][f], + xx, + yy + ) + this.subcircuitDrawSegment( + -10, + -17, + -10, + 1, + ['lightgrey', this.actualColor][e], + xx, + yy + ) + this.subcircuitDrawSegment( + -8, + -38, + 8, + -38, + ['lightgrey', this.actualColor][a], + xx, + yy + ) + this.subcircuitDrawSegment( + -8, + -18, + 8, + -18, + ['lightgrey', this.actualColor][g], + xx, + yy + ) + this.subcircuitDrawSegment( + -8, + 1, + 8, + 1, + ['lightgrey', this.actualColor][d], + xx, + yy + ) + + ctx.beginPath() + ctx.strokeStyle = 'black' + ctx.lineWidth = correctWidth(1) + rect2(ctx, -15, -42, 33, 51, xx, yy, this.direction) + ctx.stroke() + + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected == this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.fillStyle = 'rgba(255, 255, 32,0.6)' + ctx.fill() + } + } + generateVerilog() { + return ` + always @ (*) + $display("HexDisplay:${this.verilogLabel}=%d", ${this.inp.verilogLabel});` + } +} + +/** + * @memberof HexDisplay + * Help Tip + * @type {string} + * @category modules + */ +HexDisplay.prototype.tooltipText = + 'Hex Display ToolTip: Inputs a 4 Bit Hex number and displays it.' + +/** + * @memberof HexDisplay + * Help URL + * @type {string} + * @category modules + */ +HexDisplay.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/3output?id=hexdisplay' +HexDisplay.prototype.objectType = 'HexDisplay' +HexDisplay.prototype.canShowInSubcircuit = true +HexDisplay.prototype.layoutProperties = { + rightDimensionX: 20, + leftDimensionX: 15, + upDimensionY: 42, + downDimensionY: 10, +} + +/** + * @memberof HexDisplay + * Mutable properties of the element + * @type {JSON} + * @category modules + */ +HexDisplay.prototype.mutableProperties = { + color: { + name: 'Color: ', + type: 'text', + func: 'changeColor', + }, +} diff --git a/v1/src/simulator/src/modules/ImageAnnotation.js b/v1/src/simulator/src/modules/ImageAnnotation.js new file mode 100644 index 00000000..67ac4d8b --- /dev/null +++ b/v1/src/simulator/src/modules/ImageAnnotation.js @@ -0,0 +1,243 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, rect, fillText, drawImage } from '../canvasApi' +import { colors } from '../themer/themer' +import { promptFile, showMessage, getImageDimensions } from '../utils' +/** + * @class + * Image + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {number=} rows - number of rows + * @param {number=} cols - number of columns. + * @category modules + */ +export default class ImageAnnotation extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + rows = 15, + cols = 20, + imageUrl = '' + ) { + super(x, y, scope, 'RIGHT', 1) + this.directionFixed = true + this.fixedBitWidth = true + this.imageUrl = imageUrl + this.cols = cols || parseInt(prompt('Enter cols:'), 10) + this.rows = rows || parseInt(prompt('Enter rows:'), 10) + this.setSize() + this.loadImage() + } + + /** + * @memberof Image + * @param {number} size - new size of rows + */ + changeRowSize(size) { + if (size === undefined || size < 5 || size > 1000) return + if (this.rows === size) return + this.rows = parseInt(size, 10) + this.setSize() + return this + } + + /** + * @memberof Image + * @param {number} size - new size of columns + */ + changeColSize(size) { + if (size === undefined || size < 5 || size > 1000) return + if (this.cols === size) return + this.cols = parseInt(size, 10) + this.setSize() + return this + } + + /** + * @memberof Image + * listener function to change direction of Image + * @param {string} dir - new direction + */ + keyDown3(dir) { + if (dir === 'ArrowRight') { + this.changeColSize(this.cols + 2) + } + if (dir === 'ArrowLeft') { + this.changeColSize(this.cols - 2) + } + if (dir === 'ArrowDown') { + this.changeRowSize(this.rows + 2) + } + if (dir === 'ArrowUp') { + this.changeRowSize(this.rows - 2) + } + } + + /** + * @memberof Image + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.rows, this.cols, this.imageUrl], + } + return data + } + + /** + * @memberof Image + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + const xx = this.x + const yy = this.y + var w = this.elementWidth + var h = this.elementHeight + if (this.image && this.image.complete) { + drawImage(ctx, this.image, xx - w / 2, yy - h / 2, w, h) + } else { + ctx.beginPath() + ctx.strokeStyle = 'rgba(0,0,0,1)' + ctx.setLineDash([5 * globalScope.scale, 5 * globalScope.scale]) + ctx.lineWidth = correctWidth(1.5) + + rect(ctx, xx - w / 2, yy - h / 2, w, h) + ctx.stroke() + + if ( + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.fillStyle = 'rgba(255, 255, 32,0.1)' + ctx.fill() + } + + ctx.beginPath() + ctx.textAlign = 'center' + ctx.fillStyle = colors['text'] + fillText(ctx, 'Double Click to Insert Image', xx, yy, 10) + ctx.fill() + + ctx.setLineDash([]) + } + } + + /** + * Procedure if image is double clicked + **/ + dblclick() { + if (embed) return + this.uploadImage() + } + + async uploadImage() { + var file = await promptFile('image/*', false) + var apiUrl = 'https://api.imgur.com/3/image' + var apiKey = '9a33b3b370f1054' + var settings = { + crossDomain: true, + processData: false, + contentType: false, + type: 'POST', + url: apiUrl, + headers: { + Authorization: 'Client-ID ' + apiKey, + Accept: 'application/json', + }, + mimeType: 'multipart/form-data', + } + var formData = new FormData() + formData.append('image', file) + settings.data = formData + + // Response contains stringified JSON + // Image URL available at response.data.link + showMessage('Uploading Image') + var response = await $.ajax(settings) + showMessage('Image Uploaded') + this.imageUrl = JSON.parse(response).data.link + this.loadImage() + } + + async loadImage() { + if (!this.imageUrl) return + this.image = new Image() + this.image.crossOrigin = 'anonymous' + this.image.src = this.imageUrl + } + /** + * @memberof Image + * function to reset or (internally) set size + */ + setSize() { + this.elementWidth = this.cols * 10 + this.elementHeight = this.rows * 10 + this.upDimensionY = this.elementHeight / 2 + this.downDimensionY = this.elementHeight / 2 + this.leftDimensionX = this.elementWidth / 2 + this.rightDimensionX = this.elementWidth / 2 + } +} + +/** + * @memberof Image + * Help Tip + * @type {string} + * @category modules + */ +ImageAnnotation.prototype.tooltipText = + 'Image ToolTip: Embed an image in the circuit for annotation' +ImageAnnotation.prototype.propagationDelayFixed = true + +/** + * @memberof Image + * Mutable properties of the element + * @type {JSON} + * @category modules + */ +ImageAnnotation.prototype.mutableProperties = { + cols: { + name: 'Columns', + type: 'number', + max: '1000', + min: '5', + func: 'changeColSize', + }, + rows: { + name: 'Rows', + type: 'number', + max: '1000', + min: '5', + func: 'changeRowSize', + }, +} +ImageAnnotation.prototype.objectType = 'ImageAnnotation' +ImageAnnotation.prototype.rectangleObject = false +ImageAnnotation.prototype.mutableProperties = { + imageUrl: { + name: 'Upload Image', + type: 'button', + func: 'uploadImage', + }, + cols: { + name: 'Columns', + type: 'number', + max: '1000', + min: '5', + func: 'changeColSize', + }, + rows: { + name: 'Rows', + type: 'number', + max: '1000', + min: '5', + func: 'changeRowSize', + }, +} diff --git a/v1/src/simulator/src/modules/Input.js b/v1/src/simulator/src/modules/Input.js new file mode 100644 index 00000000..d33c8627 --- /dev/null +++ b/v1/src/simulator/src/modules/Input.js @@ -0,0 +1,210 @@ +/* eslint-disable no-unused-expressions */ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, oppositeDirection, fillText } from '../canvasApi' +import { getNextPosition } from '../modules' +import { generateId } from '../utils' +/** + * @class + * Input + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @param {Object=} layoutProperties - x,y and id + * @category modules + */ +import { colors } from '../themer/themer' + +function bin2dec(binString) { + return parseInt(binString, 2) +} + +function dec2bin(dec, bitWidth = undefined) { + // only for positive nos + var bin = dec.toString(2) + if (bitWidth == undefined) return bin + return '0'.repeat(bitWidth - bin.length) + bin +} + +export default class Input extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + bitWidth = 1, + layoutProperties + ) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['Input'].push(this); + */ + if (layoutProperties) { + this.layoutProperties = layoutProperties + } else { + this.layoutProperties = {} + this.layoutProperties.x = 0 + this.layoutProperties.y = getNextPosition(0, scope) + this.layoutProperties.id = generateId() + } + + // Call base class constructor + this.state = 0 + this.orientationFixed = false + this.state = bin2dec(this.state) // in integer format + this.output1 = new Node(this.bitWidth * 10, 0, 1, this) + this.wasClicked = false + this.directionFixed = true + this.setWidth(this.bitWidth * 10) + this.rectangleObject = true // Trying to make use of base class draw + } + + /** + * @memberof Input + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + nodes: { + output1: findNode(this.output1), + }, + values: { + state: this.state, + }, + constructorParamaters: [ + this.direction, + this.bitWidth, + this.layoutProperties, + ], + } + return data + } + + /** + * @memberof Input + * resolve output values based on inputData + */ + resolve() { + this.output1.value = this.state + simulationArea.simulationQueue.add(this.output1) + } + + // Check if override is necessary!! + + /** + * @memberof Input + * function to change bitwidth of the element + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + if (bitWidth < 1) return + const diffBitWidth = bitWidth - this.bitWidth + this.bitWidth = bitWidth // ||parseInt(prompt("Enter bitWidth"),10); + this.setWidth(this.bitWidth * 10) + this.state = 0 + this.output1.bitWidth = bitWidth + if (this.direction === 'RIGHT') { + this.x -= 10 * diffBitWidth + this.output1.x = 10 * this.bitWidth + this.output1.leftx = 10 * this.bitWidth + } else if (this.direction === 'LEFT') { + this.x += 10 * diffBitWidth + this.output1.x = -10 * this.bitWidth + this.output1.leftx = 10 * this.bitWidth + } + } + + /** + * @memberof Input + * listener function to set selected index + */ + click() { + // toggle + let pos = this.findPos() + if (pos === 0) pos = 1 // minor correction + if (pos < 1 || pos > this.bitWidth) return + this.state = ((this.state >>> 0) ^ (1 << (this.bitWidth - pos))) >>> 0 + } + + /** + * @memberof Input + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.beginPath() + ctx.lineWidth = correctWidth(3) + const xx = this.x + const yy = this.y + ctx.beginPath() + ctx.fillStyle = colors['input_text'] + ctx.textAlign = 'center' + const bin = dec2bin(this.state, this.bitWidth) + for (let k = 0; k < this.bitWidth; k++) { + fillText(ctx, bin[k], xx - 10 * this.bitWidth + 10 + k * 20, yy + 5) + } + ctx.fill() + } + + /** + * @memberof Input + * function to change direction of input + * @param {string} dir - new direction + */ + newDirection(dir) { + if (dir === this.direction) return + this.direction = dir + this.output1.refresh() + if (dir === 'RIGHT' || dir === 'LEFT') { + this.output1.leftx = 10 * this.bitWidth + this.output1.lefty = 0 + } else { + this.output1.leftx = 10 // 10*this.bitWidth; + this.output1.lefty = 0 + } + + this.output1.refresh() + this.labelDirection = oppositeDirection[this.direction] + } + + /** + * @memberof Input + * function to find position of mouse click + */ + findPos() { + return Math.round( + (simulationArea.mouseX - this.x + 10 * this.bitWidth) / 20.0 + ) + } +} + +/** + * @memberof Input + * Help Tip + * @type {string} + * @category modules + */ +Input.prototype.tooltipText = + 'Input ToolTip: Toggle the individual bits by clicking on them.' + +/** + * @memberof Input + * Help URL + * @type {string} + * @category modules + */ +Input.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/2input?id=input' + +/** + * @memberof Input + * @type {number} + * @category modules + */ +Input.prototype.propagationDelay = 0 +Input.prototype.objectType = 'Input' diff --git a/v1/src/simulator/src/modules/LSB.js b/v1/src/simulator/src/modules/LSB.js new file mode 100644 index 00000000..0f7e4e61 --- /dev/null +++ b/v1/src/simulator/src/modules/LSB.js @@ -0,0 +1,144 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode, dec2bin } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, rect, fillText } from '../canvasApi' +/** + * @class + * LSB + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +import { colors } from '../themer/themer' + +export default class LSB extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT', bitWidth = 1) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['LSB'].push(this); + */ + this.leftDimensionX = 10 + this.rightDimensionX = 20 + this.setHeight(30) + this.directionFixed = true + this.bitWidth = bitWidth || parseInt(prompt('Enter bitWidth'), 10) + this.rectangleObject = false + // this.inputSize = 1 << this.bitWidth; + this.intputSize = this.bitWidth + + this.inp1 = new Node(-10, 0, 0, this, this.inputSize) + this.output1 = new Node(20, 0, 1, this, this.bitWidth) + this.enable = new Node(20, 20, 1, this, 1) + } + + /** + * @memberof LSB + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + nodes: { + inp1: findNode(this.inp1), + output1: findNode(this.output1), + enable: findNode(this.enable), + }, + constructorParamaters: [this.direction, this.bitWidth], + } + return data + } + + /** + * @memberof LSB + * function to change bitwidth of the element + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + // this.inputSize = 1 << bitWidth + this.inputSize = bitWidth + this.inp1.bitWidth = this.inputSize + this.bitWidth = bitWidth + this.output1.bitWidth = bitWidth + } + + /** + * @memberof LSB + * resolve output values based on inputData + */ + resolve() { + const inp = dec2bin(this.inp1.value) + let out = 0 + for (let i = inp.length - 1; i >= 0; i--) { + if (inp[i] === '1') { + out = inp.length - 1 - i + break + } + } + this.output1.value = out + simulationArea.simulationQueue.add(this.output1) + if (inp != 0) { + this.enable.value = 1 + } else { + this.enable.value = 0 + } + simulationArea.simulationQueue.add(this.enable) + } + + /** + * @memberof LSB + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.lineWidth = correctWidth(3) + const xx = this.x + const yy = this.y + rect(ctx, xx - 10, yy - 30, 30, 60) + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + + ctx.beginPath() + ctx.fillStyle = 'black' + ctx.textAlign = 'center' + fillText(ctx, 'LSB', xx + 6, yy - 12, 10) + fillText(ctx, 'EN', xx + this.enable.x - 12, yy + this.enable.y + 3, 8) + ctx.fill() + + ctx.beginPath() + ctx.fillStyle = 'green' + ctx.textAlign = 'center' + if (this.output1.value !== undefined) { + fillText(ctx, this.output1.value, xx + 5, yy + 14, 13) + } + ctx.stroke() + ctx.fill() + } + generateVerilog() { + return `assign ${this.output1.verilogLabel} = (${this.enable.verilogLabel}!=0) ? ${this.inp1.verilogLabel}[0] : 0;` + } +} + +/** + * @memberof LSB + * Help Tip + * @type {string} + * @category modules + */ +LSB.prototype.tooltipText = + 'LSB ToolTip : The least significant bit or the low-order bit.' +LSB.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/5muxandplex?id=least-significant-bit-lsb-detector' +LSB.prototype.objectType = 'LSB' diff --git a/v1/src/simulator/src/modules/MSB.js b/v1/src/simulator/src/modules/MSB.js new file mode 100644 index 00000000..fbe14b2c --- /dev/null +++ b/v1/src/simulator/src/modules/MSB.js @@ -0,0 +1,141 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode, dec2bin } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, rect, fillText } from '../canvasApi' +/** + * @class + * MSB + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +import { colors } from '../themer/themer' + +export default class MSB extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT', bitWidth = 1) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['MSB'].push(this); + */ + // this.setDimensions(20, 20); + this.leftDimensionX = 10 + this.rightDimensionX = 20 + this.setHeight(30) + this.directionFixed = true + this.bitWidth = bitWidth || parseInt(prompt('Enter bitWidth'), 10) + this.rectangleObject = false + // this.inputSize = 1 << this.bitWidth; + this.intputSize = this.bitWidth + + this.inp1 = new Node(-10, 0, 0, this, this.inputSize) + this.output1 = new Node(20, 0, 1, this, this.bitWidth) + this.enable = new Node(20, 20, 1, this, 1) + } + + /** + * @memberof MSB + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + nodes: { + inp1: findNode(this.inp1), + output1: findNode(this.output1), + enable: findNode(this.enable), + }, + constructorParamaters: [this.direction, this.bitWidth], + } + return data + } + + /** + * @memberof MSB + * function to change bitwidth of the element + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + // this.inputSize = 1 << bitWidth + this.inputSize = bitWidth + this.inp1.bitWidth = this.inputSize + this.bitWidth = bitWidth + this.output1.bitWidth = bitWidth + } + + /** + * @memberof MSB + * resolve output values based on inputData + */ + resolve() { + const inp = this.inp1.value + this.output1.value = dec2bin(inp).length - 1 + simulationArea.simulationQueue.add(this.output1) + if (inp != 0) { + this.enable.value = 1 + } else { + this.enable.value = 0 + } + simulationArea.simulationQueue.add(this.enable) + } + + /** + * @memberof MSB + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.lineWidth = correctWidth(3) + const xx = this.x + const yy = this.y + rect(ctx, xx - 10, yy - 30, 30, 60) + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + + ctx.beginPath() + ctx.fillStyle = 'black' + ctx.textAlign = 'center' + fillText(ctx, 'MSB', xx + 6, yy - 12, 10) + fillText(ctx, 'EN', xx + this.enable.x - 12, yy + this.enable.y + 3, 8) + ctx.fill() + + ctx.beginPath() + ctx.fillStyle = 'green' + ctx.textAlign = 'center' + if (this.output1.value !== undefined) { + fillText(ctx, this.output1.value, xx + 5, yy + 14, 13) + } + ctx.stroke() + ctx.fill() + } + + generateVerilog() { + return `assign ${this.output1.verilogLabel} = (${ + this.enable.verilogLabel + }!=0) ? ${this.inp1.verilogLabel}[${this.inp1.bitWidth - 1}] : 0;` + } +} + +/** + * @memberof MSB + * Help Tip + * @type {string} + * @category modules + */ +MSB.prototype.tooltipText = + 'MSB ToolTip : The most significant bit or the high-order bit.' +MSB.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/5muxandplex?id=most-significant-bit-msb-detector' +MSB.prototype.objectType = 'MSB' diff --git a/v1/src/simulator/src/modules/Multiplexer.js b/v1/src/simulator/src/modules/Multiplexer.js new file mode 100644 index 00000000..638b9d01 --- /dev/null +++ b/v1/src/simulator/src/modules/Multiplexer.js @@ -0,0 +1,344 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, fillText } from '../canvasApi' +import { changeInputSize } from '../modules' +/** + * @class + * Multiplexer + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @param {number=} controlSignalSize - 1 by default + * @category modules + */ +import { colors } from '../themer/themer' + +export default class Multiplexer extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + bitWidth = 1, + controlSignalSize = 1 + ) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['Multiplexer'].push(this); + */ + this.controlSignalSize = + controlSignalSize || + parseInt(prompt('Enter control signal bitWidth'), 10) + this.inputSize = 1 << this.controlSignalSize + this.xOff = 0 + this.yOff = 1 + if (this.controlSignalSize === 1) { + this.xOff = 10 + } + if (this.controlSignalSize <= 3) { + this.yOff = 2 + } + this.setDimensions(20 - this.xOff, this.yOff * 5 * this.inputSize) + this.rectangleObject = false + this.inp = [] + for (let i = 0; i < this.inputSize; i++) { + const a = new Node( + -20 + this.xOff, + +this.yOff * 10 * (i - this.inputSize / 2) + 10, + 0, + this + ) + this.inp.push(a) + } + this.output1 = new Node(20 - this.xOff, 0, 1, this) + this.controlSignalInput = new Node( + 0, + this.yOff * 10 * (this.inputSize / 2 - 1) + this.xOff + 10, + 0, + this, + this.controlSignalSize, + 'Control Signal' + ) + } + + /** + * @memberof Multiplexer + * function to change control signal of the element + */ + changeControlSignalSize(size) { + if (size === undefined || size < 1 || size > 32) return + if (this.controlSignalSize === size) return + const obj = new Multiplexer( + this.x, + this.y, + this.scope, + this.direction, + this.bitWidth, + size + ) + this.cleanDelete() + simulationArea.lastSelected = obj + return obj + } + + /** + * @memberof Multiplexer + * function to change bitwidth of the element + * @param {number} bitWidth - bitwidth + */ + newBitWidth(bitWidth) { + this.bitWidth = bitWidth + for (let i = 0; i < this.inputSize; i++) { + this.inp[i].bitWidth = bitWidth + } + this.output1.bitWidth = bitWidth + } + + /** + * @memberof Multiplexer + * @type {boolean} + */ + isResolvable() { + if ( + this.controlSignalInput.value !== undefined && + this.inp[this.controlSignalInput.value].value !== undefined + ) + return true + return false + } + + /** + * @memberof Multiplexer + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [ + this.direction, + this.bitWidth, + this.controlSignalSize, + ], + nodes: { + inp: this.inp.map(findNode), + output1: findNode(this.output1), + controlSignalInput: findNode(this.controlSignalInput), + }, + } + return data + } + + /** + * @memberof Multiplexer + * resolve output values based on inputData + */ + resolve() { + if (this.isResolvable() === false) { + return + } + this.output1.value = this.inp[this.controlSignalInput.value].value + simulationArea.simulationQueue.add(this.output1) + } + + /** + * @memberof Multiplexer + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + + const xx = this.x + const yy = this.y + + ctx.beginPath() + moveTo( + ctx, + 0, + this.yOff * 10 * (this.inputSize / 2 - 1) + 10 + 0.5 * this.xOff, + xx, + yy, + this.direction + ) + lineTo( + ctx, + 0, + this.yOff * 5 * (this.inputSize - 1) + this.xOff, + xx, + yy, + this.direction + ) + ctx.stroke() + + ctx.lineWidth = correctWidth(3) + ctx.beginPath() + ctx.strokeStyle = colors['stroke'] + + ctx.fillStyle = colors['fill'] + moveTo( + ctx, + -20 + this.xOff, + -this.yOff * 10 * (this.inputSize / 2), + xx, + yy, + this.direction + ) + lineTo( + ctx, + -20 + this.xOff, + 20 + this.yOff * 10 * (this.inputSize / 2 - 1), + xx, + yy, + this.direction + ) + lineTo( + ctx, + 20 - this.xOff, + +this.yOff * 10 * (this.inputSize / 2 - 1) + this.xOff, + xx, + yy, + this.direction + ) + lineTo( + ctx, + 20 - this.xOff, + -this.yOff * 10 * (this.inputSize / 2) - this.xOff + 20, + xx, + yy, + this.direction + ) + + ctx.closePath() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.fillStyle = colors['hover_select'] + } + ctx.fill() + ctx.stroke() + + ctx.beginPath() + // ctx.lineWidth = correctWidth(2); + ctx.fillStyle = 'black' + ctx.textAlign = 'center' + for (let i = 0; i < this.inputSize; i++) { + if (this.direction === 'RIGHT') + fillText( + ctx, + String(i), + xx + this.inp[i].x + 7, + yy + this.inp[i].y + 2, + 10 + ) + else if (this.direction === 'LEFT') + fillText( + ctx, + String(i), + xx + this.inp[i].x - 7, + yy + this.inp[i].y + 2, + 10 + ) + else if (this.direction === 'UP') + fillText( + ctx, + String(i), + xx + this.inp[i].x, + yy + this.inp[i].y - 4, + 10 + ) + else + fillText( + ctx, + String(i), + xx + this.inp[i].x, + yy + this.inp[i].y + 10, + 10 + ) + } + ctx.fill() + } + + verilogBaseType() { + return this.verilogName() + this.inp.length + } + + //this code to generate Verilog + generateVerilog() { + Multiplexer.selSizes.add(this.controlSignalSize) + return CircuitElement.prototype.generateVerilog.call(this) + } + + //generate the needed modules + static moduleVerilog() { + var output = '' + + for (var size of Multiplexer.selSizes) { + var numInput = 1 << size + var inpString = '' + for (var j = 0; j < numInput; j++) { + inpString += `in${j}, ` + } + output += `\nmodule Multiplexer${numInput}(out, ${inpString}sel);\n` + + output += ' parameter WIDTH = 1;\n' + output += ' output reg [WIDTH-1:0] out;\n' + + output += ' input [WIDTH-1:0] ' + for (var j = 0; j < numInput - 1; j++) { + output += `in${j}, ` + } + output += 'in' + (numInput - 1) + ';\n' + + output += ` input [${size - 1}:0] sel;\n` + output += ' \n' + + output += ' always @ (*)\n' + output += ' case (sel)\n' + for (var j = 0; j < numInput; j++) { + output += ` ${j} : out = in${j};\n` + } + output += ' endcase\n' + output += 'endmodule\n' + output += '\n' + } + + return output + } + //reset the sized before Verilog generation + static resetVerilog() { + Multiplexer.selSizes = new Set() + } +} + +/** + * @memberof Multiplexer + * Help Tip + * @type {string} + * @category modules + */ +Multiplexer.prototype.tooltipText = + 'Multiplexer ToolTip : Multiple inputs and a single line output.' +Multiplexer.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/5muxandplex?id=multiplexer' + +/** + * @memberof Multiplexer + * multable properties of element + * @type {JSON} + * @category modules + */ +Multiplexer.prototype.mutableProperties = { + controlSignalSize: { + name: 'Control Signal Size', + type: 'number', + max: '10', + min: '1', + func: 'changeControlSignalSize', + }, +} +Multiplexer.prototype.objectType = 'Multiplexer' diff --git a/v1/src/simulator/src/modules/NandGate.js b/v1/src/simulator/src/modules/NandGate.js new file mode 100644 index 00000000..1cf25e82 --- /dev/null +++ b/v1/src/simulator/src/modules/NandGate.js @@ -0,0 +1,169 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, drawCircle2, arc } from '../canvasApi' +import { changeInputSize } from '../modules' +import { gateGenerateVerilog } from '../utils' + +/** + * @class + * NandGate + * @extends CircuitElement + * @param {number} x - x coordinate of nand Gate. + * @param {number} y - y coordinate of nand Gate. + * @param {Scope=} scope - Cirucit on which nand gate is drawn + * @param {string=} dir - direction of nand Gate + * @param {number=} inputLength - number of input nodes + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +import { colors } from '../themer/themer' + +export default class NandGate extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + inputLength = 2, + bitWidth = 1 + ) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['NandGate'].push(this); + */ + this.rectangleObject = false + this.setDimensions(15, 20) + this.inp = [] + this.inputSize = inputLength + // variable inputLength , node creation + if (inputLength % 2 === 1) { + for (let i = 0; i < inputLength / 2 - 1; i++) { + const a = new Node(-10, -10 * (i + 1), 0, this) + this.inp.push(a) + } + let a = new Node(-10, 0, 0, this) + this.inp.push(a) + for (let i = inputLength / 2 + 1; i < inputLength; i++) { + a = new Node(-10, 10 * (i + 1 - inputLength / 2 - 1), 0, this) + this.inp.push(a) + } + } else { + for (let i = 0; i < inputLength / 2; i++) { + const a = new Node(-10, -10 * (i + 1), 0, this) + this.inp.push(a) + } + for (let i = inputLength / 2; i < inputLength; i++) { + const a = new Node(-10, 10 * (i + 1 - inputLength / 2), 0, this) + this.inp.push(a) + } + } + this.output1 = new Node(30, 0, 1, this) + } + + /** + * @memberof NandGate + * fn to create save Json Data of object + * @return {JSON} + */ + // fn to create save Json Data of object + customSave() { + const data = { + constructorParamaters: [ + this.direction, + this.inputSize, + this.bitWidth, + ], + nodes: { + inp: this.inp.map(findNode), + output1: findNode(this.output1), + }, + } + return data + } + + /** + * @memberof NandGate + * resolve output values based on inputData + */ + resolve() { + let result = this.inp[0].value || 0 + if (this.isResolvable() === false) { + return + } + for (let i = 1; i < this.inputSize; i++) + result &= this.inp[i].value || 0 + result = + ((~result >>> 0) << (32 - this.bitWidth)) >>> (32 - this.bitWidth) + this.output1.value = result + simulationArea.simulationQueue.add(this.output1) + } + + /** + * @memberof NandGate + * function to draw nand Gate + */ + customDraw() { + var ctx = simulationArea.context + ctx.beginPath() + ctx.lineWidth = correctWidth(3) + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + const xx = this.x + const yy = this.y + moveTo(ctx, -10, -20, xx, yy, this.direction) + lineTo(ctx, 0, -20, xx, yy, this.direction) + arc(ctx, 0, 0, 20, -Math.PI / 2, Math.PI / 2, xx, yy, this.direction) + lineTo(ctx, -10, 20, xx, yy, this.direction) + lineTo(ctx, -10, -20, xx, yy, this.direction) + ctx.closePath() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + ctx.beginPath() + drawCircle2(ctx, 25, 0, 5, xx, yy, this.direction) + ctx.stroke() + } + + generateVerilog() { + return gateGenerateVerilog.call(this, '&', true) + } +} + +/** + * @memberof NandGate + * Help Tip + * @type {string} + * @category modules + */ +NandGate.prototype.tooltipText = + 'Nand Gate ToolTip : Combination of AND and NOT gates' + +/** + * @memberof NandGate + * @type {boolean} + * @category modules + */ +NandGate.prototype.alwaysResolve = true + +/** + * @memberof NandGate + * function to change input nodes of the gate + * @category modules + */ +NandGate.prototype.changeInputSize = changeInputSize + +/** + * @memberof NandGate + * @type {string} + * @category modules + */ +NandGate.prototype.verilogType = 'nand' +NandGate.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/4gates?id=nand-gate' +NandGate.prototype.objectType = 'NandGate' diff --git a/v1/src/simulator/src/modules/NorGate.js b/v1/src/simulator/src/modules/NorGate.js new file mode 100644 index 00000000..38572dd5 --- /dev/null +++ b/v1/src/simulator/src/modules/NorGate.js @@ -0,0 +1,183 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { gateGenerateVerilog } from '../utils' + +import { + correctWidth, + bezierCurveTo, + moveTo, + arc2, + drawCircle2, +} from '../canvasApi' +import { changeInputSize } from '../modules' +/** + * @class + * NorGate + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} inputs - number of input nodes + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +import { colors } from '../themer/themer' + +export default class NorGate extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + inputs = 2, + bitWidth = 1 + ) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['NorGate'].push(this); + */ + this.rectangleObject = false + this.setDimensions(15, 20) + + this.inp = [] + this.inputSize = inputs + + if (inputs % 2 === 1) { + for (let i = 0; i < inputs / 2 - 1; i++) { + const a = new Node(-10, -10 * (i + 1), 0, this) + this.inp.push(a) + } + let a = new Node(-10, 0, 0, this) + this.inp.push(a) + for (let i = inputs / 2 + 1; i < inputs; i++) { + a = new Node(-10, 10 * (i + 1 - inputs / 2 - 1), 0, this) + this.inp.push(a) + } + } else { + for (let i = 0; i < inputs / 2; i++) { + const a = new Node(-10, -10 * (i + 1), 0, this) + this.inp.push(a) + } + for (let i = inputs / 2; i < inputs; i++) { + const a = new Node(-10, 10 * (i + 1 - inputs / 2), 0, this) + this.inp.push(a) + } + } + this.output1 = new Node(30, 0, 1, this) + } + + /** + * @memberof NorGate + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [ + this.direction, + this.inputSize, + this.bitWidth, + ], + nodes: { + inp: this.inp.map(findNode), + output1: findNode(this.output1), + }, + } + return data + } + + /** + * @memberof NorGate + * resolve output values based on inputData + */ + resolve() { + let result = this.inp[0].value || 0 + for (let i = 1; i < this.inputSize; i++) + result |= this.inp[i].value || 0 + result = + ((~result >>> 0) << (32 - this.bitWidth)) >>> (32 - this.bitWidth) + this.output1.value = result + simulationArea.simulationQueue.add(this.output1) + } + + /** + * @memberof NorGate + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.strokeStyle = colors['stroke'] + ctx.lineWidth = correctWidth(3) + + const xx = this.x + const yy = this.y + ctx.beginPath() + ctx.fillStyle = colors['fill'] + + moveTo(ctx, -10, -20, xx, yy, this.direction, true) + bezierCurveTo(0, -20, +15, -10, 20, 0, xx, yy, this.direction) + bezierCurveTo( + 0 + 15, + 0 + 10, + 0, + 0 + 20, + -10, + +20, + xx, + yy, + this.direction + ) + bezierCurveTo(0, 0, 0, 0, -10, -20, xx, yy, this.direction) + ctx.closePath() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + ctx.beginPath() + drawCircle2(ctx, 25, 0, 5, xx, yy, this.direction) + ctx.stroke() + // for debugging + } + + generateVerilog() { + return gateGenerateVerilog.call(this, '|', true) + } +} + +/** + * @memberof NorGate + * Help Tip + * @type {string} + * @category modules + */ +NorGate.prototype.tooltipText = + 'Nor Gate ToolTip : Combination of OR gate and NOT gate.' + +/** + * @memberof NorGate + * @type {boolean} + * @category modules + */ +NorGate.prototype.alwaysResolve = true + +/** + * @memberof SevenSegDisplay + * function to change input nodes of the element + * @category modules + */ +NorGate.prototype.changeInputSize = changeInputSize + +/** + * @memberof SevenSegDisplay + * @type {string} + * @category modules + */ +NorGate.prototype.verilogType = 'nor' +NorGate.prototype.helplink = 'https://docs.circuitverse.org/#/chapter4/4gates?id=nor-gate' +NorGate.prototype.objectType = 'NorGate' diff --git a/v1/src/simulator/src/modules/NotGate.js b/v1/src/simulator/src/modules/NotGate.js new file mode 100644 index 00000000..38ec3a05 --- /dev/null +++ b/v1/src/simulator/src/modules/NotGate.js @@ -0,0 +1,113 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, drawCircle2 } from '../canvasApi' +import { changeInputSize } from '../modules' +/** + * @class + * NotGate + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +import { colors } from '../themer/themer' + +export default class NotGate extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT', bitWidth = 1) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['NotGate'].push(this); + */ + this.rectangleObject = false + this.setDimensions(15, 15) + + this.inp1 = new Node(-10, 0, 0, this) + this.output1 = new Node(20, 0, 1, this) + } + + /** + * @memberof NotGate + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.direction, this.bitWidth], + nodes: { + output1: findNode(this.output1), + inp1: findNode(this.inp1), + }, + } + return data + } + + /** + * @memberof NotGate + * resolve output values based on inputData + */ + resolve() { + if (this.isResolvable() === false) { + return + } + this.output1.value = + ((~this.inp1.value >>> 0) << (32 - this.bitWidth)) >>> + (32 - this.bitWidth) + simulationArea.simulationQueue.add(this.output1) + } + + /** + * @memberof NotGate + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.strokeStyle = colors['stroke'] + ctx.lineWidth = correctWidth(3) + + const xx = this.x + const yy = this.y + ctx.beginPath() + ctx.fillStyle = colors['fill'] + moveTo(ctx, -10, -10, xx, yy, this.direction) + lineTo(ctx, 10, 0, xx, yy, this.direction) + lineTo(ctx, -10, 10, xx, yy, this.direction) + ctx.closePath() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + ctx.beginPath() + drawCircle2(ctx, 15, 0, 5, xx, yy, this.direction) + ctx.stroke() + } + + generateVerilog() { + return ( + 'assign ' + + this.output1.verilogLabel + + ' = ~' + + this.inp1.verilogLabel + + ';' + ) + } +} + +/** + * @memberof NotGate + * Help Tip + * @type {string} + * @category modules + */ +NotGate.prototype.tooltipText = + 'Not Gate Tooltip : Inverts the input digital signal.' +NotGate.prototype.helplink = 'https://docs.circuitverse.org/#/chapter4/4gates?id=not-gate' +NotGate.prototype.objectType = 'NotGate' +NotGate.prototype.verilogType = 'not' diff --git a/v1/src/simulator/src/modules/OrGate.js b/v1/src/simulator/src/modules/OrGate.js new file mode 100644 index 00000000..31b557cd --- /dev/null +++ b/v1/src/simulator/src/modules/OrGate.js @@ -0,0 +1,175 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, bezierCurveTo, moveTo, arc2 } from '../canvasApi' +import { changeInputSize } from '../modules' +import { gateGenerateVerilog } from '../utils' + +/** + * @class + * OrGate + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} inputs - number of input nodes + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +import { colors } from '../themer/themer' + +export default class OrGate extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + inputs = 2, + bitWidth = 1 + ) { + // Calling base class constructor + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['OrGate'].push(this); + */ + this.rectangleObject = false + this.setDimensions(15, 20) + // Inherit base class prototype + this.inp = [] + this.inputSize = inputs + if (inputs % 2 === 1) { + for (let i = Math.floor(inputs / 2) - 1; i >= 0; i--) { + const a = new Node(-10, -10 * (i + 1), 0, this) + this.inp.push(a) + } + let a = new Node(-10, 0, 0, this) + this.inp.push(a) + for (let i = 0; i < Math.floor(inputs / 2); i++) { + a = new Node(-10, 10 * (i + 1), 0, this) + this.inp.push(a) + } + } else { + for (let i = inputs / 2 - 1; i >= 0; i--) { + const a = new Node(-10, -10 * (i + 1), 0, this) + this.inp.push(a) + } + for (let i = 0; i < inputs / 2; i++) { + const a = new Node(-10, 10 * (i + 1), 0, this) + this.inp.push(a) + } + } + this.output1 = new Node(20, 0, 1, this) + } + + /** + * @memberof OrGate + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [ + this.direction, + this.inputSize, + this.bitWidth, + ], + + nodes: { + inp: this.inp.map(findNode), + output1: findNode(this.output1), + }, + } + return data + } + + /** + * @memberof OrGate + * resolve output values based on inputData + */ + resolve() { + let result = this.inp[0].value || 0 + if (this.isResolvable() === false) { + return + } + for (let i = 1; i < this.inputSize; i++) + result |= this.inp[i].value || 0 + this.output1.value = result >>> 0 + simulationArea.simulationQueue.add(this.output1) + } + + /** + * @memberof OrGate + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.strokeStyle = colors['stroke'] + ctx.lineWidth = correctWidth(3) + + const xx = this.x + const yy = this.y + ctx.beginPath() + ctx.fillStyle = colors['fill'] + + moveTo(ctx, -10, -20, xx, yy, this.direction, true) + bezierCurveTo(0, -20, +15, -10, 20, 0, xx, yy, this.direction) + bezierCurveTo( + 0 + 15, + 0 + 10, + 0, + 0 + 20, + -10, + +20, + xx, + yy, + this.direction + ) + bezierCurveTo(0, 0, 0, 0, -10, -20, xx, yy, this.direction) + ctx.closePath() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + } + + generateVerilog() { + return gateGenerateVerilog.call(this, '|') + } +} + +/** + * @memberof OrGate + * Help Tip + * @type {string} + * @category modules + */ +OrGate.prototype.tooltipText = + 'Or Gate Tooltip : Implements logical disjunction' + +/** + * @memberof OrGate + * function to change input nodes of the element + * @category modules + */ +OrGate.prototype.changeInputSize = changeInputSize + +/** + * @memberof SevenSegDisplay + * @type {boolean} + * @category modules + */ +OrGate.prototype.alwaysResolve = true + +/** + * @memberof SevenSegDisplay + * @type {string} + * @category modules + */ +OrGate.prototype.verilogType = 'or' +OrGate.prototype.helplink = 'https://docs.circuitverse.org/#/chapter4/4gates?id=or-gate' +OrGate.prototype.objectType = 'OrGate' diff --git a/v1/src/simulator/src/modules/Output.js b/v1/src/simulator/src/modules/Output.js new file mode 100644 index 00000000..23ef3975 --- /dev/null +++ b/v1/src/simulator/src/modules/Output.js @@ -0,0 +1,219 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, fillText, rect2, oppositeDirection } from '../canvasApi' +import { getNextPosition } from '../modules' +import { generateId } from '../utils' +import { colors } from '../themer/themer' + +function bin2dec(binString) { + return parseInt(binString, 2) +} + +function dec2bin(dec, bitWidth = undefined) { + // only for positive nos + var bin = dec.toString(2) + if (bitWidth == undefined) return bin + return '0'.repeat(bitWidth - bin.length) + bin +} + +/** + * @class + * Output + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} inputLength - number of input nodes + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +export default class Output extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'LEFT', + bitWidth = 1, + layoutProperties + ) { + // Calling base class constructor + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['Output'].push(this); + */ + if (layoutProperties) { + this.layoutProperties = layoutProperties + } else { + this.layoutProperties = {} + this.layoutProperties.x = scope.layout.width + this.layoutProperties.y = getNextPosition(scope.layout.width, scope) + this.layoutProperties.id = generateId() + } + + this.rectangleObject = false + this.directionFixed = true + this.orientationFixed = false + this.setDimensions(this.bitWidth * 10, 10) + this.inp1 = new Node(this.bitWidth * 10, 0, 0, this) + } + + /** + * @memberof Output + * function to generate verilog for output + * @return {string} + */ + generateVerilog() { + return `assign ${this.label} = ${this.inp1.verilogLabel};` + } + + /** + * @memberof Output + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + nodes: { + inp1: findNode(this.inp1), + }, + constructorParamaters: [ + this.direction, + this.bitWidth, + this.layoutProperties, + ], + } + return data + } + + /** + * @memberof Output + * function to change bitwidth of the element + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + if (bitWidth < 1) return + const diffBitWidth = bitWidth - this.bitWidth + this.state = undefined + this.inp1.bitWidth = bitWidth + this.bitWidth = bitWidth + this.setWidth(10 * this.bitWidth) + + if (this.direction === 'RIGHT') { + this.x -= 10 * diffBitWidth + this.inp1.x = 10 * this.bitWidth + this.inp1.leftx = 10 * this.bitWidth + } else if (this.direction === 'LEFT') { + this.x += 10 * diffBitWidth + this.inp1.x = -10 * this.bitWidth + this.inp1.leftx = 10 * this.bitWidth + } + } + + /** + * @memberof Output + * function to draw element + */ + customDraw() { + this.state = this.inp1.value + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = [colors['out_rect'], colors['stroke_alt']][ + +(this.inp1.value === undefined) + ] + ctx.fillStyle = colors['fill'] + ctx.lineWidth = correctWidth(3) + const xx = this.x + const yy = this.y + + rect2( + ctx, + -10 * this.bitWidth, + -10, + 20 * this.bitWidth, + 20, + xx, + yy, + 'RIGHT' + ) + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.fillStyle = colors['hover_select'] + } + + ctx.fill() + ctx.stroke() + + ctx.beginPath() + ctx.font = '20px Raleway' + ctx.fillStyle = colors['input_text'] + ctx.textAlign = 'center' + let bin + if (this.state === undefined) { + bin = 'x'.repeat(this.bitWidth) + } else { + bin = dec2bin(this.state, this.bitWidth) + } + + for (let k = 0; k < this.bitWidth; k++) { + fillText(ctx, bin[k], xx - 10 * this.bitWidth + 10 + k * 20, yy + 5) + } + ctx.fill() + } + + /** + * @memberof Output + * function to change direction of Output + * @param {string} dir - new direction + */ + newDirection(dir) { + if (dir === this.direction) return + this.direction = dir + this.inp1.refresh() + if (dir === 'RIGHT' || dir === 'LEFT') { + this.inp1.leftx = 10 * this.bitWidth + this.inp1.lefty = 0 + } else { + this.inp1.leftx = 10 // 10*this.bitWidth; + this.inp1.lefty = 0 + } + + this.inp1.refresh() + this.labelDirection = oppositeDirection[this.direction] + } + + generateVerilog() { + return ( + 'assign ' + this.verilogLabel + ' = ' + this.inp1.verilogLabel + ';' + ) + } +} + +/** + * @memberof Output + * Help Tip + * @type {string} + * @category modules + */ +Output.prototype.tooltipText = + 'Output ToolTip: Simple output element showing output in binary.' + +/** + * @memberof Output + * Help URL + * @type {string} + * @category modules + */ +Output.prototype.helplink = 'https://docs.circuitverse.org/#/chapter4/3output?id=output' + +/** + * @memberof Output + * @type {number} + * @category modules + */ +Output.prototype.propagationDelay = 0 +Output.prototype.objectType = 'Output' diff --git a/v1/src/simulator/src/modules/Power.js b/v1/src/simulator/src/modules/Power.js new file mode 100644 index 00000000..87c419a5 --- /dev/null +++ b/v1/src/simulator/src/modules/Power.js @@ -0,0 +1,146 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, arc } from '../canvasApi' +import { changeInputSize } from '../modules' +/** + * @class + * Power + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +import { colors } from '../themer/themer' + +export default class Power extends CircuitElement { + constructor(x, y, scope = globalScope, bitWidth = 1) { + super(x, y, scope, 'RIGHT', bitWidth) + /* this is done in this.baseSetup() now + this.scope['Power'].push(this); + */ + this.directionFixed = true + this.rectangleObject = false + this.setDimensions(10, 10) + this.output1 = new Node(0, 10, 1, this) + } + + /** + * @memberof Power + * resolve output values based on inputData + */ + resolve() { + this.output1.value = ~0 >>> (32 - this.bitWidth) + simulationArea.simulationQueue.add(this.output1) + } + + /** + * @memberof Power + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + nodes: { + output1: findNode(this.output1), + }, + constructorParamaters: [this.bitWidth], + } + return data + } + + /** + * @memberof Power + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + const xx = this.x + const yy = this.y + ctx.beginPath() + ctx.strokeStyle = 'rgba(0,0,0,1)' + ctx.lineWidth = correctWidth(3) + ctx.fillStyle = colors['fill'] + moveTo(ctx, 0, -10, xx, yy, this.direction) + lineTo(ctx, -10, 0, xx, yy, this.direction) + lineTo(ctx, 10, 0, xx, yy, this.direction) + lineTo(ctx, 0, -10, xx, yy, this.direction) + ctx.closePath() + ctx.stroke() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + moveTo(ctx, 0, 0, xx, yy, this.direction) + lineTo(ctx, 0, 10, xx, yy, this.direction) + ctx.stroke() + } + + generateVerilog() { + return `assign ${this.output1.verilogLabel} = ~${this.bitWidth}'b0;` + } +} + +/** + * @memberof Power + * Help Tip + * @type {string} + * @category modules + */ +Power.prototype.tooltipText = 'Power: All bits are High(1).' + +/** + * @memberof Power + * Help URL + * @type {string} + * @category modules + */ +Power.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/2input?id=power' + +/** + * @memberof Power + * @type {number} + * @category modules + */ +Power.prototype.propagationDelay = 0 + +function getNextPosition(x = 0, scope = globalScope) { + let possibleY = 20 + const done = {} + for (let i = 0; i < scope.Input.length; i++) { + if (scope.Input[i].layoutProperties.x === x) { + done[scope.Input[i].layoutProperties.y] = 1 + } + } + for (let i = 0; i < scope.Output.length; i++) { + if (scope.Output[i].layoutProperties.x === x) { + done[scope.Output[i].layoutProperties.y] = 1 + } + } + while (done[possibleY] || done[possibleY + 10] || done[possibleY - 10]) { + possibleY += 10 + } + const height = possibleY + 20 + if (height > scope.layout.height) { + const oldHeight = scope.layout.height + scope.layout.height = height + for (let i = 0; i < scope.Input.length; i++) { + if (scope.Input[i].layoutProperties.y === oldHeight) { + scope.Input[i].layoutProperties.y = scope.layout.height + } + } + for (let i = 0; i < scope.Output.length; i++) { + if (scope.Output[i].layoutProperties.y === oldHeight) { + scope.Output[i].layoutProperties.y = scope.layout.height + } + } + } + return possibleY +} +Power.prototype.objectType = 'Power' diff --git a/v1/src/simulator/src/modules/PriorityEncoder.js b/v1/src/simulator/src/modules/PriorityEncoder.js new file mode 100644 index 00000000..b74d7f90 --- /dev/null +++ b/v1/src/simulator/src/modules/PriorityEncoder.js @@ -0,0 +1,276 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode, dec2bin } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, rect, fillText } from '../canvasApi' +/** + * @class + * PriorityEncoder + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +import { colors } from '../themer/themer' + +export default class PriorityEncoder extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT', bitWidth = 1) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['PriorityEncoder'].push(this); + */ + this.bitWidth = bitWidth || parseInt(prompt('Enter bitWidth'), 10) + this.inputSize = 1 << this.bitWidth + + this.yOff = 1 + if (this.bitWidth <= 3) { + this.yOff = 2 + } + + this.setDimensions(20, this.yOff * 5 * this.inputSize) + this.directionFixed = true + this.rectangleObject = false + + this.inp1 = [] + for (let i = 0; i < this.inputSize; i++) { + const a = new Node( + -10, + +this.yOff * 10 * (i - this.inputSize / 2) + 10, + 0, + this, + 1 + ) + this.inp1.push(a) + } + + this.output1 = [] + for (let i = 0; i < this.bitWidth; i++) { + const a = new Node( + 30, + +2 * 10 * (i - this.bitWidth / 2) + 10, + 1, + this, + 1 + ) + this.output1.push(a) + } + + this.enable = new Node( + 10, + 20 + this.inp1[this.inputSize - 1].y, + 1, + this, + 1 + ) + } + + /** + * @memberof PriorityEncoder + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + nodes: { + inp1: this.inp1.map(findNode), + output1: this.output1.map(findNode), + enable: findNode(this.enable), + }, + constructorParamaters: [this.direction, this.bitWidth], + } + return data + } + + /** + * @memberof PriorityEncoder + * function to change bitwidth of the element + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + if (bitWidth === undefined || bitWidth < 1 || bitWidth > 32) return + if (this.bitWidth === bitWidth) return + + this.bitWidth = bitWidth + const obj = new PriorityEncoder( + this.x, + this.y, + this.scope, + this.direction, + this.bitWidth + ) + this.inputSize = 1 << bitWidth + + this.cleanDelete() + simulationArea.lastSelected = obj + return obj + } + + /** + * @memberof PriorityEncoder + * resolve output values based on inputData + */ + resolve() { + let out = 0 + let temp = 0 + for (let i = this.inputSize - 1; i >= 0; i--) { + if (this.inp1[i].value === 1) { + out = dec2bin(i) + break + } + } + temp = out + + if (out.length !== undefined) { + this.enable.value = 1 + } else { + this.enable.value = 0 + } + simulationArea.simulationQueue.add(this.enable) + + if (temp.length === undefined) { + temp = '0' + for (let i = 0; i < this.bitWidth - 1; i++) { + temp = `0${temp}` + } + } + + if (temp.length !== this.bitWidth) { + for (let i = temp.length; i < this.bitWidth; i++) { + temp = `0${temp}` + } + } + + for (let i = this.bitWidth - 1; i >= 0; i--) { + this.output1[this.bitWidth - 1 - i].value = Number(temp[i]) + simulationArea.simulationQueue.add( + this.output1[this.bitWidth - 1 - i] + ) + } + } + + /** + * @memberof PriorityEncoder + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.lineWidth = correctWidth(3) + const xx = this.x + const yy = this.y + if (this.bitWidth <= 3) { + rect( + ctx, + xx - 10, + yy - 10 - this.yOff * 5 * this.inputSize, + 40, + 20 * (this.inputSize + 1) + ) + } else { + rect( + ctx, + xx - 10, + yy - 10 - this.yOff * 5 * this.inputSize, + 40, + 10 * (this.inputSize + 3) + ) + } + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + + ctx.beginPath() + ctx.fillStyle = 'black' + ctx.textAlign = 'center' + for (let i = 0; i < this.inputSize; i++) { + fillText(ctx, String(i), xx, yy + this.inp1[i].y + 2, 10) + } + for (let i = 0; i < this.bitWidth; i++) { + fillText( + ctx, + String(i), + xx + this.output1[0].x - 10, + yy + this.output1[i].y + 2, + 10 + ) + } + fillText(ctx, 'EN', xx + this.enable.x, yy + this.enable.y - 5, 10) + ctx.fill() + } + + verilogBaseType() { + return this.verilogName() + this.inp1.length + } + + generateVerilog() { + PriorityEncoder.selSizes.add(this.bitWidth) + return CircuitElement.prototype.generateVerilog.call(this) + } + + static moduleVerilog() { + var output = '' + + for (var size of PriorityEncoder.selSizes) { + var numInput = 1 << size + output += '\n' + output += 'module PriorityEncoder' + numInput + output += '(sel, ze, ' + for (var j = 0; j < numInput - 1; j++) { + output += 'in' + j + ', ' + } + output += 'in' + (numInput - 1) + ');\n' + + output += ' output reg [' + (size - 1) + ':0] sel;\n' + output += ' output reg ze;\n' + + output += ' input ' + for (var j = 0; j < numInput - 1; j++) { + output += 'in' + j + ', ' + } + output += 'in' + (numInput - 1) + ';\n' + output += '\n' + + output += ' always @ (*) begin\n' + output += ' sel = 0;\n' + output += ' ze = 0;\n' + output += ' if (in' + (numInput - 1) + ')\n' + output += ' sel = ' + (numInput - 1) + ';\n' + for (var j = numInput - 2; j <= 0; j++) { + output += ' else if (in' + j + ')\n' + output += ' sel = ' + j + ';\n' + } + output += ' else\n' + output += ' ze = 1;\n' + output += ' end\n' + output += 'endmodule\n' + } + + return output + } + + //reset the sized before Verilog generation + static resetVerilog() { + PriorityEncoder.selSizes = new Set() + } +} + +/** + * @memberof PriorityEncoder + * Help Tip + * @type {string} + * @category modules + */ +PriorityEncoder.prototype.tooltipText = + 'Priority Encoder ToolTip : Compresses binary inputs into a smaller number of outputs.' +PriorityEncoder.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/5muxandplex?id=priority-encoder' +PriorityEncoder.prototype.objectType = 'PriorityEncoder' diff --git a/v1/src/simulator/src/modules/RGBLed.js b/v1/src/simulator/src/modules/RGBLed.js new file mode 100644 index 00000000..a1bcc659 --- /dev/null +++ b/v1/src/simulator/src/modules/RGBLed.js @@ -0,0 +1,177 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, arc, drawCircle2 } from '../canvasApi' +import { changeInputSize } from '../modules' +/** + * @class + * RGBLed + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @category modules + */ +import { colors } from '../themer/themer' + +export default class RGBLed extends CircuitElement { + constructor(x, y, scope = globalScope) { + // Calling base class constructor + super(x, y, scope, 'UP', 8) + /* this is done in this.baseSetup() now + this.scope['RGBLed'].push(this); + */ + this.rectangleObject = false + this.inp = [] + this.setDimensions(10, 10) + this.inp1 = new Node(-40, -10, 0, this, 8) + this.inp2 = new Node(-40, 0, 0, this, 8) + this.inp3 = new Node(-40, 10, 0, this, 8) + this.inp.push(this.inp1) + this.inp.push(this.inp2) + this.inp.push(this.inp3) + this.directionFixed = true + this.fixedBitWidth = true + } + + /** + * @memberof RGBLed + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + nodes: { + inp1: findNode(this.inp1), + inp2: findNode(this.inp2), + inp3: findNode(this.inp3), + }, + } + return data + } + + /** + * @memberof RGBLed + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + + const xx = this.x + const yy = this.y + + ctx.strokeStyle = 'green' + ctx.lineWidth = correctWidth(3) + ctx.beginPath() + moveTo(ctx, -20, 0, xx, yy, this.direction) + lineTo(ctx, -40, 0, xx, yy, this.direction) + ctx.stroke() + + ctx.strokeStyle = 'red' + ctx.lineWidth = correctWidth(3) + ctx.beginPath() + moveTo(ctx, -20, -10, xx, yy, this.direction) + lineTo(ctx, -40, -10, xx, yy, this.direction) + ctx.stroke() + + ctx.strokeStyle = 'blue' + ctx.lineWidth = correctWidth(3) + ctx.beginPath() + moveTo(ctx, -20, 10, xx, yy, this.direction) + lineTo(ctx, -40, 10, xx, yy, this.direction) + ctx.stroke() + + const a = this.inp1.value + const b = this.inp2.value + const c = this.inp3.value + ctx.strokeStyle = '#d3d4d5' + ctx.fillStyle = [ + `rgba(${a}, ${b}, ${c}, 0.8)`, + 'rgba(227, 228, 229, 0.8)', + ][(a === undefined || b === undefined || c === undefined) + 0] + // ctx.fillStyle = ["rgba(200, 200, 200, 0.3)","rgba(227, 228, 229, 0.8)"][((a === undefined || b === undefined || c === undefined) || (a === 0 && b === 0 && c === 0)) + 0]; + ctx.lineWidth = correctWidth(1) + + ctx.beginPath() + + moveTo(ctx, -18, -11, xx, yy, this.direction) + lineTo(ctx, 0, -11, xx, yy, this.direction) + arc(ctx, 0, 0, 11, -Math.PI / 2, Math.PI / 2, xx, yy, this.direction) + lineTo(ctx, -18, 11, xx, yy, this.direction) + lineTo(ctx, -21, 15, xx, yy, this.direction) + arc( + ctx, + 0, + 0, + Math.sqrt(666), + Math.PI / 2 + Math.acos(15 / Math.sqrt(666)), + -Math.PI / 2 - Math.asin(21 / Math.sqrt(666)), + xx, + yy, + this.direction + ) + lineTo(ctx, -18, -11, xx, yy, this.direction) + ctx.stroke() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + } + + // Draws the element in the subcuircuit. Used in layout mode + subcircuitDraw(xOffset = 0, yOffset = 0) { + var ctx = simulationArea.context + + var xx = this.subcircuitMetadata.x + xOffset + var yy = this.subcircuitMetadata.y + yOffset + var dimensionSize = 6 + + var a = this.inp1.value + var b = this.inp2.value + var c = this.inp3.value + ctx.strokeStyle = '#090a0a' + ctx.fillStyle = [ + 'rgba(' + a + ', ' + b + ', ' + c + ', 0.8)', + 'rgba(227, 228, 229, 0.8)', + ][(a === undefined || b === undefined || c === undefined) + 0] + ctx.lineWidth = correctWidth(1) + + ctx.beginPath() + drawCircle2(ctx, 0, 0, dimensionSize, xx, yy, this.direction) + ctx.stroke() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected == this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = 'rgba(255, 255, 32,0.8)' + ctx.fill() + } + generateVerilog() { + return ` + always @ (*) + $display("RGBLed:{${this.inp1.verilogLabel},${this.inp2.verilogLabel},${this.inp3.verilogLabel}} = {%d,%d,%d}", ${this.inp1.verilogLabel}, ${this.inp2.verilogLabel}, ${this.inp3.verilogLabel});` + } +} + +/** + * @memberof RGBLed + * Help Tip + * @type {string} + * @category modules + */ +RGBLed.prototype.tooltipText = + 'RGB Led ToolTip: RGB Led inputs 8 bit values for the colors RED, GREEN and BLUE.' + +/** + * @memberof RGBLed + * Help URL + * @type {string} + * @category modules + */ +RGBLed.prototype.helplink = 'https://docs.circuitverse.org/#/chapter4/3output?id=rgbled' +RGBLed.prototype.objectType = 'RGBLed' +RGBLed.prototype.canShowInSubcircuit = true diff --git a/v1/src/simulator/src/modules/RGBLedMatrix.js b/v1/src/simulator/src/modules/RGBLedMatrix.js new file mode 100644 index 00000000..00fe1757 --- /dev/null +++ b/v1/src/simulator/src/modules/RGBLedMatrix.js @@ -0,0 +1,383 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, rect2, rotate, lineTo, moveTo } from '../canvasApi' + +/** + * @class + * RGBLedMatrix + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {number=} rows - number of rows + * @param {number=} cols - number of columns. + * @category modules + */ +export default class RGBLedMatrix extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + { + rows = 8, + columns = 8, + ledSize = 2, + showGrid = true, + colors = [], + } = {} + ) { + super(x, y, scope, 'RIGHT', 8) + /* this is done in this.baseSetup() now + this.scope['RGBLedMatrix'].push(this); + */ + this.fixedBitWidth = true + this.directionFixed = true + this.rectangleObject = true + this.alwaysResolve = true + this.labelDirection = 'UP' + this.leftDimensionX = 0 + this.upDimensionY = 0 + + // These pins provide bulk-editing of the colors + this.rowEnableNodes = [] // 1-bit pin for each row, on the left side. + this.columnEnableNodes = [] // 1-bit pin for each column, on the bottom. + this.columnColorNodes = [] // 24-bit pin for each column, on the top. + + // These pins provide single-pixel editing; these are on the right side. + this.colorNode = new Node(0, -10, NODE_INPUT, this, 24, 'COLOR') + this.rowNode = new Node(0, 0, NODE_INPUT, this, 1, 'ROW') + this.columnNode = new Node(0, 10, NODE_INPUT, this, 1, 'COLUMN') + + this.colors = colors + this.showGrid = showGrid + this.changeSize(rows, columns, ledSize, false) + } + + toggleGrid() { + this.showGrid = !this.showGrid + } + + changeRows(rows) { + this.changeSize(rows, this.columns, this.ledSize, true) + } + + changeColumns(columns) { + this.changeSize(this.rows, columns, this.ledSize, true) + } + + changeLedSize(ledSize) { + this.changeSize(this.rows, this.columns, ledSize, true) + } + + changeSize(rows, columns, ledSize, move) { + rows = parseInt(rows, 10) + if (isNaN(rows) || rows < 0 || rows > this.maxRows) return + + columns = parseInt(columns, 10) + if (isNaN(columns) || columns < 0 || columns > this.maxColumns) return + + ledSize = parseInt(ledSize, 10) + if (isNaN(ledSize) || ledSize < 0 || ledSize > this.maxLedSize) return + + // The size of an individual LED, in canvas units. + var ledWidth = 10 * ledSize + var ledHeight = 10 * ledSize + + // The size of the LED matrix, in canvas units. + var gridWidth = ledWidth * columns + var gridHeight = ledHeight * rows + + // We need to position the element in the 10x10 grid. + // Depending on the size of the leds we need to add different paddings so position correctly. + var padding = ledSize % 2 ? 5 : 10 + + // The dimensions of the element, in canvas units. + var halfWidth = gridWidth / 2 + padding + var halfHeight = gridHeight / 2 + padding + + // Move the element in order to keep the position of the nodes stable so wires don't break. + if (move) { + this.x -= this.leftDimensionX - halfWidth + this.y -= this.upDimensionY - halfHeight + } + + // Update the dimensions of the element. + this.setDimensions(halfWidth, halfHeight) + + // Offset of the nodes in relation to the element's center. + var nodePadding = [10, 20, 20][ledSize - 1] + var nodeOffsetX = nodePadding - halfWidth + var nodeOffsetY = nodePadding - halfHeight + + // When the led size changes it is better to delete all nodes to break connected the wires. + // Otherwise, wires can end up connected in unexpected ways. + var resetAllNodes = ledSize != this.ledSize + + // Delete unused row-enable nodes, reposition remaining nodes and add new nodes. + this.rowEnableNodes + .splice(resetAllNodes ? 0 : rows) + .forEach((node) => node.delete()) + this.rowEnableNodes.forEach((node, i) => { + node.x = node.leftx = -halfWidth + node.y = node.lefty = i * ledHeight + nodeOffsetY + }) + while (this.rowEnableNodes.length < rows) { + this.rowEnableNodes.push( + new Node( + -halfWidth, + this.rowEnableNodes.length * ledHeight + nodeOffsetY, + NODE_INPUT, + this, + 1, + 'R' + this.rowEnableNodes.length + ) + ) + } + + // Delete unused column-enable nodes, reposition remaining nodes and add new nodes. + this.columnEnableNodes + .splice(resetAllNodes ? 0 : columns) + .forEach((node) => node.delete()) + this.columnEnableNodes.forEach((node, i) => { + node.x = node.leftx = i * ledWidth + nodeOffsetX + node.y = node.lefty = halfHeight + }) + while (this.columnEnableNodes.length < columns) { + this.columnEnableNodes.push( + new Node( + this.columnEnableNodes.length * ledWidth + nodeOffsetX, + halfHeight, + NODE_INPUT, + this, + 1, + 'C' + this.columnEnableNodes.length + ) + ) + } + + // Delete unused column color nodes, reposition remaining nodes and add new nodes. + this.columnColorNodes + .splice(resetAllNodes ? 0 : columns) + .forEach((node) => node.delete()) + this.columnColorNodes.forEach((node, i) => { + node.x = node.leftx = i * ledWidth + nodeOffsetX + node.y = node.lefty = -halfHeight + }) + while (this.columnColorNodes.length < columns) { + this.columnColorNodes.push( + new Node( + this.columnColorNodes.length * ledWidth + nodeOffsetX, + -halfHeight, + NODE_INPUT, + this, + 24, + 'CLR' + this.columnColorNodes.length + ) + ) + } + + // Delete unused color storage and add storage for new rows. + this.colors.splice(rows) + this.colors.forEach((c) => c.splice(columns)) + while (this.colors.length < rows) { + this.colors.push([]) + } + + // Reposition the single-pixel nodes + this.rowNode.bitWidth = Math.ceil(Math.log2(rows)) + this.rowNode.label = 'ROW (' + this.rowNode.bitWidth + ' bits)' + this.columnNode.bitWidth = Math.ceil(Math.log2(columns)) + this.columnNode.label = 'COLUMN (' + this.columnNode.bitWidth + ' bits)' + var singlePixelNodePadding = rows > 1 ? nodeOffsetY : nodeOffsetY - 10 + var singlePixelNodeDistance = rows <= 2 ? 10 : ledHeight + ;[this.colorNode, this.rowNode, this.columnNode].forEach((node, i) => { + node.x = node.leftx = halfWidth + node.y = node.lefty = + i * singlePixelNodeDistance + singlePixelNodePadding + }) + + // Store the new values + this.rows = rows + this.columns = columns + this.ledSize = ledSize + + return this + } + + customSave() { + // Save the size of the LED matrix. + // Unlike a read LED matrix, we also persist the color of each pixel. + // This allows circuit preview to show the colors at the time the simulation was saved. + return { + constructorParamaters: [ + { + rows: this.rows, + columns: this.columns, + ledSize: this.ledSize, + showGrid: this.showGrid, + colors: this.colors, + }, + ], + nodes: { + rowEnableNodes: this.rowEnableNodes.map(findNode), + columnEnableNodes: this.columnEnableNodes.map(findNode), + columnColorNodes: this.columnColorNodes.map(findNode), + colorNode: findNode(this.colorNode), + rowNode: findNode(this.rowNode), + columnNode: findNode(this.columnNode), + }, + } + } + + resolve() { + var colorValue = this.colorNode.value + var hasColorValue = colorValue != undefined + + var rows = this.rows + var columns = this.columns + var rowEnableNodes = this.rowEnableNodes + var columnEnableNodes = this.columnEnableNodes + var columnColorNodes = this.columnColorNodes + var colors = this.colors + + for (var row = 0; row < rows; row++) { + if (rowEnableNodes[row].value === 1) { + for (var column = 0; column < columns; column++) { + // Method 1: set pixel by rowEnable + columnColor pins + var columnColor = columnColorNodes[column].value + if (columnColor !== undefined) { + colors[row][column] = columnColor + } + + // Method 2: set pixel by rowEnable + columnEnable + color pins + if ( + hasColorValue && + columnEnableNodes[column].value === 1 + ) { + colors[row][column] = colorValue + } + } + } + } + + // Method 3: set pixel by write + pixel index + color pins. + var hasRowNodeValue = this.rowNode.value != undefined || rows == 1 + var hasColumnNodeValue = + this.columnNode.value != undefined || columns == 1 + if (hasColorValue && hasRowNodeValue && hasColumnNodeValue) { + var rowNodeValue = this.rowNode.value || 0 + var columnNodeValue = this.columnNode.value || 0 + if (rowNodeValue < rows && columnNodeValue < columns) { + colors[rowNodeValue][columnNodeValue] = colorValue + } + } + } + + customDraw() { + var ctx = simulationArea.context + var rows = this.rows + var columns = this.columns + var colors = this.colors + var xx = this.x + var yy = this.y + var dir = this.direction + var ledWidth = 10 * this.ledSize + var ledHeight = 10 * this.ledSize + var top = this.rowEnableNodes[0].y - ledHeight / 2 + var left = this.columnColorNodes[0].x - ledWidth / 2 + var width = this.columns * ledWidth + var height = this.rows * ledHeight + var bottom = top + height + var right = left + width + + var [w, h] = rotate( + ledWidth * globalScope.scale, + ledHeight * globalScope.scale, + dir + ) + var xoffset = Math.round(globalScope.ox + xx * globalScope.scale) + var yoffset = Math.round(globalScope.oy + yy * globalScope.scale) + for (var row = 0; row < rows; row++) { + for (var column = 0; column < columns; column++) { + var color = colors[row][column] || 0 + ctx.beginPath() + ctx.fillStyle = + 'rgb(' + + ((color & 0xff0000) >> 16) + + ',' + + ((color & 0xff00) >> 8) + + ',' + + (color & 0xff) + + ')' + let x1, y1 + ;[x1, y1] = rotate( + left + column * ledWidth, + top + row * ledHeight, + dir + ) + x1 = x1 * globalScope.scale + y1 = y1 * globalScope.scale + ctx.rect(xoffset + x1, yoffset + y1, w, h) + ctx.fill() + } + } + + if (this.showGrid) { + ctx.beginPath() + ctx.strokeStyle = '#323232' + ctx.lineWidth = correctWidth(1) + rect2(ctx, left, top, width, height, xx, yy, dir) + for (var x = left + ledWidth; x < right; x += ledWidth) { + moveTo(ctx, x, top, xx, yy, dir) + lineTo(ctx, x, bottom, xx, yy, dir) + } + for (var y = top + ledHeight; y < bottom; y += ledHeight) { + moveTo(ctx, left, y, xx, yy, dir) + lineTo(ctx, right, y, xx, yy, dir) + } + ctx.stroke() + } + } +} + +RGBLedMatrix.prototype.tooltipText = 'RGB Led Matrix' + +// Limit the size of the matrix otherwise the simulation starts to lag. +RGBLedMatrix.prototype.maxRows = 128 +RGBLedMatrix.prototype.maxColumns = 128 + +// Let the user choose between 3 sizes of LEDs: small, medium and large. +RGBLedMatrix.prototype.maxLedSize = 3 + +RGBLedMatrix.prototype.mutableProperties = { + rows: { + name: 'Rows', + type: 'number', + max: RGBLedMatrix.prototype.maxRows, + min: 1, + func: 'changeRows', + }, + columns: { + name: 'Columns', + type: 'number', + max: RGBLedMatrix.prototype.maxColumns, + min: 1, + func: 'changeColumns', + }, + ledSize: { + name: 'LED Size', + type: 'number', + max: RGBLedMatrix.prototype.maxLedSize, + min: 1, + func: 'changeLedSize', + }, + showGrid: { + name: 'Toggle Grid', + type: 'button', + max: RGBLedMatrix.prototype.maxLedSize, + min: 1, + func: 'toggleGrid', + }, +} +RGBLedMatrix.prototype.objectType = 'RGBLedMatrix' diff --git a/v1/src/simulator/src/modules/Random.js b/v1/src/simulator/src/modules/Random.js new file mode 100644 index 00000000..01c9427b --- /dev/null +++ b/v1/src/simulator/src/modules/Random.js @@ -0,0 +1,172 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { fillText, lineTo, moveTo, correctWidth, rect2 } from '../canvasApi' +/** + * @class + * Random + * Random is used to generate random value. + * It has 2 input node: + * clock and max random output value + * @extends CircuitElement + * @param {number} x - x coord of element + * @param {number} y - y coord of element + * @param {Scope=} scope - the ciruit in which we want the Element + * @param {string=} dir - direcion in which element has to drawn + * @category modules + */ +import { colors } from '../themer/themer' + +export default class Random extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT', bitWidth = 1) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['Random'].push(this); + */ + this.directionFixed = true + this.setDimensions(20, 20) + this.rectangleObject = true + this.currentRandomNo = 0 + this.clockInp = new Node(-20, +10, 0, this, 1, 'Clock') + this.maxValue = new Node(-20, -10, 0, this, this.bitWidth, 'MaxValue') + this.output = new Node(20, -10, 1, this, this.bitWidth, 'RandomValue') + this.prevClockState = 0 + this.wasClicked = false + } + + /** + * @memberof Random + * return true if clock is connected and if maxValue is set or unconnected. + */ + isResolvable() { + if ( + this.clockInp.value != undefined && + (this.maxValue.value != undefined || + this.maxValue.connections.length == 0) + ) { + return true + } + return false + } + + newBitWidth(bitWidth) { + this.bitWidth = bitWidth + this.maxValue.bitWidth = bitWidth + this.output.bitWidth = bitWidth + } + + /** + * @memberof Random + * Edge triggered when the clock state changes a + * Random number is generated less then the maxValue. + */ + resolve() { + var maxValue = this.maxValue.connections.length + ? this.maxValue.value + 1 + : 2 << (this.bitWidth - 1) + if (this.clockInp.value != undefined) { + if (this.clockInp.value != this.prevClockState) { + if (this.clockInp.value == 1) { + this.currentRandomNo = Math.floor(Math.random() * maxValue) + } + this.prevClockState = this.clockInp.value + } + } + if (this.output.value != this.currentRandomNo) { + this.output.value = this.currentRandomNo + simulationArea.simulationQueue.add(this.output) + } + } + + customSave() { + var data = { + nodes: { + clockInp: findNode(this.clockInp), + maxValue: findNode(this.maxValue), + output: findNode(this.output), + }, + constructorParamaters: [this.direction, this.bitWidth], + } + return data + } + + customDraw() { + var ctx = simulationArea.context + // + ctx.fillStyle = colors['fill'] + ctx.strokeStyle = colors['stroke'] + ctx.beginPath() + var xx = this.x + var yy = this.y + ctx.font = '20px Raleway' + ctx.fillStyle = colors['input_text'] + ctx.textAlign = 'center' + fillText(ctx, this.currentRandomNo.toString(10), this.x, this.y + 5) + ctx.fill() + ctx.beginPath() + moveTo(ctx, -20, 5, xx, yy, this.direction) + lineTo(ctx, -15, 10, xx, yy, this.direction) + lineTo(ctx, -20, 15, xx, yy, this.direction) + ctx.stroke() + } + + // Draws the element in the subcircuit. Used in layout mode + subcircuitDraw(xOffset = 0, yOffset = 0) { + var ctx = simulationArea.context + var xx = this.subcircuitMetadata.x + xOffset + var yy = this.subcircuitMetadata.y + yOffset + + ctx.beginPath() + ctx.font = '20px Raleway' + ctx.fillStyle = 'green' + ctx.textAlign = 'center' + fillText(ctx, this.currentRandomNo.toString(16), xx + 10, yy + 17) + ctx.fill() + + ctx.beginPath() + ctx.lineWidth = correctWidth(1) + rect2(ctx, 0, 0, 20, 20, xx, yy, this.direction) + ctx.stroke() + + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected == this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.fillStyle = 'rgba(255, 255, 32,0.6)' + ctx.fill() + } + } + + static moduleVerilog() { + return ` + module Random(val, clk, max); + parameter WIDTH = 1; + output reg [WIDTH-1:0] val; + input clk; + input [WIDTH-1:0] max; + + always @ (posedge clk) + if (^max === 1'bX) + val = $urandom_range(0, {WIDTH{1'b1}}); + else + val = $urandom_range(0, max); + endmodule + ` + } +} + +Random.prototype.tooltipText = 'Random ToolTip : Random Selected.' + +Random.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/2input?id=random' + +Random.prototype.objectType = 'Random' + +Random.prototype.canShowInSubcircuit = true +Random.prototype.layoutProperties = { + rightDimensionX: 20, + leftDimensionX: 0, + upDimensionY: 0, + downDimensionY: 20, +} diff --git a/v1/src/simulator/src/modules/Rectangle.js b/v1/src/simulator/src/modules/Rectangle.js new file mode 100644 index 00000000..09bf240f --- /dev/null +++ b/v1/src/simulator/src/modules/Rectangle.js @@ -0,0 +1,159 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, rect } from '../canvasApi' +/** + * @class + * Rectangle + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {number=} rows - number of rows + * @param {number=} cols - number of columns. + * @category modules + */ +export default class Rectangle extends CircuitElement { + constructor(x, y, scope = globalScope, rows = 15, cols = 20) { + super(x, y, scope, 'RIGHT', 1) + /* this is done in this.baseSetup() now + this.scope['Rectangle'].push(this); + */ + this.directionFixed = true + this.fixedBitWidth = true + this.rectangleObject = false + this.cols = cols || parseInt(prompt('Enter cols:'), 10) + this.rows = rows || parseInt(prompt('Enter rows:'), 10) + this.setSize() + } + + /** + * @memberof Rectangle + * @param {number} size - new size of rows + */ + changeRowSize(size) { + if (size === undefined || size < 5 || size > 1000) return + if (this.rows === size) return + this.rows = parseInt(size, 10) + this.setSize() + return this + } + + /** + * @memberof Rectangle + * @param {number} size - new size of columns + */ + changeColSize(size) { + if (size === undefined || size < 5 || size > 1000) return + if (this.cols === size) return + this.cols = parseInt(size, 10) + this.setSize() + return this + } + + /** + * @memberof Rectangle + * listener function to change direction of rectangle + * @param {string} dir - new direction + */ + keyDown3(dir) { + if (dir === 'ArrowRight') { + this.changeColSize(this.cols + 2) + } + if (dir === 'ArrowLeft') { + this.changeColSize(this.cols - 2) + } + if (dir === 'ArrowDown') { + this.changeRowSize(this.rows + 2) + } + if (dir === 'ArrowUp') { + this.changeRowSize(this.rows - 2) + } + } + + /** + * @memberof Rectangle + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.rows, this.cols], + } + return data + } + + /** + * @memberof Rectangle + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = 'rgba(0,0,0,1)' + ctx.setLineDash([5 * globalScope.scale, 5 * globalScope.scale]) + ctx.lineWidth = correctWidth(1.5) + const xx = this.x + const yy = this.y + rect(ctx, xx, yy, this.elementWidth, this.elementHeight) + ctx.stroke() + + if ( + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.fillStyle = 'rgba(255, 255, 32,0.1)' + ctx.fill() + } + ctx.setLineDash([]) + } + + /** + * @memberof Rectangle + * function to reset or (internally) set size + */ + setSize() { + this.elementWidth = this.cols * 10 + this.elementHeight = this.rows * 10 + this.upDimensionY = 0 + this.leftDimensionX = 0 + this.rightDimensionX = this.elementWidth + this.downDimensionY = this.elementHeight + } +} + +/** + * @memberof Rectangle + * Help Tip + * @type {string} + * @category modules + */ +Rectangle.prototype.tooltipText = + 'Rectangle ToolTip : Used to Box the Circuit or area you want to highlight.' +Rectangle.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/7annotation?id=rectangle' +Rectangle.prototype.propagationDelayFixed = true + +/** + * @memberof Rectangle + * Mutable properties of the element + * @type {JSON} + * @category modules + */ +Rectangle.prototype.mutableProperties = { + cols: { + name: 'Columns', + type: 'number', + max: '1000', + min: '5', + func: 'changeColSize', + }, + rows: { + name: 'Rows', + type: 'number', + max: '1000', + min: '5', + func: 'changeRowSize', + }, +} +Rectangle.prototype.objectType = 'Rectangle' diff --git a/v1/src/simulator/src/modules/SevenSegDisplay.js b/v1/src/simulator/src/modules/SevenSegDisplay.js new file mode 100644 index 00000000..eedd26f0 --- /dev/null +++ b/v1/src/simulator/src/modules/SevenSegDisplay.js @@ -0,0 +1,321 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { + colorToRGBA, + correctWidth, + lineTo, + moveTo, + rect, + rect2, + validColor, +} from '../canvasApi' + +/** + * @class + * SevenSegDisplay + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @category modules + */ +export default class SevenSegDisplay extends CircuitElement { + constructor(x, y, scope = globalScope, color = 'Red') { + super(x, y, scope, 'RIGHT', 1) + /* this is done in this.baseSetup() now + this.scope['SevenSegDisplay'].push(this); + */ + this.fixedBitWidth = true + this.directionFixed = true + this.setDimensions(30, 50) + + this.g = new Node(-20, -50, 0, this) + this.f = new Node(-10, -50, 0, this) + this.a = new Node(+10, -50, 0, this) + this.b = new Node(+20, -50, 0, this) + this.e = new Node(-20, +50, 0, this) + this.d = new Node(-10, +50, 0, this) + this.c = new Node(+10, +50, 0, this) + this.dot = new Node(+20, +50, 0, this) + this.direction = 'RIGHT' + this.color = color + this.actualColor = color + } + + /** + * @memberof SevenSegDisplay + * fn to change the color of SevenSegmentDisplay + * @return {JSON} + */ + changeColor(value) { + if (validColor(value)) { + if (value.trim() === '') { + this.color = 'Red' + this.actualColor = 'rgba(255, 0, 0, 1)' + } else { + this.color = value + const temp = colorToRGBA(value) + this.actualColor = `rgba(${temp[0]},${temp[1]},${temp[2]}, ${temp[3]})` + } + } + } + /** + * @memberof SevenSegDisplay + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.color], + nodes: { + g: findNode(this.g), + f: findNode(this.f), + a: findNode(this.a), + b: findNode(this.b), + d: findNode(this.d), + e: findNode(this.e), + c: findNode(this.c), + dot: findNode(this.dot), + }, + } + return data + } + + /** + * @memberof SevenSegDisplay + * helper function to create save Json Data of object + */ + customDrawSegment(x1, y1, x2, y2, color) { + if (color === undefined) color = 'lightgrey' + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = color + ctx.lineWidth = correctWidth(5) + const xx = this.x + const yy = this.y + moveTo(ctx, x1, y1, xx, yy, this.direction) + lineTo(ctx, x2, y2, xx, yy, this.direction) + ctx.closePath() + ctx.stroke() + } + + /** + * @memberof SevenSegDisplay + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + const xx = this.x + const yy = this.y + this.customDrawSegment( + 18, + -3, + 18, + -38, + ['lightgrey', this.actualColor][this.b.value] + ) + this.customDrawSegment( + 18, + 3, + 18, + 38, + ['lightgrey', this.actualColor][this.c.value] + ) + this.customDrawSegment( + -18, + -3, + -18, + -38, + ['lightgrey', this.actualColor][this.f.value] + ) + this.customDrawSegment( + -18, + 3, + -18, + 38, + ['lightgrey', this.actualColor][this.e.value] + ) + this.customDrawSegment( + -17, + -38, + 17, + -38, + ['lightgrey', this.actualColor][this.a.value] + ) + this.customDrawSegment( + -17, + 0, + 17, + 0, + ['lightgrey', this.actualColor][this.g.value] + ) + this.customDrawSegment( + -15, + 38, + 17, + 38, + ['lightgrey', this.actualColor][this.d.value] + ) + ctx.beginPath() + const dotColor = + ['lightgrey', this.actualColor][this.dot.value] || 'lightgrey' + ctx.strokeStyle = dotColor + rect(ctx, xx + 22, yy + 42, 2, 2) + ctx.stroke() + } + + subcircuitDrawSegment(x1, y1, x2, y2, color, xxSegment, yySegment) { + if (color == undefined) color = 'lightgrey' + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = color + ctx.lineWidth = correctWidth(3) + var xx = xxSegment + var yy = yySegment + moveTo(ctx, x1, y1, xx, yy, this.direction) + lineTo(ctx, x2, y2, xx, yy, this.direction) + ctx.closePath() + ctx.stroke() + } + + // Draws the element in the subcuircuit. Used in layout mode + subcircuitDraw(xOffset = 0, yOffset = 0) { + var ctx = simulationArea.context + + var xx = this.subcircuitMetadata.x + xOffset + var yy = this.subcircuitMetadata.y + yOffset + + this.subcircuitDrawSegment( + 10, + -20, + 10, + -38, + ['lightgrey', this.actualColor][this.b.value], + xx, + yy + ) + this.subcircuitDrawSegment( + 10, + -17, + 10, + 1, + ['lightgrey', this.actualColor][this.c.value], + xx, + yy + ) + this.subcircuitDrawSegment( + -10, + -20, + -10, + -38, + ['lightgrey', this.actualColor][this.f.value], + xx, + yy + ) + this.subcircuitDrawSegment( + -10, + -17, + -10, + 1, + ['lightgrey', this.actualColor][this.e.value], + xx, + yy + ) + this.subcircuitDrawSegment( + -8, + -38, + 8, + -38, + ['lightgrey', this.actualColor][this.a.value], + xx, + yy + ) + this.subcircuitDrawSegment( + -8, + -18, + 8, + -18, + ['lightgrey', this.actualColor][this.g.value], + xx, + yy + ) + this.subcircuitDrawSegment( + -8, + 1, + 8, + 1, + ['lightgrey', this.actualColor][this.d.value], + xx, + yy + ) + + ctx.beginPath() + var dotColor = + ['lightgrey', this.actualColor][this.dot.value] || 'lightgrey' + ctx.strokeStyle = dotColor + rect(ctx, xx + 13, yy + 5, 1, 1) + ctx.stroke() + + ctx.beginPath() + ctx.strokeStyle = 'black' + ctx.lineWidth = correctWidth(1) + rect2(ctx, -15, -42, 33, 51, xx, yy, this.direction) + ctx.stroke() + + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected == this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.fillStyle = 'rgba(255, 255, 32,0.6)' + ctx.fill() + } + } + generateVerilog() { + return ` + always @ (*) + $display("SevenSegDisplay:${this.verilogLabel}.abcdefg. = %b%b%b%b%b%b%b%b}", + ${this.a.verilogLabel}, ${this.b.verilogLabel}, ${this.c.verilogLabel}, ${this.d.verilogLabel}, ${this.e.verilogLabel}, ${this.f.verilogLabel}, ${this.g.verilogLabel}, ${this.dot.verilogLabel});` + } +} + +/** + * @memberof SevenSegDisplay + * Help Tip + * @type {string} + * @category modules + */ +SevenSegDisplay.prototype.tooltipText = + 'Seven Display ToolTip: Consists of 7+1 single bit inputs.' + +/** + * @memberof SevenSegDisplay + * Help URL + * @type {string} + * @category modules + */ +SevenSegDisplay.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/3output?id=sevensegdisplay' +SevenSegDisplay.prototype.objectType = 'SevenSegDisplay' +SevenSegDisplay.prototype.canShowInSubcircuit = true +SevenSegDisplay.prototype.layoutProperties = { + rightDimensionX: 20, + leftDimensionX: 15, + upDimensionY: 42, + downDimensionY: 10, +} + +/** + * @memberof SevenSegDisplay + * Mutable properties of the element + * @type {JSON} + * @category modules + */ +SevenSegDisplay.prototype.mutableProperties = { + color: { + name: 'Color: ', + type: 'text', + func: 'changeColor', + }, +} diff --git a/v1/src/simulator/src/modules/SixteenSegDisplay.js b/v1/src/simulator/src/modules/SixteenSegDisplay.js new file mode 100644 index 00000000..944ba443 --- /dev/null +++ b/v1/src/simulator/src/modules/SixteenSegDisplay.js @@ -0,0 +1,489 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { + colorToRGBA, + correctWidth, + lineTo, + moveTo, + rect, + rect2, + validColor, +} from '../canvasApi' +import { changeInputSize } from '../modules' +/** + * @class + * SixteenSegDisplay + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @category modules + */ +export default class SixteenSegDisplay extends CircuitElement { + constructor(x, y, scope = globalScope, color = 'Red') { + super(x, y, scope, 'RIGHT', 16) + /* this is done in this.baseSetup() now + this.scope['SixteenSegDisplay'].push(this); + */ + this.fixedBitWidth = true + this.directionFixed = true + this.setDimensions(30, 50) + this.input1 = new Node(0, -50, 0, this, 16) + this.dot = new Node(0, 50, 0, this, 1) + this.direction = 'RIGHT' + this.color = color + this.actualColor = color + } + + /** + * @memberof SixteenSegDisplay + * fn to change the color of SixteenSegmentDisplay + * @return {JSON} + */ + changeColor(value) { + if (validColor(value)) { + if (value.trim() === '') { + this.color = 'Red' + this.actualColor = 'rgba(255, 0, 0, 1)' + } else { + this.color = value + const temp = colorToRGBA(value) + this.actualColor = `rgba(${temp[0]},${temp[1]},${temp[2]}, ${temp[3]})` + } + } + } + + /** + * @memberof SixteenSegDisplay + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.color], + nodes: { + input1: findNode(this.input1), + dot: findNode(this.dot), + }, + } + return data + } + + /** + * @memberof SixteenSegDisplay + * function to draw element + */ + customDrawSegment(x1, y1, x2, y2, color) { + if (color === undefined) color = 'lightgrey' + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = color + ctx.lineWidth = correctWidth(4) + const xx = this.x + const yy = this.y + moveTo(ctx, x1, y1, xx, yy, this.direction) + lineTo(ctx, x2, y2, xx, yy, this.direction) + ctx.closePath() + ctx.stroke() + } + + /** + * @memberof SixteenSegDisplay + * function to draw element + */ + customDrawSegmentSlant(x1, y1, x2, y2, color) { + if (color === undefined) color = 'lightgrey' + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = color + ctx.lineWidth = correctWidth(3) + const xx = this.x + const yy = this.y + moveTo(ctx, x1, y1, xx, yy, this.direction) + lineTo(ctx, x2, y2, xx, yy, this.direction) + ctx.closePath() + ctx.stroke() + } + + /** + * @memberof SixteenSegDisplay + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + const xx = this.x + const yy = this.y + const color = ['lightgrey', this.actualColor] + const { value } = this.input1 + this.customDrawSegment( + -20, + -38, + 0, + -38, + ['lightgrey', this.actualColor][(value >> 15) & 1] + ) // a1 + this.customDrawSegment( + 20, + -38, + 0, + -38, + ['lightgrey', this.actualColor][(value >> 14) & 1] + ) // a2 + this.customDrawSegment( + 21.5, + -2, + 21.5, + -36, + ['lightgrey', this.actualColor][(value >> 13) & 1] + ) // b + this.customDrawSegment( + 21.5, + 2, + 21.5, + 36, + ['lightgrey', this.actualColor][(value >> 12) & 1] + ) // c + this.customDrawSegment( + -20, + 38, + 0, + 38, + ['lightgrey', this.actualColor][(value >> 11) & 1] + ) // d1 + this.customDrawSegment( + 20, + 38, + 0, + 38, + ['lightgrey', this.actualColor][(value >> 10) & 1] + ) // d2 + this.customDrawSegment( + -21.5, + 2, + -21.5, + 36, + ['lightgrey', this.actualColor][(value >> 9) & 1] + ) // e + this.customDrawSegment( + -21.5, + -36, + -21.5, + -2, + ['lightgrey', this.actualColor][(value >> 8) & 1] + ) // f + this.customDrawSegment( + -20, + 0, + 0, + 0, + ['lightgrey', this.actualColor][(value >> 7) & 1] + ) // g1 + this.customDrawSegment( + 20, + 0, + 0, + 0, + ['lightgrey', this.actualColor][(value >> 6) & 1] + ) // g2 + this.customDrawSegmentSlant( + 0, + 0, + -21, + -37, + ['lightgrey', this.actualColor][(value >> 5) & 1] + ) // h + this.customDrawSegment( + 0, + -2, + 0, + -36, + ['lightgrey', this.actualColor][(value >> 4) & 1] + ) // i + this.customDrawSegmentSlant( + 0, + 0, + 21, + -37, + ['lightgrey', this.actualColor][(value >> 3) & 1] + ) // j + this.customDrawSegmentSlant( + 0, + 0, + 21, + 37, + ['lightgrey', this.actualColor][(value >> 2) & 1] + ) // k + this.customDrawSegment( + 0, + 2, + 0, + 36, + ['lightgrey', this.actualColor][(value >> 1) & 1] + ) // l + this.customDrawSegmentSlant( + 0, + 0, + -21, + 37, + ['lightgrey', this.actualColor][(value >> 0) & 1] + ) // m + ctx.beginPath() + const dotColor = + ['lightgrey', this.actualColor][this.dot.value] || 'lightgrey' + ctx.strokeStyle = dotColor + rect(ctx, xx + 22, yy + 42, 2, 2) + ctx.stroke() + } + + subcircuitDrawSegment(x1, y1, x2, y2, color, xxSegment, yySegment) { + if (color == undefined) color = 'lightgrey' + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = color + ctx.lineWidth = correctWidth(3) + var xx = xxSegment + var yy = yySegment + moveTo(ctx, x1, y1, xx, yy, this.direction) + lineTo(ctx, x2, y2, xx, yy, this.direction) + ctx.closePath() + ctx.stroke() + } + + subcircuitDrawSegmentSlant(x1, y1, x2, y2, color, xxSegment, yySegment) { + if (color == undefined) color = 'lightgrey' + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = color + ctx.lineWidth = correctWidth(2) + var xx = xxSegment + var yy = yySegment + moveTo(ctx, x1, y1, xx, yy, this.direction) + lineTo(ctx, x2, y2, xx, yy, this.direction) + ctx.closePath() + ctx.stroke() + } + + // Draws the element in the subcircuit. Used in layout mode + subcircuitDraw(xOffset = 0, yOffset = 0) { + var ctx = simulationArea.context + + var xx = this.subcircuitMetadata.x + xOffset + var yy = this.subcircuitMetadata.y + yOffset + + var color = ['lightgrey', this.actualColor] + var value = this.input1.value + + this.subcircuitDrawSegment( + -10, + -38, + 0, + -38, + ['lightgrey', this.actualColor][(value >> 15) & 1], + xx, + yy + ) //a1 + this.subcircuitDrawSegment( + 10, + -38, + 0, + -38, + ['lightgrey', this.actualColor][(value >> 14) & 1], + xx, + yy + ) //a2 + this.subcircuitDrawSegment( + 11.5, + -19, + 11.5, + -36, + ['lightgrey', this.actualColor][(value >> 13) & 1], + xx, + yy + ) //b + this.subcircuitDrawSegment( + 11.5, + 2, + 11.5, + -15, + ['lightgrey', this.actualColor][(value >> 12) & 1], + xx, + yy + ) //c + this.subcircuitDrawSegment( + -10, + 4, + 0, + 4, + ['lightgrey', this.actualColor][(value >> 11) & 1], + xx, + yy + ) //d1 + this.subcircuitDrawSegment( + 10, + 4, + 0, + 4, + ['lightgrey', this.actualColor][(value >> 10) & 1], + xx, + yy + ) //d2 + this.subcircuitDrawSegment( + -11.5, + 2, + -11.5, + -15, + ['lightgrey', this.actualColor][(value >> 9) & 1], + xx, + yy + ) //e + this.subcircuitDrawSegment( + -11.5, + -36, + -11.5, + -19, + ['lightgrey', this.actualColor][(value >> 8) & 1], + xx, + yy + ) //f + this.subcircuitDrawSegment( + -10, + -17, + 0, + -17, + ['lightgrey', this.actualColor][(value >> 7) & 1], + xx, + yy + ) //g1 + this.subcircuitDrawSegment( + 10, + -17, + 0, + -17, + ['lightgrey', this.actualColor][(value >> 6) & 1], + xx, + yy + ) //g2 + this.subcircuitDrawSegmentSlant( + 0, + -17, + -9, + -36, + ['lightgrey', this.actualColor][(value >> 5) & 1], + xx, + yy + ) //h + this.subcircuitDrawSegment( + 0, + -36, + 0, + -19, + ['lightgrey', this.actualColor][(value >> 4) & 1], + xx, + yy + ) //i + this.subcircuitDrawSegmentSlant( + 0, + -17, + 9, + -36, + ['lightgrey', this.actualColor][(value >> 3) & 1], + xx, + yy + ) //j + this.subcircuitDrawSegmentSlant( + 0, + -17, + 9, + 0, + ['lightgrey', this.actualColor][(value >> 2) & 1], + xx, + yy + ) //k + this.subcircuitDrawSegment( + 0, + -17, + 0, + 2, + ['lightgrey', this.actualColor][(value >> 1) & 1], + xx, + yy + ) //l + this.subcircuitDrawSegmentSlant( + 0, + -17, + -9, + 0, + ['lightgrey', this.actualColor][(value >> 0) & 1], + xx, + yy + ) //m + + ctx.beginPath() + var dotColor = + ['lightgrey', this.actualColor][this.dot.value] || 'lightgrey' + ctx.strokeStyle = dotColor + rect(ctx, xx + 13, yy + 5, 1, 1) + ctx.stroke() + + ctx.beginPath() + ctx.strokeStyle = 'black' + ctx.lineWidth = correctWidth(1) + rect2(ctx, -15, -42, 33, 51, xx, yy, this.direction) + ctx.stroke() + + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected == this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.fillStyle = 'rgba(255, 255, 32,0.6)' + ctx.fill() + } + } + generateVerilog() { + return ` + always @ (*) + $display("SixteenSegDisplay:{${this.input1.verilogLabel},${this.dot.verilogLabel}} = {%16b,%1b}", ${this.input1.verilogLabel}, ${this.dot.verilogLabel});` + } +} + +/** + * @memberof SixteenSegDisplay + * Help Tip + * @type {string} + * @category modules + */ +SixteenSegDisplay.prototype.tooltipText = + 'Sixteen Display ToolTip: Consists of 16+1 bit inputs.' + +/** + * @memberof SixteenSegDisplay + * Help URL + * @type {string} + * @category modules + */ +SixteenSegDisplay.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/3output?id=sixteensegdisplay' +SixteenSegDisplay.prototype.objectType = 'SixteenSegDisplay' +SixteenSegDisplay.prototype.canShowInSubcircuit = true +SixteenSegDisplay.prototype.layoutProperties = { + rightDimensionX: 20, + leftDimensionX: 15, + upDimensionY: 42, + downDimensionY: 10, +} + +/** + * @memberof SixteenSegDisplay + * Mutable properties of the element + * @type {JSON} + * @category modules + */ +SixteenSegDisplay.prototype.mutableProperties = { + color: { + name: 'Color: ', + type: 'text', + func: 'changeColor', + }, +} diff --git a/v1/src/simulator/src/modules/Splitter.js b/v1/src/simulator/src/modules/Splitter.js new file mode 100644 index 00000000..ffcb296c --- /dev/null +++ b/v1/src/simulator/src/modules/Splitter.js @@ -0,0 +1,359 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, fillText2 } from '../canvasApi' +import { colors } from '../themer/themer' + +function extractBits(num, start, end) { + return (num << (32 - end)) >>> (32 - (end - start + 1)) +} + +/** + * @class + * Splitter + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @param {number=} bitWidthSplit - number of input nodes + * @category modules + */ +export default class Splitter extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + bitWidth = undefined, + bitWidthSplit = undefined + ) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['Splitter'].push(this); + */ + this.rectangleObject = false + + this.bitWidthSplit = + bitWidthSplit || + ( + prompt('Enter bitWidth Split') || + `${'1 '.repeat((this.bitWidth || 1) - 1)}1` + ) + .split(' ') + .filter((lambda) => lambda !== '') + .map((lambda) => parseInt(lambda, 10) || 1) + this.splitCount = this.bitWidthSplit.length + + this.setDimensions(10, (this.splitCount - 1) * 10 + 10) + this.yOffset = (this.splitCount / 2 - 1) * 20 + + this.inp1 = new Node(-10, 10 + this.yOffset, 0, this, this.bitWidth) + + this.outputs = [] + // this.prevOutValues=new Array(this.splitCount) + for (let i = 0; i < this.splitCount; i++) { + this.outputs.push( + new Node( + 20, + i * 20 - this.yOffset - 20, + 0, + this, + this.bitWidthSplit[i] + ) + ) + } + + this.prevInpValue = undefined + } + + /** + * @memberof Splitter + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [ + this.direction, + this.bitWidth, + this.bitWidthSplit, + ], + nodes: { + outputs: this.outputs.map(findNode), + inp1: findNode(this.inp1), + }, + } + return data + } + + /** + * @memberof Splitter + * fn to remove proporgation delay. + * @return {JSON} + */ + removePropagation() { + if (this.inp1.value === undefined) { + let i = 0 + for (i = 0; i < this.outputs.length; i++) { + // False Hit + if (this.outputs[i].value === undefined) return + } + for (i = 0; i < this.outputs.length; i++) { + if (this.outputs[i].value !== undefined) { + this.outputs[i].value = undefined + simulationArea.simulationQueue.add(this.outputs[i]) + } + } + } else if (this.inp1.value !== undefined) { + this.inp1.value = undefined + simulationArea.simulationQueue.add(this.inp1) + } + this.prevInpValue = undefined + } + + /** + * @memberof Splitter + * Checks if the element is resolvable + * @return {boolean} + */ + isResolvable() { + let resolvable = false + if (this.inp1.value !== this.prevInpValue) { + if (this.inp1.value !== undefined) return true + return false + } + let i + for (i = 0; i < this.splitCount; i++) { + if (this.outputs[i].value === undefined) break + } + if (i === this.splitCount) resolvable = true + return resolvable + } + + /** + * @memberof Splitter + * resolve output values based on inputData + */ + resolve() { + if (this.isResolvable() === false) { + return + } + if ( + this.inp1.value !== undefined && + this.inp1.value !== this.prevInpValue + ) { + let bitCount = 1 + for (let i = 0; i < this.splitCount; i++) { + const bitSplitValue = extractBits( + this.inp1.value, + bitCount, + bitCount + this.bitWidthSplit[i] - 1 + ) + if (this.outputs[i].value !== bitSplitValue) { + if (this.outputs[i].value !== bitSplitValue) { + this.outputs[i].value = bitSplitValue + simulationArea.simulationQueue.add(this.outputs[i]) + } + } + bitCount += this.bitWidthSplit[i] + } + } else { + let n = 0 + for (let i = this.splitCount - 1; i >= 0; i--) { + n <<= this.bitWidthSplit[i] + n += this.outputs[i].value + } + if (this.inp1.value !== n >>> 0) { + this.inp1.value = n >>> 0 + simulationArea.simulationQueue.add(this.inp1) + } + // else if (this.inp1.value !== n) { + // } + } + this.prevInpValue = this.inp1.value + } + + /** + * @memberof Splitter + * fn to reset values of splitter + */ + reset() { + this.prevInpValue = undefined + } + + /** + * @memberof Splitter + * fn to process verilog of the element + * @return {JSON} + */ + processVerilog() { + if ( + this.inp1.verilogLabel !== '' && + this.outputs[0].verilogLabel === '' + ) { + let bitCount = 0 + for (let i = 0; i < this.splitCount; i++) { + // let bitSplitValue = extractBits(this.inp1.value, bitCount, bitCount + this.bitWidthSplit[i] - 1); + if (this.bitWidthSplit[i] > 1) { + const label = `${this.inp1.verilogLabel}[ ${ + bitCount + this.bitWidthSplit[i] - 1 + }:${bitCount}]` + } else { + const label = `${this.inp1.verilogLabel}[${bitCount}]` + } + if (this.outputs[i].verilogLabel !== label) { + this.outputs[i].verilogLabel = label + this.scope.stack.push(this.outputs[i]) + } + bitCount += this.bitWidthSplit[i] + } + } else if ( + this.inp1.verilogLabel === '' && + this.outputs[0].verilogLabel !== '' + ) { + const label = `{${this.outputs + .map((x) => x.verilogLabel) + .join(',')}}` + if (this.inp1.verilogLabel !== label) { + this.inp1.verilogLabel = label + this.scope.stack.push(this.inp1) + } + } + } + + /** + * @memberof Splitter + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + // + ctx.strokeStyle = [colors['splitter'], 'brown'][ + ((this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this)) + 0 + ] + ctx.lineWidth = correctWidth(3) + const xx = this.x + const yy = this.y + ctx.beginPath() + moveTo(ctx, -10, 10 + this.yOffset, xx, yy, this.direction) + lineTo(ctx, 0, 0 + this.yOffset, xx, yy, this.direction) + lineTo( + ctx, + 0, + -20 * (this.splitCount - 1) + this.yOffset, + xx, + yy, + this.direction + ) + let bitCount = 0 + for (let i = this.splitCount - 1; i >= 0; i--) { + moveTo(ctx, 0, -20 * i + this.yOffset, xx, yy, this.direction) + lineTo(ctx, 20, -20 * i + this.yOffset, xx, yy, this.direction) + } + ctx.stroke() + ctx.beginPath() + ctx.fillStyle = colors['text'] + for (let i = this.splitCount - 1; i >= 0; i--) { + var splitLabel + if (this.bitWidthSplit[this.splitCount - i - 1] == 1) + splitLabel = `${bitCount}` + else + splitLabel = `${bitCount}:${ + bitCount + this.bitWidthSplit[this.splitCount - i - 1] - 1 + }` + + fillText2( + ctx, + splitLabel, + 16, + -20 * i + this.yOffset + 10, + xx, + yy, + this.direction + ) + bitCount += this.bitWidthSplit[this.splitCount - i - 1] + } + ctx.fill() + } + + processVerilog() { + // Combiner + if (this.inp1.verilogLabel == '') { + this.isSplitter = false + this.inp1.verilogLabel = this.verilogLabel + '_cmb' + if ( + !this.scope.verilogWireList[this.bitWidth].contains( + this.inp1.verilogLabel + ) + ) + this.scope.verilogWireList[this.bitWidth].push( + this.inp1.verilogLabel + ) + this.scope.stack.push(this.inp1) + return + } + + // Splitter + this.isSplitter = true + for (var j = 0; j < this.outputs.length; j++) { + var bitCount = 0 + var inpLabel = this.inp1.verilogLabel + // Already Split Regex + var re = /^(.*)\[(\d*):(\d*)\]$/ + if (re.test(inpLabel)) { + var matches = inpLabel.match(re) + inpLabel = matches[1] + bitCount = parseInt(matches[3]) + } + for (var i = 0; i < this.splitCount; i++) { + if (this.bitWidthSplit[i] > 1) + var label = + inpLabel + + '[' + + (bitCount + this.bitWidthSplit[i] - 1) + + ':' + + bitCount + + ']' + else var label = inpLabel + '[' + bitCount + ']' + if (this.outputs[i].verilogLabel != label) { + this.outputs[i].verilogLabel = label + this.scope.stack.push(this.outputs[i]) + } + bitCount += this.bitWidthSplit[i] + } + } + } + //added to generate Splitter INPUTS + generateVerilog() { + var res = '' + if (!this.isSplitter) { + res += 'assign ' + this.inp1.verilogLabel + ' = {' + for (var i = this.outputs.length - 1; i > 0; i--) + res += this.outputs[i].verilogLabel + ',' + res += this.outputs[0].verilogLabel + '};' + } + return res + } +} + +/** + * @memberof Splitter + * Help Tip + * @type {string} + * @category modules + */ +Splitter.prototype.tooltipText = + 'Splitter ToolTip: Split multiBit Input into smaller bitwidths or vice versa.' + +/** + * @memberof Splitter + * Help URL + * @type {string} + * @category modules + */ +Splitter.prototype.helplink = 'https://docs.circuitverse.org/#/chapter4/8misc?id=splitter' +Splitter.prototype.objectType = 'Splitter' diff --git a/v1/src/simulator/src/modules/SquareRGBLed.js b/v1/src/simulator/src/modules/SquareRGBLed.js new file mode 100644 index 00000000..b93e0bb6 --- /dev/null +++ b/v1/src/simulator/src/modules/SquareRGBLed.js @@ -0,0 +1,215 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, rect2 } from '../canvasApi' + +/** + * @class + * SquareRGBLed + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} pinLength - pins per node. + * @category modules + */ +export default class SquareRGBLed extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'UP', pinLength = 1) { + super(x, y, scope, dir, 8) + /* this is done in this.baseSetup() now + this.scope['SquareRGBLed'].push(this); + */ + this.rectangleObject = false + this.setDimensions(15, 15) + this.pinLength = pinLength === undefined ? 1 : pinLength + const nodeX = -10 - 10 * pinLength + this.inp1 = new Node(nodeX, -10, 0, this, 8, 'R') + this.inp2 = new Node(nodeX, 0, 0, this, 8, 'G') + this.inp3 = new Node(nodeX, 10, 0, this, 8, 'B') + this.inp = [this.inp1, this.inp2, this.inp3] + this.labelDirection = 'UP' + this.fixedBitWidth = true + + // eslint-disable-next-line no-shadow + this.changePinLength = function (pinLength) { + if (pinLength === undefined) return + pinLength = parseInt(pinLength, 10) + if (pinLength < 0 || pinLength > 1000) return + + // Calculate the new position of the LED, so the nodes will stay in the same place. + const diff = 10 * (pinLength - this.pinLength) + // eslint-disable-next-line no-nested-ternary + const diffX = + this.direction === 'LEFT' + ? -diff + : this.direction === 'RIGHT' + ? diff + : 0 + // eslint-disable-next-line no-nested-ternary + const diffY = + this.direction === 'UP' + ? -diff + : this.direction === 'DOWN' + ? diff + : 0 + + // Build a new LED with the new values; preserve label properties too. + const obj = new SquareRGBLed( + this.x + diffX, + this.y + diffY, + this.scope, + this.direction, + pinLength + ) + obj.label = this.label + obj.labelDirection = this.labelDirection + + this.cleanDelete() + simulationArea.lastSelected = obj + return obj + } + + this.mutableProperties = { + pinLength: { + name: 'Pin Length', + type: 'number', + max: '1000', + min: '0', + func: 'changePinLength', + }, + } + } + + /** + * @memberof SquareRGBLed + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.direction, this.pinLength], + nodes: { + inp1: findNode(this.inp1), + inp2: findNode(this.inp2), + inp3: findNode(this.inp3), + }, + } + return data + } + + /** + * @memberof SquareRGBLed + * function to draw element + */ + customDraw() { + const ctx = simulationArea.context + const xx = this.x + const yy = this.y + const r = this.inp1.value + const g = this.inp2.value + const b = this.inp3.value + + const colors = ['rgb(174,20,20)', 'rgb(40,174,40)', 'rgb(0,100,255)'] + for (let i = 0; i < 3; i++) { + const x = -10 - 10 * this.pinLength + const y = i * 10 - 10 + ctx.lineWidth = correctWidth(3) + + // A gray line, which makes it easy on the eyes when the pin length is large + ctx.beginPath() + ctx.lineCap = 'butt' + ctx.strokeStyle = 'rgb(227, 228, 229)' + moveTo(ctx, -15, y, xx, yy, this.direction) + lineTo(ctx, x + 10, y, xx, yy, this.direction) + ctx.stroke() + + // A colored line, so people know which pin does what. + ctx.lineCap = 'round' + ctx.beginPath() + ctx.strokeStyle = colors[i] + moveTo(ctx, x + 10, y, xx, yy, this.direction) + lineTo(ctx, x, y, xx, yy, this.direction) + ctx.stroke() + } + + ctx.strokeStyle = '#d3d4d5' + ctx.fillStyle = + r === undefined && g === undefined && b === undefined + ? 'rgb(227, 228, 229)' + : `rgb(${r || 0}, ${g || 0}, ${b || 0})` + ctx.lineWidth = correctWidth(1) + ctx.beginPath() + rect2(ctx, -15, -15, 30, 30, xx, yy, this.direction) + ctx.stroke() + + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.fillStyle = 'rgba(255, 255, 32)' + } + + ctx.fill() + } + // Draws the element in the subcuircuit. Used in layout mode + subcircuitDraw(xOffset = 0, yOffset = 0) { + var ctx = simulationArea.context + var xx = this.subcircuitMetadata.x + xOffset + var yy = this.subcircuitMetadata.y + yOffset + var r = this.inp1.value + var g = this.inp2.value + var b = this.inp3.value + + ctx.strokeStyle = '#d3d4d5' + ctx.fillStyle = + r === undefined && g === undefined && b === undefined + ? 'rgb(227, 228, 229)' + : 'rgb(' + (r || 0) + ', ' + (g || 0) + ', ' + (b || 0) + ')' + ctx.lineWidth = correctWidth(1) + ctx.beginPath() + rect2(ctx, 0, 0, 15, 15, xx, yy, this.direction) + ctx.stroke() + + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected == this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.fillStyle = 'rgba(255, 255, 32)' + } + + ctx.fill() + } + + generateVerilog() { + return this.generateVerilog.call(this) + } +} + +/** + * @memberof SquareRGBLed + * Help Tip + * @type {string} + * @category modules + */ +SquareRGBLed.prototype.tooltipText = + 'Square RGB Led ToolTip: RGB Led inputs 8 bit values for the colors RED, GREEN and BLUE.' + +/** + * @memberof SquareRGBLed + * Help URL + * @type {string} + * @category modules + */ +SquareRGBLed.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/3output?id=squarergbled' +SquareRGBLed.prototype.objectType = 'SquareRGBLed' +SquareRGBLed.prototype.canShowInSubcircuit = true +SquareRGBLed.prototype.layoutProperties = { + rightDimensionX: 15, + leftDimensionX: 0, + upDimensionY: 15, + downDimensionY: 0, +} diff --git a/v1/src/simulator/src/modules/Stepper.js b/v1/src/simulator/src/modules/Stepper.js new file mode 100644 index 00000000..c6cfbd3f --- /dev/null +++ b/v1/src/simulator/src/modules/Stepper.js @@ -0,0 +1,102 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { fillText } from '../canvasApi' +import { changeInputSize } from '../modules' +/** + * @class + * Stepper + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bitwidth of element + * @category modules + */ +import { colors } from '../themer/themer' + +export default class Stepper extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT', bitWidth = 8) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['Stepper'].push(this); + */ + this.setDimensions(20, 20) + + this.output1 = new Node(20, 0, 1, this, bitWidth) + this.state = 0 + } + + /** + * @memberof Stepper + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + var data = { + constructorParamaters: [this.direction, this.bitWidth], + nodes: { + output1: findNode(this.output1), + }, + values: { + state: this.state, + }, + } + return data + } + + /** + * @memberof Stepper + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.beginPath() + ctx.font = '20px Raleway' + ctx.fillStyle = colors['input_text'] + ctx.textAlign = 'center' + fillText(ctx, this.state.toString(16), this.x, this.y + 5) + ctx.fill() + } + + /** + * @memberof Stepper + * resolve output values based on inputData + */ + resolve() { + this.state = Math.min(this.state, (1 << this.bitWidth) - 1) + this.output1.value = this.state + simulationArea.simulationQueue.add(this.output1) + } + + /** + * Listener function for increasing value of state + * @memberof Stepper + * @param {string} key - the key pressed + */ + keyDown2(key) { + if (this.state < 1 << this.bitWidth && (key === '+' || key === '=')) + this.state++ + if (this.state > 0 && (key === '_' || key === '-')) this.state-- + } +} + +/** + * @memberof Stepper + * Help Tip + * @type {string} + * @category modules + */ +Stepper.prototype.tooltipText = + 'Stepper ToolTip: Increase/Decrease value by selecting the stepper and using +/- keys.' + +/** + * @memberof Stepper + * Help URL + * @type {string} + * @category modules + */ +Stepper.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/2input?id=stepper' +Stepper.prototype.objectType = 'Stepper' diff --git a/v1/src/simulator/src/modules/Text.js b/v1/src/simulator/src/modules/Text.js new file mode 100644 index 00000000..7248c575 --- /dev/null +++ b/v1/src/simulator/src/modules/Text.js @@ -0,0 +1,208 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { rect2, fillText } from '../canvasApi' +/** + * @class + * Text + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} label - label of element + * @param {number=} fontSize - font size + * @category modules + */ +import { colors } from '../themer/themer' +import { copy, paste } from '../events' + +export default class Text extends CircuitElement { + constructor(x, y, scope = globalScope, label = '', fontSize = 14) { + super(x, y, scope, 'RIGHT', 1) + /* this is done in this.baseSetup() now + this.scope['Text'].push(this); + */ + // this.setDimensions(15, 15); + this.fixedBitWidth = true + this.directionFixed = true + this.labelDirectionFixed = true + this.setLabel(label) + this.setFontSize(fontSize) + } + + /** + * @memberof Text + * function for setting text inside the element + * @param {string=} str - the label + */ + setLabel(str = '') { + this.label = str + var ctx = simulationArea.context + ctx.font = `${this.fontSize}px Raleway` + this.leftDimensionX = 10 + this.rightDimensionX = ctx.measureText(this.label).width + 10 + this.setTextboxSize() + } + + /** + * @memberof Text + * function for setting font size inside the element + * @param {number=} str - the font size + */ + setFontSize(fontSize = 14) { + this.fontSize = fontSize + var ctx = simulationArea.context + ctx.font = `${this.fontSize}px Raleway` + this.setTextboxSize() + } + + setTextboxSize() { + this.leftDimensionX = 10 + var maxWidth = 0 + var labels = this.label.split('\n') + var ctx = simulationArea.context + labels.forEach( + (l) => (maxWidth = Math.max(maxWidth, ctx.measureText(l).width)) + ) + this.rightDimensionX = maxWidth + 10 + this.downDimensionY = labels.length * this.fontSize + } + + /** + * @memberof Text + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.label, this.fontSize], + } + return data + } + + /** + * @memberof Text + * Listener function for Text Box + * @param {string} key - the label + */ + keyDown(key) { + if (simulationArea.controlDown && (key === 'c' || key === 'C')) { + const textToPutOnClipboard = copy([this]) + navigator.clipboard.writeText(textToPutOnClipboard) + localStorage.setItem('clipboardData', textToPutOnClipboard) + } else if (simulationArea.controlDown && (key === 'v' || key === 'V')) { + paste(localStorage.getItem('clipboardData')) + } else if (key.length === 1) { + if (this.label === 'Enter Text Here') { + this.setLabel(key) + } else { + this.setLabel(this.label + key) + } + } else if (key === 'Backspace') { + if (this.label === 'Enter Text Here') { + this.setLabel('') + } else { + this.setLabel(this.label.slice(0, -1)) + } + } else if (key === 'Enter') { + if (this.label === 'Enter Text Here') { + this.setLabel('') + } else { + this.setLabel(this.label + '\n') + } + } + $('[name=setLabel]').val(this.label) + } + + /** + * @memberof Text + * Function for drawing text box + */ + draw() { + // + if (this.label.length === 0 && simulationArea.lastSelected !== this) + this.delete() + var ctx = simulationArea.context + ctx.strokeStyle = colors['stroke'] + ctx.lineWidth = 1 + const xx = this.x + const yy = this.y + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.beginPath() + ctx.fillStyle = colors['fill'] + const magicDimension = this.fontSize - 14 + rect2( + ctx, + -this.leftDimensionX, + -this.upDimensionY - magicDimension, + this.leftDimensionX + this.rightDimensionX, + this.upDimensionY + this.downDimensionY + magicDimension, + this.x, + this.y, + 'RIGHT' + ) + ctx.fillStyle = 'rgba(255, 255, 32,0.1)' + ctx.fill() + ctx.stroke() + } + ctx.beginPath() + ctx.textAlign = 'left' + ctx.fillStyle = colors['text'] + var labels = this.label.split('\n') + for (var i = 0; i < labels.length; i++) { + fillText( + ctx, + labels[i], + xx, + yy + 5 + i * this.fontSize, + this.fontSize + ) + } + ctx.fill() + } +} + +/** + * @memberof Text + * Help Tip + * @type {string} + * @category modules + */ +Text.prototype.tooltipText = 'Text ToolTip: Use this to document your circuit.' + +/** + * @memberof Text + * Help URL + * @type {string} + * @category modules + */ +Text.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/7annotation?id=text' + +/** + * @memberof Text + * Mutable properties of the element + * @type {JSON} + * @category modules + */ +Text.prototype.mutableProperties = { + fontSize: { + name: 'Font size: ', + type: 'number', + max: '84', + min: '14', + func: 'setFontSize', + }, + label: { + name: 'Text: ', + type: 'textarea', + func: 'setLabel', + }, +} +Text.prototype.disableLabel = true +Text.prototype.objectType = 'Text' +Text.prototype.propagationDelayFixed = true diff --git a/v1/src/simulator/src/modules/TriState.js b/v1/src/simulator/src/modules/TriState.js new file mode 100644 index 00000000..e4048fc9 --- /dev/null +++ b/v1/src/simulator/src/modules/TriState.js @@ -0,0 +1,129 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, arc } from '../canvasApi' +import { changeInputSize } from '../modules' +/** + * @class + * TriState + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +import { colors } from '../themer/themer' + +export default class TriState extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT', bitWidth = 1) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['TriState'].push(this); + */ + this.rectangleObject = false + this.setDimensions(15, 15) + + this.inp1 = new Node(-10, 0, 0, this) + this.output1 = new Node(20, 0, 1, this) + this.state = new Node(0, 0, 0, this, 1, 'Enable') + } + + // TriState.prototype.propagationDelay=10000; + + /** + * @memberof TriState + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.direction, this.bitWidth], + nodes: { + output1: findNode(this.output1), + inp1: findNode(this.inp1), + state: findNode(this.state), + }, + } + return data + } + + /** + * @memberof TriState + * function to change bitwidth of the element + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + this.inp1.bitWidth = bitWidth + this.output1.bitWidth = bitWidth + this.bitWidth = bitWidth + } + + /** + * @memberof TriState + * resolve output values based on inputData + */ + resolve() { + if (this.isResolvable() === false) { + return + } + + if (this.state.value === 1) { + if (this.output1.value !== this.inp1.value) { + this.output1.value = this.inp1.value // >>>0)<<(32-this.bitWidth))>>>(32-this.bitWidth); + simulationArea.simulationQueue.add(this.output1) + } + simulationArea.contentionPending.clean(this) + } else if ( + this.output1.value !== undefined && + !simulationArea.contentionPending.contains(this) + ) { + this.output1.value = undefined + simulationArea.simulationQueue.add(this.output1) + } + simulationArea.contentionPending.clean(this) + } + + /** + * @memberof TriState + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.strokeStyle = colors['stroke'] + ctx.lineWidth = correctWidth(3) + const xx = this.x + const yy = this.y + ctx.beginPath() + ctx.fillStyle = colors['fill'] + moveTo(ctx, -10, -15, xx, yy, this.direction) + lineTo(ctx, 20, 0, xx, yy, this.direction) + lineTo(ctx, -10, 15, xx, yy, this.direction) + ctx.closePath() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + } + + generateVerilog() { + return `assign ${this.output1.verilogLabel} = (${this.state.verilogLabel}!=0) ? ${this.inp1.verilogLabel} : ${this.inp1.bitWidth}'b?;` + } +} + +/** + * @memberof TriState + * Help Tip + * @type {string} + * @category modules + */ +TriState.prototype.tooltipText = + 'TriState ToolTip : Effectively removes the output from the circuit.' +TriState.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/8misc?id=tristate-buffer' +TriState.prototype.objectType = 'TriState' diff --git a/v1/src/simulator/src/modules/Tunnel.js b/v1/src/simulator/src/modules/Tunnel.js new file mode 100644 index 00000000..7e3f96c5 --- /dev/null +++ b/v1/src/simulator/src/modules/Tunnel.js @@ -0,0 +1,351 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, rect2, fillText } from '../canvasApi' +import plotArea from '../plotArea' +import { showError } from '../utils' +/** + * @class + * Tunnel + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @param {string=} identifier - number of input nodes + * @category modules + */ +import { colors } from '../themer/themer' + +export default class Tunnel extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'LEFT', + bitWidth = 1, + identifier + ) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['Tunnel'].push(this); + */ + this.rectangleObject = false + this.centerElement = true + this.xSize = 10 + this.plotValues = [] + this.inp1 = new Node(0, 0, 0, this) + this.checked = false // has this tunnel been checked by another paired tunnel + this.setIdentifier(identifier || 'T') + this.setBounds() + // if tunnels with this's identifier exist, then set the bitwidth to that of those tunnels + if (this.scope.tunnelList[this.identifier].length > 0) { + this.newBitWidth(this.scope.tunnelList[this.identifier][0].bitWidth) + } + } + + /** + * @memberof Tunnel + * function to change direction of Tunnel + * @param {string} dir - new direction + */ + newDirection(dir) { + if (this.direction === dir) return + this.direction = dir + this.setBounds() + } + + setBounds() { + let xRotate = 0 + let yRotate = 0 + if (this.direction === 'LEFT') { + xRotate = 0 + yRotate = 0 + } else if (this.direction === 'RIGHT') { + xRotate = 120 - this.xSize + yRotate = 0 + } else if (this.direction === 'UP') { + xRotate = 60 - this.xSize / 2 + yRotate = -20 + } else { + xRotate = 60 - this.xSize / 2 + yRotate = 20 + } + + this.leftDimensionX = Math.abs(-120 + xRotate + this.xSize) + this.upDimensionY = Math.abs(-20 + yRotate) + this.rightDimensionX = Math.abs(xRotate) + this.downDimensionY = Math.abs(20 + yRotate) + + // rect2(ctx, -120 + xRotate + this.xSize, -20 + yRotate, 120 - this.xSize, 40, xx, yy, "RIGHT"); + } + + /** + * @memberof Tunnel + * resolve output values based on inputData + */ + resolve() { + // Don't check for paired tunnels' value if already checked by another paired tunnel (O(n)) + if (this.checked) { + this.checked = false + return + } + // Check for bitwidth error since it bypasses node's resolve() function which usually checks bitwidths + for (const tunnel of this.scope.tunnelList[this.identifier]) { + if (tunnel.inp1.bitWidth !== this.inp1.bitWidth) { + this.inp1.highlighted = true + tunnel.inp1.highlighted = true + showError( + `BitWidth Error: ${this.inp1.bitWidth} and ${tunnel.inp1.bitWidth}` + ) + } + if (tunnel.inp1.value !== this.inp1.value) { + tunnel.inp1.value = this.inp1.value + simulationArea.simulationQueue.add(tunnel.inp1) + } + if (tunnel !== this) tunnel.checked = true + } + } + + /** + * @memberof Tunnel + * function to set tunnel value + * @param {Scope} scope - tunnel value + */ + updateScope(scope) { + this.scope = scope + this.inp1.updateScope(scope) + this.setIdentifier(this.identifier) + } + + /** + * @memberof Tunnel + * function to set plot value + */ + setPlotValue() { + return + const time = plotArea.stopWatch.ElapsedMilliseconds + if ( + this.plotValues.length && + this.plotValues[this.plotValues.length - 1][0] === time + ) { + this.plotValues.pop() + } + + if (this.plotValues.length === 0) { + this.plotValues.push([time, this.inp1.value]) + return + } + + if ( + this.plotValues[this.plotValues.length - 1][1] === this.inp1.value + ) { + return + } + this.plotValues.push([time, this.inp1.value]) + } + + /** + * @memberof Tunnel + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [ + this.direction, + this.bitWidth, + this.identifier, + ], + nodes: { + inp1: findNode(this.inp1), + }, + values: { + identifier: this.identifier, + }, + } + return data + } + + /** + * @memberof Tunnel + * function to set tunnel identifier value + * @param {string=} id - id so that every link is unique + */ + setIdentifier(id = '') { + if (id.length === 0) return + if (this.scope.tunnelList[this.identifier]) + this.scope.tunnelList[this.identifier].clean(this) + this.identifier = id + if (this.scope.tunnelList[this.identifier]) + this.scope.tunnelList[this.identifier].push(this) + else this.scope.tunnelList[this.identifier] = [this] + + // Change the bitwidth to be same as the other elements with this.identifier + if ( + this.scope.tunnelList[this.identifier] && + this.scope.tunnelList[this.identifier].length > 1 + ) { + this.bitWidth = this.inp1.bitWidth = + this.scope.tunnelList[this.identifier][0].bitWidth + } + + const len = this.identifier.length + if (len === 1) this.xSize = 40 + else if (len > 1 && len < 4) this.xSize = 20 + else this.xSize = 0 + this.setBounds() + } + + /** + * @memberof Tunnel + * delete the tunnel element + */ + delete() { + this.scope.Tunnel.clean(this) + this.scope.tunnelList[this.identifier].clean(this) + super.delete() + } + + /** + * @memberof Tunnel + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.lineWidth = correctWidth(1) + const xx = this.x + const yy = this.y + + let xRotate = 0 + let yRotate = 0 + if (this.direction === 'LEFT') { + xRotate = 0 + yRotate = 0 + } else if (this.direction === 'RIGHT') { + xRotate = 120 - this.xSize + yRotate = 0 + } else if (this.direction === 'UP') { + xRotate = 60 - this.xSize / 2 + yRotate = -20 + } else { + xRotate = 60 - this.xSize / 2 + yRotate = 20 + } + + rect2( + ctx, + -120 + xRotate + this.xSize, + -20 + yRotate, + 120 - this.xSize, + 40, + xx, + yy, + 'RIGHT' + ) + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.fillStyle = colors['hover_select'] + } + ctx.fill() + ctx.stroke() + + ctx.font = '14px Raleway' + this.xOff = ctx.measureText(this.identifier).width + ctx.beginPath() + rect2( + ctx, + -105 + xRotate + this.xSize, + -11 + yRotate, + this.xOff + 10, + 23, + xx, + yy, + 'RIGHT' + ) + ctx.fillStyle = '#eee' + ctx.strokeStyle = '#ccc' + ctx.fill() + ctx.stroke() + + ctx.beginPath() + ctx.textAlign = 'center' + ctx.fillStyle = 'black' + fillText( + ctx, + this.identifier, + xx - 100 + this.xOff / 2 + xRotate + this.xSize, + yy + 6 + yRotate, + 14 + ) + ctx.fill() + + ctx.beginPath() + ctx.font = '30px Raleway' + ctx.textAlign = 'center' + ctx.fillStyle = ['blue', 'red'][+(this.inp1.value === undefined)] + if (this.inp1.value !== undefined) { + fillText( + ctx, + this.inp1.value.toString(16), + xx - 23 + xRotate, + yy + 8 + yRotate, + 25 + ) + } else { + fillText(ctx, 'x', xx - 23 + xRotate, yy + 8 + yRotate, 25) + } + ctx.fill() + } + + /** + * Overridden from CircuitElement. Sets all paired tunnels' bitwidths for syncronization + * @param {number} bitWidth - bitwidth to set to + */ + newBitWidth(bitWidth) { + for (let tunnel of this.scope.tunnelList[this.identifier]) { + if (tunnel.fixedBitWidth) continue + if (tunnel.bitWidth === undefined) continue + if (tunnel.bitWidth < 1) continue + tunnel.bitWidth = bitWidth + for (let i = 0; i < tunnel.nodeList.length; i++) { + tunnel.nodeList[i].bitWidth = bitWidth + } + } + } +} + +/** + * @memberof Tunnel + * Help Tip + * @type {string} + * @category modules + */ +Tunnel.prototype.tooltipText = 'Tunnel ToolTip : Tunnel Selected.' +Tunnel.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/8misc?id=tunnel' + +Tunnel.prototype.overrideDirectionRotation = true + +/** + * @memberof Tunnel + * Mutable properties of the element + * @type {JSON} + * @category modules + */ +Tunnel.prototype.mutableProperties = { + identifier: { + name: 'Debug Flag identifier', + type: 'text', + maxlength: '5', + func: 'setIdentifier', + }, +} +Tunnel.prototype.objectType = 'Tunnel' diff --git a/v1/src/simulator/src/modules/TwoComplement.js b/v1/src/simulator/src/modules/TwoComplement.js new file mode 100644 index 00000000..ace62ec7 --- /dev/null +++ b/v1/src/simulator/src/modules/TwoComplement.js @@ -0,0 +1,102 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, fillText, drawCircle2 } from '../canvasApi' +import { changeInputSize } from '../modules' +/** + * @class + * TwoComplement + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +import { colors } from '../themer/themer' + +export default class TwoComplement extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT', bitWidth = 1) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['TwoComplement'].push(this); + */ + this.rectangleObject = false + this.setDimensions(15, 15) + this.inp1 = new Node(-10, 0, 0, this, this.bitWidth, 'input stream') + this.output1 = new Node(20, 0, 1, this, this.bitWidth, "2's complement") + } + + /** + * @memberof TwoComplement + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.direction, this.bitWidth], + nodes: { + output1: findNode(this.output1), + inp1: findNode(this.inp1), + }, + } + return data + } + + /** + * @memberof TwoComplement + * resolve output values based on inputData + */ + resolve() { + if (this.isResolvable() === false) { + return + } + let output = + ((~this.inp1.value >>> 0) << (32 - this.bitWidth)) >>> + (32 - this.bitWidth) + output += 1 + this.output1.value = + (output << (32 - this.bitWidth)) >>> (32 - this.bitWidth) + simulationArea.simulationQueue.add(this.output1) + } + + /** + * @memberof TwoComplement + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.strokeStyle = colors['stroke'] + ctx.lineWidth = correctWidth(3) + const xx = this.x + const yy = this.y + ctx.beginPath() + ctx.fillStyle = 'black' + fillText(ctx, "2'", xx, yy, 10) + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.beginPath() + drawCircle2(ctx, 5, 0, 15, xx, yy, this.direction) + ctx.stroke() + } + + generateVerilog() { + return `assign ${this.output1.verilogLabel} = ~${this.inp1.verilogLabel} + 1;` + } +} + +/** + * @memberof TwoComplement + * Help Tip + * @type {string} + * @category modules + */ +TwoComplement.prototype.tooltipText = + "Two's Complement Tooltip : Calculates the two's complement" +TwoComplement.prototype.objectType = 'TwoComplement' diff --git a/v1/src/simulator/src/modules/VariableLed.js b/v1/src/simulator/src/modules/VariableLed.js new file mode 100644 index 00000000..a32cb5aa --- /dev/null +++ b/v1/src/simulator/src/modules/VariableLed.js @@ -0,0 +1,205 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { + correctWidth, + lineTo, + moveTo, + arc, + drawCircle2, + colorToRGBA, + validColor, +} from '../canvasApi' +import { changeInputSize } from '../modules' +/** + * @class + * VariableLed + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @category modules + */ +import { colors } from '../themer/themer' + +export default class VariableLed extends CircuitElement { + constructor(x, y, scope = globalScope, color = 'Red') { + // Calling base class constructor + + super(x, y, scope, 'UP', 8) + /* this is done in this.baseSetup() now + this.scope['VariableLed'].push(this); + */ + this.rectangleObject = false + this.setDimensions(10, 20) + this.inp1 = new Node(-40, 0, 0, this, 8) + this.directionFixed = true + this.fixedBitWidth = true + this.color = color + const temp = colorToRGBA(this.color) + this.actualColor = `rgba(${temp[0]},${temp[1]},${temp[2]},${0.8})` + } + + /** + * @memberof VariableLed + * fn to change the color of VariableLed + * @return {JSON} + */ + changeColor(value) { + if (validColor(value)) { + if (value.trim() === '') { + this.color = 'Red' + this.actualColor = 'rgba(255, 0, 0, 1)' + } else { + this.color = value + const temp = colorToRGBA(value) + this.actualColor = `rgba(${temp[0]},${temp[1]},${temp[2]}, ${temp[3]})` + } + } + } + + /** + * @memberof VariableLed + * fn to set the rgba value of the color + * @return {JSON} + */ + createRGBA(alpha = 1) { + const len = this.actualColor.length + const temp = this.actualColor + .split('') + .slice(5, len - 1) + .join('') + .split(',') + if (alpha.toString() === 'NaN') + return `rgba(${temp[0]}, ${temp[1]}, ${temp[2]}, 1)` + return `rgba(${temp[0]},${temp[1]},${temp[2]},${alpha})` + } + + /** + * @memberof VariableLed + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.color], + nodes: { + inp1: findNode(this.inp1), + }, + } + return data + } + + /** + * @memberof VariableLed + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + + const xx = this.x + const yy = this.y + + ctx.strokeStyle = '#353535' + ctx.lineWidth = correctWidth(3) + ctx.beginPath() + moveTo(ctx, -20, 0, xx, yy, this.direction) + lineTo(ctx, -40, 0, xx, yy, this.direction) + ctx.stroke() + const c = this.inp1.value + const alpha = c / 255 + ctx.strokeStyle = '#090a0a' + ctx.fillStyle = [this.createRGBA(alpha), 'rgba(227, 228, 229, 0.8)'][ + (c === undefined || c === 0) + 0 + ] + ctx.lineWidth = correctWidth(1) + + ctx.beginPath() + + moveTo(ctx, -20, -9, xx, yy, this.direction) + lineTo(ctx, 0, -9, xx, yy, this.direction) + arc(ctx, 0, 0, 9, -Math.PI / 2, Math.PI / 2, xx, yy, this.direction) + lineTo(ctx, -20, 9, xx, yy, this.direction) + /* lineTo(ctx,-18,12,xx,yy,this.direction); + arc(ctx,0,0,Math.sqrt(468),((Math.PI/2) + Math.acos(12/Math.sqrt(468))),((-Math.PI/2) - Math.asin(18/Math.sqrt(468))),xx,yy,this.direction); + + */ + lineTo(ctx, -20, -9, xx, yy, this.direction) + ctx.stroke() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + } + + // Draws the element in the subcuircuit. Used in layout mode + subcircuitDraw(xOffset = 0, yOffset = 0) { + var ctx = simulationArea.context + + var xx = this.subcircuitMetadata.x + xOffset + var yy = this.subcircuitMetadata.y + yOffset + + var c = this.inp1.value + var alpha = c / 255 + ctx.strokeStyle = '#090a0a' + ctx.fillStyle = [this.createRGBA(alpha), 'rgba(227, 228, 229, 0.8)'][ + (c === undefined || c == 0) + 0 + ] + ctx.lineWidth = correctWidth(1) + + ctx.beginPath() + drawCircle2(ctx, 0, 0, 6, xx, yy, this.direction) + ctx.stroke() + + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected == this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = 'rgba(255, 255, 32,0.8)' + ctx.fill() + } + + generateVerilog() { + return ` + always @ (*) + $display("VeriableLed:${this.inp1.verilogLabel}=%d", ${this.inp1.verilogLabel});` + } +} + +/** + * @memberof VariableLed + * Help Tip + * @type {string} + * @category modules + */ +VariableLed.prototype.tooltipText = + 'Variable Led ToolTip: Variable LED inputs an 8 bit value and glows with a proportional intensity.' + +/** + * @memberof VariableLed + * Mutable properties of the element + * @type {JSON} + * @category modules + */ +VariableLed.prototype.mutableProperties = { + color: { + name: 'Color: ', + type: 'text', + func: 'changeColor', + }, +} + +/** + * @memberof VariableLed + * Help URL + * @type {string} + * @category modules + */ +VariableLed.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/3output?id=variableled' +VariableLed.prototype.objectType = 'VariableLed' +VariableLed.prototype.canShowInSubcircuit = true diff --git a/v1/src/simulator/src/modules/XnorGate.js b/v1/src/simulator/src/modules/XnorGate.js new file mode 100644 index 00000000..dd93e734 --- /dev/null +++ b/v1/src/simulator/src/modules/XnorGate.js @@ -0,0 +1,199 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { + correctWidth, + bezierCurveTo, + moveTo, + arc2, + drawCircle2, +} from '../canvasApi' +import { gateGenerateVerilog } from '../utils' + +import { changeInputSize } from '../modules' +/** + * @class + * XnorGate + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} inputLength - number of input nodes + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +import { colors } from '../themer/themer' + +export default class XnorGate extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + inputs = 2, + bitWidth = 1 + ) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['XnorGate'].push(this); + */ + this.rectangleObject = false + this.setDimensions(15, 20) + + this.inp = [] + this.inputSize = inputs + + if (inputs % 2 === 1) { + for (let i = 0; i < inputs / 2 - 1; i++) { + const a = new Node(-20, -10 * (i + 1), 0, this) + this.inp.push(a) + } + let a = new Node(-20, 0, 0, this) + this.inp.push(a) + for (let i = inputs / 2 + 1; i < inputs; i++) { + a = new Node(-20, 10 * (i + 1 - inputs / 2 - 1), 0, this) + this.inp.push(a) + } + } else { + for (let i = 0; i < inputs / 2; i++) { + const a = new Node(-20, -10 * (i + 1), 0, this) + this.inp.push(a) + } + for (let i = inputs / 2; i < inputs; i++) { + const a = new Node(-20, 10 * (i + 1 - inputs / 2), 0, this) + this.inp.push(a) + } + } + this.output1 = new Node(30, 0, 1, this) + } + + /** + * @memberof XnorGate + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [ + this.direction, + this.inputSize, + this.bitWidth, + ], + nodes: { + inp: this.inp.map(findNode), + output1: findNode(this.output1), + }, + } + return data + } + + /** + * @memberof XnorGate + * resolve output values based on inputData + */ + resolve() { + let result = this.inp[0].value || 0 + if (this.isResolvable() === false) { + return + } + for (let i = 1; i < this.inputSize; i++) + result ^= this.inp[i].value || 0 + result = + ((~result >>> 0) << (32 - this.bitWidth)) >>> (32 - this.bitWidth) + this.output1.value = result + simulationArea.simulationQueue.add(this.output1) + } + + /** + * @memberof XnorGate + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.strokeStyle = colors['stroke'] + ctx.lineWidth = correctWidth(3) + + const xx = this.x + const yy = this.y + ctx.beginPath() + ctx.fillStyle = colors['fill'] + moveTo(ctx, -10, -20, xx, yy, this.direction, true) + bezierCurveTo(0, -20, +15, -10, 20, 0, xx, yy, this.direction) + bezierCurveTo( + 0 + 15, + 0 + 10, + 0, + 0 + 20, + -10, + +20, + xx, + yy, + this.direction + ) + bezierCurveTo(0, 0, 0, 0, -10, -20, xx, yy, this.direction) + // arc(ctx, 0, 0, -20, (-Math.PI / 2), (Math.PI / 2), xx, yy, this.direction); + ctx.closePath() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + ctx.beginPath() + arc2( + ctx, + -35, + 0, + 25, + 1.7 * Math.PI, + 0.3 * Math.PI, + xx, + yy, + this.direction + ) + ctx.stroke() + ctx.beginPath() + drawCircle2(ctx, 25, 0, 5, xx, yy, this.direction) + ctx.stroke() + } + + generateVerilog() { + return gateGenerateVerilog.call(this, '^', true) + } +} + +/** + * @memberof XnorGate + * @type {boolean} + * @category modules + */ +XnorGate.prototype.alwaysResolve = true + +/** + * @memberof XnorGate + * Help Tip + * @type {string} + * @category modules + */ +XnorGate.prototype.tooltipText = + 'Xnor Gate ToolTip : Logical complement of the XOR gate' + +/** + * @memberof XnorGate + * function to change input nodes of the element + * @category modules + */ +XnorGate.prototype.changeInputSize = changeInputSize + +/** + * @memberof XnorGate + * @type {string} + * @category modules + */ +XnorGate.prototype.verilogType = 'xnor' +XnorGate.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/4gates?id=xnor-gate' +XnorGate.prototype.objectType = 'XnorGate' diff --git a/v1/src/simulator/src/modules/XorGate.js b/v1/src/simulator/src/modules/XorGate.js new file mode 100644 index 00000000..74c5b0d8 --- /dev/null +++ b/v1/src/simulator/src/modules/XorGate.js @@ -0,0 +1,187 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, bezierCurveTo, moveTo, arc2 } from '../canvasApi' +import { changeInputSize } from '../modules' +import { gateGenerateVerilog } from '../utils' + +/** + * @class + * XorGate + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} inputs - number of input nodes + * @param {number=} bitWidth - bit width per node. + * @category modules + */ +import { colors } from '../themer/themer' + +export default class XorGate extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + inputs = 2, + bitWidth = 1 + ) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['XorGate'].push(this); + */ + this.rectangleObject = false + this.setDimensions(15, 20) + + this.inp = [] + this.inputSize = inputs + + if (inputs % 2 === 1) { + for (let i = 0; i < inputs / 2 - 1; i++) { + const a = new Node(-20, -10 * (i + 1), 0, this) + this.inp.push(a) + } + let a = new Node(-20, 0, 0, this) + this.inp.push(a) + for (let i = inputs / 2 + 1; i < inputs; i++) { + a = new Node(-20, 10 * (i + 1 - inputs / 2 - 1), 0, this) + this.inp.push(a) + } + } else { + for (let i = 0; i < inputs / 2; i++) { + const a = new Node(-20, -10 * (i + 1), 0, this) + this.inp.push(a) + } + for (let i = inputs / 2; i < inputs; i++) { + const a = new Node(-20, 10 * (i + 1 - inputs / 2), 0, this) + this.inp.push(a) + } + } + this.output1 = new Node(20, 0, 1, this) + } + + /** + * @memberof XorGate + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [ + this.direction, + this.inputSize, + this.bitWidth, + ], + nodes: { + inp: this.inp.map(findNode), + output1: findNode(this.output1), + }, + } + return data + } + + /** + * @memberof XorGate + * resolve output values based on inputData + */ + resolve() { + let result = this.inp[0].value || 0 + if (this.isResolvable() === false) { + return + } + for (let i = 1; i < this.inputSize; i++) + result ^= this.inp[i].value || 0 + + this.output1.value = result + simulationArea.simulationQueue.add(this.output1) + } + + /** + * @memberof XorGate + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + ctx.strokeStyle = colors['stroke'] + ctx.lineWidth = correctWidth(3) + + const xx = this.x + const yy = this.y + ctx.beginPath() + ctx.fillStyle = colors['fill'] + moveTo(ctx, -10, -20, xx, yy, this.direction, true) + bezierCurveTo(0, -20, +15, -10, 20, 0, xx, yy, this.direction) + bezierCurveTo( + 0 + 15, + 0 + 10, + 0, + 0 + 20, + -10, + +20, + xx, + yy, + this.direction + ) + bezierCurveTo(0, 0, 0, 0, -10, -20, xx, yy, this.direction) + // arc(ctx, 0, 0, -20, (-Math.PI / 2), (Math.PI / 2), xx, yy, this.direction); + ctx.closePath() + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + ctx.beginPath() + arc2( + ctx, + -35, + 0, + 25, + 1.7 * Math.PI, + 0.3 * Math.PI, + xx, + yy, + this.direction + ) + ctx.stroke() + } + + generateVerilog() { + return gateGenerateVerilog.call(this, '^') + } +} + +/** + * @memberof XorGate + * Help Tip + * @type {string} + * @category modules + */ +XorGate.prototype.tooltipText = 'Xor Gate Tooltip : Implements an exclusive OR.' + +/** + * @memberof XorGate + * @type {boolean} + * @category modules + */ +XorGate.prototype.alwaysResolve = true + +/** + * @memberof XorGate + * function to change input nodes of the element + * @category modules + */ +XorGate.prototype.changeInputSize = changeInputSize + +/** + * @memberof XorGate + * @type {string} + * @category modules + */ +XorGate.prototype.verilogType = 'xor' +XorGate.prototype.helplink = 'https://docs.circuitverse.org/#/chapter4/4gates?id=xor-gate' +XorGate.prototype.objectType = 'XorGate' diff --git a/v1/src/simulator/src/modules/verilogDivider.js b/v1/src/simulator/src/modules/verilogDivider.js new file mode 100644 index 00000000..cd6acaf5 --- /dev/null +++ b/v1/src/simulator/src/modules/verilogDivider.js @@ -0,0 +1,127 @@ +/* eslint-disable no-bitwise */ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' + +/** + * @class + * verilogDivider + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. modules + * @category modules + */ +export default class verilogDivider extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + bitWidth = 1, + outputBitWidth = 1 + ) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['verilogDivider'].push(this); + */ + this.setDimensions(20, 20) + this.outputBitWidth = outputBitWidth + this.inpA = new Node(-20, -10, 0, this, this.bitWidth, 'A') + this.inpB = new Node(-20, 0, 0, this, this.bitWidth, 'B') + this.quotient = new Node( + 20, + 0, + 1, + this, + this.outputBitWidth, + 'Quotient' + ) + this.remainder = new Node( + 20, + 0, + 1, + this, + this.outputBitWidth, + 'Remainder' + ) + } + + /** + * @memberof verilogDivider + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [ + this.direction, + this.bitWidth, + this.outputBitWidth, + ], + nodes: { + inpA: findNode(this.inpA), + inpB: findNode(this.inpB), + quotient: findNode(this.quotient), + remainder: findNode(this.remainder), + }, + } + return data + } + + /** + * @memberof verilogDivider + * Checks if the element is resolvable + * @return {boolean} + */ + isResolvable() { + return this.inpA.value !== undefined && this.inpB.value !== undefined + } + + /** + * @memberof verilogDivider + * function to change bitwidth of the element + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + this.bitWidth = bitWidth + this.inpA.bitWidth = bitWidth + this.inpB.bitWidth = bitWidth + this.quotient.bitWidth = bitWidth + this.remainder.bitWidth = bitWidth + } + + /** + * @memberof verilogDivider + * resolve output values based on inputData + */ + resolve() { + if (this.isResolvable() === false) { + return + } + const quotient = this.inpA.value / this.inpB.value + const remainder = this.inpA.value % this.inpB.value + this.remainder.value = + (remainder << (32 - this.outputBitWidth)) >>> + (32 - this.outputBitWidth) + this.quotient.value = + (quotient << (32 - this.outputBitWidth)) >>> + (32 - this.outputBitWidth) + simulationArea.simulationQueue.add(this.quotient) + simulationArea.simulationQueue.add(this.remainder) + } +} + +/** + * @memberof verilogDivider + * Help Tip + * @type {string} + * @category modules + */ +verilogDivider.prototype.tooltipText = + 'verilogDivider ToolTip : Performs addition of numbers.' +verilogDivider.prototype.helplink = + 'https://docs.circuitverse.org/#/miscellaneous?id=verilogDivider' +verilogDivider.prototype.objectType = 'verilogDivider' diff --git a/v1/src/simulator/src/modules/verilogMultiplier.js b/v1/src/simulator/src/modules/verilogMultiplier.js new file mode 100644 index 00000000..d428c036 --- /dev/null +++ b/v1/src/simulator/src/modules/verilogMultiplier.js @@ -0,0 +1,106 @@ +/* eslint-disable no-bitwise */ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' + +/** + * @class + * verilogMultiplier + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. modules + * @category modules + */ +export default class verilogMultiplier extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + bitWidth = 1, + outputBitWidth = 1 + ) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['verilogMultiplier'].push(this); + */ + this.setDimensions(20, 20) + this.outputBitWidth = outputBitWidth + this.inpA = new Node(-20, -10, 0, this, this.bitWidth, 'A') + this.inpB = new Node(-20, 0, 0, this, this.bitWidth, 'B') + this.product = new Node(20, 0, 1, this, this.outputBitWidth, 'Product') + } + + /** + * @memberof verilogMultiplier + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [ + this.direction, + this.bitWidth, + this.outputBitWidth, + ], + nodes: { + inpA: findNode(this.inpA), + inpB: findNode(this.inpB), + product: findNode(this.product), + }, + } + return data + } + + /** + * @memberof verilogMultiplier + * Checks if the element is resolvable + * @return {boolean} + */ + isResolvable() { + return this.inpA.value !== undefined && this.inpB.value !== undefined + } + + /** + * @memberof verilogMultiplier + * function to change bitwidth of the element + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + this.bitWidth = bitWidth + this.inpA.bitWidth = bitWidth + this.inpB.bitWidth = bitWidth + this.product.bitWidth = bitWidth + } + + /** + * @memberof verilogMultiplier + * resolve output values based on inputData + */ + resolve() { + if (this.isResolvable() === false) { + return + } + const product = this.inpA.value * this.inpB.value + + this.product.value = + (product << (32 - this.outputBitWidth)) >>> + (32 - this.outputBitWidth) + simulationArea.simulationQueue.add(this.product) + } +} + +/** + * @memberof verilogMultiplier + * Help Tip + * @type {string} + * @category modules + */ +verilogMultiplier.prototype.tooltipText = + 'verilogMultiplier ToolTip : Performs addition of numbers.' +verilogMultiplier.prototype.helplink = + 'https://docs.circuitverse.org/#/miscellaneous?id=verilogMultiplier' +verilogMultiplier.prototype.objectType = 'verilogMultiplier' diff --git a/v1/src/simulator/src/modules/verilogPower.js b/v1/src/simulator/src/modules/verilogPower.js new file mode 100644 index 00000000..ded6fdc1 --- /dev/null +++ b/v1/src/simulator/src/modules/verilogPower.js @@ -0,0 +1,106 @@ +/* eslint-disable no-bitwise */ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' + +/** + * @class + * verilogPower + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. modules + * @category modules + */ +export default class verilogPower extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + bitWidth = 1, + outputBitWidth = 1 + ) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['verilogPower'].push(this); + */ + this.setDimensions(20, 20) + this.outputBitWidth = outputBitWidth + this.inpA = new Node(-20, -10, 0, this, this.bitWidth, 'A') + this.inpB = new Node(-20, 0, 0, this, this.bitWidth, 'B') + this.answer = new Node(20, 0, 1, this, this.outputBitWidth, 'Answer') + } + + /** + * @memberof verilogPower + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [ + this.direction, + this.bitWidth, + this.outputBitWidth, + ], + nodes: { + inpA: findNode(this.inpA), + inpB: findNode(this.inpB), + answer: findNode(this.answer), + }, + } + return data + } + + /** + * @memberof verilogPower + * Checks if the element is resolvable + * @return {boolean} + */ + isResolvable() { + return this.inpA.value !== undefined && this.inpB.value !== undefined + } + + /** + * @memberof verilogPower + * function to change bitwidth of the element + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + this.bitWidth = bitWidth + this.inpA.bitWidth = bitWidth + this.inpB.bitWidth = bitWidth + this.answer.bitWidth = bitWidth + } + + /** + * @memberof verilogPower + * resolve output values based on inputData + */ + resolve() { + if (this.isResolvable() === false) { + return + } + const answer = Math.pow(this.inpA.value, this.inpB.value) + + this.answer.value = + (answer << (32 - this.outputBitWidth)) >>> + (32 - this.outputBitWidth) + simulationArea.simulationQueue.add(this.answer) + } +} + +/** + * @memberof verilogPower + * Help Tip + * @type {string} + * @category modules + */ +verilogPower.prototype.tooltipText = + 'verilogPower ToolTip : Performs addition of numbers.' +verilogPower.prototype.helplink = + 'https://docs.circuitverse.org/#/miscellaneous?id=verilogPower' +verilogPower.prototype.objectType = 'verilogPower' diff --git a/v1/src/simulator/src/modules/verilogShiftLeft.js b/v1/src/simulator/src/modules/verilogShiftLeft.js new file mode 100644 index 00000000..65a61a4c --- /dev/null +++ b/v1/src/simulator/src/modules/verilogShiftLeft.js @@ -0,0 +1,108 @@ +/* eslint-disable no-bitwise */ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' + +/** + * @class + * verilogShiftLeft + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. modules + * @category modules + */ +export default class verilogShiftLeft extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + bitWidth = 1, + outputBitWidth = 1 + ) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['verilogShiftLeft'].push(this); + */ + this.setDimensions(20, 20) + this.outputBitWidth = outputBitWidth + this.inp1 = new Node(-20, -10, 0, this, this.bitWidth, 'Input') + this.shiftInp = new Node(-20, 0, 0, this, this.bitWidth, 'ShiftInput') + this.output1 = new Node(20, 0, 1, this, this.outputBitWidth, 'Output') + } + + /** + * @memberof verilogShiftLeft + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [ + this.direction, + this.bitWidth, + this.outputBitWidth, + ], + nodes: { + inp1: findNode(this.inp1), + shiftInp: findNode(this.shiftInp), + output1: findNode(this.output1), + }, + } + return data + } + + /** + * @memberof verilogShiftLeft + * Checks if the element is resolvable + * @return {boolean} + */ + isResolvable() { + return ( + this.inp1.value !== undefined && this.shiftInp.value !== undefined + ) + } + + /** + * @memberof verilogShiftLeft + * function to change bitwidth of the element + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + this.bitWidth = bitWidth + this.inp1.bitWidth = bitWidth + this.shiftInp.bitWidth = bitWidth + this.output1.bitWidth = bitWidth + } + + /** + * @memberof verilogShiftLeft + * resolve output values based on inputData + */ + resolve() { + if (this.isResolvable() === false) { + return + } + const output1 = this.inp1.value << this.shiftInp.value + + this.output1.value = + (output1 << (32 - this.outputBitWidth)) >>> + (32 - this.outputBitWidth) + simulationArea.simulationQueue.add(this.output1) + } +} + +/** + * @memberof verilogShiftLeft + * Help Tip + * @type {string} + * @category modules + */ +verilogShiftLeft.prototype.tooltipText = + 'verilogShiftLeft ToolTip : Performs addition of numbers.' +verilogShiftLeft.prototype.helplink = + 'https://docs.circuitverse.org/#/miscellaneous?id=verilogShiftLeft' +verilogShiftLeft.prototype.objectType = 'verilogShiftLeft' diff --git a/v1/src/simulator/src/modules/verilogShiftRight.js b/v1/src/simulator/src/modules/verilogShiftRight.js new file mode 100644 index 00000000..a37332fb --- /dev/null +++ b/v1/src/simulator/src/modules/verilogShiftRight.js @@ -0,0 +1,108 @@ +/* eslint-disable no-bitwise */ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' + +/** + * @class + * verilogShiftRight + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. modules + * @category modules + */ +export default class verilogShiftRight extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + bitWidth = 1, + outputBitWidth = 1 + ) { + super(x, y, scope, dir, bitWidth) + /* this is done in this.baseSetup() now + this.scope['verilogShiftRight'].push(this); + */ + this.setDimensions(20, 20) + this.outputBitWidth = outputBitWidth + this.inp1 = new Node(-20, -10, 0, this, this.bitWidth, 'Input') + this.shiftInp = new Node(-20, 0, 0, this, this.bitWidth, 'ShiftInput') + this.output1 = new Node(20, 0, 1, this, this.outputBitWidth, 'Output') + } + + /** + * @memberof verilogShiftRight + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [ + this.direction, + this.bitWidth, + this.outputBitWidth, + ], + nodes: { + inp1: findNode(this.inp1), + shiftInp: findNode(this.shiftInp), + output1: findNode(this.output1), + }, + } + return data + } + + /** + * @memberof verilogShiftRight + * Checks if the element is resolvable + * @return {boolean} + */ + isResolvable() { + return ( + this.inp1.value !== undefined && this.shiftInp.value !== undefined + ) + } + + /** + * @memberof verilogShiftRight + * function to change bitwidth of the element + * @param {number} bitWidth - new bitwidth + */ + newBitWidth(bitWidth) { + this.bitWidth = bitWidth + this.inp1.bitWidth = bitWidth + this.shiftInp.bitWidth = bitWidth + this.output1.bitWidth = bitWidth + } + + /** + * @memberof verilogShiftRight + * resolve output values based on inputData + */ + resolve() { + if (this.isResolvable() === false) { + return + } + const output1 = this.inp1.value >> this.shiftInp.value + + this.output1.value = + (output1 << (32 - this.outputBitWidth)) >>> + (32 - this.outputBitWidth) + simulationArea.simulationQueue.add(this.output1) + } +} + +/** + * @memberof verilogShiftRight + * Help Tip + * @type {string} + * @category modules + */ +verilogShiftRight.prototype.tooltipText = + 'verilogShiftRight ToolTip : Performs addition of numbers.' +verilogShiftRight.prototype.helplink = + 'https://docs.circuitverse.org/#/miscellaneous?id=verilogShiftRight' +verilogShiftRight.prototype.objectType = 'verilogShiftRight' diff --git a/v1/src/simulator/src/node.js b/v1/src/simulator/src/node.js new file mode 100644 index 00000000..696a664b --- /dev/null +++ b/v1/src/simulator/src/node.js @@ -0,0 +1,1021 @@ +/* eslint-disable import/no-cycle */ +import { drawCircle, drawLine, arc } from './canvasApi' +import simulationArea from './simulationArea' +import { distance, showError } from './utils' +import { + renderCanvas, + scheduleUpdate, + wireToBeCheckedSet, + updateSimulationSet, + updateCanvasSet, + forceResetNodesSet, + canvasMessageData, +} from './engine' +import Wire from './wire' +// import { colors } from './themer/themer'; +import { colors } from './themer/themer' + +/** + * Constructs all the connections of Node node + * @param {Node} node - node to be constructed + * @param {JSON} data - the saved data which is used to load + * @category node + */ +export function constructNodeConnections(node, data) { + for (var i = 0; i < data.connections.length; i++) { + if ( + !node.connections.contains(node.scope.allNodes[data.connections[i]]) + ) + node.connect(node.scope.allNodes[data.connections[i]]) + } +} + +/** + * Fn to replace node by node @ index in global Node List - used when loading + * @param {Node} node - node to be replaced + * @param {number} index - index of node to be replaced + * @category node + */ +export function replace(node, index) { + if (index == -1) { + return node + } + var { scope } = node + var { parent } = node + parent.nodeList.clean(node) + node.delete() + node = scope.allNodes[index] + node.parent = parent + parent.nodeList.push(node) + node.updateRotation() + return node +} +function rotate(x1, y1, dir) { + if (dir == 'LEFT') { + return [-x1, y1] + } + if (dir == 'DOWN') { + return [y1, x1] + } + if (dir == 'UP') { + return [y1, -x1] + } + return [x1, y1] +} + +export function extractBits(num, start, end) { + return (num << (32 - end)) >>> (32 - (end - start + 1)) +} + +export function bin2dec(binString) { + return parseInt(binString, 2) +} + +export function dec2bin(dec, bitWidth = undefined) { + // only for positive nos + var bin = dec.toString(2) + if (bitWidth == undefined) return bin + return '0'.repeat(bitWidth - bin.length) + bin +} + +/** + * find Index of a node + * @param {Node} x - Node to be dound + * @category node + */ +export function findNode(x) { + return x.scope.allNodes.indexOf(x) +} + +/** + * function makes a node according to data providede + * @param {JSON} data - the data used to load a Project + * @param {Scope} scope - scope to which node has to be loaded + * @category node + */ +export function loadNode(data, scope) { + var n = new Node( + data.x, + data.y, + data.type, + scope.root, + data.bitWidth, + data.label + ) +} + +/** + * get Node in index x in scope and set parent + * @param {Node} x - the desired node + * @param {Scope} scope - the scope + * @param {CircuitElement} parent - The parent of node + * @category node + */ +function extractNode(x, scope, parent) { + var n = scope.allNodes[x] + n.parent = parent + return n +} + +// output node=1 +// input node=0 +// intermediate node =2 + +window.NODE_INPUT = 0 +window.NODE_OUTPUT = 1 +window.NODE_INTERMEDIATE = 2 +/** + * used to give id to a node. + * @type {number} + * @category node + */ +var uniqueIdCounter = 10 + +/** + * This class is responsible for all the Nodes.Nodes are connected using Wires + * Nodes are of 3 types; + * NODE_INPUT = 0; + * NODE_OUTPUT = 1; + * NODE_INTERMEDIATE = 2; + * Input and output nodes belong to some CircuitElement(it's parent) + * @param {number} x - x coord of Node + * @param {number} y - y coord of Node + * @param {number} type - type of node + * @param {CircuitElement} parent - parent element + * @param {?number} bitWidth - the bits of node in input and output nodes + * @param {string=} label - label for a node + * @category node + */ +export default class Node { + constructor(x, y, type, parent, bitWidth = undefined, label = '') { + // Should never raise, but just in case + if (isNaN(x) || isNaN(y)) { + this.delete() + showError('Fatal error occurred') + return + } + + forceResetNodesSet(true) + + this.objectType = 'Node' + this.subcircuitOverride = false + this.id = `node${uniqueIdCounter}` + uniqueIdCounter++ + this.parent = parent + if (type != 2 && this.parent.nodeList !== undefined) { + this.parent.nodeList.push(this) + } + + if (bitWidth == undefined) { + this.bitWidth = parent.bitWidth + } else { + this.bitWidth = bitWidth + } + this.label = label + this.prevx = undefined + this.prevy = undefined + this.leftx = x + this.lefty = y + this.x = x + this.y = y + + this.type = type + this.connections = new Array() + this.value = undefined + this.radius = 5 + this.clicked = false + this.hover = false + this.wasClicked = false + this.scope = this.parent.scope + /** + * @type {string} + * value of this.prev is + * 'a' : whenever a node is not being dragged this.prev is 'a' + * 'x' : when node is being dragged horizontally + * 'y' : when node is being dragged vertically + */ + this.prev = 'a' + this.count = 0 + this.highlighted = false + + // This fn is called during rotations and setup + this.refresh() + + if (this.type == 2) { + this.parent.scope.nodes.push(this) + } + + this.parent.scope.allNodes.push(this) + + this.queueProperties = { + inQueue: false, + time: undefined, + index: undefined, + } + } + + /** + * @param {string} - new label + * Function to set label + */ + setLabel(label) { + this.label = label // || ""; + } + + /** + * function to convert a node to intermediate node + */ + converToIntermediate() { + this.type = 2 + this.x = this.absX() + this.y = this.absY() + this.parent = this.scope.root + this.scope.nodes.push(this) + } + + /** + * Helper fuction to move a node. Sets up some variable which help in changing node. + */ + startDragging() { + this.oldx = this.x + this.oldy = this.y + } + + /** + * Helper fuction to move a node. + */ + drag() { + this.x = this.oldx + simulationArea.mouseX - simulationArea.mouseDownX + this.y = this.oldy + simulationArea.mouseY - simulationArea.mouseDownY + } + + /** + * Funciton for saving a node + */ + saveObject() { + if (this.type == 2) { + this.leftx = this.x + this.lefty = this.y + } + var data = { + x: this.leftx, + y: this.lefty, + type: this.type, + bitWidth: this.bitWidth, + label: this.label, + connections: [], + } + for (var i = 0; i < this.connections.length; i++) { + data.connections.push(findNode(this.connections[i])) + } + return data + } + + /** + * helper function to help rotating parent + */ + updateRotation() { + var x + var y + ;[x, y] = rotate(this.leftx, this.lefty, this.parent.direction) + this.x = x + this.y = y + } + + /** + * Refreshes a node after roation of parent + */ + refresh() { + this.updateRotation() + for (var i = 0; i < this.connections.length; i++) { + this.connections[i].connections.clean(this) + } + this.connections = [] + } + + /** + * gives absolute x position of the node + */ + absX() { + return this.x + this.parent.x + } + + /** + * gives absolute y position of the node + */ + absY() { + return this.y + this.parent.y + } + + /** + * update the scope of a node + */ + updateScope(scope) { + this.scope = scope + if (this.type == 2) this.parent = scope.root + } + + /** + * return true if node is connected or not connected but false if undefined. + */ + isResolvable() { + return this.value != undefined + } + + /** + * function used to reset the nodes + */ + reset() { + this.value = undefined + this.highlighted = false + } + + /** + * function to connect two nodes. + */ + connect(n) { + if (n == this) return + if (n.connections.contains(this)) return + var w = new Wire(this, n, this.parent.scope) + this.connections.push(n) + n.connections.push(this) + + updateCanvasSet(true) + updateSimulationSet(true) + scheduleUpdate() + } + + /** + * connects but doesnt draw the wire between nodes + */ + connectWireLess(n) { + if (n == this) return + if (n.connections.contains(this)) return + this.connections.push(n) + n.connections.push(this) + + updateCanvasSet(true) + updateSimulationSet(true) + scheduleUpdate() + } + + /** + * disconnecting two nodes connected wirelessly + */ + disconnectWireLess(n) { + this.connections.clean(n) + n.connections.clean(this) + } + + /** + * function to resolve a node + */ + resolve() { + // Remove Propogation of values (TriState) + if (this.value == undefined) { + for (var i = 0; i < this.connections.length; i++) { + if (this.connections[i].value !== undefined) { + this.connections[i].value = undefined + simulationArea.simulationQueue.add(this.connections[i]) + } + } + + if (this.type == NODE_INPUT) { + if (this.parent.objectType == 'Splitter') { + this.parent.removePropagation() + } else if (this.parent.isResolvable()) { + simulationArea.simulationQueue.add(this.parent) + } else { + this.parent.removePropagation() + } + } + + if (this.type == NODE_OUTPUT && !this.subcircuitOverride) { + if ( + this.parent.isResolvable() && + !this.parent.queueProperties.inQueue + ) { + if (this.parent.objectType == 'TriState') { + if (this.parent.state.value) { + simulationArea.simulationQueue.add(this.parent) + } + } else { + simulationArea.simulationQueue.add(this.parent) + } + } + } + + return + } + + if (this.type == 0) { + if (this.parent.isResolvable()) { + simulationArea.simulationQueue.add(this.parent) + } + } + + for (var i = 0; i < this.connections.length; i++) { + const node = this.connections[i] + + if (node.value != this.value || node.bitWidth != this.bitWidth) { + if ( + node.type == 1 && + node.value != undefined && + node.parent.objectType != 'TriState' && + !(node.subcircuitOverride && node.scope != this.scope) && // Subcircuit Input Node Output Override + node.parent.objectType != 'SubCircuit' + ) { + // Subcircuit Output Node Override + this.highlighted = true + node.highlighted = true + var circuitName = node.scope.name + var circuitElementName = node.parent.objectType + showError( + `Contention Error: ${this.value} and ${node.value} at ${circuitElementName} in ${circuitName}` + ) + } else if (node.bitWidth == this.bitWidth || node.type == 2) { + if ( + node.parent.objectType == 'TriState' && + node.value != undefined && + node.type == 1 + ) { + if (node.parent.state.value) { + simulationArea.contentionPending.push(node.parent) + } + } + + node.bitWidth = this.bitWidth + node.value = this.value + simulationArea.simulationQueue.add(node) + } else { + this.highlighted = true + node.highlighted = true + showError( + `BitWidth Error: ${this.bitWidth} and ${node.bitWidth}` + ) + } + } + } + } + + /** + * this function checks if hover over the node + */ + checkHover() { + if (!simulationArea.mouseDown) { + if (simulationArea.hover == this) { + this.hover = this.isHover() + if (!this.hover) { + simulationArea.hover = undefined + this.showHover = false + } + } else if (!simulationArea.hover) { + this.hover = this.isHover() + if (this.hover) { + simulationArea.hover = this + } else { + this.showHover = false + } + } else { + this.hover = false + this.showHover = false + } + } + } + + /** + * this function draw a node + */ + draw() { + const ctx = simulationArea.context + // + const color = colors['color_wire_draw'] + if (this.clicked) { + if (this.prev == 'x') { + drawLine( + ctx, + this.absX(), + this.absY(), + simulationArea.mouseX, + this.absY(), + color, + 3 + ) + drawLine( + ctx, + simulationArea.mouseX, + this.absY(), + simulationArea.mouseX, + simulationArea.mouseY, + color, + 3 + ) + } else if (this.prev == 'y') { + drawLine( + ctx, + this.absX(), + this.absY(), + this.absX(), + simulationArea.mouseY, + color, + 3 + ) + drawLine( + ctx, + this.absX(), + simulationArea.mouseY, + simulationArea.mouseX, + simulationArea.mouseY, + color, + 3 + ) + } else if ( + Math.abs(this.x + this.parent.x - simulationArea.mouseX) > + Math.abs(this.y + this.parent.y - simulationArea.mouseY) + ) { + drawLine( + ctx, + this.absX(), + this.absY(), + simulationArea.mouseX, + this.absY(), + color, + 3 + ) + } else { + drawLine( + ctx, + this.absX(), + this.absY(), + this.absX(), + simulationArea.mouseY, + color, + 3 + ) + } + } + var colorNode = colors['stroke'] + const colorNodeConnect = colors['color_wire_con'] + const colorNodePow = colors['color_wire_pow'] + const colorNodeLose = colors['color_wire_lose'] + const colorNodeSelected = colors['node'] + + if (this.bitWidth == 1) + colorNode = [colorNodeConnect, colorNodePow][this.value] + if (this.value == undefined) colorNode = colorNodeLose + if (this.type == 2) this.checkHover() + if (this.type == 2) { + drawCircle(ctx, this.absX(), this.absY(), 3, colorNode) + } else { + drawCircle(ctx, this.absX(), this.absY(), 3, colorNodeSelected) + } + + if ( + this.highlighted || + simulationArea.lastSelected == this || + (this.isHover() && + !simulationArea.selected && + !simulationArea.shiftDown) || + simulationArea.multipleObjectSelections.contains(this) + ) { + ctx.strokeStyle = colorNodeSelected + ctx.beginPath() + ctx.lineWidth = 3 + arc( + ctx, + this.x, + this.y, + 8, + 0, + Math.PI * 2, + this.parent.x, + this.parent.y, + 'RIGHT' + ) + ctx.closePath() + ctx.stroke() + } + + if (this.hover || simulationArea.lastSelected == this) { + if (this.showHover || simulationArea.lastSelected == this) { + canvasMessageData.x = this.absX() + canvasMessageData.y = this.absY() - 15 + if (this.type == 2) { + var v = 'X' + if (this.value !== undefined) { + v = this.value.toString(16) + } + if (this.label.length) { + canvasMessageData.string = `${this.label} : ${v}` + } else { + canvasMessageData.string = v + } + } else if (this.label.length) { + canvasMessageData.string = this.label + } + } else { + setTimeout(() => { + if (simulationArea.hover) + simulationArea.hover.showHover = true + updateCanvasSet(true) + renderCanvas(globalScope) + }, 400) + } + } + } + + /** + * checks if a node has been deleted + */ + checkDeleted() { + if (this.deleted) this.delete() + if (this.connections.length == 0 && this.type == 2) this.delete() + } + + /** + * used to update nodes if there is a event like click or hover on the node. + * many booleans are used to check if certain properties are to be updated. + */ + update() { + if (embed) return + + if (this == simulationArea.hover) simulationArea.hover = undefined + this.hover = this.isHover() + + if (!simulationArea.mouseDown) { + if (this.absX() != this.prevx || this.absY() != this.prevy) { + // Connect to any node + this.prevx = this.absX() + this.prevy = this.absY() + this.nodeConnect() + } + } + + if (this.hover) { + simulationArea.hover = this + } + + if ( + simulationArea.mouseDown && + ((this.hover && !simulationArea.selected) || + simulationArea.lastSelected == this) + ) { + simulationArea.selected = true + simulationArea.lastSelected = this + this.clicked = true + } else { + this.clicked = false + } + + if (!this.wasClicked && this.clicked) { + this.wasClicked = true + this.prev = 'a' + if (this.type == 2) { + if ( + !simulationArea.shiftDown && + simulationArea.multipleObjectSelections.contains(this) + ) { + for ( + var i = 0; + i < simulationArea.multipleObjectSelections.length; + i++ + ) { + simulationArea.multipleObjectSelections[ + i + ].startDragging() + } + } + + if (simulationArea.shiftDown) { + simulationArea.lastSelected = undefined + if ( + simulationArea.multipleObjectSelections.contains(this) + ) { + simulationArea.multipleObjectSelections.clean(this) + } else { + simulationArea.multipleObjectSelections.push(this) + } + } else { + simulationArea.lastSelected = this + } + } + } else if (this.wasClicked && this.clicked) { + if ( + !simulationArea.shiftDown && + simulationArea.multipleObjectSelections.contains(this) + ) { + for ( + var i = 0; + i < simulationArea.multipleObjectSelections.length; + i++ + ) { + simulationArea.multipleObjectSelections[i].drag() + } + } + if (this.type == 2) { + if ( + this.connections.length == 1 && + this.connections[0].absX() == simulationArea.mouseX && + this.absX() == simulationArea.mouseX + ) { + this.y = simulationArea.mouseY - this.parent.y + this.prev = 'a' + return + } + if ( + this.connections.length == 1 && + this.connections[0].absY() == simulationArea.mouseY && + this.absY() == simulationArea.mouseY + ) { + this.x = simulationArea.mouseX - this.parent.x + this.prev = 'a' + return + } + if ( + this.connections.length == 1 && + this.connections[0].absX() == this.absX() && + this.connections[0].absY() == this.absY() + ) { + this.connections[0].clicked = true + this.connections[0].wasClicked = true + simulationArea.lastSelected = this.connections[0] + this.delete() + return + } + } + + if ( + this.prev == 'a' && + distance( + simulationArea.mouseX, + simulationArea.mouseY, + this.absX(), + this.absY() + ) >= 10 + ) { + if ( + Math.abs(this.x + this.parent.x - simulationArea.mouseX) > + Math.abs(this.y + this.parent.y - simulationArea.mouseY) + ) { + this.prev = 'x' + } else { + this.prev = 'y' + } + } else if ( + this.prev == 'x' && + this.absY() == simulationArea.mouseY + ) { + this.prev = 'a' + } else if ( + this.prev == 'y' && + this.absX() == simulationArea.mouseX + ) { + this.prev = 'a' + } + } else if (this.wasClicked && !this.clicked) { + this.wasClicked = false + + if ( + simulationArea.mouseX == this.absX() && + simulationArea.mouseY == this.absY() + ) { + return // no new node situation + } + + var x1 + var y1 + var x2 + var y2 + var flag = 0 + var n1 + var n2 + + // (x,y) present node, (x1,y1) node 1 , (x2,y2) node 2 + // n1 - node 1, n2 - node 2 + // node 1 may or may not be there + // flag = 0 - node 2 only + // flag = 1 - node 1 and node 2 + x2 = simulationArea.mouseX + y2 = simulationArea.mouseY + const x = this.absX() + const y = this.absY() + + if (x != x2 && y != y2) { + // Rare Exception Cases + if ( + this.prev == 'a' && + distance( + simulationArea.mouseX, + simulationArea.mouseY, + this.absX(), + this.absY() + ) >= 10 + ) { + if ( + Math.abs( + this.x + this.parent.x - simulationArea.mouseX + ) > + Math.abs(this.y + this.parent.y - simulationArea.mouseY) + ) { + this.prev = 'x' + } else { + this.prev = 'y' + } + } + + flag = 1 + if (this.prev == 'x') { + x1 = x2 + y1 = y + } else if (this.prev == 'y') { + y1 = y2 + x1 = x + } + } + + if (flag == 1) { + for (var i = 0; i < this.parent.scope.allNodes.length; i++) { + if ( + x1 == this.parent.scope.allNodes[i].absX() && + y1 == this.parent.scope.allNodes[i].absY() + ) { + n1 = this.parent.scope.allNodes[i] + break + } + } + + if (n1 == undefined) { + n1 = new Node(x1, y1, 2, this.scope.root) + for (var i = 0; i < this.parent.scope.wires.length; i++) { + if (this.parent.scope.wires[i].checkConvergence(n1)) { + this.parent.scope.wires[i].converge(n1) + break + } + } + } + this.connect(n1) + } + + for (var i = 0; i < this.parent.scope.allNodes.length; i++) { + if ( + x2 == this.parent.scope.allNodes[i].absX() && + y2 == this.parent.scope.allNodes[i].absY() + ) { + n2 = this.parent.scope.allNodes[i] + break + } + } + + if (n2 == undefined) { + n2 = new Node(x2, y2, 2, this.scope.root) + for (var i = 0; i < this.parent.scope.wires.length; i++) { + if (this.parent.scope.wires[i].checkConvergence(n2)) { + this.parent.scope.wires[i].converge(n2) + break + } + } + } + if (flag == 0) this.connect(n2) + else n1.connect(n2) + if (simulationArea.lastSelected == this) + simulationArea.lastSelected = n2 + } + + if (this.type == 2 && simulationArea.mouseDown == false) { + if (this.connections.length == 2) { + if ( + this.connections[0].absX() == this.connections[1].absX() || + this.connections[0].absY() == this.connections[1].absY() + ) { + this.connections[0].connect(this.connections[1]) + this.delete() + } + } else if (this.connections.length == 0) this.delete() + } + } + + /** + * function delete a node + */ + delete() { + updateSimulationSet(true) + this.deleted = true + this.parent.scope.allNodes.clean(this) + this.parent.scope.nodes.clean(this) + + this.parent.scope.root.nodeList.clean(this) // Hope this works! - Can cause bugs + + if (simulationArea.lastSelected == this) + simulationArea.lastSelected = undefined + for (var i = 0; i < this.connections.length; i++) { + this.connections[i].connections.clean(this) + this.connections[i].checkDeleted() + } + wireToBeCheckedSet(1) + forceResetNodesSet(true) + scheduleUpdate() + } + + isClicked() { + return ( + this.absX() == simulationArea.mouseX && + this.absY() == simulationArea.mouseY + ) + } + + isHover() { + return ( + this.absX() == simulationArea.mouseX && + this.absY() == simulationArea.mouseY + ) + } + + /** + * if input nodde: it resolves the parent + * else: it adds all the nodes onto the stack + * and they are processed to generate verilog + */ + nodeConnect() { + var x = this.absX() + var y = this.absY() + var n + + for (var i = 0; i < this.parent.scope.allNodes.length; i++) { + if ( + this != this.parent.scope.allNodes[i] && + x == this.parent.scope.allNodes[i].absX() && + y == this.parent.scope.allNodes[i].absY() + ) { + n = this.parent.scope.allNodes[i] + if (this.type == 2) { + for (var j = 0; j < this.connections.length; j++) { + n.connect(this.connections[j]) + } + this.delete() + } else { + this.connect(n) + } + + break + } + } + + if (n == undefined) { + for (var i = 0; i < this.parent.scope.wires.length; i++) { + if (this.parent.scope.wires[i].checkConvergence(this)) { + var n = this + if (this.type != 2) { + n = new Node( + this.absX(), + this.absY(), + 2, + this.scope.root + ) + this.connect(n) + } + this.parent.scope.wires[i].converge(n) + break + } + } + } + } + + processVerilog() { + if (this.type == NODE_INPUT) { + if (this.parent.isVerilogResolvable()) { + this.scope.stack.push(this.parent) + } + } + + for (var i = 0; i < this.connections.length; i++) { + if (this.connections[i].verilogLabel != this.verilogLabel) { + this.connections[i].verilogLabel = this.verilogLabel + this.scope.stack.push(this.connections[i]) + } + } + } +} + +/** + * delay in simulation of the node. + * @category node + */ +Node.prototype.propagationDelay = 0 + +/** + * backward comaptibilty? + * @category node + */ +Node.prototype.cleanDelete = Node.prototype.delete + +Node.prototype.processVerilog = function () { + if (this.type == NODE_INPUT) { + this.scope.stack.push(this.parent) + } + for (var i = 0; i < this.connections.length; i++) { + if (this.connections[i].verilogLabel != this.verilogLabel) { + this.connections[i].verilogLabel = this.verilogLabel + this.scope.stack.push(this.connections[i]) + } + } +} diff --git a/v1/src/simulator/src/plotArea.js b/v1/src/simulator/src/plotArea.js new file mode 100644 index 00000000..79bf2582 --- /dev/null +++ b/v1/src/simulator/src/plotArea.js @@ -0,0 +1,521 @@ +import simulationArea from './simulationArea' +import { convertors } from './utils' + +var DPR = window.devicePixelRatio || 1 + +// Helper function to scale to display +function sh(x) { + return x * DPR +} + +/** + * Spec Constants + * Size Spec Diagram - https://app.diagrams.net/#G1HFoesRvNyDap95sNJswTy3nH09emDriC + * NOTE: Since DPR is set on page load, changing of screen in runtime will not work well + * @TODO + * - Support for color themes + * - Replace constants with functions? - Can support Zoom in and Zoom out of canvas then + */ +var frameInterval = 100 // Refresh rate +var timeLineHeight = sh(20) +var padding = sh(2) +var plotHeight = sh(20) +var waveFormPadding = sh(5) +var waveFormHeight = plotHeight - 2 * waveFormPadding +var flagLabelWidth = sh(75) +var cycleWidth = sh(30) +var backgroundColor = 'black' +var foregroundColor = '#eee' +var textColor = 'black' +var waveFormColor = 'cyan' +var timeLineStartX = flagLabelWidth + padding + +// Helper functions for canvas + +function getFullHeight(flagCount) { + return timeLineHeight + (plotHeight + padding) * flagCount +} + +function getFlagStartY(flagIndex) { + return getFullHeight(flagIndex) + padding +} + +function getCycleStartX(cycleNumber) { + return timeLineStartX + (cycleNumber - plotArea.cycleOffset) * cycleWidth +} + +/** + * @type {Object} plotArea + * @category plotArea + */ +const plotArea = { + cycleOffset: 0, // Determines timeline offset + DPR: window.devicePixelRatio || 1, + canvas: document.getElementById('plotArea'), + cycleCount: 0, // Number of clock cycles passed + cycleTime: 0, // Time of last clock tick (in ms) + executionStartTime: 0, // Last time play() function ran in engine.js (in ms) + autoScroll: true, // if true, timeline will scroll to keep current time in display + width: 0, // canvas width + height: 0, // canvas height + unitUsed: 0, // Number of simulation units used by the engine + cycleUnit: 1000, // Number of simulation units per cycle + mouseDown: false, + mouseX: 0, // Current mouse position + mouseDownX: 0, // position of mouse when clicked + mouseDownTime: 0, // time when mouse clicked (in ms) + // Reset timeline to 0 and resume autoscroll + reset() { + this.cycleCount = 0 + this.cycleTime = new Date().getTime() + for (var i = 0; i < globalScope.Flag.length; i++) { + globalScope.Flag[i].plotValues = [ + [0, globalScope.Flag[i].inp1.value], + ] + globalScope.Flag[i].cachedIndex = 0 + } + this.unitUsed = 0 + this.resume() + this.resize() + }, + // Resume autoscroll + resume() { + this.autoScroll = true + }, + // pause autoscroll + pause() { + this.autoScroll = false + plotArea.scrollAcc = 0 + }, + // Called every time clock is ticked + nextCycle() { + this.cycleCount++ + this.cycleTime = new Date().getTime() + }, + // Called everytime play() function is execute in engine.js + setExecutionTime() { + this.executionStartTime = new Date().getTime() + }, + // Scale timeline up + zoomIn() { + cycleWidth += sh(2) + }, + // Scale timeline down + zoomOut() { + cycleWidth -= sh(2) + }, + // download as image + download() { + var img = this.canvas.toDataURL(`image/png`) + const anchor = document.createElement('a') + anchor.href = img + anchor.download = `waveform.png` + anchor.click() + }, + // update canvas size to use full screen + resize() { + var oldHeight = this.height + var oldWidth = this.width + this.width = document.getElementById('plot').clientWidth * this.DPR + this.height = getFullHeight(globalScope.Flag.length) + if (oldHeight == this.height && oldWidth == this.width) return + this.canvas.width = this.width + this.canvas.height = this.height + this.plot() + }, + // Setup function, called on page load + setup() { + this.canvas = document.getElementById('plotArea') + if (!embed) { + this.ctx = this.canvas.getContext('2d') + } + this.timeOutPlot = setInterval(() => { + plotArea.plot() + }, frameInterval) + this.reset() + }, + // Used to resolve analytical time in clock cycles + getPlotTime(timeUnit) { + var time = this.cycleCount // Current cycle count + time += timeUnit / this.cycleUnit // Add propagation delay + // For user interactions like buttons - calculate time since clock tick + var timePeriod = simulationArea.timePeriod + var executionDelay = this.executionStartTime - this.cycleTime + var delayFraction = executionDelay / timePeriod + // Add time since clock tick + time += delayFraction + return time + }, + // Auto calibrate clock simulation units based on usage + calibrate() { + var recommendedUnit = Math.max(20, Math.round(this.unitUsed * 3)) + this.cycleUnit = recommendedUnit + $('#timing-diagram-units').val(recommendedUnit) + this.reset() + }, + // Get current time in clock cycles + getCurrentTime() { + var time = this.cycleCount + var timePeriod = simulationArea.timePeriod + var delay = new Date().getTime() - this.cycleTime + var delayFraction = delay / timePeriod + time += delayFraction + return time + }, + update() { + this.resize() + var dangerColor = '#dc5656' + var normalColor = '#42b983' + this.unitUsed = Math.max( + this.unitUsed, + simulationArea.simulationQueue.time + ) + var unitUsed = this.unitUsed + var units = this.cycleUnit + var utilization = Math.round((unitUsed * 10000) / units) / 100 + $('#timing-diagram-log').html( + `Utilization: ${Math.round(unitUsed)} Units (${utilization}%)` + ) + if (utilization >= 90 || utilization <= 10) { + var recommendedUnit = Math.max(20, Math.round(unitUsed * 3)) + $('#timing-diagram-log').append( + ` Recommended Units: ${recommendedUnit}` + ) + $('#timing-diagram-log').css('background-color', dangerColor) + if (utilization >= 100) { + this.clear() + return + } + } else { + $('#timing-diagram-log').css('background-color', normalColor) + } + + var width = this.width + var endTime = this.getCurrentTime() + + if (this.autoScroll) { + // Formula used: + // (endTime - x) * cycleWidth = width - timeLineStartX; + // x = endTime - (width - timeLineStartX) / cycleWidth + this.cycleOffset = Math.max( + 0, + endTime - (width - timeLineStartX) / cycleWidth + ) + } else if (!plotArea.mouseDown) { + // Scroll + this.cycleOffset -= plotArea.scrollAcc + // Friction + plotArea.scrollAcc *= 0.95 + // No negative numbers allowed, so negative scroll to 0 + if (this.cycleOffset < 0) plotArea.scrollAcc = this.cycleOffset / 5 + // Set position to 0, to avoid infinite scrolling + if (Math.abs(this.cycleOffset) < 0.01) this.cycleOffset = 0 + } + }, + render() { + var { width, height } = this + this.canvas.height = height + this.canvas.width = width + var endTime = this.getCurrentTime() + // Reset canvas + this.clear() + var ctx = this.ctx + + // Background Color + ctx.fillStyle = backgroundColor + ctx.fillRect(0, 0, width, height) + + ctx.lineWidth = sh(1) + ctx.font = `${sh(15)}px Raleway` + ctx.textAlign = 'left' + + // Timeline + ctx.fillStyle = foregroundColor + ctx.fillRect(timeLineStartX, 0, this.canvas.width, timeLineHeight) + ctx.fillRect(0, 0, flagLabelWidth, timeLineHeight) + ctx.fillStyle = textColor + ctx.fillText('Time', sh(5), timeLineHeight * 0.7) + + // Timeline numbers + ctx.font = `${sh(9)}px Times New Roman` + ctx.strokeStyle = textColor + ctx.textAlign = 'center' + for ( + var i = Math.floor(plotArea.cycleOffset); + getCycleStartX(i) <= width; + i++ + ) { + var x = getCycleStartX(i) + // Large ticks + number + // @TODO - collapse number if it doesn't fit + if (x >= timeLineStartX) { + ctx.fillText(`${i}`, x, timeLineHeight - sh(15) / 2) + ctx.beginPath() + ctx.moveTo(x, timeLineHeight - sh(5)) + ctx.lineTo(x, timeLineHeight) + ctx.stroke() + } + // Small ticks + for (var j = 1; j < 5; j++) { + var x1 = x + Math.round((j * cycleWidth) / 5) + if (x1 >= timeLineStartX) { + ctx.beginPath() + ctx.moveTo(x1, timeLineHeight - sh(2)) + ctx.lineTo(x1, timeLineHeight) + ctx.stroke() + } + } + } + + // Flag Labels + ctx.textAlign = 'left' + for (var i = 0; i < globalScope.Flag.length; i++) { + var startHeight = getFlagStartY(i) + ctx.fillStyle = foregroundColor + ctx.fillRect(0, startHeight, flagLabelWidth, plotHeight) + ctx.fillStyle = textColor + ctx.fillText( + globalScope.Flag[i].identifier, + sh(5), + startHeight + plotHeight * 0.7 + ) + } + + // Waveform Status Flags + const WAVEFORM_NOT_STARTED = 0 + const WAVEFORM_STARTED = 1 + const WAVEFORM_OVER = 3 + + // Waveform + ctx.strokeStyle = waveFormColor + ctx.textAlign = 'center' + var endX = Math.min(getCycleStartX(endTime), width) + + for (var i = 0; i < globalScope.Flag.length; i++) { + var plotValues = globalScope.Flag[i].plotValues + var startHeight = getFlagStartY(i) + waveFormPadding + var yTop = startHeight + var yMid = startHeight + waveFormHeight / 2 + var yBottom = startHeight + waveFormHeight + var state = WAVEFORM_NOT_STARTED + var prevY + + // Find correct index to start plotting from + var j = 0 + // Using caching for optimal performance + if (globalScope.Flag[i].cachedIndex) { + j = globalScope.Flag[i].cachedIndex + } + // Move to beyond timeLineStartX + while ( + j + 1 < plotValues.length && + getCycleStartX(plotValues[j][0]) < timeLineStartX + ) { + j++ + } + // Move to just before timeLineStartX + while (j > 0 && getCycleStartX(plotValues[j][0]) > timeLineStartX) { + j-- + } + // Cache index + globalScope.Flag[i].cachedIndex = j + + // Plot + for (; j < plotValues.length; j++) { + var x = getCycleStartX(plotValues[j][0]) + + // Handle out of bound + if (x < timeLineStartX) { + if (j + 1 != plotValues.length) { + // Next one also is out of bound, so skip this one completely + var x1 = getCycleStartX(plotValues[j + 1][0]) + if (x1 < timeLineStartX) continue + } + x = timeLineStartX + } + + var value = plotValues[j][1] + if (value === undefined) { + if (state == WAVEFORM_STARTED) { + ctx.stroke() + } + state = WAVEFORM_NOT_STARTED + continue + } + if (globalScope.Flag[i].bitWidth == 1) { + if (x > endX) break + var y = value == 1 ? yTop : yBottom + if (state == WAVEFORM_NOT_STARTED) { + // Start new plot + state = WAVEFORM_STARTED + ctx.beginPath() + ctx.moveTo(x, y) + } else { + ctx.lineTo(x, prevY) + ctx.lineTo(x, y) + } + prevY = y + } else { + var endX + if (j + 1 == plotValues.length) { + endX = getCycleStartX(endTime) + } else { + endX = getCycleStartX(plotValues[j + 1][0]) + } + var smallOffset = waveFormHeight / 2 + ctx.beginPath() + ctx.moveTo(endX, yMid) + ctx.lineTo(endX - smallOffset, yTop) + ctx.lineTo(x + smallOffset, yTop) + ctx.lineTo(x, yMid) + ctx.lineTo(x + smallOffset, yBottom) + ctx.lineTo(endX - smallOffset, yBottom) + ctx.closePath() + ctx.stroke() + + // Text position + // Clamp start and end are within the screen + var x1 = Math.max(x, timeLineStartX) + var x2 = Math.min(endX, width) + var textPositionX = (x1 + x2) / 2 + + ctx.font = `${sh(9)}px Times New Roman` + ctx.fillStyle = 'white' + ctx.fillText( + convertors.dec2hex(value), + textPositionX, + yMid + sh(3) + ) + } + if (x > width) { + state = WAVEFORM_OVER + ctx.stroke() + break + } + } + if (state == WAVEFORM_STARTED) { + if (globalScope.Flag[i].bitWidth == 1) { + ctx.lineTo(endX, prevY) + } + ctx.stroke() + } + } + }, + // Driver function to render and update + plot() { + if (embed) return + if (globalScope.Flag.length === 0) { + this.canvas.width = 0 + this.canvas.height = 0 + return + } + + this.update() + this.render() + }, + clear() { + this.ctx.clearRect(0, 0, plotArea.canvas.width, plotArea.canvas.height) + }, +} +export default plotArea + +/** + * type {Object} timingDiagramButtonActions + * @category plotArea + * @description Actions for buttons in timing diagram + * @property {function} smallHeight - Decrease waveform height + * @property {function} largeHeight - Increase waveform height + */ + +const timingDiagramButtonActions = { + smallHeight() { + if (plotHeight >= sh(20)) { + plotHeight -= sh(5) + waveFormHeight = plotHeight - 2 * waveFormPadding + } + }, + largeHeight() { + if (plotHeight < sh(50)) { + plotHeight += sh(5) + waveFormHeight = plotHeight - 2 * waveFormPadding + } + }, +} + +export { timingDiagramButtonActions } + +export function setupTimingListeners() { + // $('.timing-diagram-smaller').on('click', () => { + // $('#plot').width(Math.max($('#plot').width() - 20, 560)) + // plotArea.resize() + // }) + // $('.timing-diagram-larger').on('click', () => { + // $('#plot').width($('#plot').width() + 20) + // plotArea.resize() + // }) + // $('.timing-diagram-small-height').on('click', () => { + // if (plotHeight >= sh(20)) { + // plotHeight -= sh(5) + // waveFormHeight = plotHeight - 2 * waveFormPadding + // } + // }) + // $('.timing-diagram-large-height').on('click', () => { + // if (plotHeight < sh(50)) { + // plotHeight += sh(5) + // waveFormHeight = plotHeight - 2 * waveFormPadding + // } + // }) + // $('.timing-diagram-reset').on('click', () => { + // plotArea.reset() + // }) + // $('.timing-diagram-calibrate').on('click', () => { + // plotArea.calibrate() + // }) + // $('.timing-diagram-resume').on('click', () => { + // plotArea.resume() + // }) + // $('.timing-diagram-pause').on('click', () => { + // plotArea.pause() + // }) + // $('.timing-diagram-download').on('click', () => { + // plotArea.download() + // }) + // $('.timing-diagram-zoom-in').on('click', () => { + // plotArea.zoomIn() + // }) + // $('.timing-diagram-zoom-out').on('click', () => { + // plotArea.zoomOut() + // }) + // $('#timing-diagram-units').on('change paste keyup', function () { + // var timeUnits = parseInt($(this).val(), 10) + // if (isNaN(timeUnits) || timeUnits < 1) return + // plotArea.cycleUnit = timeUnits + // }) + document.getElementById('plotArea').addEventListener('mousedown', (e) => { + var rect = plotArea.canvas.getBoundingClientRect() + var x = sh(e.clientX - rect.left) + plotArea.scrollAcc = 0 + plotArea.autoScroll = false + plotArea.mouseDown = true + plotArea.mouseX = x + plotArea.mouseDownX = x + plotArea.mouseDownTime = new Date().getTime() + }) + document.getElementById('plotArea').addEventListener('mouseup', (e) => { + plotArea.mouseDown = false + var time = new Date().getTime() - plotArea.mouseDownTime + var offset = (plotArea.mouseX - plotArea.mouseDownX) / cycleWidth + plotArea.scrollAcc = (offset * frameInterval) / time + }) + + document.getElementById('plotArea').addEventListener('mousemove', (e) => { + var rect = plotArea.canvas.getBoundingClientRect() + var x = sh(e.clientX - rect.left) + if (plotArea.mouseDown) { + plotArea.cycleOffset -= (x - plotArea.mouseX) / cycleWidth + plotArea.mouseX = x + } else { + plotArea.mouseDown = false + } + }) +} diff --git a/v1/src/simulator/src/quinMcCluskey.js b/v1/src/simulator/src/quinMcCluskey.js new file mode 100644 index 00000000..f15041db --- /dev/null +++ b/v1/src/simulator/src/quinMcCluskey.js @@ -0,0 +1,227 @@ +// Algorithm used for Combinational Analysis + +export default function BooleanMinimize( + numVarsArg, + minTermsArg, + dontCaresArg = [] +) { + var __result + + Object.defineProperties(this, { + minTerms: { + value: minTermsArg, + enumerable: false, + writable: false, + configurable: true, + }, + + dontCares: { + value: dontCaresArg, + enumerable: false, + writable: false, + configurable: true, + }, + + numVars: { + value: numVarsArg, + enumerable: false, + writable: false, + configurable: true, + }, + + result: { + enumerable: true, + configurable: true, + get: function () { + if (__result === undefined) { + __result = BooleanMinimize.prototype.solve.call(this) + } + + return __result + }, + set: function () { + throw new Error('result cannot be assigned a value') + }, + }, + }) +} + +BooleanMinimize.prototype.solve = function () { + function dec_to_binary_string(n) { + var str = n.toString(2) + + while (str.length != this.numVars) { + str = '0' + str + } + + return str + } + + function num_set_bits(s) { + var ans = 0 + for (let i = 0; i < s.length; ++i) if (s[i] === '1') ans++ + return ans + } + + function get_prime_implicants(allTerms) { + var table = [] + var primeImplicants = new Set() + var reduced + + while (1) { + for (let i = 0; i <= this.numVars; ++i) table[i] = new Set() + for (let i = 0; i < allTerms.length; ++i) + table[num_set_bits(allTerms[i])].add(allTerms[i]) + + allTerms = [] + reduced = new Set() + + for (let i = 0; i < table.length - 1; ++i) { + for (let str1 of table[i]) { + for (let str2 of table[i + 1]) { + let diff = -1 + + for (let j = 0; j < this.numVars; ++j) { + if (str1[j] != str2[j]) { + if (diff === -1) { + diff = j + } else { + diff = -1 + break + } + } + } + + if (diff !== -1) { + allTerms.push( + str1.slice(0, diff) + '-' + str1.slice(diff + 1) + ) + reduced.add(str1) + reduced.add(str2) + } + } + } + } + + for (let t of table) { + for (let str of t) { + if (!reduced.has(str)) primeImplicants.add(str) + } + } + + if (!reduced.size) break + } + + return primeImplicants + } + + function get_essential_prime_implicants(primeImplicants, minTerms) { + var table = [], + column + + function check_if_similar(minTerm, primeImplicant) { + for (let i = 0; i < primeImplicant.length; ++i) { + if ( + primeImplicant[i] !== '-' && + minTerm[i] !== primeImplicant[i] + ) + return false + } + + return true + } + + function get_complexity(terms) { + var complexity = terms.length + + for (let t of terms) { + for (let i = 0; i < t.length; ++i) { + if (t[i] !== '-') { + complexity++ + if (t[i] === '0') complexity++ + } + } + } + + return complexity + } + + function isSubset(sub, sup) { + for (let i of sub) { + if (!sup.has(i)) return false + } + + return true + } + + for (let m of minTerms) { + column = [] + + for (let i = 0; i < primeImplicants.length; ++i) { + if (check_if_similar(m, primeImplicants[i])) { + column.push(i) + } + } + + table.push(column) + } + + var possibleSets = [], + tempSets + + for (let i of table[0]) { + possibleSets.push(new Set([i])) + } + + for (let i = 1; i < table.length; ++i) { + tempSets = [] + for (let s of possibleSets) { + for (let p of table[i]) { + let x = new Set(s) + x.add(p) + let append = true + + for (let j = tempSets.length - 1; j >= 0; --j) { + if (isSubset(x, tempSets[j])) { + tempSets.splice(j, 1) + } else { + append = false + } + } + + if (append) { + tempSets.push(x) + } + } + + possibleSets = tempSets + } + } + + var essentialImplicants, + minComplexity = 1e9 + + for (let s of possibleSets) { + let p = [] + for (let i of s) { + p.push(primeImplicants[i]) + } + let comp = get_complexity(p) + if (comp < minComplexity) { + essentialImplicants = p + minComplexity = comp + } + } + + return essentialImplicants + } + + var minTerms = this.minTerms.map(dec_to_binary_string.bind(this)) + var dontCares = this.dontCares.map(dec_to_binary_string.bind(this)) + + return get_essential_prime_implicants.call( + this, + Array.from(get_prime_implicants.call(this, minTerms.concat(dontCares))), + minTerms + ) +} diff --git a/v1/src/simulator/src/restrictedElementDiv.js b/v1/src/simulator/src/restrictedElementDiv.js new file mode 100644 index 00000000..74e1aad5 --- /dev/null +++ b/v1/src/simulator/src/restrictedElementDiv.js @@ -0,0 +1,44 @@ +export function updateRestrictedElementsList() { + if (restrictedElements.length === 0) return + + const { restrictedCircuitElementsUsed } = globalScope + let restrictedStr = '' + + restrictedCircuitElementsUsed.forEach((element) => { + restrictedStr += `${element}, ` + }) + + if (restrictedStr === '') { + restrictedStr = 'None' + } else { + restrictedStr = restrictedStr.slice(0, -2) + } + + document.getElementById('restrictedElementsDiv--list').innerHTML = restrictedStr +} + +export function updateRestrictedElementsInScope(scope = globalScope) { + // Do nothing if no restricted elements + if (restrictedElements.length === 0) return + + const restrictedElementsUsed = [] + restrictedElements.forEach((element) => { + if (scope[element].length > 0) { + restrictedElementsUsed.push(element) + } + }) + + scope.restrictedCircuitElementsUsed = restrictedElementsUsed + updateRestrictedElementsList() +} + +export function showRestricted() { + document.getElementById('restrictedDiv').classList.remove('display--none') + // Show no help text for restricted elements + document.getElementById('Help').classList.remove('show') + document.getElementById('restrictedDiv').innerHTML = 'The element has been restricted by mentor. Usage might lead to deduction in marks' +} + +export function hideRestricted() { + document.getElementById('restrictedDiv').classList.add('display--none') +} diff --git a/v1/src/simulator/src/sequential.js b/v1/src/simulator/src/sequential.js new file mode 100644 index 00000000..bd1740de --- /dev/null +++ b/v1/src/simulator/src/sequential.js @@ -0,0 +1,25 @@ +import { scheduleUpdate, play, updateCanvasSet } from './engine' +import simulationArea from './simulationArea' + +/** + * a global function as a helper for simulationArea.changeClockEnable + * @category sequential + */ +export function changeClockEnable(val) { + simulationArea.clockEnabled = val +} + +/** + * WIP function defined and used + * @param {number} n + * @category sequential + */ +export function runTest(n = 10) { + var t = new Date().getTime() + for (var i = 0; i < n; i++) { + clockTick() + } + updateCanvasSet(true) + play() + scheduleUpdate() +} diff --git a/v1/src/simulator/src/sequential/Clock.js b/v1/src/simulator/src/sequential/Clock.js new file mode 100644 index 00000000..86e24955 --- /dev/null +++ b/v1/src/simulator/src/sequential/Clock.js @@ -0,0 +1,95 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo } from '../canvasApi' +import { colors } from '../themer/themer' +/** + * @class + * Clock + * Clock + * @extends CircuitElement + * @param {number} x - x coord of element + * @param {number} y - y coord of element + * @param {Scope=} scope - the ciruit in which we want the Element + * @param {string=} dir - direcion in which element has to drawn + * @category sequential + */ +export default class Clock extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT') { + super(x, y, scope, dir, 1) + /* + this.scope['Clock'].push(this); + */ + this.fixedBitWidth = true + this.output1 = new Node(10, 0, 1, this, 1) + this.state = 0 + this.output1.value = this.state + this.wasClicked = false + this.interval = null + } + + customSave() { + var data = { + nodes: { + output1: findNode(this.output1), + }, + constructorParamaters: [this.direction], + } + return data + } + + resolve() { + this.output1.value = this.state + simulationArea.simulationQueue.add(this.output1) + } + + toggleState() { + // toggleState + this.state = (this.state + 1) % 2 + this.output1.value = this.state + } + + customDraw() { + var ctx = simulationArea.context + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.lineWidth = correctWidth(3) + var xx = this.x + var yy = this.y + + ctx.beginPath() + ctx.strokeStyle = [colors['color_wire_con'], colors['color_wire_pow']][ + this.state + ] + ctx.lineWidth = correctWidth(2) + if (this.state == 0) { + moveTo(ctx, -6, 0, xx, yy, 'RIGHT') + lineTo(ctx, -6, 5, xx, yy, 'RIGHT') + lineTo(ctx, 0, 5, xx, yy, 'RIGHT') + lineTo(ctx, 0, -5, xx, yy, 'RIGHT') + lineTo(ctx, 6, -5, xx, yy, 'RIGHT') + lineTo(ctx, 6, 0, xx, yy, 'RIGHT') + } else { + moveTo(ctx, -6, 0, xx, yy, 'RIGHT') + lineTo(ctx, -6, -5, xx, yy, 'RIGHT') + lineTo(ctx, 0, -5, xx, yy, 'RIGHT') + lineTo(ctx, 0, 5, xx, yy, 'RIGHT') + lineTo(ctx, 6, 5, xx, yy, 'RIGHT') + lineTo(ctx, 6, 0, xx, yy, 'RIGHT') + } + ctx.stroke() + } + + static verilogInstructions() { + return 'Clock - Use a single global clock\n' + } +} + +Clock.prototype.tooltipText = 'Clock' + +Clock.prototype.click = Clock.prototype.toggleState +Clock.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/6sequentialelements?id=clock' +Clock.prototype.objectType = 'Clock' +Clock.prototype.propagationDelay = 0 +Clock.prototype.propagationDelayFixed = true diff --git a/v1/src/simulator/src/sequential/DflipFlop.js b/v1/src/simulator/src/sequential/DflipFlop.js new file mode 100644 index 00000000..4543ba61 --- /dev/null +++ b/v1/src/simulator/src/sequential/DflipFlop.js @@ -0,0 +1,168 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, fillText } from '../canvasApi' +import { colors } from '../themer/themer' +/** + * @class + * DflipFlop + * D flip flop has 5 input nodes: + * clock, data input, preset, reset ,enable. + * @extends CircuitElement + * @param {number} x - x coord of element + * @param {number} y - y coord of element + * @param {Scope=} scope - the ciruit in which we want the Element + * @param {string=} dir - direcion in which element has to drawn + * @category sequential + */ +export default class DflipFlop extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT', bitWidth = 1) { + super(x, y, scope, dir, bitWidth) + /* + this.scope['DflipFlop'].push(this); + */ + this.directionFixed = true + this.setDimensions(20, 20) + this.rectangleObject = true + this.clockInp = new Node(-20, +10, 0, this, 1, 'Clock') + this.dInp = new Node(-20, -10, 0, this, this.bitWidth, 'D') + this.qOutput = new Node(20, -10, 1, this, this.bitWidth, 'Q') + this.qInvOutput = new Node(20, 10, 1, this, this.bitWidth, 'Q Inverse') + this.reset = new Node(10, 20, 0, this, 1, 'Asynchronous Reset') + this.preset = new Node(0, 20, 0, this, this.bitWidth, 'Preset') + this.en = new Node(-10, 20, 0, this, 1, 'Enable') + this.masterState = 0 + this.slaveState = 0 + this.prevClockState = 0 + + this.wasClicked = false + } + + /** + * WIP always resolvable? + */ + isResolvable() { + return true + // if (this.reset.value == 1) return true; + // if (this.clockInp.value != undefined && this.dInp.value != undefined) return true; + // return false; + } + + newBitWidth(bitWidth) { + this.bitWidth = bitWidth + this.dInp.bitWidth = bitWidth + this.qOutput.bitWidth = bitWidth + this.qInvOutput.bitWidth = bitWidth + this.preset.bitWidth = bitWidth + } + + /** + * @memberof DflipFlop + * On the leading edge of the clock signal (LOW-to-HIGH) the first stage, + * the “master” latches the input condition at D, while the output stage is deactivated. + * On the trailing edge of the clock signal (HIGH-to-LOW) the second “slave” stage is + * now activated, latching on to the output from the first master circuit. + * Then the output stage appears to be triggered on the negative edge of the clock pulse. + * This fuction sets the value for the node qOutput based on the previous state + * and input of the clock. We flip the bits to find qInvOutput + */ + resolve() { + if (this.reset.value == 1) { + this.masterState = this.slaveState = this.preset.value || 0 + } else if (this.en.value == 0) { + this.prevClockState = this.clockInp.value + } else if (this.en.value == 1 || this.en.connections.length == 0) { + // if(this.en.value==1) // Creating Infinite Loop, WHY ?? + if (this.clockInp.value == this.prevClockState) { + if (this.clockInp.value == 0 && this.dInp.value != undefined) { + this.masterState = this.dInp.value + } + } else if (this.clockInp.value != undefined) { + if (this.clockInp.value == 1) { + this.slaveState = this.masterState + } else if ( + this.clockInp.value == 0 && + this.dInp.value != undefined + ) { + this.masterState = this.dInp.value + } + this.prevClockState = this.clockInp.value + } + } + + if (this.qOutput.value != this.slaveState) { + this.qOutput.value = this.slaveState + this.qInvOutput.value = this.flipBits(this.slaveState) + simulationArea.simulationQueue.add(this.qOutput) + simulationArea.simulationQueue.add(this.qInvOutput) + } + } + + customSave() { + var data = { + nodes: { + clockInp: findNode(this.clockInp), + dInp: findNode(this.dInp), + qOutput: findNode(this.qOutput), + qInvOutput: findNode(this.qInvOutput), + reset: findNode(this.reset), + preset: findNode(this.preset), + en: findNode(this.en), + }, + constructorParamaters: [this.direction, this.bitWidth], + } + return data + } + + customDraw() { + var ctx = simulationArea.context + // + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.beginPath() + ctx.lineWidth = correctWidth(3) + var xx = this.x + var yy = this.y + // rect(ctx, xx - 20, yy - 20, 40, 40); + moveTo(ctx, -20, 5, xx, yy, this.direction) + lineTo(ctx, -15, 10, xx, yy, this.direction) + lineTo(ctx, -20, 15, xx, yy, this.direction) + // if ((this.b.hover&&!simulationArea.shiftDown)|| simulationArea.lastSelected == this || simulationArea.multipleObjectSelections.contains(this)) ctx.fillStyle = "rgba(255, 255, 32,0.8)";ctx.fill(); + ctx.stroke() + + ctx.beginPath() + ctx.font = '20px Raleway' + ctx.fillStyle = colors['input_text'] + ctx.textAlign = 'center' + fillText(ctx, this.slaveState.toString(16), xx, yy + 5) + ctx.fill() + } + + static moduleVerilog() { + return ` +module DflipFlop(q, q_inv, clk, d, a_rst, pre, en); + parameter WIDTH = 1; + output reg [WIDTH-1:0] q, q_inv; + input clk, a_rst, pre, en; + input [WIDTH-1:0] d; + + always @ (posedge clk or posedge a_rst) + if (a_rst) begin + q <= 'b0; + q_inv <= 'b1; + end else if (en == 0) ; + else begin + q <= d; + q_inv <= ~d; + end +endmodule + ` + } +} + +DflipFlop.prototype.tooltipText = + 'D FlipFlop ToolTip : Introduces delay in timing circuit.' +DflipFlop.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/6sequentialelements?id=d-flip-flop' + +DflipFlop.prototype.objectType = 'DflipFlop' diff --git a/v1/src/simulator/src/sequential/Dlatch.js b/v1/src/simulator/src/sequential/Dlatch.js new file mode 100644 index 00000000..7532d3db --- /dev/null +++ b/v1/src/simulator/src/sequential/Dlatch.js @@ -0,0 +1,119 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, fillText } from '../canvasApi' +/** + * @class + * Dlatch + * D latch has 2 input nodes: + * clock, data input. + * Difference between this and D - FlipFlop is + * that Flip flop must have a clock. + * @extends CircuitElement + * @param {number} x - x coord of element + * @param {number} y - y coord of element + * @param {Scope=} scope - the ciruit in which we want the Element + * @param {string=} dir - direcion in which element has to drawn + * @category sequential + */ +import { colors } from '../themer/themer' +export default class Dlatch extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT', bitWidth = 1) { + super(x, y, scope, dir, bitWidth) + /* + this.scope['Dlatch'].push(this); + */ + this.directionFixed = true + this.setDimensions(20, 20) + this.rectangleObject = true + this.clockInp = new Node(-20, +10, 0, this, 1, 'Clock') + this.dInp = new Node(-20, -10, 0, this, this.bitWidth, 'D') + this.qOutput = new Node(20, -10, 1, this, this.bitWidth, 'Q') + this.qInvOutput = new Node(20, 10, 1, this, this.bitWidth, 'Q Inverse') + // this.reset = new Node(10, 20, 0, this, 1, "Asynchronous Reset"); + // this.preset = new Node(0, 20, 0, this, this.bitWidth, "Preset"); + // this.en = new Node(-10, 20, 0, this, 1, "Enable"); + this.state = 0 + this.prevClockState = 0 + this.wasClicked = false + } + + /** + * Idea: shoould be D FF? + */ + isResolvable() { + if (this.clockInp.value != undefined && this.dInp.value != undefined) + return true + return false + } + + newBitWidth(bitWidth) { + this.bitWidth = bitWidth + this.dInp.bitWidth = bitWidth + this.qOutput.bitWidth = bitWidth + this.qInvOutput.bitWidth = bitWidth + // this.preset.bitWidth = bitWidth; + } + + /** + * @memberof Dlatch + * when the clock input is high we update the state + * qOutput is set to the state + */ + resolve() { + if (this.clockInp.value == 1 && this.dInp.value != undefined) { + this.state = this.dInp.value + } + + if (this.qOutput.value != this.state) { + this.qOutput.value = this.state + this.qInvOutput.value = this.flipBits(this.state) + simulationArea.simulationQueue.add(this.qOutput) + simulationArea.simulationQueue.add(this.qInvOutput) + } + } + + customSave() { + var data = { + nodes: { + clockInp: findNode(this.clockInp), + dInp: findNode(this.dInp), + qOutput: findNode(this.qOutput), + qInvOutput: findNode(this.qInvOutput), + // reset: findNode(this.reset), + // preset: findNode(this.preset), + // en: findNode(this.en), + }, + constructorParamaters: [this.direction, this.bitWidth], + } + return data + } + + customDraw() { + var ctx = simulationArea.context + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.beginPath() + ctx.lineWidth = correctWidth(3) + var xx = this.x + var yy = this.y + // rect(ctx, xx - 20, yy - 20, 40, 40); + moveTo(ctx, -20, 5, xx, yy, this.direction) + lineTo(ctx, -15, 10, xx, yy, this.direction) + lineTo(ctx, -20, 15, xx, yy, this.direction) + // if ((this.b.hover&&!simulationArea.shiftDown)|| simulationArea.lastSelected == this || simulationArea.multipleObjectSelections.contains(this)) ctx.fillStyle = "rgba(255, 255, 32,0.8)";ctx.fill(); + ctx.stroke() + ctx.beginPath() + ctx.font = '20px Raleway' + ctx.fillStyle = colors['input_text'] + ctx.textAlign = 'center' + fillText(ctx, this.state.toString(16), xx, yy + 5) + ctx.fill() + } +} + +Dlatch.prototype.tooltipText = 'D Latch : Single input Flip flop or D FlipFlop' +Dlatch.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/6sequentialelements?id=d-latch' + +Dlatch.prototype.objectType = 'Dlatch' diff --git a/v1/src/simulator/src/sequential/EEPROM.js b/v1/src/simulator/src/sequential/EEPROM.js new file mode 100644 index 00000000..3a3427b8 --- /dev/null +++ b/v1/src/simulator/src/sequential/EEPROM.js @@ -0,0 +1,104 @@ +import RAM from './RAM' +/** + * @class + * EEPROM Component. + * @extends CircuitElement + * @param {number} x - x coord of element + * @param {number} y - y coord of element + * @param {Scope=} scope - the ciruit in which we want the Element + * @param {string=} dir - direcion in which element has to drawn + + * + * This is basically a RAM component that persists its contents. + * + * We consider EEPROMs more 'expensive' than RAMs, so we arbitrarily limit + * the addressWith to a maximum of 10 bits (1024 addresses) with a default of 8-bit (256). + * + * In the EEPROM all addresses are initialized to zero. + * This way we serialize unused values as "0" instead of "null". + * + * These two techniques help keep reduce the size of saved projects. + * @category sequential + */ +export default class EEPROM extends RAM { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + bitWidth = 8, + addressWidth = 8, + data = null + ) { + super(x, y, scope, dir, bitWidth, addressWidth) + /* + this.scope['EEPROM'].push(this); + */ + this.data = data || this.data + } + + clearData() { + super.clearData() + for (var i = 0; i < this.data.length; i++) + this.data[i] = this.data[i] || 0 + } + + customSave() { + var saveInfo = super.customSave(this) + + // Normalize this.data to use zeroes instead of null when serialized. + var { data } = this + + saveInfo.constructorParamaters.push(data) + return saveInfo + } + + //This is a EERAM without a clock - not normal + //reset is supported + static moduleVerilog() { + return ` + module EEPROM(dout, addr, din, we, dmp, rst); + parameter WIDTH = 8; + parameter ADDR = 10; + output [WIDTH-1:0] dout; + input [ADDR-1:0] addr; + input [WIDTH-1:0] din; + input we; + input dmp; + input rst; + reg [WIDTH-1:0] mem[2**ADDR-1:0]; + integer j; + + assign dout = mem[addr]; + + always @ (*) begin + if (!rst) + for (j=0; j < 2**ADDR-1; j=j+1) begin + mem[j] = 0; + end + if (!we) + mem[addr] = din; + dout = mem[addr]; + end + endmodule + ` + } +} + +EEPROM.prototype.tooltipText = + 'Electrically Erasable Programmable Read-Only Memory' +EEPROM.prototype.shortName = 'EEPROM' +EEPROM.prototype.maxAddressWidth = 10 +EEPROM.prototype.mutableProperties = { + addressWidth: { + name: 'Address Width', + type: 'number', + max: '10', + min: '1', + func: 'changeAddressWidth', + }, + dump: RAM.prototype.mutableProperties.dump, + load: RAM.prototype.mutableProperties.load, + reset: RAM.prototype.mutableProperties.reset, +} +EEPROM.prototype.objectType = 'EEPROM' diff --git a/v1/src/simulator/src/sequential/JKflipFlop.js b/v1/src/simulator/src/sequential/JKflipFlop.js new file mode 100644 index 00000000..c02eae3f --- /dev/null +++ b/v1/src/simulator/src/sequential/JKflipFlop.js @@ -0,0 +1,166 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, fillText } from '../canvasApi' +/** + * @class + * JKflipFlop + * JK flip flop has 6 input nodes: + * clock, J input, K input, preset, reset ,enable. + * @extends CircuitElement + * @param {number} x - x coord of element + * @param {number} y - y coord of element + * @param {Scope=} scope - the ciruit in which we want the Element + * @param {string=} dir - direcion in which element has to drawn + * @category sequential + */ +import { colors } from '../themer/themer' +export default class JKflipFlop extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT') { + super(x, y, scope, dir, 1) + /* + this.scope['JKflipFlop'].push(this); + */ + this.directionFixed = true + this.fixedBitWidth = true + this.setDimensions(20, 20) + this.rectangleObject = true + this.J = new Node(-20, -10, 0, this, 1, 'J') + this.K = new Node(-20, 0, 0, this, 1, 'K') + this.clockInp = new Node(-20, 10, 0, this, 1, 'Clock') + this.qOutput = new Node(20, -10, 1, this, 1, 'Q') + this.qInvOutput = new Node(20, 10, 1, this, 1, 'Q Inverse') + this.reset = new Node(10, 20, 0, this, 1, 'Asynchronous Reset') + this.preset = new Node(0, 20, 0, this, 1, 'Preset') + this.en = new Node(-10, 20, 0, this, 1, 'Enable') + this.state = 0 + this.slaveState = 0 + this.masterState = 0 + this.prevClockState = 0 + + // this.wasClicked = false; + } + + /** + * @memberof JKflipFlop + * if none of the predefined nodes have been deleted it isresolvable + */ + isResolvable() { + if (this.reset.value == 1) return true + if ( + this.clockInp.value != undefined && + this.J.value != undefined && + this.K.value != undefined + ) + return true + return false + } + + newBitWidth(bitWidth) { + this.bitWidth = bitWidth + this.dInp.bitWidth = bitWidth + this.qOutput.bitWidth = bitWidth + this.qInvOutput.bitWidth = bitWidth + this.preset.bitWidth = bitWidth + } + + /** + * @memberof JKflipFlop + * Edge triggered master slave JK flip flop is resolved by + * setting the slaveState = masterState when there is an edge + * in the clock. masterState = this.J when no change in clock. + */ + resolve() { + if (this.reset.value == 1) { + this.masterState = this.slaveState = this.preset.value || 0 + } else if (this.en.value == 0) { + this.prevClockState = this.clockInp.value + } else if (this.en.value == 1 || this.en.connections.length == 0) { + if (this.clockInp.value == this.prevClockState) { + if ( + this.clockInp.value == 0 && + this.J.value != undefined && + this.K.value != undefined + ) { + if (this.J.value && this.K.value) { + this.masterState = 1 ^ this.slaveState + } else if (this.J.value ^ this.K.value) { + this.masterState = this.J.value + } + } + } else if (this.clockInp.value != undefined) { + if (this.clockInp.value == 1) { + this.slaveState = this.masterState + } else if ( + this.clockInp.value == 0 && + this.J.value != undefined && + this.K.value != undefined + ) { + if (this.J.value && this.K.value) { + this.masterState = 1 ^ this.slaveState + } else if (this.J.value ^ this.K.value) { + this.masterState = this.J.value + } + } + this.prevClockState = this.clockInp.value + } + } + + if (this.qOutput.value != this.slaveState) { + this.qOutput.value = this.slaveState + this.qInvOutput.value = this.flipBits(this.slaveState) + simulationArea.simulationQueue.add(this.qOutput) + simulationArea.simulationQueue.add(this.qInvOutput) + } + } + + customSave() { + var data = { + nodes: { + J: findNode(this.J), + K: findNode(this.K), + clockInp: findNode(this.clockInp), + qOutput: findNode(this.qOutput), + qInvOutput: findNode(this.qInvOutput), + reset: findNode(this.reset), + preset: findNode(this.preset), + en: findNode(this.en), + }, + constructorParamaters: [this.direction], + } + return data + } + + customDraw() { + var ctx = simulationArea.context + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.beginPath() + ctx.lineWidth = correctWidth(3) + var xx = this.x + var yy = this.y + + // rect(ctx, xx - 20, yy - 20, 40, 40); + moveTo(ctx, -20, 5, xx, yy, this.direction) + lineTo(ctx, -15, 10, xx, yy, this.direction) + lineTo(ctx, -20, 15, xx, yy, this.direction) + + // if ((this.b.hover&&!simulationArea.shiftDown)|| simulationArea.lastSelected == this || simulationArea.multipleObjectSelections.contains(this)) ctx.fillStyle = "rgba(255, 255, 32,0.8)";ctx.fill(); + ctx.stroke() + + ctx.beginPath() + ctx.font = '20px Raleway' + ctx.fillStyle = colors['input_text'] + ctx.textAlign = 'center' + fillText(ctx, this.slaveState.toString(16), xx, yy + 5) + ctx.fill() + } +} + +JKflipFlop.prototype.tooltipText = + 'JK FlipFlop ToolTip : gated SR flip-flop with the addition of a clock input.' + +JKflipFlop.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/6sequentialelements?id=jk-flip-flop' + +JKflipFlop.prototype.objectType = 'JKflipFlop' diff --git a/v1/src/simulator/src/sequential/Keyboard.js b/v1/src/simulator/src/sequential/Keyboard.js new file mode 100644 index 00000000..ffeb7a9c --- /dev/null +++ b/v1/src/simulator/src/sequential/Keyboard.js @@ -0,0 +1,232 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, fillText3 } from '../canvasApi' +/** + * @class + * Keyboard + * KeyBoard - We can give 3 inputs: clock, enable and available. + * An output of 7 bits is given out when clockInp = 1. + * @extends CircuitElement + * @param {number} x - x coord of element + * @param {number} y - y coord of element + * @param {Scope=} scope - the ciruit in which we want the Element + * @param {string=} dir - direcion in which element has to drawn + * @category sequential + */ +import { colors } from '../themer/themer' +export default class Keyboard extends CircuitElement { + constructor(x, y, scope = globalScope, bufferSize = 32) { + super(x, y, scope, 'RIGHT', 1) + /* + this.scope['Keyboard'].push(this); + */ + this.directionFixed = true + this.fixedBitWidth = true + + this.bufferSize = bufferSize || parseInt(prompt('Enter buffer size:')) + this.elementWidth = Math.max(80, Math.ceil(this.bufferSize / 2) * 20) + this.elementHeight = 40 // Math.max(40,Math.ceil(this.rows*15/20)*20); + this.setWidth(this.elementWidth / 2) + this.setHeight(this.elementHeight / 2) + + this.clockInp = new Node( + -this.elementWidth / 2, + this.elementHeight / 2 - 10, + 0, + this, + 1, + 'Clock' + ) + this.asciiOutput = new Node( + 30, + this.elementHeight / 2, + 1, + this, + 7, + 'Ascii Output' + ) + this.available = new Node( + 10, + this.elementHeight / 2, + 1, + this, + 1, + 'Available' + ) + this.reset = new Node(-10, this.elementHeight / 2, 0, this, 1, 'Reset') + this.en = new Node(-30, this.elementHeight / 2, 0, this, 1, 'Enable') + this.prevClockState = 0 + this.buffer = '' + this.bufferOutValue = undefined + } + + /** + * @memberof Keyboard + * this funcion sets the size of maximum input that can + * be given to the keyboard at once before it starts sending data. + */ + changeBufferSize(size) { + if (size == undefined || size < 20 || size > 100) return + if (this.bufferSize == size) return + var obj = new Keyboard(this.x, this.y, this.scope, size) + this.delete() + simulationArea.lastSelected = obj + return obj + } + + /** + * @memberof Keyboard + * Adds the keyy pressed to the buffer + */ + keyDown(key) { + if (key.length != 1) return + this.buffer += key + if (this.buffer.length > this.bufferSize) { + this.buffer = this.buffer.slice(1) + } + } + + /** + * @memberof Keyboard + * not resolvable if enable = 0 or clock is undefined + */ + isResolvable() { + if (this.reset.value == 1) return true + if ( + this.en.value == 0 || + (this.en.connections.length && this.en.value == undefined) + ) + return false + else if (this.clockInp.value == undefined) return false + return true + } + + /** + * @memberof Keyboard + * Whenever clock is enabled (1) then one charecter + * from the buffer is converted to ascii and transmitted + * through the output nodes. + */ + resolve() { + if (this.reset.value == 1) { + this.buffer = '' + return + } + if (this.en.value == 0) { + return + } + + if (this.available.value != 0) { + this.available.value = 0 // this.bufferOutValue; + simulationArea.simulationQueue.add(this.available) + } + + if (this.clockInp.value == this.prevClockState) { + if (this.clockInp.value == 0) { + if (this.buffer.length) { + this.bufferOutValue = this.buffer[0].charCodeAt(0) + } else { + this.bufferOutValue = undefined + } + } + } else if (this.clockInp.value != undefined) { + if (this.clockInp.value == 1 && this.buffer.length) { + if (this.bufferOutValue == this.buffer[0].charCodeAt(0)) { + // WHY IS THIS REQUIRED ?? + this.buffer = this.buffer.slice(1) + } + } else if (this.buffer.length) { + this.bufferOutValue = this.buffer[0].charCodeAt(0) + } else { + this.bufferOutValue = undefined + } + this.prevClockState = this.clockInp.value + } + + if (this.asciiOutput.value != this.bufferOutValue) { + this.asciiOutput.value = this.bufferOutValue + simulationArea.simulationQueue.add(this.asciiOutput) + } + + if (this.bufferOutValue !== undefined && this.available.value != 1) { + this.available.value = 1 // this.bufferOutValue; + simulationArea.simulationQueue.add(this.available) + } + } + + customSave() { + var data = { + nodes: { + clockInp: findNode(this.clockInp), + asciiOutput: findNode(this.asciiOutput), + available: findNode(this.available), + reset: findNode(this.reset), + en: findNode(this.en), + }, + constructorParamaters: [this.bufferSize], + } + return data + } + + customDraw() { + var ctx = simulationArea.context + // + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.beginPath() + ctx.lineWidth = correctWidth(3) + var xx = this.x + var yy = this.y + moveTo( + ctx, + -this.elementWidth / 2, + this.elementHeight / 2 - 15, + xx, + yy, + this.direction + ) + lineTo( + ctx, + 5 - this.elementWidth / 2, + this.elementHeight / 2 - 10, + xx, + yy, + this.direction + ) + lineTo( + ctx, + -this.elementWidth / 2, + this.elementHeight / 2 - 5, + xx, + yy, + this.direction + ) + + ctx.stroke() + + ctx.beginPath() + ctx.fillStyle = colors['input_text'] + ctx.textAlign = 'center' + var lineData = + this.buffer + ' '.repeat(this.bufferSize - this.buffer.length) + fillText3(ctx, lineData, 0, +5, xx, yy, 15, 'Courier New', 'center') + ctx.fill() + } +} + +Keyboard.prototype.tooltipText = 'Keyboard' +Keyboard.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/6sequentialelements?id=keyboard' + +Keyboard.prototype.mutableProperties = { + bufferSize: { + name: 'Buffer Size', + type: 'number', + max: '100', + min: '20', + func: 'changeBufferSize', + }, +} + +Keyboard.prototype.objectType = 'Keyboard' diff --git a/v1/src/simulator/src/sequential/RAM.js b/v1/src/simulator/src/sequential/RAM.js new file mode 100644 index 00000000..b406f921 --- /dev/null +++ b/v1/src/simulator/src/sequential/RAM.js @@ -0,0 +1,374 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, fillText2, fillText4, drawCircle2 } from '../canvasApi' +import { parseNumber, showMessage } from '../utils' +/** + * @class + * RAM Component. + * @extends CircuitElement + * @param {number} x - x coord of element + * @param {number} y - y coord of element + * @param {Scope=} scope - the ciruit in which we want the Element + * @param {string=} dir - direcion in which element has to drawn + * + * Two settings are available: + * - addressWidth: 1 to 20, default=10. Controls the width of the address input. + * - bitWidth: 1 to 32, default=8. Controls the width of data pins. + * + * Amount of memory in the element is 2^addressWidth x bitWidth bits. + * Minimum RAM size is: 2^1 x 1 = 2 bits. + * Maximum RAM size is: 2^20 x 32 = 1M x 32 bits => 32 Mbits => 4MB. + * Maximum 8-bits size: 2^20 x 8 = 1M x 8 bits => 1MB. + * Default RAM size is: 2^10 x 8 = 1024 bytes => 1KB. + * + * RAMs are volatile therefore this component does not persist the memory contents. + * + * Changes to addressWidth and bitWidth also cause data to be lost. + * Think of these operations as being equivalent to taking a piece of RAM out of a + * circuit board and replacing it with another RAM of different size. + * + * The contents of the RAM can be reset to zero by setting the RESET pin 1 or + * or by selecting the component and pressing the "Reset" button in the properties window. + * + * The contents of the RAM can be dumped to the console by transitioning CORE DUMP pin to 1 + * or by selecting the component and pressing the "Core Dump" button in the properties window. + * Address spaces that have not been written will show up as `undefined` in the core dump. + * + * NOTE: The maximum address width of 20 is arbitrary. + * Larger values are possible, but in practice circuits won't need this much + * memory and keeping the value small helps avoid allocating too much memory on the browser. + * Internally we use a sparse array, so only the addresses that are written are actually + * allocated. Nevertheless, it is better to prevent large allocations from happening + * by keeping the max addressWidth small. If needed, we can increase the max. + * @category sequential + */ +import { colors } from '../themer/themer' +import { showError } from '../utils' +export default class RAM extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + bitWidth = 8, + addressWidth = 10 + ) { + super(x, y, scope, dir, Math.min(Math.max(1, bitWidth), 32)) + /* + this.scope['RAM'].push(this); + */ + this.setDimensions(60, 40) + + this.directionFixed = true + this.labelDirection = 'UP' + + this.addressWidth = Math.min( + Math.max(1, addressWidth), + this.maxAddressWidth + ) + this.address = new Node( + -this.leftDimensionX, + -20, + 0, + this, + this.addressWidth, + 'ADDRESS' + ) + this.dataIn = new Node( + -this.leftDimensionX, + 0, + 0, + this, + this.bitWidth, + 'DATA IN' + ) + this.write = new Node(-this.leftDimensionX, 20, 0, this, 1, 'WRITE') + this.reset = new Node(0, this.downDimensionY, 0, this, 1, 'RESET') + this.coreDump = new Node( + -20, + this.downDimensionY, + 0, + this, + 1, + 'CORE DUMP' + ) + this.dataOut = new Node( + this.rightDimensionX, + 0, + 1, + this, + this.bitWidth, + 'DATA OUT' + ) + this.prevCoreDumpValue = undefined + + this.clearData() + } + + customSave() { + return { + // NOTE: data is not persisted since RAMs are volatile. + constructorParamaters: [ + this.direction, + this.bitWidth, + this.addressWidth, + ], + nodes: { + address: findNode(this.address), + dataIn: findNode(this.dataIn), + write: findNode(this.write), + reset: findNode(this.reset), + coreDump: findNode(this.coreDump), + dataOut: findNode(this.dataOut), + }, + } + } + + newBitWidth(value) { + value = parseInt(value) + if ( + !isNaN(value) && + this.bitWidth != value && + value >= 1 && + value <= 32 + ) { + this.bitWidth = value + this.dataIn.bitWidth = value + this.dataOut.bitWidth = value + this.clearData() + } + } + + changeAddressWidth(value) { + value = parseInt(value) + if ( + !isNaN(value) && + this.addressWidth != value && + value >= 1 && + value <= this.maxAddressWidth + ) { + this.addressWidth = value + this.address.bitWidth = value + this.clearData() + } + } + + clearData() { + this.data = new Array(Math.pow(2, this.addressWidth)) + this.tooltipText = `${this.memSizeString()} ${this.shortName}` + } + + isResolvable() { + return ( + this.address.value !== undefined || + this.reset.value !== undefined || + this.coreDump.value !== undefined + ) + } + + resolve() { + if (this.write.value == 1) { + this.data[this.address.value] = this.dataIn.value + } + + if (this.reset.value == 1) { + this.clearData() + } + + if ( + this.coreDump.value && + this.prevCoreDumpValue != this.coreDump.value + ) { + this.dump() + } + this.prevCoreDumpValue = this.coreDump.value + + this.dataOut.value = this.data[this.address.value] || 0 + simulationArea.simulationQueue.add(this.dataOut) + } + + customDraw() { + var ctx = simulationArea.context + // + var xx = this.x + var yy = this.y + + ctx.beginPath() + ctx.strokeStyle = 'gray' + ctx.fillStyle = this.write.value ? 'red' : 'lightgreen' + ctx.lineWidth = correctWidth(1) + drawCircle2(ctx, 50, -30, 3, xx, yy, this.direction) + ctx.fill() + ctx.stroke() + + ctx.beginPath() + ctx.textAlign = 'center' + ctx.fillStyle = 'black' + fillText4(ctx, this.memSizeString(), 0, -10, xx, yy, this.direction, 12) + fillText4(ctx, this.shortName, 0, 10, xx, yy, this.direction, 12) + fillText2( + ctx, + 'A', + this.address.x + 12, + this.address.y, + xx, + yy, + this.direction + ) + fillText2( + ctx, + 'DI', + this.dataIn.x + 12, + this.dataIn.y, + xx, + yy, + this.direction + ) + fillText2( + ctx, + 'W', + this.write.x + 12, + this.write.y, + xx, + yy, + this.direction + ) + fillText2( + ctx, + 'DO', + this.dataOut.x - 15, + this.dataOut.y, + xx, + yy, + this.direction + ) + ctx.fill() + } + + memSizeString() { + var mag = ['', 'K', 'M'] + var unit = + this.bitWidth == 8 + ? 'B' + : this.bitWidth == 1 + ? 'b' + : ` x ${this.bitWidth}b` + var v = Math.pow(2, this.addressWidth) + var m = 0 + while (v >= 1024 && m < mag.length - 1) { + v /= 1024 + m++ + } + return v + mag[m] + unit + } + + dump() { + var logLabel = console.group && this.label + if (logLabel) { + console.group(this.label) + } + + showMessage('Data dumped to developer Console') + + console.log(JSON.stringify(this.data)) + + if (logLabel) { + console.groupEnd() + } + } + + dblclick() { + this.promptData() + } + + promptData() { + var data = prompt( + 'Enter Data (separated by space, comma, tab or newline) (data can be in hex, binary, octal or decimal)' + ) + if (!data) { + showError('No data entered.') + return + } + var oldData = this.data + try { + var ramSize = 1 << this.addressWidth + var maxNumber = 1 << this.bitWidth + this.clearData() + + data = data.split(/[, \n\t]/) + data = data.filter((x) => x.length) + if (data.length > ramSize) { + throw `Capacity: ${ramSize}. But ${data.length} data cells found` + } + + for (var i = 0; i < data.length; i++) { + var dataCell = parseNumber(data[i]) + if (isNaN(dataCell)) + throw `Address ${i}: ${data[i]} is not a number` + if (dataCell < 0) throw `Address ${i}: ${data[i]} is negative` + if (dataCell >= maxNumber) + throw `Address ${i}: ${data[i]} is too large` + this.data[i] = dataCell + } + showMessage(`${data.length} data cells loaded`) + } catch (e) { + this.data = oldData + showError(e) + } + } + + //This is a RAM without a clock - not normal + //reset is not supported + static moduleVerilog() { + return ` + module RAM(dout, addr, din, we, dmp, rst); + parameter WIDTH = 8; + parameter ADDR = 10; + output [WIDTH-1:0] dout; + input [ADDR-1:0] addr; + input [WIDTH-1:0] din; + input we; + input dmp; + input rst; + reg [WIDTH-1:0] mem [2**ADDR-1:0]; + + assign dout = mem[addr]; + + always @ (*) begin + if (!we) + mem[addr] = din; + end + endmodule + ` + } +} + +RAM.prototype.tooltipText = 'Random Access Memory' +RAM.prototype.shortName = 'RAM' +RAM.prototype.maxAddressWidth = 20 +RAM.prototype.mutableProperties = { + addressWidth: { + name: 'Address Width', + type: 'number', + max: '20', + min: '1', + func: 'changeAddressWidth', + }, + dump: { + name: 'Core Dump', + type: 'button', + func: 'dump', + }, + load: { + name: 'Load Data', + type: 'button', + func: 'promptData', + }, + reset: { + name: 'Reset', + type: 'button', + func: 'clearData', + }, +} +RAM.prototype.objectType = 'RAM' diff --git a/v1/src/simulator/src/sequential/Rom.js b/v1/src/simulator/src/sequential/Rom.js new file mode 100644 index 00000000..69bb544b --- /dev/null +++ b/v1/src/simulator/src/sequential/Rom.js @@ -0,0 +1,314 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, rect2, fillText3 } from '../canvasApi' +/** + * @class + * Rom + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {Array=} data - bit width per node. + * @category sequential + */ +import { colors } from '../themer/themer' +export default class Rom extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + data = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + ) { + super(x, y, scope, 'RIGHT', 1) + /* + this.scope['Rom'].push(this); + */ + this.fixedBitWidth = true + this.directionFixed = true + this.rectangleObject = false + this.setDimensions(80, 50) + this.memAddr = new Node(-80, 0, 0, this, 4, 'Address') + this.en = new Node(0, 50, 0, this, 1, 'Enable') + this.dataOut = new Node(80, 0, 1, this, 8, 'DataOut') + this.data = + data || + prompt('Enter data') + .split(' ') + .map((lambda) => parseInt(lambda, 16)) + } + + /** + * @memberof Rom + * Checks if the element is resolvable + * @return {boolean} + */ + isResolvable() { + if ( + (this.en.value === 1 || this.en.connections.length === 0) && + this.memAddr.value !== undefined + ) + return true + return false + } + + /** + * @memberof Rom + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.data], + nodes: { + memAddr: findNode(this.memAddr), + dataOut: findNode(this.dataOut), + en: findNode(this.en), + }, + } + return data + } + + /** + * @memberof Rom + * function to find position of the index of part of rom selected. + * @return {number} + */ + findPos() { + const i = Math.floor((simulationArea.mouseX - this.x + 35) / 20) + const j = Math.floor((simulationArea.mouseY - this.y + 35) / 16) + if (i < 0 || j < 0 || i > 3 || j > 3) return undefined + return j * 4 + i + } + + /** + * @memberof Rom + * listener function to set selected index + * @return {number} + */ + click() { + // toggle + this.selectedIndex = this.findPos() + } + + /** + * @memberof Rom + * to take input in rom + * @return {number} + */ + keyDown(key) { + if (key === 'Backspace') this.delete() + if (this.selectedIndex === undefined) return + key = key.toLowerCase() + if (!~'1234567890abcdef'.indexOf(key)) return + + this.data[this.selectedIndex] = + (this.data[this.selectedIndex] * 16 + parseInt(key, 16)) % 256 + } + + /** + * @memberof Rom + * function to draw element + */ + customDraw() { + const ctx = simulationArea.context + const xx = this.x + const yy = this.y + const hoverIndex = this.findPos() + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.lineWidth = correctWidth(3) + ctx.beginPath() + rect2( + ctx, + -this.leftDimensionX, + -this.upDimensionY, + this.leftDimensionX + this.rightDimensionX, + this.upDimensionY + this.downDimensionY, + this.x, + this.y, + [this.direction, 'RIGHT'][+this.directionFixed] + ) + if ( + hoverIndex === undefined && + ((!simulationArea.shiftDown && this.hover) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this)) + ) + ctx.fillStyle = colors['hover_select'] + ctx.fill() + ctx.stroke() + ctx.strokeStyle = 'black' + ctx.fillStyle = '#fafafa' + ctx.lineWidth = correctWidth(1) + ctx.beginPath() + for (let i = 0; i < 16; i += 4) { + for (let j = i; j < i + 4; j++) { + rect2(ctx, (j % 4) * 20, i * 4, 20, 16, xx - 35, yy - 35) + } + } + ctx.fill() + ctx.stroke() + if (hoverIndex !== undefined) { + ctx.beginPath() + ctx.fillStyle = 'yellow' + rect2( + ctx, + (hoverIndex % 4) * 20, + Math.floor(hoverIndex / 4) * 16, + 20, + 16, + xx - 35, + yy - 35 + ) + ctx.fill() + ctx.stroke() + } + if (this.selectedIndex !== undefined) { + ctx.beginPath() + ctx.fillStyle = 'lightgreen' + rect2( + ctx, + (this.selectedIndex % 4) * 20, + Math.floor(this.selectedIndex / 4) * 16, + 20, + 16, + xx - 35, + yy - 35 + ) + ctx.fill() + ctx.stroke() + } + if (this.memAddr.value !== undefined) { + ctx.beginPath() + ctx.fillStyle = colors['input_text'] + rect2( + ctx, + (this.memAddr.value % 4) * 20, + Math.floor(this.memAddr.value / 4) * 16, + 20, + 16, + xx - 35, + yy - 35 + ) + ctx.fill() + ctx.stroke() + } + + ctx.beginPath() + ctx.fillStyle = 'Black' + fillText3(ctx, 'A', -65, 5, xx, yy, 16, 'Raleway', 'right') + fillText3(ctx, 'D', 75, 5, xx, yy, 16, 'Raleway', 'right') + fillText3(ctx, 'En', 5, 47, xx, yy, 16, 'Raleway', 'right') + ctx.fill() + + ctx.beginPath() + ctx.fillStyle = 'Black' + for (let i = 0; i < 16; i += 4) { + for (let j = i; j < i + 4; j++) { + let s = this.data[j].toString(16) + if (s.length < 2) s = `0${s}` + fillText3( + ctx, + s, + (j % 4) * 20, + i * 4, + xx - 35 + 10, + yy - 35 + 12, + 14, + 'Raleway', + 'center' + ) + } + } + ctx.fill() + + ctx.beginPath() + ctx.fillStyle = 'Black' + for (let i = 0; i < 16; i += 4) { + let s = i.toString(16) + if (s.length < 2) s = `0${s}` + fillText3( + ctx, + s, + 0, + i * 4, + xx - 40, + yy - 35 + 12, + 14, + 'Raleway', + 'right' + ) + } + ctx.fill() + } + + /** + * @memberof Rom + * resolve output values based on inputData + */ + resolve() { + if (this.isResolvable() === false) { + return + } + this.dataOut.value = this.data[this.memAddr.value] + simulationArea.simulationQueue.add(this.dataOut) + } + + verilogBaseType() { + return this.verilogName() + (Rom.selSizes.length - 1) + } + //this code to generate Verilog + generateVerilog() { + Rom.selSizes.push(this.data) + return CircuitElement.prototype.generateVerilog.call(this) + } + + //This code to determine what sizes are used to generate the needed modules + //generate the needed modules + static moduleVerilog() { + var output = '' + + for (var i = 0; i < Rom.selSizes.length; i++) { + output += ` + module Rom${i}(dout, addr, en); + parameter WIDTH = 8; + parameter ADDR = 4; + output reg [WIDTH-1:0] dout; + input [ADDR-1:0] addr; + input en; + + always @ (*) begin + if (en == 0) + dout = {WIDTH{1'bz}}; + else + case (addr) + ` + for (var j = 0; j < 1 << 4; j++) { + output += + ' ' + j + ' : dout = ' + Rom.selSizes[i][j] + ';\n' + } + + output += ` endcase + end + endmodule + ` + } + + return output + } + //reset the sized before Verilog generation + static resetVerilog() { + Rom.selSizes = [] + } +} + +/** + * @memberof Rom + * Help Tip + * @type {string} + * @category sequential + */ +Rom.prototype.tooltipText = 'Read-only memory' +Rom.prototype.helplink = 'https://docs.circuitverse.org/#/chapter4/6sequentialelements?id=rom' +Rom.prototype.objectType = 'Rom' diff --git a/v1/src/simulator/src/sequential/SRflipFlop.js b/v1/src/simulator/src/sequential/SRflipFlop.js new file mode 100644 index 00000000..af1a16ce --- /dev/null +++ b/v1/src/simulator/src/sequential/SRflipFlop.js @@ -0,0 +1,131 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, fillText } from '../canvasApi' +/** + * @class + * SRflipFlop + * SR flip flop has 6 input nodes: + * clock, S input, R input, preset, reset ,enable. + * @extends CircuitElement + * @param {number} x - x coord of element + * @param {number} y - y coord of element + * @param {Scope=} scope - the ciruit in which we want the Element + * @param {string=} dir - direcion in which element has to drawn + * @category sequential + */ +import { colors } from '../themer/themer' +export default class SRflipFlop extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT') { + super(x, y, scope, dir, 1) + /* + this.scope['SRflipFlop'].push(this); + */ + this.directionFixed = true + this.fixedBitWidth = true + this.setDimensions(20, 20) + this.rectangleObject = true + this.R = new Node(-20, +10, 0, this, 1, 'R') + this.S = new Node(-20, -10, 0, this, 1, 'S') + this.qOutput = new Node(20, -10, 1, this, 1, 'Q') + this.qInvOutput = new Node(20, 10, 1, this, 1, 'Q Inverse') + this.reset = new Node(10, 20, 0, this, 1, 'Asynchronous Reset') + this.preset = new Node(0, 20, 0, this, 1, 'Preset') + this.en = new Node(-10, 20, 0, this, 1, 'Enable') + this.state = 0 + // this.slaveState = 0; + // this.prevClockState = 0; + // this.wasClicked = false; + } + + newBitWidth(bitWidth) { + this.bitWidth = bitWidth + this.dInp.bitWidth = bitWidth + this.qOutput.bitWidth = bitWidth + this.qInvOutput.bitWidth = bitWidth + this.preset.bitWidth = bitWidth + } + + /** + * @memberof SRflipFlop + * always resolvable + */ + isResolvable() { + return true + if (this.reset.value == 1) return true + if (this.S.value != undefined && this.R.value != undefined) return true + return false + } + + /** + * @memberof SRflipFlop + * function to resolve SR flip flop if S != R we can + * set this.state to value S. + */ + resolve() { + if (this.reset.value == 1) { + this.state = this.preset.value || 0 + } else if ( + (this.en.value == 1 || this.en.connections == 0) && + this.S.value ^ this.R.value + ) { + this.state = this.S.value + } + + if (this.qOutput.value != this.state) { + this.qOutput.value = this.state + this.qInvOutput.value = this.flipBits(this.state) + simulationArea.simulationQueue.add(this.qOutput) + simulationArea.simulationQueue.add(this.qInvOutput) + } + } + + customSave() { + var data = { + nodes: { + S: findNode(this.S), + R: findNode(this.R), + qOutput: findNode(this.qOutput), + qInvOutput: findNode(this.qInvOutput), + reset: findNode(this.reset), + preset: findNode(this.preset), + en: findNode(this.en), + }, + constructorParamaters: [this.direction], + } + return data + } + + customDraw() { + var ctx = simulationArea.context + // + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.beginPath() + ctx.lineWidth = correctWidth(3) + var xx = this.x + var yy = this.y + + // rect(ctx, xx - 20, yy - 20, 40, 40); + // moveTo(ctx, -20, 5, xx, yy, this.direction); + // lineTo(ctx, -15, 10, xx, yy, this.direction); + // lineTo(ctx, -20, 15, xx, yy, this.direction); + + // if ((this.b.hover&&!simulationArea.shiftDown)|| simulationArea.lastSelected == this || simulationArea.multipleObjectSelections.contains(this)) ctx.fillStyle = "rgba(255, 255, 32,0.8)";ctx.fill(); + ctx.stroke() + + ctx.beginPath() + ctx.font = '20px Raleway' + ctx.fillStyle = colors['input_text'] + ctx.textAlign = 'center' + fillText(ctx, this.state.toString(16), xx, yy + 5) + ctx.fill() + } +} + +SRflipFlop.prototype.tooltipText = 'SR FlipFlop ToolTip : SR FlipFlop Selected.' + +SRflipFlop.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/6sequentialelements?id=sr-flip-flop' + +SRflipFlop.prototype.objectType = 'SRflipFlop' diff --git a/v1/src/simulator/src/sequential/TTY.js b/v1/src/simulator/src/sequential/TTY.js new file mode 100644 index 00000000..d0c89461 --- /dev/null +++ b/v1/src/simulator/src/sequential/TTY.js @@ -0,0 +1,250 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, fillText3 } from '../canvasApi' +import { colors } from '../themer/themer' + +/** + * @class + * TTY + * TypeWriter - We can give 4 inputs: + * clock and input of 7 bits are main input required + * on the edge change the data is added onto the display + * screen of the typewriter + * @extends CircuitElement + * @param {number} x - x coord of element + * @param {number} y - y coord of element + * @param {Scope=} scope - the ciruit in which we want the Element + * @param {string=} dir - direcion in which element has to drawn + * @category sequential + */ +export default class TTY extends CircuitElement { + constructor(x, y, scope = globalScope, rows = 3, cols = 32) { + super(x, y, scope, 'RIGHT', 1) + /* + this.scope['TTY'].push(this); + */ + this.directionFixed = true + this.fixedBitWidth = true + this.cols = cols || parseInt(prompt('Enter cols:')) + this.rows = rows || parseInt(prompt('Enter rows:')) + + this.elementWidth = Math.max(40, Math.ceil(this.cols / 2) * 20) + this.elementHeight = Math.max(40, Math.ceil((this.rows * 15) / 20) * 20) + this.setWidth(this.elementWidth / 2) + this.setHeight(this.elementHeight / 2) + // this.element = new Element(x, y, "TTY",this.elementWidth/2, this,this.elementHeight/2); + + this.clockInp = new Node( + -this.elementWidth / 2, + this.elementHeight / 2 - 10, + 0, + this, + 1, + 'Clock' + ) + this.asciiInp = new Node( + -this.elementWidth / 2, + this.elementHeight / 2 - 30, + 0, + this, + 7, + 'Ascii Input' + ) + // this.qOutput = new Node(20, -10, 1, this); + this.reset = new Node( + 30 - this.elementWidth / 2, + this.elementHeight / 2, + 0, + this, + 1, + 'Reset' + ) + this.en = new Node( + 10 - this.elementWidth / 2, + this.elementHeight / 2, + 0, + this, + 1, + 'Enable' + ) + // this.masterState = 0; + // this.slaveState = 0; + this.prevClockState = 0 + + this.data = '' + this.buffer = '' + } + + /** + * @memberof TTY + * this funciton is used to change the size of the screen + */ + changeRowSize(size) { + if (size == undefined || size < 1 || size > 10) return + if (this.rows == size) return + var obj = new TTY(this.x, this.y, this.scope, size, this.cols) + this.delete() + simulationArea.lastSelected = obj + return obj + } + + /** + * @memberof TTY + * this funciton is used to change the size of the screen + */ + changeColSize(size) { + if (size == undefined || size < 20 || size > 100) return + if (this.cols == size) return + var obj = new TTY(this.x, this.y, this.scope, this.rows, size) + this.delete() + simulationArea.lastSelected = obj + return obj + } + + /** + * @memberof TTY + * if no input or enable key is set to 0 returns false + */ + isResolvable() { + if (this.reset.value == 1) return true + if ( + this.en.value == 0 || + (this.en.connections.length && this.en.value == undefined) + ) + return false + else if (this.clockInp.value == undefined) return false + else if (this.asciiInp.value == undefined) return false + return true + } + + /** + * @memberof TTY + * To resolve the Typewriter clock and input of 7 bits are + * used to get the ascii and then on the edge change the + * data is added onto the display screen of the typewriter. + */ + resolve() { + if (this.reset.value == 1) { + this.data = '' + return + } + if (this.en.value == 0) { + this.buffer = '' + return + } + + if (this.clockInp.value == this.prevClockState) { + if (this.clockInp.value == 0) { + this.buffer = String.fromCharCode(this.asciiInp.value) + } + } else if (this.clockInp.value != undefined) { + if (this.clockInp.value == 1) { + this.data += this.buffer + if (this.data.length > this.cols * this.rows) { + this.data = this.data.slice(1) + } + } else if (this.clockInp.value == 0) { + this.buffer = String.fromCharCode(this.asciiInp.value) + } + this.prevClockState = this.clockInp.value + } + } + + customSave() { + var data = { + nodes: { + clockInp: findNode(this.clockInp), + asciiInp: findNode(this.asciiInp), + reset: findNode(this.reset), + en: findNode(this.en), + }, + constructorParamaters: [this.rows, this.cols], + } + return data + } + + customDraw() { + var ctx = simulationArea.context + // + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.beginPath() + ctx.lineWidth = correctWidth(3) + var xx = this.x + var yy = this.y + // rect(ctx, xx - this.elementWidth/2, yy - this.elementHeight/2, this.elementWidth, this.elementHeight); + + moveTo( + ctx, + -this.elementWidth / 2, + this.elementHeight / 2 - 15, + xx, + yy, + this.direction + ) + lineTo( + ctx, + 5 - this.elementWidth / 2, + this.elementHeight / 2 - 10, + xx, + yy, + this.direction + ) + lineTo( + ctx, + -this.elementWidth / 2, + this.elementHeight / 2 - 5, + xx, + yy, + this.direction + ) + + // if ((this.b.hover&&!simulationArea.shiftDown)|| simulationArea.lastSelected == this || simulationArea.multipleObjectSelections.contains(this)) + // ctx.fillStyle = "rgba(255, 255, 32,0.8)"; + ctx.stroke() + + ctx.beginPath() + ctx.fillStyle = colors['input_text'] + ctx.textAlign = 'center' + var startY = -7.5 * this.rows + 3 + for (var i = 0; i < this.data.length; i += this.cols) { + var lineData = this.data.slice(i, i + this.cols) + lineData += ' '.repeat(this.cols - lineData.length) + fillText3( + ctx, + lineData, + 0, + startY + (i / this.cols) * 15 + 9, + xx, + yy, + 15, + 'Courier New', + 'center' + ) + } + ctx.fill() + } +} + +TTY.prototype.tooltipText = 'TTY ToolTip : Tele typewriter selected.' +TTY.prototype.helplink = 'https://docs.circuitverse.org/#/chapter4/6sequentialelements?id=tty' + +TTY.prototype.mutableProperties = { + cols: { + name: 'Columns', + type: 'number', + max: '100', + min: '20', + func: 'changeColSize', + }, + rows: { + name: 'Rows', + type: 'number', + max: '10', + min: '1', + func: 'changeRowSize', + }, +} + +TTY.prototype.objectType = 'TTY' diff --git a/v1/src/simulator/src/sequential/TflipFlop.js b/v1/src/simulator/src/sequential/TflipFlop.js new file mode 100644 index 00000000..cedb9f6b --- /dev/null +++ b/v1/src/simulator/src/sequential/TflipFlop.js @@ -0,0 +1,179 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, fillText } from '../canvasApi' +import { colors } from '../themer/themer' + +/** + * @class + * TflipFlop + * T flip flop has 5 input nodes: + * clock, data input, preset, reset ,enable. + * @extends CircuitElement + * @param {number} x - x coord of element + * @param {number} y - y coord of element + * @param {Scope=} scope - the ciruit in which we want the Element + * @param {string=} dir - direcion in which element has to drawn + * @category sequential + */ +export default class TflipFlop extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT') { + super(x, y, scope, dir, 1) + /* + this.scope['TflipFlop'].push(this); + */ + this.directionFixed = true + this.fixedBitWidth = true + this.setDimensions(20, 20) + this.rectangleObject = true + this.clockInp = new Node(-20, +10, 0, this, 1, 'Clock') + this.dInp = new Node(-20, -10, 0, this, this.bitWidth, 'T') + this.qOutput = new Node(20, -10, 1, this, this.bitWidth, 'Q') + this.qInvOutput = new Node(20, 10, 1, this, this.bitWidth, 'Q Inverse') + this.reset = new Node(10, 20, 0, this, 1, 'Asynchronous Reset') + this.preset = new Node(0, 20, 0, this, this.bitWidth, 'Preset') + this.en = new Node(-10, 20, 0, this, 1, 'Enable') + this.masterState = 0 + this.slaveState = 0 + this.prevClockState = 0 + + // this.wasClicked = false; + } + + /** + * @memberof TflipFlop + * returns true if clock is defined + */ + isResolvable() { + if (this.reset.value == 1) return true + if (this.clockInp.value != undefined && this.dInp.value != undefined) + return true + return false + } + + /** + * @memberof TflipFlop + * @param {number} bitWidth - the new bitwidth + */ + newBitWidth(bitWidth) { + this.bitWidth = bitWidth + this.dInp.bitWidth = bitWidth + this.qOutput.bitWidth = bitWidth + this.qInvOutput.bitWidth = bitWidth + this.preset.bitWidth = bitWidth + } + + /** + * @memberof TflipFlop + * On the leading edge of the clock signal (LOW-to-HIGH) the first stage, + * the “master” latches the input condition at D, while the output stage is deactivated. + * On the trailing edge of the clock signal (HIGH-to-LOW) the second “slave” stage is + * now activated, latching on to the output from the first master circuit. + * This fuction sets the value for the node qOutput based on + * the previous state and input of the clock by taking xor. + * We flip the bits to find qInvOutput + */ + resolve() { + if (this.reset.value == 1) { + // if reset bit is set + this.masterState = this.slaveState = this.preset.value || 0 + } else if (this.en.value == 0) { + // if enabled bit is 0 + this.prevClockState = this.clockInp.value + } else if (this.en.value == 1 || this.en.connections.length == 0) { + // if enabled bit is 1 or not connected to anything. + if (this.clockInp.value == this.prevClockState) { + if (this.clockInp.value == 0 && this.dInp.value != undefined) { + // value is xor of + this.masterState = this.dInp.value ^ this.slaveState + } + } else if (this.clockInp.value != undefined) { + if (this.clockInp.value == 1) { + this.slaveState = this.masterState + } else if ( + this.clockInp.value == 0 && + this.dInp.value != undefined + ) { + this.masterState = this.dInp.value ^ this.slaveState + } + this.prevClockState = this.clockInp.value + } + } + + if (this.qOutput.value != this.slaveState) { + this.qOutput.value = this.slaveState + this.qInvOutput.value = this.flipBits(this.slaveState) + simulationArea.simulationQueue.add(this.qOutput) + simulationArea.simulationQueue.add(this.qInvOutput) + } + } + + customSave() { + var data = { + nodes: { + clockInp: findNode(this.clockInp), + dInp: findNode(this.dInp), + qOutput: findNode(this.qOutput), + qInvOutput: findNode(this.qInvOutput), + reset: findNode(this.reset), + preset: findNode(this.preset), + en: findNode(this.en), + }, + constructorParamaters: [this.direction, this.bitWidth], + } + return data + } + + customDraw() { + var ctx = simulationArea.context + // + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.beginPath() + ctx.lineWidth = correctWidth(3) + var xx = this.x + var yy = this.y + // rect(ctx, xx - 20, yy - 20, 40, 40); + moveTo(ctx, -20, 5, xx, yy, this.direction) + lineTo(ctx, -15, 10, xx, yy, this.direction) + lineTo(ctx, -20, 15, xx, yy, this.direction) + + // if ((this.b.hover&&!simulationArea.shiftDown)|| simulationArea.lastSelected == this || simulationArea.multipleObjectSelections.contains(this)) ctx.fillStyle = "rgba(255, 255, 32,0.8)";ctx.fill(); + ctx.stroke() + ctx.beginPath() + ctx.font = '20px Raleway' + ctx.fillStyle = colors['input_text'] + ctx.textAlign = 'center' + fillText(ctx, this.slaveState.toString(16), xx, yy + 5) + ctx.fill() + } + + static moduleVerilog() { + return ` + module TflipFlop(q, q_inv, clk, t, a_rst, pre, en); + parameter WIDTH = 1; + output reg [WIDTH-1:0] q, q_inv; + input clk, a_rst, pre, en; + input [WIDTH-1:0] t; + + always @ (posedge clk or posedge a_rst) + if (a_rst) begin + q <= 'b0; + q_inv <= 'b1; + end else if (en == 0) ; + else if (t) begin + q <= q ^ t; + q_inv <= ~q ^ t; + end + endmodule + ` + } +} + +TflipFlop.prototype.tooltipText = + 'T FlipFlop ToolTip : Changes state / Toggles whenever the clock input is strobed.' + +TflipFlop.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/6sequentialelements?id=t-flip-flop' + +TflipFlop.prototype.objectType = 'TflipFlop' diff --git a/v1/src/simulator/src/sequential/verilogRAM.js b/v1/src/simulator/src/sequential/verilogRAM.js new file mode 100644 index 00000000..d1e9c71d --- /dev/null +++ b/v1/src/simulator/src/sequential/verilogRAM.js @@ -0,0 +1,577 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, fillText2, fillText4, drawCircle2 } from '../canvasApi' +/** + * @class + * verilogRAM Component. + * @extends CircuitElement + * @param {number} x - x coord of element + * @param {number} y - y coord of element + * @param {Scope=} scope - the ciruit in which we want the Element + * @param {string=} dir - direcion in which element has to drawn + * + * Two settings are available: + * - addressWidth: 1 to 20, default=10. Controls the width of the address input. + * - bitWidth: 1 to 32, default=8. Controls the width of data pins. + * + * Amount of memory in the element is 2^addressWidth x bitWidth bits. + * Minimum verilogRAM size is: 2^1 x 1 = 2 bits. + * Maximum verilogRAM size is: 2^20 x 32 = 1M x 32 bits => 32 Mbits => 4MB. + * Maximum 8-bits size: 2^20 x 8 = 1M x 8 bits => 1MB. + * Default verilogRAM size is: 2^10 x 8 = 1024 bytes => 1KB. + * + * verilogRAMs are volatile therefore this component does not persist the memory contents. + * + * Changes to addressWidth and bitWidth also cause data to be lost. + * Think of these operations as being equivalent to taking a piece of verilogRAM out of a + * circuit board and replacing it with another verilogRAM of different size. + * + * The contents of the verilogRAM can be reset to zero by setting the RESET pin 1 or + * or by selecting the component and pressing the "Reset" button in the properties window. + * + * The contents of the verilogRAM can be dumped to the console by transitioning CORE DUMP pin to 1 + * or by selecting the component and pressing the "Core Dump" button in the properties window. + * Address spaces that have not been written will show up as `undefined` in the core dump. + * + * NOTE: The maximum address width of 20 is arbitrary. + * Larger values are possible, but in practice circuits won't need this much + * memory and keeping the value small helps avoid allocating too much memory on the browser. + * Internally we use a sparse array, so only the addresses that are written are actually + * allocated. Nevertheless, it is better to prevent large allocations from happening + * by keeping the max addressWidth small. If needed, we can increase the max. + * @category sequential + */ +import { colors } from '../themer/themer' + +function customResolve( + clockInp, + dInp, + qOutput, + en, + masterState, + slaveState, + prevClockState, + clock_polarity, + enable_polarity, + numIterations +) { + for (var i = 0; i < numIterations; i++) { + if (clock_polarity[i] != undefined) { + clock_polarity[i] == true ? 1 : 0 + } + + if (enable_polarity[i] != undefined) { + enable_polarity[i] == true ? 1 : 0 + } + + if (clock_polarity[i] == undefined && enable_polarity[i] == undefined) { + if (dInp[i].value != undefined) { + qOutput[i].value = dInp[i].value + simulationArea.simulationQueue.add(qOutput[i]) + } + } else if ( + clock_polarity[i] == undefined && + enable_polarity[i] != undefined + ) { + if ( + (en_value[i] == undefined || + en[i].value == enable_polarity[i]) && + dInp[i].value != undefined + ) { + qOutput[i].value = dInp[i].value + simulationArea.simulationQueue.add(qOutput[i]) + } + } else if ( + clock_polarity[i] != undefined && + enable_polarity[i] == undefined + ) { + if (clockInp[i].value == prevClockState[i]) { + if (clockInp[i].value == 0 && dInp[i].value != undefined) { + masterState[i] = dInp[i].value + } + } else if (clockInp[i].value != undefined) { + if (clockInp[i].value == 1) { + slaveState[i] = masterState[i] + } else if ( + clockInp[i].value == 0 && + dInp[i].value != undefined + ) { + masterState[i] = dInp[i].value + } + prevClockState[i] = clockInp[i].value + } + + if (qOutput[i].value != slaveState[i]) { + qOutput[i].value = slaveState[i] + simulationArea.simulationQueue.add(qOutput[i]) + } + } else { + if (en[i].value == 0) { + prevClockState[i] = clockInp[i].value + } else if (en[i].value == 1 || en[i].connections.length == 0) { + // if(en.value==1) // Creating Infinite Loop, WHY ?? + if (clockInp[i].value == prevClockState[i]) { + if (clockInp[i].value == 0 && dInp[i].value != undefined) { + masterState[i] = dInp[i].value + } + } else if (clockInp[i].value != undefined) { + if (clockInp[i].value == 1) { + slaveState[i] = masterState[i] + } else if ( + clockInp[i].value == 0 && + dInp[i].value != undefined + ) { + masterState[i] = dInp[i].value + } + prevClockState[i] = clockInp[i].value + } + } + + if (qOutput[i].value != slaveState[i]) { + qOutput[i].value = slaveState[i] + simulationArea.simulationQueue.add(qOutput[i]) + } + } + } +} + +export default class verilogRAM extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + bitWidth = 8, + addressWidth = 10, + memData, + words, + numRead, + numWrite, + rdports, + wrports + ) { + super(x, y, scope, dir, Math.min(Math.max(1, bitWidth), 32)) + /* + this.scope['verilogRAM'].push(this); + */ + this.setDimensions(60, 40) + + this.directionFixed = true + this.labelDirection = 'UP' + + this.addressWidth = Math.min( + Math.max(1, addressWidth), + this.maxAddressWidth + ) + + this.readAddress = [] + for (var i = 0; i < numRead; i++) { + this.readAddress.push( + new Node( + -this.leftDimensionX, + -20, + 0, + this, + this.addressWidth, + 'READ_ADDRESS' + i.toString() + ) + ) + } + + this.writeAddress = [] + for (var i = 0; i < numWrite; i++) { + this.writeAddress.push( + new Node( + -this.leftDimensionX, + -20, + 0, + this, + this.addressWidth, + 'WRITE_ADDRESS' + i.toString() + ) + ) + } + + this.writeDataIn = [] + this.writeEnable = [] + + this.writeDffClock = [] + this.writeDffDInp = [] + this.writeDffQOutput = [] + this.writeDffEn = [] + this.writeDffMasterState = [] + this.writeDffSlaveState = [] + this.writeDffprevClockState = [] + this.writeDffClockPolarity = [] + this.writeDffEnPolarity = [] + + for (var i = 0; i < numWrite; i++) { + var currWriteData = new Node( + -this.leftDimensionX, + 0, + 0, + this, + this.bitWidth, + 'DATA IN' + i.toString() + ) + + var clockInp = new Node(-20, +10, 0, this, 1, 'Clock') + var dInp = new Node(-20, -10, 0, this, this.bitWidth, 'D') + var qOutput = new Node(20, -10, 1, this, this.bitWidth, 'Q') + var en = new Node(-10, 20, 0, this, 1, 'Enable') + var masterState = 0 + var slaveState = 0 + var prevClockState = 0 + var clockPolarity = wrports[i]['clock_polarity'] + var enPolarity = wrports[i]['enable_polarity'] + + if (enPolarity == undefined) { + enPolarity = true + } + + currWriteData.connect(dInp) + + this.writeDffClock.push(clockInp) + this.writeDffDInp.push(dInp) + this.writeDffQOutput.push(qOutput) + this.writeDffEn.push(en) + this.writeDffMasterState.push(masterState) + this.writeDffSlaveState.push(slaveState) + this.writeDffprevClockState.push(prevClockState) + this.writeDffClockPolarity.push(clockPolarity) + this.writeDffEnPolarity.push(enPolarity) + + this.writeDataIn.push(currWriteData) + this.writeEnable.push( + new Node( + -this.leftDimensionX, + 20, + 0, + this, + 1, + 'WRITE_ENABLE' + i.toString() + ) + ) + } + + this.reset = new Node(0, this.downDimensionY, 0, this, 1, 'RESET') + this.coreDump = new Node( + -20, + this.downDimensionY, + 0, + this, + 1, + 'CORE DUMP' + ) + this.dataOut = [] + + this.readDffClock = [] + this.readDffDInp = [] + this.readDffQOutput = [] + this.readDffEn = [] + this.readDffMasterState = [] + this.readDffSlaveState = [] + this.readDffprevClockState = [] + this.readDffClockPolarity = [] + this.readDffEnPolarity = [] + + for (var i = 0; i < numRead; i++) { + var currReadOut = new Node( + this.rightDimensionX, + 0, + 1, + this, + this.bitWidth, + 'DATA OUT' + i.toString() + ) + + var clockInp = new Node(-20, +10, 0, this, 1, 'Clock') + var dInp = new Node(-20, -10, 0, this, this.bitWidth, 'D') + var qOutput = new Node(20, -10, 1, this, this.bitWidth, 'Q') + var en = new Node(-10, 20, 0, this, 1, 'Enable') + var masterState = 0 + var slaveState = 0 + var prevClockState = 0 + var clockPolarity = rdports[i]['clock_polarity'] + var enPolarity = rdports[i]['enable_polarity'] + + this.readDffClock.push(clockInp) + this.readDffDInp.push(dInp) + this.readDffQOutput.push(qOutput) + this.readDffEn.push(en) + this.readDffMasterState.push(masterState) + this.readDffSlaveState.push(slaveState) + this.readDffprevClockState.push(prevClockState) + this.readDffClockPolarity.push(clockPolarity) + this.readDffEnPolarity.push(enPolarity) + + currReadOut.connect(dInp) + + this.dataOut.push(currReadOut) + } + + this.prevCoreDumpValue = undefined + this.words = words + this.numRead = numRead + this.numWrite = numWrite + this.memData = memData + this.rdports = rdports + this.wrports = wrports + this.clearData() + this.fillData(memData) + } + + fillData(memData) { + for (var i = 0; i < this.words; i++) { + this.data[i] = 0 + } + var len = memData.length + var dataIndex = 0 + for (var i = 0; i < len; i++) { + if (Number.isInteger(memData[i])) { + var data = memData[i + 1] + + if (data.startsWith('x')) { + dataIndex += memData[i] + continue + } + + var dataValue = 0 + var power2 = 1 + + for (var j = this.bitWidth - 1; j >= 0; j--) { + if (data[j] == '1') { + dataValue += power2 + } + power2 *= 2 + } + + for (var j = 0; j < memData[i]; j++) { + this.data[dataIndex++] = dataValue + } + i++ + } else { + var data = memData[i] + + if (data.startsWith('x')) { + dataIndex++ + continue + } + + var dataValue = 0 + var power2 = 1 + + for (var j = this.bitWidth - 1; j >= 0; j--) { + if (data[j] == '1') { + dataValue += power2 + } + power2 *= 2 + } + + this.data[dataIndex++] = dataValue + } + } + } + + customSave() { + this.dataOut.map(findNode) + const data = { + // NOTE: data is not persisted since verilogRAMs are volatile. + constructorParamaters: [ + this.direction, + this.bitWidth, + this.addressWidth, + this.memData, + this.words, + this.numRead, + this.numWrite, + this.rdports, + this.wrports, + ], + + nodes: { + readAddress: this.readAddress.map(findNode), + writeAddress: this.writeAddress.map(findNode), + writeDataIn: this.writeDataIn.map(findNode), + writeEnable: this.writeEnable.map(findNode), + dataOut: this.dataOut.map(findNode), + reset: findNode(this.reset), + coreDump: findNode(this.coreDump), + readDffClock: this.readDffClock.map(findNode), + readDffDInp: this.readDffDInp.map(findNode), + readDffQOutput: this.readDffQOutput.map(findNode), + readDffEn: this.readDffEn.map(findNode), + writeDffClock: this.writeDffClock.map(findNode), + writeDffDInp: this.writeDffDInp.map(findNode), + writeDffQOutput: this.writeDffQOutput.map(findNode), + writeDffEn: this.writeDffEn.map(findNode), + }, + } + + return data + } + + newBitWidth(value) { + // value = parseInt(value); + // if (!isNaN(value) && this.bitWidth != value && value >= 1 && value <= 32) { + // this.bitWidth = value; + // this.dataIn.bitWidth = value; + // this.dataOut.bitWidth = value; + // this.clearData(); + // } + } + + changeAddressWidth(value) { + // value = parseInt(value); + // if (!isNaN(value) && this.addressWidth != value && value >= 1 && value <= this.maxAddressWidth) { + // this.addressWidth = value; + // this.address.bitWidth = value; + // this.clearData(); + // } + } + + clearData() { + this.data = new Array(this.words) + this.tooltipText = `${this.memSizeString()} ${this.shortName}` + } + + isResolvable() { + for (var i = 0; i < this.numRead; i++) { + if (this.readAddress[i] != undefined) return true + } + return this.reset.value != undefined || this.coreDump.value != undefined + } + + resolve() { + // resolve write + + customResolve( + this.writeDffClock, + this.writeDffDInp, + this.writeDffQOutput, + this.writeDffEn, + this.writeDffMasterState, + this.writeDffSlaveState, + this.writeDffprevClockState, + this.writeDffClockPolarity, + this.writeDffEnPolarity, + this.numWrite + ) + + for (var i = 0; i < this.numWrite; i++) { + if (this.writeEnable[i].value == 1) { + this.data[this.writeAddress[i].value] = + this.writeDffQOutput[i].value + } + } + + if (this.reset.value == 1) { + this.clearData() + } + + if ( + this.coreDump.value && + this.prevCoreDumpValue != this.coreDump.value + ) { + this.dump() + } + this.prevCoreDumpValue = this.coreDump.value + + for (var i = 0; i < this.numRead; i++) { + this.dataOut[i].value = this.data[this.readAddress[i].value] || 0 + simulationArea.simulationQueue.add(this.dataOut[i]) + } + + customResolve( + this.readDffClock, + this.readDffDInp, + this.readDffQOutput, + this.readDffEn, + this.readDffMasterState, + this.readDffSlaveState, + this.readDffprevClockState, + this.readDffClockPolarity, + this.readDffEnPolarity, + this.numRead + ) + } + + customDraw() { + // var ctx = simulationArea.context; + // // + // var xx = this.x; + // var yy = this.y; + // ctx.beginPath(); + // ctx.strokeStyle = 'gray'; + // ctx.fillStyle = this.write.value ? 'red' : 'lightgreen'; + // ctx.lineWidth = correctWidth(1); + // drawCircle2(ctx, 50, -30, 3, xx, yy, this.direction); + // ctx.fill(); + // ctx.stroke(); + // ctx.beginPath(); + // ctx.textAlign = 'center'; + // ctx.fillStyle = 'black'; + // fillText4(ctx, this.memSizeString(), 0, -10, xx, yy, this.direction, 12); + // fillText4(ctx, this.shortName, 0, 10, xx, yy, this.direction, 12); + // fillText2(ctx, 'A', this.address.x + 12, this.address.y, xx, yy, this.direction); + // fillText2(ctx, 'DI', this.dataIn.x + 12, this.dataIn.y, xx, yy, this.direction); + // fillText2(ctx, 'W', this.write.x + 12, this.write.y, xx, yy, this.direction); + // fillText2(ctx, 'DO', this.dataOut.x - 15, this.dataOut.y, xx, yy, this.direction); + // ctx.fill(); + } + + memSizeString() { + var mag = ['', 'K', 'M'] + var unit = + this.bitWidth == 8 + ? 'B' + : this.bitWidth == 1 + ? 'b' + : ` x ${this.bitWidth}b` + var v = Math.pow(2, this.addressWidth) + var m = 0 + while (v >= 1024 && m < mag.length - 1) { + v /= 1024 + m++ + } + return v + mag[m] + unit + } + + dump() { + var logLabel = console.group && this.label + if (logLabel) { + console.group(this.label) + } + + console.log(this.data) + + if (logLabel) { + console.groupEnd() + } + } +} + +verilogRAM.prototype.tooltipText = 'Random Access Memory' +verilogRAM.prototype.shortName = 'verilogRAM' +verilogRAM.prototype.maxAddressWidth = 20 +verilogRAM.prototype.mutableProperties = { + addressWidth: { + name: 'Address Width', + type: 'number', + max: '20', + min: '1', + func: 'changeAddressWidth', + }, + dump: { + name: 'Core Dump', + type: 'button', + func: 'dump', + }, + reset: { + name: 'Reset', + type: 'button', + func: 'clearData', + }, +} +verilogRAM.prototype.objectType = 'verilogRAM' diff --git a/v1/src/simulator/src/setup.js b/v1/src/simulator/src/setup.js new file mode 100644 index 00000000..6992115c --- /dev/null +++ b/v1/src/simulator/src/setup.js @@ -0,0 +1,211 @@ +/* eslint-disable import/no-cycle */ +/* eslint-disable no-restricted-syntax */ +/* eslint-disable guard-for-in */ + +import { Tooltip } from 'bootstrap' +import metadata from './metadata.json' +import { generateId, showMessage } from './utils' +import backgroundArea from './backgroundArea' +import plotArea from './plotArea' +import simulationArea from './simulationArea' +import { dots } from './canvasApi' +import { update, updateSimulationSet, updateCanvasSet } from './engine' +import { setupUI } from './ux' +import startMainListeners from './listeners' +// import startEmbedListeners from './embedListeners' +import './embed' +import { newCircuit, scopeList } from './circuit' +import load from './data/load' +import save from './data/save' +import { showTourGuide } from './tutorials' +import setupModules from './moduleSetup' +import 'codemirror/lib/codemirror.css' +import 'codemirror/addon/hint/show-hint.css' +import 'codemirror/mode/javascript/javascript' // verilog.js from codemirror is not working because array prototype is changed. +import 'codemirror/addon/edit/closebrackets' +import 'codemirror/addon/hint/anyword-hint' +import 'codemirror/addon/hint/show-hint' +import { setupCodeMirrorEnvironment } from './Verilog2CV' +// import { keyBinder } from '#/components/DialogBox/CustomShortcut.vue' +import '../vendor/jquery-ui.min.css' +import '../vendor/jquery-ui.min' +import { confirmSingleOption } from '#/components/helpers/confirmComponent/ConfirmComponent.vue' +import { getToken } from '#/pages/simulatorHandler.vue' + +/** + * to resize window and setup things it + * sets up new width for the canvas variables. + * Also redraws the grid. + * @category setup + */ +export function resetup() { + DPR = window.devicePixelRatio || 1 + if (lightMode) { + DPR = 1 + } + width = document.getElementById('simulationArea').clientWidth * DPR + if (!embed) { + height = + (document.body.clientHeight - + document.getElementById('toolbar').clientHeight) * + DPR + } else { + height = document.getElementById('simulation').clientHeight * DPR + } + // setup simulationArea and backgroundArea variables used to make changes to canvas. + backgroundArea.setup() + simulationArea.setup() + // redraw grid + dots() + document.getElementById('backgroundArea').style.height = + height / DPR + 100 + 'px' + document.getElementById('backgroundArea').style.width = + width / DPR + 100 + 'px' + document.getElementById('canvasArea').style.height = height / DPR + 'px' + simulationArea.canvas.width = width + simulationArea.canvas.height = height + backgroundArea.canvas.width = width + 100 * DPR + backgroundArea.canvas.height = height + 100 * DPR + if (!embed) { + plotArea.setup() + } + updateCanvasSet(true) + update() // INEFFICIENT, needs to be deprecated + simulationArea.prevScale = 0 + dots() +} + +window.onresize = resetup // listener +window.onorientationchange = resetup // listener + +// for mobiles +window.addEventListener('orientationchange', resetup) // listener + +/** + * function to setup environment variables like projectId and DPR + * @category setup + */ +function setupEnvironment() { + setupModules() + const projectId = generateId() + window.projectId = projectId + updateSimulationSet(true) + // const DPR = window.devicePixelRatio || 1 // unused variable + newCircuit('Main') + window.data = {} + resetup() + setupCodeMirrorEnvironment() +} + +/** + * It initializes some useful array which are helpful + * while simulating, saving and loading project. + * It also draws icons in the sidebar + * @category setup + */ +function setupElementLists() { + // $('#menu').empty() + + window.circuitElementList = metadata.circuitElementList + window.annotationList = metadata.annotationList + window.inputList = metadata.inputList + window.subCircuitInputList = metadata.subCircuitInputList + window.moduleList = [...circuitElementList, ...annotationList] + window.updateOrder = [ + 'wires', + ...circuitElementList, + 'nodes', + ...annotationList, + ] // Order of update + window.renderOrder = [...moduleList.slice().reverse(), 'wires', 'allNodes'] // Order of render +} + +/** + * Fetches project data from API and loads it into the simulator. + * @param {number} projectId The ID of the project to fetch data for + * @category setup + */ +async function fetchProjectData(projectId) { + try { + const response = await fetch( + `/api/v1/projects/${projectId}/circuit_data`, + { + method: 'GET', + headers: { + Accept: 'application/json', + Authorization: `Token ${getToken('cvt')}`, + }, + } + ) + if (response.ok) { + const data = await response.json() + await load(data) + await simulationArea.changeClockTime(data.timePeriod || 500) + $('.loadingIcon').fadeOut() + } else { + throw new Error('API call failed') + } + } catch (error) { + console.error(error) + confirmSingleOption('Error: Could not load.') + $('.loadingIcon').fadeOut() + } +} + +/** + * Load project data immediately when available. + * Improvement to eliminate delay caused by setTimeout in previous implementation revert if issues arise. + * @category setup + */ +async function loadProjectData() { + window.logixProjectId = window.logixProjectId ?? 0 + if (window.logixProjectId !== 0) { + $('.loadingIcon').fadeIn() + await fetchProjectData(window.logixProjectId) + } else if (localStorage.getItem('recover_login') && window.isUserLoggedIn) { + // Restore unsaved data and save + const data = JSON.parse(localStorage.getItem('recover_login')) + await load(data) + localStorage.removeItem('recover') + localStorage.removeItem('recover_login') + await save() + } else if (localStorage.getItem('recover')) { + // Restore unsaved data which didn't get saved due to error + showMessage( + "We have detected that you did not save your last work. Don't worry we have recovered them. Access them using Project->Recover" + ) + } +} + +/** + * Show tour guide if it hasn't been completed yet. + * The tour is shown after a delay of 2 seconds. + * @category setup + */ +function showTour() { + if (!localStorage.tutorials_tour_done && !embed) { + setTimeout(() => { + showTourGuide() + }, 2000) + } +} + +/** + * The first function to be called to setup the whole simulator. + * This function sets up the simulator environment, the UI, the listeners, + * loads the project data, and shows the tour guide. + * @category setup + */ +export function setup() { + // let embed = false + // const startListeners = embed ? startEmbedListeners : startMainListeners + setupElementLists() + setupEnvironment() + if (!embed) { + setupUI() + startMainListeners() + } + // startListeners() + loadProjectData() + showTour() +} diff --git a/v1/src/simulator/src/simulationArea.js b/v1/src/simulator/src/simulationArea.js new file mode 100644 index 00000000..3784988a --- /dev/null +++ b/v1/src/simulator/src/simulationArea.js @@ -0,0 +1,99 @@ +/* eslint-disable import/no-cycle */ +import EventQueue from './eventQueue' +import { clockTick } from './utils' + +/** + * simulation environment object - holds simulation canvas + * @type {Object} simulationArea + * @property {HTMLCanvasElement} canvas + * @property {boolean} selected + * @property {boolean} hover + * @property {number} clockState + * @property {boolean} clockEnabled + * @property {undefined} lastSelected + * @property {Array} stack + * @property {number} prevScale + * @property {number} oldx + * @property {number} oldy + * @property {Array} objectList + * @property {number} maxHeight + * @property {number} maxWidth + * @property {number} minHeight + * @property {number} minWidth + * @property {Array} multipleObjectSelections + * @property {Array} copyList - List of selected elements + * @property {boolean} shiftDown - shift down or not + * @property {boolean} controlDown - contol down or not + * @property {number} timePeriod - time period + * @property {number} mouseX - mouse x + * @property {number} mouseY - mouse y + * @property {number} mouseDownX - mouse click x + * @property {number} mouseDownY - mouse click y + * @property {Array} simulationQueue - simulation queue + * @property {number} clickCount - number of clicks + * @property {string} lock - locked or unlocked + * @property {function} timer - timer + * @property {function} setup - to setup the simulaton area + * @property {function} changeClockTime - change clock time + * @property {function} clear - clear the simulation area + * @category simulationArea + */ +const simulationArea = { + canvas: document.getElementById('simulationArea'), + selected: false, + hover: false, + clockState: 0, + clockEnabled: true, + lastSelected: undefined, + stack: [], + prevScale: 0, + oldx: 0, + oldy: 0, + objectList: [], + maxHeight: 0, + maxWidth: 0, + minHeight: 0, + minWidth: 0, + multipleObjectSelections: [], + copyList: [], + shiftDown: false, + controlDown: false, + timePeriod: 500, + mouseX: 0, + mouseY: 0, + mouseDownX: 0, + mouseDownY: 0, + simulationQueue: undefined, + multiAddElement: false, + + clickCount: 0, // double click + lock: 'unlocked', + timer() { + ckickTimer = setTimeout(() => { + simulationArea.clickCount = 0 + }, 600) + }, + + setup() { + this.canvas = document.getElementById('simulationArea') + this.canvas.width = width + this.canvas.height = height + this.simulationQueue = new EventQueue(10000) + this.context = this.canvas.getContext('2d') + simulationArea.changeClockTime(simulationArea.timePeriod) + this.mouseDown = false + }, + changeClockTime(t) { + if (t < 50) return + clearInterval(simulationArea.ClockInterval) + t = t || prompt('Enter Time Period:') + simulationArea.timePeriod = t + simulationArea.ClockInterval = setInterval(clockTick, t) + }, + clear() { + if (!this.context) return + this.context.clearRect(0, 0, this.canvas.width, this.canvas.height) + }, +} +export const { changeClockTime } = simulationArea +export default simulationArea diff --git a/v1/src/simulator/src/subcircuit.js b/v1/src/simulator/src/subcircuit.js new file mode 100644 index 00000000..85fdc53e --- /dev/null +++ b/v1/src/simulator/src/subcircuit.js @@ -0,0 +1,747 @@ +/* eslint-disable import/no-cycle */ +import Scope, { scopeList, switchCircuit } from './circuit' +import CircuitElement from './circuitElement' +import simulationArea from './simulationArea' +import { scheduleBackup, checkIfBackup } from './data/backupCircuit' +import { + scheduleUpdate, + updateSimulationSet, + updateCanvasSet, + updateSubcircuitSet, + forceResetNodesSet, +} from './engine' +import { loadScope } from './data/load' +import { showError } from './utils' + +import Node, { findNode } from './node' +import { fillText, correctWidth, rect2 } from './canvasApi' +import { colors } from './themer/themer' +import { layoutModeGet } from './layoutMode' +import { verilogModeGet } from './Verilog2CV' +import { sanitizeLabel } from './verilogHelpers' +import { SimulatorStore } from '#/store/SimulatorStore/SimulatorStore' +/** + * Function to load a subcicuit + * @category subcircuit + */ +export function loadSubCircuit(savedData, scope) { + new SubCircuit(savedData.x, savedData.y, scope, savedData.id, savedData) +} + +/** + * Prompt to create subcircuit, shows list of circuits which dont depend on the current circuit + * @param {Scope=} scope + * @category subcircuit + */ +export function createSubCircuitPrompt(scope = globalScope) { + if (verilogModeGet() || layoutModeGet()) { + showError('Subcircuit cannot be inserted in this mode') + return + } + const simulatorStore = SimulatorStore() + simulatorStore.dialogBox.insertsubcircuit_dialog = true + /* + $('#insertSubcircuitDialog').empty() + let flag = true + for (id in scopeList) { + if ( + !scopeList[id].checkDependency(scope.id) && + scopeList[id].isVisible() + ) { + flag = false + $('#insertSubcircuitDialog').append( + `` + ) + } + } + if (flag) + $('#insertSubcircuitDialog').append( + "

Looks like there are no other circuits which doesn't have this circuit as a dependency. Create a new one!

" + ) + $('#insertSubcircuitDialog').dialog({ + resizable: false, + maxHeight: 800, + width: 450, + maxWidth: 800, + minWidth: 250, + buttons: !flag + ? [ + { + text: 'Insert SubCircuit', + click() { + if (!$('input[name=subCircuitId]:checked').val()) + return + simulationArea.lastSelected = new SubCircuit( + undefined, + undefined, + globalScope, + $('input[name=subCircuitId]:checked').val() + ) + $(this).dialog('close') + }, + }, + ] + : [], + }) + */ +} + +/** + * @class + * @extends CircuitElement + * @param {number} x - x coord of subcircuit + * @param {number} y - y coord of subcircuit + * @param {Scope=} scope - the circuit in which subcircuit has been added + * @param {string} id - the id of the subcircuit scope + * @param {JSON} savedData - the saved data + * @category subcircuit + */ +export default class SubCircuit extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + id = undefined, + savedData = undefined + ) { + super(x, y, scope, 'RIGHT', 1) // super call + this.objectType = 'SubCircuit' + this.scope.SubCircuit.push(this) + this.id = id || prompt('Enter Id: ') + this.directionFixed = true + this.fixedBitWidth = true + this.savedData = savedData + this.inputNodes = [] + this.outputNodes = [] + this.localScope = new Scope() + this.preventCircuitSwitch = false // prevents from switching circuit if double clicking element + this.rectangleObject = false + + var subcircuitScope = scopeList[this.id] // Scope of the subcircuit + // Error handing + if (subcircuitScope == undefined) { + // if no such scope for subcircuit exists + showError( + `SubCircuit : ${ + (savedData && savedData.title) || this.id + } Not found` + ) + } else if (!checkIfBackup(subcircuitScope)) { + // if there is no input/output nodes there will be no backup + showError( + `SubCircuit : ${ + (savedData && savedData.title) || subcircuitScope.name + } is an empty circuit` + ) + } else if (subcircuitScope.checkDependency(scope.id)) { + // check for cyclic dependency + showError('Cyclic Circuit Error') + } + // Error handling, cleanup + if ( + subcircuitScope == undefined || + subcircuitScope.checkDependency(scope.id) + ) { + if (savedData) { + for (var i = 0; i < savedData.inputNodes.length; i++) { + scope.allNodes[savedData.inputNodes[i]].deleted = true + } + for (var i = 0; i < savedData.outputNodes.length; i++) { + scope.allNodes[savedData.outputNodes[i]].deleted = true + } + } + return + } + + if (this.savedData != undefined) { + updateSubcircuitSet(true) + scheduleUpdate() + this.version = this.savedData.version || '1.0' + + this.id = this.savedData.id + this.label = this.savedData.label || '' + this.labelDirection = this.savedData.labelDirection || 'RIGHT' + for (var i = 0; i < this.savedData.inputNodes.length; i++) { + this.inputNodes.push( + this.scope.allNodes[this.savedData.inputNodes[i]] + ) + this.inputNodes[i].parent = this + this.inputNodes[i].layout_id = + subcircuitScope.Input[i]?.layoutProperties.id + } + for (var i = 0; i < this.savedData.outputNodes.length; i++) { + this.outputNodes.push( + this.scope.allNodes[this.savedData.outputNodes[i]] + ) + this.outputNodes[i].parent = this + this.outputNodes[i].layout_id = + subcircuitScope.Output[i]?.layoutProperties.id + } + if (this.version == '1.0') { + // For backward compatibility + this.version = '2.0' + this.x -= subcircuitScope.layout.width / 2 + this.y -= subcircuitScope.layout.height / 2 + for (var i = 0; i < this.inputNodes.length; i++) { + this.inputNodes[i].x = + subcircuitScope.Input[i].layoutProperties.x + this.inputNodes[i].y = + subcircuitScope.Input[i].layoutProperties.y + this.inputNodes[i].leftx = this.inputNodes[i].x + this.inputNodes[i].lefty = this.inputNodes[i].y + } + for (var i = 0; i < this.outputNodes.length; i++) { + this.outputNodes[i].x = + subcircuitScope.Output[i].layoutProperties.x + this.outputNodes[i].y = + subcircuitScope.Output[i].layoutProperties.y + this.outputNodes[i].leftx = this.outputNodes[i].x + this.outputNodes[i].lefty = this.outputNodes[i].y + } + } + + if (this.version == '2.0') { + this.leftDimensionX = 0 + this.upDimensionY = 0 + this.rightDimensionX = subcircuitScope.layout.width + this.downDimensionY = subcircuitScope.layout.height + } + + this.nodeList.extend(this.inputNodes) + this.nodeList.extend(this.outputNodes) + } else { + this.version = '2.0' + } + + this.data = JSON.parse(scheduleBackup(subcircuitScope)) + this.buildCircuit() // load the localScope for the subcircuit + this.makeConnections() // which will be wireless + } + + /** + * actually make all connection but are invisible so + * it seems like the simulation is happening in other + * Scope but it actually is not. + */ + makeConnections() { + for (let i = 0; i < this.inputNodes.length; i++) { + this.localScope.Input[i]?.output1.connectWireLess( + this.inputNodes[i] + ) + this.localScope.Input[i].output1.subcircuitOverride = true + } + + for (let i = 0; i < this.outputNodes.length; i++) { + this.localScope.Output[i]?.inp1.connectWireLess(this.outputNodes[i]) + this.outputNodes[i].subcircuitOverride = true + } + } + + /** + * Function to remove wireless connections + */ + removeConnections() { + for (let i = 0; i < this.inputNodes.length; i++) { + this.localScope.Input[i]?.output1.disconnectWireLess( + this.inputNodes[i] + ) + } + + for (let i = 0; i < this.outputNodes.length; i++) { + this.localScope.Output[i]?.inp1.disconnectWireLess( + this.outputNodes[i] + ) + } + } + + /** + * loads the subcircuit and draws all the nodes + */ + buildCircuit() { + var subcircuitScope = scopeList[this.id] + loadScope(this.localScope, this.data) + this.localScope.name = this.data.name + this.lastUpdated = this.localScope.timeStamp + updateSimulationSet(true) + updateCanvasSet(true) + + if (this.savedData == undefined) { + this.leftDimensionX = 0 + this.upDimensionY = 0 + this.rightDimensionX = subcircuitScope.layout.width + this.downDimensionY = subcircuitScope.layout.height + for (var i = 0; i < subcircuitScope.Output.length; i++) { + var a = new Node( + subcircuitScope.Output[i].layoutProperties.x, + subcircuitScope.Output[i].layoutProperties.y, + 1, + this, + subcircuitScope.Output[i].bitWidth + ) + a.layout_id = subcircuitScope.Output[i].layoutProperties.id + this.outputNodes.push(a) + } + for (var i = 0; i < subcircuitScope.Input.length; i++) { + var a = new Node( + subcircuitScope.Input[i].layoutProperties.x, + subcircuitScope.Input[i].layoutProperties.y, + 0, + this, + subcircuitScope.Input[i].bitWidth + ) + a.layout_id = subcircuitScope.Input[i].layoutProperties.id + this.inputNodes.push(a) + } + } + } + + // Needs to be deprecated, removed + reBuild() { + // new SubCircuit(x = this.x, y = this.y, scope = this.scope, this.id); + // this.scope.backups = []; // Because all previous states are invalid now + // this.delete(); + // showMessage('Subcircuit: ' + subcircuitScope.name + ' has been reloaded.'); + } + + /** + * rebuilds the subcircuit if any change to localscope is made + */ + reBuildCircuit() { + this.data = JSON.parse(scheduleBackup(scopeList[this.id])) + this.localScope = new Scope(data.name) + loadScope(this.localScope, this.data) + this.lastUpdated = this.localScope.timeStamp + this.scope.timeStamp = this.localScope.timeStamp + } + + reset() { + this.removeConnections() + + var subcircuitScope = scopeList[this.id] + + for (var i = 0; i < subcircuitScope.SubCircuit.length; i++) { + subcircuitScope.SubCircuit[i].reset() + } + + // No Inputs or Outputs + let emptyCircuit = + subcircuitScope.Input.length == 0 && + subcircuitScope.Output.length == 0 + // No LayoutElements + for (let element of circuitElementList) { + if ( + subcircuitScope[element].length > 0 && + subcircuitScope[element][0].canShowInSubcircuit + ) { + emptyCircuit = false + break + } + } + + if (emptyCircuit) { + showError( + `SubCircuit : ${subcircuitScope.name} is an empty circuit` + ) + } + + subcircuitScope.layout.height = subcircuitScope.layout.height + subcircuitScope.layout.width = subcircuitScope.layout.width + this.leftDimensionX = 0 + this.upDimensionY = 0 + this.rightDimensionX = subcircuitScope.layout.width + this.downDimensionY = subcircuitScope.layout.height + + var temp_map_inp = {} + for (var i = 0; i < subcircuitScope.Input.length; i++) { + temp_map_inp[subcircuitScope.Input[i].layoutProperties.id] = [ + subcircuitScope.Input[i], + undefined, + ] + } + for (var i = 0; i < this.inputNodes.length; i++) { + if (temp_map_inp.hasOwnProperty(this.inputNodes[i].layout_id)) { + temp_map_inp[this.inputNodes[i].layout_id][1] = + this.inputNodes[i] + } else { + this.scope.backups = [] + this.inputNodes[i].delete() + this.nodeList.clean(this.inputNodes[i]) + } + } + + for (id in temp_map_inp) { + if (temp_map_inp[id][1]) { + if ( + temp_map_inp[id][0].layoutProperties.x == + temp_map_inp[id][1].x && + temp_map_inp[id][0].layoutProperties.y == + temp_map_inp[id][1].y + ) { + temp_map_inp[id][1].bitWidth = temp_map_inp[id][0].bitWidth + } else { + this.scope.backups = [] + temp_map_inp[id][1].delete() + this.nodeList.clean(temp_map_inp[id][1]) + temp_map_inp[id][1] = new Node( + temp_map_inp[id][0].layoutProperties.x, + temp_map_inp[id][0].layoutProperties.y, + 0, + this, + temp_map_inp[id][0].bitWidth + ) + temp_map_inp[id][1].layout_id = id + } + } + } + + this.inputNodes = [] + for (var i = 0; i < subcircuitScope.Input.length; i++) { + var input = + temp_map_inp[subcircuitScope.Input[i].layoutProperties.id][0] + if (temp_map_inp[input.layoutProperties.id][1]) { + this.inputNodes.push(temp_map_inp[input.layoutProperties.id][1]) + } else { + var a = new Node( + input.layoutProperties.x, + input.layoutProperties.y, + 0, + this, + input.bitWidth + ) + a.layout_id = input.layoutProperties.id + this.inputNodes.push(a) + } + } + + var temp_map_out = {} + for (var i = 0; i < subcircuitScope.Output.length; i++) { + temp_map_out[subcircuitScope.Output[i].layoutProperties.id] = [ + subcircuitScope.Output[i], + undefined, + ] + } + for (var i = 0; i < this.outputNodes.length; i++) { + if (temp_map_out.hasOwnProperty(this.outputNodes[i].layout_id)) { + temp_map_out[this.outputNodes[i].layout_id][1] = + this.outputNodes[i] + } else { + this.outputNodes[i].delete() + this.nodeList.clean(this.outputNodes[i]) + } + } + + for (id in temp_map_out) { + if (temp_map_out[id][1]) { + if ( + temp_map_out[id][0].layoutProperties.x == + temp_map_out[id][1].x && + temp_map_out[id][0].layoutProperties.y == + temp_map_out[id][1].y + ) { + temp_map_out[id][1].bitWidth = temp_map_out[id][0].bitWidth + } else { + temp_map_out[id][1].delete() + this.nodeList.clean(temp_map_out[id][1]) + temp_map_out[id][1] = new Node( + temp_map_out[id][0].layoutProperties.x, + temp_map_out[id][0].layoutProperties.y, + 1, + this, + temp_map_out[id][0].bitWidth + ) + temp_map_out[id][1].layout_id = id + } + } + } + + this.outputNodes = [] + for (var i = 0; i < subcircuitScope.Output.length; i++) { + var output = + temp_map_out[subcircuitScope.Output[i].layoutProperties.id][0] + if (temp_map_out[output.layoutProperties.id][1]) { + this.outputNodes.push( + temp_map_out[output.layoutProperties.id][1] + ) + } else { + var a = new Node( + output.layoutProperties.x, + output.layoutProperties.y, + 1, + this, + output.bitWidth + ) + a.layout_id = output.layoutProperties.id + this.outputNodes.push(a) + } + } + + if (subcircuitScope.timeStamp > this.lastUpdated) { + this.reBuildCircuit() + } + + // Should this be done here or only when this.reBuildCircuit() is called? + { + this.localScope.reset() + updateSimulationSet(true) + forceResetNodesSet(true) + } + + this.makeConnections() + } + + /** + * Procedure after a element is clicked inside a subcircuit + **/ + click() { + var elementClicked = this.getElementHover() + if (elementClicked) { + this.lastClickedElement = elementClicked + elementClicked.wasClicked = true + } + } + + getElementHover() { + var rX = this.layoutProperties.rightDimensionX + var lX = this.layoutProperties.leftDimensionX + var uY = this.layoutProperties.upDimensionY + var dY = this.layoutProperties.downDimensionY + + for (let el of circuitElementList) { + if (this.localScope[el].length === 0) continue + if (!this.localScope[el][0].canShowInSubcircuit) continue + for (let i = 0; i < this.localScope[el].length; i++) { + var obj = this.localScope[el][i] + if ( + obj.subcircuitMetadata.showInSubcircuit && + obj.isSubcircuitHover(this.x, this.y) + ) { + return obj + } + } + } + } + + /** + * Sets the elements' wasClicked property in the subcircuit to false + **/ + releaseClick() { + if (this.lastClickedElement !== undefined) { + this.lastClickedElement.wasClicked = false + this.lastClickedElement = undefined + } + } + + /** + * adds all local scope inputs to the global scope simulation queue + */ + addInputs() { + for (let i = 0; i < subCircuitInputList.length; i++) { + for ( + let j = 0; + j < this.localScope[subCircuitInputList[i]].length; + j++ + ) { + simulationArea.simulationQueue.add( + this.localScope[subCircuitInputList[i]][j], + 0 + ) + } + } + for (let j = 0; j < this.localScope.SubCircuit.length; j++) { + this.localScope.SubCircuit[j].addInputs() + } + } + + /** + * Procedure if any element is double clicked inside a subcircuit + **/ + dblclick() { + if (this.elementHover) return + switchCircuit(this.id) + } + + /** + * Returns a javascript object of subcircuit data. + * Does not include data of subcircuit elements apart from Input and Output (that is a part of element.subcircuitMetadata) + **/ + saveObject() { + var data = { + x: this.x, + y: this.y, + id: this.id, + label: this.label, + labelDirection: this.labelDirection, + inputNodes: this.inputNodes.map(findNode), + outputNodes: this.outputNodes.map(findNode), + version: this.version, + } + return data + } + + /** + * By design, subcircuit element's input and output nodes are wirelessly + * connected to the localscope (clone of the scope of the subcircuit's + * circuit). So it is almost like the actual circuit is copied in the + * location of the subcircuit element. Therefore no resolve needed. + */ + isResolvable() { + return false + } + + /** + * If element not resolvable (always in subcircuits), removePropagation + * is called on it. + */ + removePropagation() { + // Leave this to the scope of the subcircuit. Do nothing. + } + + verilogName() { + return sanitizeLabel(scopeList[this.id].name) + } + /** + * determines where to show label + */ + determine_label(x, y) { + if (x == 0) return ['left', 5, 5] + if (x == scopeList[this.id].layout.width) return ['right', -5, 5] + if (y == 0) return ['center', 0, 13] + return ['center', 0, -6] + } + + checkHover() { + super.checkHover() + if (this.elementHover) { + this.elementHover.hover = false + this.elementHover = undefined + simulationArea.hover = undefined + } + var elementHover = this.getElementHover() + if (elementHover) { + elementHover.hover = true + this.elementHover = elementHover + this.hover = false + simulationArea.hover = elementHover + } + } + + /** + * Draws the subcircuit (and contained elements) on the screen when the subcircuit is included + in another circuit + **/ + customDraw() { + var subcircuitScope = scopeList[this.id] + + var ctx = simulationArea.context + + ctx.lineWidth = globalScope.scale * 3 + ctx.strokeStyle = colors['stroke'] // ("rgba(0,0,0,1)"); + ctx.fillStyle = colors['fill'] + var xx = this.x + var yy = this.y + + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.lineWidth = correctWidth(3) + ctx.beginPath() + rect2( + ctx, + -this.leftDimensionX, + -this.upDimensionY, + this.leftDimensionX + this.rightDimensionX, + this.upDimensionY + this.downDimensionY, + this.x, + this.y, + [this.direction, 'RIGHT'][+this.directionFixed] + ) + if (!this.elementHover) { + if ( + (this.hover && !simulationArea.shiftDown) || + simulationArea.lastSelected === this || + simulationArea.multipleObjectSelections.contains(this) + ) + ctx.fillStyle = colors['hover_select'] + } + ctx.fill() + ctx.stroke() + + ctx.beginPath() + + ctx.textAlign = 'center' + ctx.fillStyle = 'black' + if (this.version == '1.0') { + fillText( + ctx, + subcircuitScope.name, + xx, + yy - subcircuitScope.layout.height / 2 + 13, + 11 + ) + } else if (this.version == '2.0') { + if (subcircuitScope.layout.titleEnabled) { + fillText( + ctx, + subcircuitScope.name, + subcircuitScope.layout.title_x + xx, + yy + subcircuitScope.layout.title_y, + 11 + ) + } + } else { + } + + for (var i = 0; i < subcircuitScope.Input.length; i++) { + if (!subcircuitScope.Input[i].label) continue + var info = this.determine_label( + this.inputNodes[i].x, + this.inputNodes[i].y + ) + ctx.textAlign = info[0] + fillText( + ctx, + subcircuitScope.Input[i].label, + this.inputNodes[i].x + info[1] + xx, + yy + this.inputNodes[i].y + info[2], + 12 + ) + } + + for (var i = 0; i < subcircuitScope.Output.length; i++) { + if (!subcircuitScope.Output[i].label) continue + var info = this.determine_label( + this.outputNodes[i].x, + this.outputNodes[i].y + ) + ctx.textAlign = info[0] + fillText( + ctx, + subcircuitScope.Output[i].label, + this.outputNodes[i].x + info[1] + xx, + yy + this.outputNodes[i].y + info[2], + 12 + ) + } + ctx.fill() + for (let i = 0; i < this.outputNodes.length; i++) { + this.outputNodes[i].draw() + } + for (let i = 0; i < this.inputNodes.length; i++) { + this.inputNodes[i].draw() + } + + // draw subcircuitElements + for (let el of circuitElementList) { + if (this.localScope[el].length === 0) continue + if (!this.localScope[el][0].canShowInSubcircuit) continue + for (let i = 0; i < this.localScope[el].length; i++) { + if ( + this.localScope[el][i].subcircuitMetadata.showInSubcircuit + ) { + this.localScope[el][i].drawLayoutMode(this.x, this.y) + } + } + } + } +} +SubCircuit.prototype.centerElement = true // To center subcircuit when new +SubCircuit.prototype.propagationDelayFixed = true diff --git a/v1/src/simulator/src/testCreator.js b/v1/src/simulator/src/testCreator.js new file mode 100644 index 00000000..4a1708c2 --- /dev/null +++ b/v1/src/simulator/src/testCreator.js @@ -0,0 +1,780 @@ +/* + This file contains all javascript related to the test creator UI + at /testbench +*/ + +import _ from '../vendor/table2csv' + +const CREATORMODE = { + NORMAL: 0, + SIMULATOR_POPUP: 1, +} + +var testMode = 'comb' +var groupIndex = 0 +var inputCount = 0 +var nextInputIndex = 0 +var outputCount = 0 +var nextOutputIndex = 0 +var cases = [0] +var creatorMode = CREATORMODE.NORMAL +var circuitScopeID + +function dataReset() { + groupIndex = -1 + cases = [0] +} + +/** + * Onload, check if it is opened in a popup. + * Check if test is being edited, or created + */ +window.onload = () => { + const query = new URLSearchParams(window.location.search) + if (query.has('popUp')) { + if (query.get('popUp') == 'true') { + creatorMode = CREATORMODE.SIMULATOR_POPUP + $('.right-button-group').append( + '' + ) + } + } + if (query.has('data')) { + $('#tb-creator-head').html('Edit Test') + circuitScopeID = query.get('scopeID') + loadData(query.get('data')) + return + } + + if (query.has('result')) { + $('#tb-creator-head').html('Test Result') + loadResult(query.get('result')) + readOnlyUI() + return + } + + circuitScopeID = query.get('scopeID') + addInput() + addOutput() + makeSortable() +} + +/* Change UI testMode between Combinational(comb) and Sequential(seq) */ +function changeTestMode(m) { + if (testMode === m) return false + dataReset() + testMode = m + $('#combSelect').removeClass('tab-selected') + $('#seqSelect').removeClass('tab-selected') + $('#tb-new-group').css('visibility', m === 'seq' ? 'visible' : 'hidden') + $(`#${m}Select`).addClass('tab-selected') + $('#dataGroup').empty() + + return true +} + +/* Adds case to a group */ +function addCase(grp) { + const currentGroupTable = $(`#data-table-${grp + 1}`) + + let s = + '
\n' + for (let i = 0; i < inputCount + outputCount; i++) + s += '0' + s += '' + + // Sortable hack + currentGroupTable.find('tbody').remove() + currentGroupTable.append(s) +} + +/* Deletes case from a group */ +function deleteCase(element) { + const row = element.parent().parent() + const grp = Number(row.parent().attr('id').split('-').pop()) + + row.remove() +} + +/* Adds group with default name 'Group N' or name supplied in @param groupName */ +/* Used without params by UI, used with params by loadData() */ +function addGroup( + groupName = `${testMode === 'comb' ? 'Group' : 'Set'} ${groupIndex + 2}` +) { + $('.plus-button').removeClass('latest-button') + groupIndex++ + + const s = ` +
+

${escapeHtml(groupName)}

+
Click + to add tests to the ${ + testMode === 'comb' ? 'group' : 'set' + }
+ + +
+ +
+ ` + cases[groupIndex] = 0 + $('#dataGroup').append(s) + + makeSortable() +} + +/* Deletes a group */ +function deleteGroup(element) { + const groupDiv = element.parent() + const grp = Number(groupDiv.attr('id').split('-').pop()) + groupDiv.remove() +} + +/* Adds input with default value 0 or values supplied in @param inputData */ +/* Used without params for UI, used with params by loadData() */ +function addInput( + label = `inp${nextInputIndex + 1}`, + bitwidth = 1, + inputData = [] +) { + nextInputIndex++ + inputCount++ + // Change head table contents + const sHead = `${escapeHtml( + label + )} ` + const sData = `${escapeHtml( + bitwidth.toString() + )}` + $('#testBenchTable') + .find('tr') + .eq(1) + .find('th') + .eq(inputCount - 1) + .after(sHead) + $('#testBenchTable') + .find('tr') + .eq(2) + .find('td') + .eq(inputCount - 1) + .after(sData) + $('#tb-inputs-head').attr('colspan', inputCount) + + // Change data tables' contents + $('#dataGroup') + .find('table') + .each(function (group_i) { + $(this) + .find('tr') + .each(function (case_i) { + const s = `${ + inputData.length + ? escapeHtml(inputData[group_i][case_i]) + : 0 + }` + $(this) + .find('td') + .eq(inputCount - 1) + .after(s) + }) + }) +} + +/* Adds output with default value 0 or values supplied in @param outputData */ +/* Used without params for UI, used with params by loadData() */ +/* Used with resultData and result=true for setting result */ +function addOutput( + label = `out${nextOutputIndex + 1}`, + bitwidth = 1, + outputData = [], + result = false, + resultData = [] +) { + nextOutputIndex++ + outputCount++ + // Change head table contents + let sHead = `${escapeHtml( + label + )} ` + let sData = `${escapeHtml( + bitwidth.toString() + )}` + + // If result then set colspan to 2 + if (result) { + sHead = `${escapeHtml( + label + )} ` + sData = `${escapeHtml( + bitwidth.toString() + )}` + } + + $('#testBenchTable') + .find('tr') + .eq(1) + .find('th') + .eq(inputCount + outputCount - 1) + .after(sHead) + $('#testBenchTable') + .find('tr') + .eq(2) + .find('td') + .eq(inputCount + outputCount - 1) + .after(sData) + // If not result then colspan is outputCount + $('#tb-outputs-head').attr('colspan', outputCount) + // else it's 2*outputCount + if (result) { + $('#tb-outputs-head').attr('colspan', 2 * outputCount) + } + + // Change data tables' contents + + // If not result just add the outputs + if (!result) { + $('#dataGroup') + .find('table') + .each(function (group_i) { + $(this) + .find('tr') + .each(function (case_i) { + const s = `${ + outputData.length + ? escapeHtml(outputData[group_i][case_i]) + : 0 + }` + $(this) + .find('td') + .eq(inputCount + outputCount - 1) + .after(s) + }) + }) + + // If result then add results besides the outputs + // Hacky + } else { + $('#dataGroup') + .find('table') + .each(function (group_i) { + $(this) + .find('tr') + .each(function (case_i) { + // Add the outputs (expected values) + const outputCellData = `${escapeHtml( + outputData[group_i][case_i] + )}` + $(this) + .find('td') + .eq(inputCount + 2 * (outputCount - 1)) + .after(outputCellData) + + // Add the actual values + const resultColor = + resultData[group_i][case_i] === + outputData[group_i][case_i] + ? 'green' + : 'red' + const resultCellData = `${escapeHtml( + resultData[group_i][case_i] + )}` + $(this) + .find('td') + .eq(inputCount + 2 * outputCount - 1) + .after(resultCellData) + }) + }) + } +} + +/* Deletes input unless there's only one input */ +function deleteInput(element) { + if (inputCount === 1) return + const columnIndex = element.parent().eq(0).index() + + $('#testBenchTable tr, .data-group tr') + .slice(1) + .each(function () { + $(this).find('td, th').eq(columnIndex).remove() + }) + + inputCount-- + $('#tb-inputs-head').attr('colspan', inputCount) +} + +/* Deletes output unless there's only one output */ +function deleteOutput(element) { + if (outputCount === 1) return + const columnIndex = element.parent().eq(0).index() + + $('#testBenchTable tr, .data-group tr') + .slice(1) + .each(function () { + $(this).find('td, th').eq(columnIndex).remove() + }) + + outputCount-- + $('#tb-outputs-head').attr('colspan', outputCount) +} + +/* Returns input/output(keys) and their bitwidths(values) */ +/* Called by getData() */ +function getBitWidths() { + const bitwidths = {} + $('#testBenchTable') + .find('tr') + .eq(1) + .find('th') + .slice(1) + .each(function (index) { + const inp = $(this).text() + const bw = $('#testBenchTable') + .find('tr') + .eq(2) + .find('td') + .slice(1) + .eq(index) + .html() + bitwidths[inp] = Number(bw) + }) + return bitwidths +} + +/* Returns data for all the groups for all inputs and outputs */ +/* Called by parse() */ +function getData() { + const bitwidths = getBitWidths() + const groups = [] + const groupCount = $('#dataGroup').children().length + for (let group_i = 0; group_i < groupCount; group_i++) { + const group = {} + group.label = getGroupTitle(group_i) + group.inputs = [] + group.outputs = [] + + const group_table = $(`#data-table-${group_i + 1}`) + group.n = group_table.find('tr').length + + // Push all the inputs in the group + for (let inp_i = 0; inp_i < inputCount; inp_i++) { + const label = Object.keys(bitwidths)[inp_i] + const input = { + label: label.slice(0, label.length - 1), + bitWidth: bitwidths[label], + values: [], + } + group_table.find('tr').each(function () { + input.values.push($(this).find('td').slice(1).eq(inp_i).html()) + }) + + group.inputs.push(input) + } + + // Push all the outputs in the group + for (let out_i = 0; out_i < outputCount; out_i++) { + const label = Object.keys(bitwidths)[inputCount + out_i] + const output = { + label: label.slice(0, label.length - 1), + bitWidth: bitwidths[label], + values: [], + } + group_table.find('tr').each(function () { + output.values.push( + $(this) + .find('td') + .slice(1) + .eq(inputCount + out_i) + .html() + ) + }) + + group.outputs.push(output) + } + + groups.push(group) + } + + return groups +} + +function getTestTitle() { + return $('#test-title-label').text() +} + +function getGroupTitle(group_i) { + return $(`#data-group-title-${group_i + 1}`).text() +} + +/* Parse UI table into Javascript Object */ +function parse() { + const data = {} + const tableData = getData() + data.type = testMode + data.title = getTestTitle() + data.groups = tableData + return data +} + +/* Export test data as a CSV file */ +function exportAsCSV() { + let csvData = '' + csvData += 'Title,Test Type,Input Count,Output Count\n' + csvData += `${getTestTitle()},${testMode},${inputCount},${outputCount}\n\n\n` + csvData += $('table').eq(0).table2CSV() + csvData += '\n\n' + $('table') + .slice(1) + .each(function (group_i) { + csvData += getGroupTitle(group_i) + csvData += '\n' + csvData += $(this).table2CSV() + csvData += '\n\n' + }) + + download(`${getTestTitle()}.csv`, csvData) + return csvData +} + +/* + Imports data from CSV and loads into the table + To achieve this, first converts to JSON then uses request param to load json to table +*/ +function importFromCSV() { + const file = $('#csvFileInput').prop('files')[0] + const reader = new FileReader() + + // If circuitScopeID exists, ie. if popup opened from testbench, then use that to redirect + const query = new URLSearchParams(window.location.search) + // Preserve popup status while redirecting + const isPopup = query.get('popUp') || false + + // When the file is read, redirect to the data location + reader.onload = () => { + const csvContent = reader.result + const jsonData = csv2json(csvContent, 1, 1) + + window.location = `/testbench?scopeID=${ + circuitScopeID || '' + }&data=${jsonData}&popUp=${isPopup}` + } + + reader.readAsText(file) +} + +// Clicks the hidden upload file button, entrypoint into importFromCSV() +// The hidden button in-turn calls importFromCSV() +function clickUpload() { + $('#csvFileInput').click() +} + +/* Converts CSV to JSON to be loaded into the table */ +function csv2json(csvFileData) { + const stripQuotes = (str) => str.replaceAll('"', '') + + /* Extracts bitwidths from the csv data */ + const getBitWidthsCSV = (csvDataBW) => { + const testMetadata = csvDataBW.split('\n\n')[0].split('\n') + const labels = testMetadata[1] + .split(',') + .slice(1) + .map((label) => stripQuotes(label)) + const bitWidths = testMetadata[2] + .split(',') + .slice(1) + .map((bw) => Number(stripQuotes(bw))) + + return { labels, bitWidths } + } + + const csvMetadata = csvFileData.split('\n\n\n')[0].split('\n')[1].split(',') + const csvData = csvFileData.split('\n\n\n')[1] + const jsonData = {} + + jsonData.title = csvMetadata[0] + jsonData.type = csvMetadata[1] + const inputCountCSV = Number(csvMetadata[2]) + const outputCountCSV = Number(csvMetadata[3]) + + jsonData.groups = [] + const { labels, bitWidths } = getBitWidthsCSV(csvData) + + const groups = csvData.split('\n\n').slice(1) + for (let group_i = 0; group_i < groups.length - 1; group_i++) { + const rows = groups[group_i].split('\n') + jsonData.groups[group_i] = { + label: rows[0], + n: rows.length - 1, + inputs: [], + outputs: [], + } + + // Parse Inputs + for (let input_i = 0; input_i < inputCountCSV; input_i++) { + const thisInput = { + label: labels[input_i], + bitWidth: bitWidths[input_i], + values: [], + } + for (let case_i = 1; case_i < rows.length; case_i++) + thisInput.values.push( + stripQuotes(rows[case_i].split(',')[input_i + 1]) + ) + + jsonData.groups[group_i].inputs.push(thisInput) + } + + // Parse Outputs + for ( + let output_i = inputCountCSV; + output_i < inputCountCSV + outputCountCSV; + output_i++ + ) { + const thisOutput = { + label: labels[output_i], + bitWidth: bitWidths[output_i], + values: [], + } + for (let case_i = 1; case_i < rows.length; case_i++) { + thisOutput.values.push( + stripQuotes(rows[case_i].split(',')[output_i + 1]) + ) + } + + jsonData.groups[group_i].outputs.push(thisOutput) + } + } + + return JSON.stringify(jsonData) +} + +/* Helper function to download generated file */ +function download(filename, text) { + var element = document.createElement('a') + element.setAttribute( + 'href', + `data:text/plain;charset=utf-8,${encodeURIComponent(text)}` + ) + element.setAttribute('download', filename) + + element.style.display = 'none' + document.body.appendChild(element) + + element.click() + + document.body.removeChild(element) +} + +/** + * Called when Save is clicked. If opened in popup, sends message to parent window + * to attach test to the testbench. + */ +function saveData() { + const testData = parse() + + if (creatorMode === CREATORMODE.SIMULATOR_POPUP) { + const postData = { scopeID: circuitScopeID, testData } + window.opener.postMessage( + { type: 'testData', data: JSON.stringify(postData) }, + '*' + ) + window.close() + } +} + +/* Loads data from JSON string into the table */ +function loadData(dataJSON) { + const data = JSON.parse(dataJSON) + if (data.title) $('#test-title-label').text(data.title) + changeTestMode() + changeTestMode(data.type) + for (let group_i = 0; group_i < data.groups.length; group_i++) { + const group = data.groups[group_i] + addGroup(group.label) + for (let case_i = 0; case_i < group.inputs[0].values.length; case_i++) { + addCase(group_i) + } + } + + // Add input values + for (let input_i = 0; input_i < data.groups[0].inputs.length; input_i++) { + const input = data.groups[0].inputs[input_i] + const values = data.groups.map((group) => group.inputs[input_i].values) + + addInput(input.label, input.bitWidth, values) + } + + // Add output values + for ( + let output_i = 0; + output_i < data.groups[0].outputs.length; + output_i++ + ) { + const output = data.groups[0].outputs[output_i] + const values = data.groups.map( + (group) => group.outputs[output_i].values + ) + + addOutput(output.label, output.bitWidth, values) + } +} + +/** + * Loads result from JSON string into the testbench creator UI + */ +function loadResult(dataJSON) { + const data = JSON.parse(dataJSON) + if (data.title) $('#test-title-label').text(data.title) + changeTestMode() + changeTestMode(data.type) + for (let group_i = 0; group_i < data.groups.length; group_i++) { + const group = data.groups[group_i] + addGroup(group.label) + for (let case_i = 0; case_i < group.inputs[0].values.length; case_i++) { + addCase(group_i) + } + } + + // Add input values + for (let input_i = 0; input_i < data.groups[0].inputs.length; input_i++) { + const input = data.groups[0].inputs[input_i] + const values = data.groups.map((group) => group.inputs[input_i].values) + + addInput(input.label, input.bitWidth, values) + } + + // Add output values + for ( + let output_i = 0; + output_i < data.groups[0].outputs.length; + output_i++ + ) { + const output = data.groups[0].outputs[output_i] + const values = data.groups.map( + (group) => group.outputs[output_i].values + ) + const results = data.groups.map( + (group) => group.outputs[output_i].results + ) + const expectedOutputs = [] + const actualOutputs = [] + + for (let group_i = 0; group_i < values.length; group_i++) { + const groupExpectedOuts = [] + const groupActualOuts = [] + for (let val_i = 0; val_i < values[group_i].length; val_i++) { + groupExpectedOuts.push(values[group_i][val_i]) + groupActualOuts.push(results[group_i][val_i]) + } + + expectedOutputs.push(groupExpectedOuts) + actualOutputs.push(groupActualOuts) + } + addOutput( + `${output.label}`, + output.bitWidth, + expectedOutputs, + true, + actualOutputs + ) + } +} + +/** + * Makes the UI read only for displaying results + */ +function readOnlyUI() { + makeContentUneditable() + makeUnsortable() + $('.lower-button, .table-button, .tb-minus').hide() + $('.tablink').attr('disabled', 'disabled') + $('.tablink').removeClass('tablink-no-override') + $('.data-group-info').text('') +} + +function makeContentUneditable() { + $('body') + .find('td, th, span, h3, div') + .each(function () { + $(this).attr('contenteditable', 'false') + }) +} + +function makeSortable() { + const helper = function (e, ui) { + const helperE = ui.clone() + helperE.children().each(function (child_i) { + $(this).width(ui.children().eq(child_i).width()) + }) + + return helperE + } + + function makePlaceholder(e, ui) { + ui.placeholder.children().each(function () { + $(this).css('border', '0px') + }) + } + + /* + Sortable hack: To allow sorting inside empty tables, the tables should have some height. + But it is not possible to give tables height without having rows, so we add a tbody. + tbody gives the table height but messes up all the other things. So we only keep tbody + if the table has no rows, and once table gets rows, we remove that tbody + */ + function removeTbody(e, ui) { + $(e.target).find('tbody').remove() + } + + function createTbody(e, ui) { + if ($(e.target).find('tr, tbody').length === 0) { + $(e.target).append('') + } + } + + $('.data-group table').sortable({ + handle: '.tb-handle', + helper, + start: makePlaceholder, + placeholder: 'clone', + connectWith: 'table', + receive: removeTbody, // For sortable hack + remove: createTbody, // For sortable hack + items: 'tr', + revert: 50, + scroll: false, + }) +} + +function makeUnsortable() { + $('.data-group table').sortable({ disabled: true }) +} + +function escapeHtml(unsafe) { + return unsafe + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, ''') +} + +// Making HTML called functions global + +window.addGroup = addGroup +window.deleteGroup = deleteGroup +window.addCase = addCase +window.deleteCase = deleteCase +window.addInput = addInput +window.deleteInput = deleteInput +window.addOutput = addOutput +window.deleteOutput = deleteOutput +window.parse = parse +window.saveData = saveData +window.changeTestMode = changeTestMode +window.exportAsCSV = exportAsCSV +window.importFromCSV = importFromCSV +window.csv2json = csv2json +window.clickUpload = clickUpload diff --git a/v1/src/simulator/src/testbench.js b/v1/src/simulator/src/testbench.js new file mode 100644 index 00000000..d6f8a070 --- /dev/null +++ b/v1/src/simulator/src/testbench.js @@ -0,0 +1,1140 @@ +/** + * This file contains all functions related the the testbench + * Contains the the testbench engine and UI modules + */ + +import { scheduleBackup } from './data/backupCircuit' +import { changeClockEnable } from './sequential' +import { play } from './engine' +import Scope from './circuit' +import { showMessage, escapeHtml } from './utils' +import { confirmOption } from '#/components/helpers/confirmComponent/ConfirmComponent.vue' + +/** + * @typedef {number} RunContext + */ +const CONTEXT = { + CONTEXT_SIMULATOR: 0, + CONTEXT_ASSIGNMENTS: 1, +} + +const VALIDATION_ERRORS = { + NOTPRESENT: 0, // Element is not present in the circuit + WRONGBITWIDTH: 1, // Element is present but has incorrect bitwidth + DUPLICATE_ID_DATA: 2, // Duplicate identifiers in test data + DUPLICATE_ID_SCOPE: 3, // Duplicate identifiers in scope + NO_RST: 4, // Sequential circuit but no reset(RST) in scope +} + +const TESTBENCH_CREATOR_PATH = '/testbench' + +// Do we have any other function to do this? +// Utility function. Converts decimal number to binary string +function dec2bin(dec, bitWidth = undefined) { + if (dec === undefined) return 'X' + const bin = (dec >>> 0).toString(2) + if (!bitWidth) return bin + + return '0'.repeat(bitWidth - bin.length) + bin +} + +/** + * Class to store all data related to the testbench and functions to use it + * @param {Object} data - Javascript object of the test data + * @param {number=} currentGroup - Current group index in the test + * @param {number=} currentCase - Current case index in the group + */ +export class TestbenchData { + constructor(data, currentGroup = 0, currentCase = 0) { + this.currentCase = currentCase + this.currentGroup = currentGroup + this.testData = data + } + + /** + * Checks whether given case-group pair exists in the test + */ + isCaseValid() { + if ( + this.currentGroup >= this.data.groups.length || + this.currentGroup < 0 + ) + return false + const caseCount = + this.testData.groups[this.currentGroup].inputs[0].values.length + if (this.currentCase >= caseCount || this.currentCase < 0) return false + + return true + } + + /** + * Validate and set case and group in the test + * @param {number} groupIndex - Group index to set + * @param {number} caseIndex - Case index to set + */ + setCase(groupIndex, caseIndex) { + const newCase = new TestbenchData(this.testData, groupIndex, caseIndex) + if (newCase.isCaseValid()) { + this.currentGroup = groupIndex + this.currentCase = caseIndex + return true + } + + return false + } + + /** + * Validate and go to the next group. + * Skips over empty groups + */ + groupNext() { + const newCase = new TestbenchData(this.testData, this.currentGroup, 0) + const groupCount = newCase.testData.groups.length + let caseCount = + newCase.testData.groups[newCase.currentGroup].inputs[0].values + .length + + while (caseCount === 0 || this.currentGroup === newCase.currentGroup) { + newCase.currentGroup++ + if (newCase.currentGroup >= groupCount) return false + caseCount = + newCase.testData.groups[newCase.currentGroup].inputs[0].values + .length + } + + this.currentGroup = newCase.currentGroup + this.currentCase = newCase.currentCase + return true + } + + /** + * Validate and go to the previous group. + * Skips over empty groups + */ + groupPrev() { + const newCase = new TestbenchData(this.testData, this.currentGroup, 0) + const groupCount = newCase.testData.groups.length + let caseCount = + newCase.testData.groups[newCase.currentGroup].inputs[0].values + .length + + while (caseCount === 0 || this.currentGroup === newCase.currentGroup) { + newCase.currentGroup-- + if (newCase.currentGroup < 0) return false + caseCount = + newCase.testData.groups[newCase.currentGroup].inputs[0].values + .length + } + + this.currentGroup = newCase.currentGroup + this.currentCase = newCase.currentCase + return true + } + + /** + * Validate and go to the next case + */ + caseNext() { + const caseCount = + this.testData.groups[this.currentGroup].inputs[0].values.length + if (this.currentCase >= caseCount - 1) return this.groupNext() + this.currentCase++ + return true + } + + /** + * Validate and go to the previous case + */ + casePrev() { + if (this.currentCase <= 0) { + if (!this.groupPrev()) return false + const caseCount = + this.testData.groups[this.currentGroup].inputs[0].values.length + this.currentCase = caseCount - 1 + return true + } + + this.currentCase-- + return true + } + + /** + * Finds and switches to the first non empty group to start the test from + */ + goToFirstValidGroup() { + const newCase = new TestbenchData(this.testData, 0, 0) + const caseCount = + newCase.testData.groups[this.currentGroup].inputs[0].values.length + + // If the first group is not empty, do nothing + if (caseCount > 0) return true + + // Otherwise go next until non empty group + const validExists = newCase.groupNext() + + // If all groups empty return false + if (!validExists) return false + + // else set case to the non empty group + this.currentGroup = newCase.currentGroup + this.currentCase = newCase.currentCase + return true + } +} + +/** + * UI Function + * Create prompt for the testbench UI when creator is opened + */ +function creatorOpenPrompt(creatorWindow) { + scheduleBackup() + const windowSVG = ` + + + + + ` + + const s = ` +
+
+ ${windowSVG} +
+

A browser pop-up is opened to create the test

+

Please save the test to open it here

+
+ ` + + $('#setTestbenchData').dialog({ + resizable: false, + width: 'auto', + buttons: [ + { + text: 'Close Pop-Up', + click() { + $(this).dialog('close') + creatorWindow.close() + }, + }, + ], + }) + + $('#setTestbenchData').empty() + $('#setTestbenchData').append(s) +} + +/** + * Interface function to run testbench. Called by testbench prompt on simulator or assignments + * @param {Object} data - Object containing Test Data + * @param {RunContext=} runContext - Whether simulator or Assignment called this function + * @param {Scope=} scope - the circuit + */ +export function runTestBench( + data, + scope = globalScope, + runContext = CONTEXT.CONTEXT_SIMULATOR +) { + const isValid = validate(data, scope) + if (!isValid.ok) { + showMessage( + 'Testbench: Some elements missing from circuit. Click Validate to know more' + ) + } + + if (runContext === CONTEXT.CONTEXT_SIMULATOR) { + const tempTestbenchData = new TestbenchData(data) + if (!tempTestbenchData.goToFirstValidGroup()) { + showMessage('Testbench: The test is empty') + return + } + + globalScope.testbenchData = tempTestbenchData + + updateTestbenchUI() + return + } + + if (runContext === CONTEXT.CONTEXT_ASSIGNMENTS) { + // Not implemented + } +} + +/** + * Updates the TestBench UI on the simulator with the current test attached + * If no test is attached then shows the 'No test attached' screen + * Called by runTestBench() when test is set, also called by UX/setupPanelListeners() + * whenever ux change requires this UI to update(such as clicking on a different circuit or + * loading a saved circuit) + */ +export function updateTestbenchUI() { + // Remove all listeners from buttons + $('.tb-dialog-button').off('click') + $('.tb-case-button').off('click') + + setupTestbenchUI() + if (globalScope.testbenchData != undefined) { + const { testbenchData } = globalScope + + // Initialize the UI + setUITableHeaders(testbenchData) + + // Add listeners to buttons + $('.tb-case-button#prev-case-btn').on( + 'click', + buttonListenerFunctions.previousCaseButton + ) + $('.tb-case-button#next-case-btn').on( + 'click', + buttonListenerFunctions.nextCaseButton + ) + $('.tb-case-button#prev-group-btn').on( + 'click', + buttonListenerFunctions.previousGroupButton + ) + $('.tb-case-button#next-group-btn').on( + 'click', + buttonListenerFunctions.nextGroupButton + ) + $('.tb-dialog-button#change-test-btn').on( + 'click', + buttonListenerFunctions.changeTestButton + ) + $('.tb-dialog-button#runall-btn').on( + 'click', + buttonListenerFunctions.runAllButton + ) + $('.tb-dialog-button#edit-test-btn').on( + 'click', + buttonListenerFunctions.editTestButton + ) + $('.tb-dialog-button#validate-btn').on( + 'click', + buttonListenerFunctions.validateButton + ) + $('.tb-dialog-button#remove-test-btn').on( + 'click', + buttonListenerFunctions.removeTestButton + ) + } + + // Add listener to attach test button + $('.tb-dialog-button#attach-test-btn').on( + 'click', + buttonListenerFunctions.attachTestButton + ) +} + +/** + * Defines all the functions called as event listeners for buttons on the UI + */ +const buttonListenerFunctions = { + previousCaseButton: () => { + const isValid = validate( + globalScope.testbenchData.testData, + globalScope + ) + if (!isValid.ok) { + showMessage( + 'Testbench: Some elements missing from circuit. Click Validate to know more' + ) + return + } + globalScope.testbenchData.casePrev() + buttonListenerFunctions.computeCase() + }, + + nextCaseButton: () => { + const isValid = validate( + globalScope.testbenchData.testData, + globalScope + ) + if (!isValid.ok) { + showMessage( + 'Testbench: Some elements missing from circuit. Click Validate to know more' + ) + return + } + globalScope.testbenchData.caseNext() + buttonListenerFunctions.computeCase() + }, + + previousGroupButton: () => { + const isValid = validate( + globalScope.testbenchData.testData, + globalScope + ) + if (!isValid.ok) { + showMessage( + 'Testbench: Some elements missing from circuit. Click Validate to know more' + ) + return + } + globalScope.testbenchData.groupPrev() + buttonListenerFunctions.computeCase() + }, + + nextGroupButton: () => { + const isValid = validate( + globalScope.testbenchData.testData, + globalScope + ) + if (!isValid.ok) { + showMessage( + 'Testbench: Some elements missing from circuit. Click Validate to know more' + ) + return + } + globalScope.testbenchData.groupNext() + buttonListenerFunctions.computeCase() + }, + + changeTestButton: () => { + openCreator('create') + }, + + runAllButton: () => { + const isValid = validate( + globalScope.testbenchData.testData, + globalScope + ) + if (!isValid.ok) { + showMessage( + 'Testbench: Some elements missing from circuit. Click Validate to know more' + ) + return + } + const results = runAll(globalScope.testbenchData.testData, globalScope) + const { passed } = results.summary + const { total } = results.summary + const resultString = JSON.stringify(results.detailed) + $('#runall-summary').text(`${passed} out of ${total}`) + $('#runall-detailed-link').on('click', () => { + openCreator('result', resultString) + }) + $('.testbench-runall-label').css('display', 'table-cell') + $('.testbench-runall-label').delay(5000).fadeOut('slow') + }, + + editTestButton: () => { + const editDataString = JSON.stringify( + globalScope.testbenchData.testData + ) + openCreator('edit', editDataString) + }, + + validateButton: () => { + const isValid = validate( + globalScope.testbenchData.testData, + globalScope + ) + showValidationUI(isValid) + }, + + removeTestButton: async () => { + if ( + await confirmOption( + 'Are you sure you want to remove the test from the circuit?' + ) + ) { + globalScope.testbenchData = undefined + setupTestbenchUI() + } + }, + + attachTestButton: () => { + openCreator('create') + }, + + rerunTestButton: () => { + buttonListenerFunctions.computeCase() + }, + + computeCase: () => { + setUICurrentCase(globalScope.testbenchData) + const result = runSingleTest(globalScope.testbenchData, globalScope) + setUIResult(globalScope.testbenchData, result) + }, +} + +/** + * UI Function + * Checks whether test is attached to the scope and switches UI accordingly + */ +export function setupTestbenchUI() { + // Don't change UI if UI is minimized (because hide() and show() are recursive) + if ($('.testbench-manual-panel .minimize').css('display') === 'none') return + + if (globalScope.testbenchData === undefined) { + $('.tb-test-not-null').hide() + $('.tb-test-null').show() + return + } + + $('.tb-test-null').hide() + $('.tb-test-not-null').show() +} + +/** + * Run all the tests automatically. Called by runTestBench() + * @param {Object} data - Object containing Test Data + * @param {Scope=} scope - the circuit + */ +export function runAll(data, scope = globalScope) { + // Stop the clocks + // TestBench will now take over clock toggling + changeClockEnable(false) + + const { inputs, outputs, reset } = bindIO(data, scope) + let totalCases = 0 + let passedCases = 0 + + data.groups.forEach((group) => { + // for (const output of group.outputs) output.results = []; + group.outputs.forEach((output) => (output.results = [])) + for (let case_i = 0; case_i < group.n; case_i++) { + totalCases++ + // Set and propagate the inputs + setInputValues(inputs, group, case_i, scope) + // If sequential, trigger clock now + if (data.type === 'seq') tickClock(scope) + // Get output values + const caseResult = getOutputValues(data, outputs) + // Put the results in the data + + let casePassed = true // Tracks if current case passed or failed + + caseResult.forEach((_, outName) => { + // TODO: find() is not the best idea because of O(n) + const output = group.outputs.find( + (dataOutput) => dataOutput.label === outName + ) + output.results.push(caseResult.get(outName)) + + if (output.values[case_i] !== caseResult.get(outName)) + casePassed = false + }) + + // If current case passed, then increment passedCases + if (casePassed) passedCases++ + } + + // If sequential, trigger reset at the end of group (set) + if (data.type === 'seq') triggerReset(reset) + }) + + // Tests done, restart the clocks + changeClockEnable(true) + + // Return results + const results = {} + results.detailed = data + results.summary = { passed: passedCases, total: totalCases } + return results +} + +/** + * Runs single test + * @param {Object} data - Object containing Test Data + * @param {number} groupIndex - Index of the group to be tested + * @param {number} caseIndex - Index of the case inside the group + * @param {Scope} scope - The circuit + */ +function runSingleTest(testbenchData, scope) { + const data = testbenchData.testData + + let result + if (data.type === 'comb') { + result = runSingleCombinational(testbenchData, scope) + } else if (data.type === 'seq') { + result = runSingleSequential(testbenchData, scope) + } + + return result +} + +/** + * Runs single combinational test + * @param {Object} data - Object containing Test Data + * @param {number} groupIndex - Index of the group to be tested + * @param {number} caseIndex - Index of the case inside the group + * @param {Scope} scope - The circuit + */ +function runSingleCombinational(testbenchData, scope) { + const data = testbenchData.testData + const groupIndex = testbenchData.currentGroup + const caseIndex = testbenchData.currentCase + + const { inputs, outputs } = bindIO(data, scope) + const group = data.groups[groupIndex] + + // Stop the clocks + changeClockEnable(false) + + // Set input values according to the test + setInputValues(inputs, group, caseIndex, scope) + // Check output values + const result = getOutputValues(data, outputs) + // Restart the clocks + changeClockEnable(true) + return result +} + +/** + * Runs single sequential test and all tests above it in the group + * Used in MANUAL mode + * @param {Object} data - Object containing Test Data + * @param {number} groupIndex - Index of the group to be tested + * @param {number} caseIndex - Index of the case inside the group + * @param {Scope} scope - The circuit + */ +function runSingleSequential(testbenchData, scope) { + const data = testbenchData.testData + const groupIndex = testbenchData.currentGroup + const caseIndex = testbenchData.currentCase + + const { inputs, outputs, reset } = bindIO(data, scope) + const group = data.groups[groupIndex] + + // Stop the clocks + changeClockEnable(false) + + // Trigger reset + triggerReset(reset, scope) + + // Run the test and tests above in the same group + for (let case_i = 0; case_i <= caseIndex; case_i++) { + setInputValues(inputs, group, case_i, scope) + tickClock(scope) + } + + const result = getOutputValues(data, outputs) + + // Restart the clocks + changeClockEnable(true) + + return result +} + +/** + * Set and propogate the input values according to the testcase. + * Called by runSingle() and runAll() + * @param {Object} inputs - Object with keys as input names and values as inputs + * @param {Object} group - Test group + * @param {number} caseIndex - Index of the case in the group + * @param {Scope} scope - the circuit + */ +function setInputValues(inputs, group, caseIndex, scope) { + group.inputs.forEach((input) => { + inputs[input.label].state = parseInt(input.values[caseIndex], 2) + }) + + // Propagate inputs + play(scope) +} + +/** + * Gets Output values as a Map with keys as output name and value as output state + * @param {Object} outputs - Object with keys as output names and values as outputs + */ +function getOutputValues(data, outputs) { + const values = new Map() + + data.groups[0].outputs.forEach((dataOutput) => { + // Using node value because output state only changes on rendering + const resultValue = outputs[dataOutput.label].nodeList[0].value + const resultBW = outputs[dataOutput.label].nodeList[0].bitWidth + values.set(dataOutput.label, dec2bin(resultValue, resultBW)) + }) + + return values +} + +/** + * UI Function + * Shows validation UI + * @param {Object} validationErrors - Object with errors returned by validate() + */ +function showValidationUI(validationErrors) { + const checkSVG = ` + + + + ` + + let s = ` +
+
+ ${checkSVG} +
+ All good. No validation errors +
+ ` + + if (!validationErrors.ok) { + s = ` +
+

Please fix these errors to run tests

+ + + + + + ` + + validationErrors.invalids.forEach((vError) => { + s += ` + + + + + ` + }) + + s += '
IdentifierError
${vError.identifier}${vError.message}
' + } + + $('#testbenchValidate').dialog({ + resizable: false, + width: 'auto', + buttons: [ + { + text: 'Ok', + click() { + $(this).dialog('close') + }, + }, + { + text: 'Auto Fix', + click() { + const fixes = validationAutoFix(validationErrors) + showMessage(`Testbench: Auto fixed ${fixes} errors`) + $(this).dialog('close') + }, + }, + ], + }) + + $('#testbenchValidate').empty() + $('#testbenchValidate').append(s) +} + +/** + * Validate if all inputs and output elements are present with correct bitwidths + * @param {Object} data - Object containing Test Data + * @param {Scope} scope - the circuit + */ +function validate(data, scope) { + let invalids = [] + + // Check for duplicate identifiers + if (!checkDistinctIdentifiersData(data)) { + invalids.push({ + type: VALIDATION_ERRORS.DUPLICATE_ID_DATA, + identifier: '-', + message: 'Duplicate identifiers in test data', + }) + } + + if (!checkDistinctIdentifiersScope(scope)) { + invalids.push({ + type: VALIDATION_ERRORS.DUPLICATE_ID_SCOPE, + identifier: '-', + message: 'Duplicate identifiers in circuit', + }) + } + + // Don't do further checks if duplicates + if (invalids.length > 0) return { ok: false, invalids } + + // Validate inputs and outputs + const inputsValid = validateInputs(data, scope) + const outputsValid = validateOutputs(data, scope) + + invalids = inputsValid.ok ? invalids : invalids.concat(inputsValid.invalids) + invalids = outputsValid.ok + ? invalids + : invalids.concat(outputsValid.invalids) + + // Validate presence of reset if test is sequential + if (data.type === 'seq') { + const resetPresent = scope.Input.some( + (simulatorReset) => + simulatorReset.label === 'RST' && + simulatorReset.bitWidth === 1 && + simulatorReset.objectType === 'Input' + ) + + if (!resetPresent) { + invalids.push({ + type: VALIDATION_ERRORS.NO_RST, + identifier: 'RST', + message: 'Reset(RST) not present in circuit', + }) + } + } + + if (invalids.length > 0) return { ok: false, invalids } + return { ok: true } +} + +/** + * Autofix whatever is possible in validation errors. + * returns number of autofixed errors + * @param {Object} validationErrors - Object with errors returned by validate() + */ +function validationAutoFix(validationErrors) { + // Currently only autofixes bitwidths + let fixedErrors = 0 + // Return if no errors + if (validationErrors.ok) return fixedErrors + + const bitwidthErrors = validationErrors.invalids.filter( + (vError) => vError.type === VALIDATION_ERRORS.WRONGBITWIDTH + ) + + bitwidthErrors.forEach((bwError) => { + const { element, expectedBitWidth } = bwError.extraInfo + element.newBitWidth(expectedBitWidth) + fixedErrors++ + }) + + return fixedErrors +} + +/** + * Checks if all the labels in the test data are unique. Called by validate() + * @param {Object} data - Object containing Test Data + */ +function checkDistinctIdentifiersData(data) { + const inputIdentifiersData = data.groups[0].inputs.map( + (input) => input.label + ) + const outputIdentifiersData = data.groups[0].outputs.map( + (output) => output.label + ) + const identifiersData = inputIdentifiersData.concat(outputIdentifiersData) + + return new Set(identifiersData).size === identifiersData.length +} + +/** + * Checks if all the input/output labels in the scope are unique. Called by validate() + * TODO: Replace with identifiers + * @param {Scope} scope - the circuit + */ +function checkDistinctIdentifiersScope(scope) { + const inputIdentifiersScope = scope.Input.map((input) => input.label) + const outputIdentifiersScope = scope.Output.map((output) => output.label) + let identifiersScope = inputIdentifiersScope.concat(outputIdentifiersScope) + + // Remove identifiers which have not been set yet (ie. empty strings) + identifiersScope = identifiersScope.filter((identifer) => identifer != '') + + return new Set(identifiersScope).size === identifiersScope.length +} + +/** + * Validates presence and bitwidths of test inputs in the circuit. + * Called by validate() + * @param {Object} data - Object containing Test Data + * @param {Scope} scope - the circuit + */ +function validateInputs(data, scope) { + const invalids = [] + + data.groups[0].inputs.forEach((dataInput) => { + const matchInput = scope.Input.find( + (simulatorInput) => simulatorInput.label === dataInput.label + ) + + if (matchInput === undefined) { + invalids.push({ + type: VALIDATION_ERRORS.NOTPRESENT, + identifier: dataInput.label, + message: 'Input is not present in the circuit', + }) + } else if (matchInput.bitWidth !== dataInput.bitWidth) { + invalids.push({ + type: VALIDATION_ERRORS.WRONGBITWIDTH, + identifier: dataInput.label, + extraInfo: { + element: matchInput, + expectedBitWidth: dataInput.bitWidth, + }, + message: `Input bitwidths don't match in circuit and test (${matchInput.bitWidth} vs ${dataInput.bitWidth})`, + }) + } + }) + + if (invalids.length > 0) return { ok: false, invalids } + return { ok: true } +} + +/** + * Validates presence and bitwidths of test outputs in the circuit. + * Called by validate() + * @param {Object} data - Object containing Test Data + * @param {Scope} scope - the circuit + */ +function validateOutputs(data, scope) { + const invalids = [] + + data.groups[0].outputs.forEach((dataOutput) => { + const matchOutput = scope.Output.find( + (simulatorOutput) => simulatorOutput.label === dataOutput.label + ) + + if (matchOutput === undefined) { + invalids.push({ + type: VALIDATION_ERRORS.NOTPRESENT, + identifier: dataOutput.label, + message: 'Output is not present in the circuit', + }) + } else if (matchOutput.bitWidth !== dataOutput.bitWidth) { + invalids.push({ + type: VALIDATION_ERRORS.WRONGBITWIDTH, + identifier: dataOutput.label, + extraInfo: { + element: matchOutput, + expectedBitWidth: dataOutput.bitWidth, + }, + message: `Output bitwidths don't match in circuit and test (${matchOutput.bitWidth} vs ${dataOutput.bitWidth})`, + }) + } + }) + + if (invalids.length > 0) return { ok: false, invalids } + return { ok: true } +} + +/** + * Returns object of scope inputs and outputs keyed by their labels + * @param {Object} data - Object containing Test Data + * @param {Scope=} scope - the circuit + */ +function bindIO(data, scope) { + const inputs = {} + const outputs = {} + let reset + + data.groups[0].inputs.forEach((dataInput) => { + inputs[dataInput.label] = scope.Input.find( + (simulatorInput) => simulatorInput.label === dataInput.label + ) + }) + + data.groups[0].outputs.forEach((dataOutput) => { + outputs[dataOutput.label] = scope.Output.find( + (simulatorOutput) => simulatorOutput.label === dataOutput.label + ) + }) + + if (data.type === 'seq') { + reset = scope.Input.find( + (simulatorOutput) => simulatorOutput.label === 'RST' + ) + } + + return { inputs, outputs, reset } +} + +/** + * Ticks clock recursively one full cycle (Only used in testbench context) + * @param {Scope} scope - the circuit whose clock to be ticked + */ +function tickClock(scope) { + scope.clockTick() + play(scope) + scope.clockTick() + play(scope) +} + +/** + * Triggers reset (Only used in testbench context) + * @param {Input} reset - reset pin to be triggered + * @param {Scope} scope - the circuit + */ +function triggerReset(reset, scope) { + reset.state = 1 + play(scope) + reset.state = 0 + play(scope) +} + +/** + * UI Function + * Sets IO labels and bitwidths on UI table + * Called by simulatorRunTestbench() + * @param {Object} data - Object containing the test data + */ +function setUITableHeaders(testbenchData) { + const data = testbenchData.testData + const inputCount = data.groups[0].inputs.length + const outputCount = data.groups[0].outputs.length + + $('#tb-manual-table-inputs-head').attr('colspan', inputCount) + $('#tb-manual-table-outputs-head').attr('colspan', outputCount) + + $('.testbench-runall-label').css('display', 'none') + + $('.tb-data#data-title') + .children() + .eq(1) + .text(data.title || 'Untitled') + $('.tb-data#data-type') + .children() + .eq(1) + .text(data.type === 'comb' ? 'Combinational' : 'Sequential') + + $('#tb-manual-table-labels').html('LABELS') + $('#tb-manual-table-bitwidths').html('Bitwidth') + + data.groups[0].inputs.concat(data.groups[0].outputs).forEach((io) => { + const label = `${escapeHtml(io.label)}` + const bw = `${escapeHtml(io.bitWidth.toString())}` + $('#tb-manual-table-labels').append(label) + $('#tb-manual-table-bitwidths').append(bw) + }) + + setUICurrentCase(testbenchData) +} + +/** + * UI Function + * Set current test case data on the UI + * @param {Object} data - Object containing the test data + * @param {number} groupIndex - Index of the group of current case + * @param {number} caseIndex - Index of the case within the group + */ +function setUICurrentCase(testbenchData) { + const data = testbenchData.testData + const groupIndex = testbenchData.currentGroup + const caseIndex = testbenchData.currentCase + + const currCaseElement = $('#tb-manual-table-current-case') + currCaseElement.empty() + currCaseElement.append('Current Case') + $('#tb-manual-table-test-result').empty() + $('#tb-manual-table-test-result').append('Result') + + data.groups[groupIndex].inputs.forEach((input) => { + currCaseElement.append( + `${escapeHtml(input.values[caseIndex])}` + ) + }) + + data.groups[groupIndex].outputs.forEach((output) => { + currCaseElement.append( + `${escapeHtml(output.values[caseIndex])}` + ) + }) + + $('.testbench-manual-panel .group-label').text( + data.groups[groupIndex].label + ) + $('.testbench-manual-panel .case-label').text(caseIndex + 1) +} + +/** + * UI Function + * Set the current test case result on the UI + * @param {Object} data - Object containing the test data + * @param {Map} result - Map containing the output values (returned by getOutputValues()) + */ +function setUIResult(testbenchData, result) { + const data = testbenchData.testData + const groupIndex = testbenchData.currentGroup + const caseIndex = testbenchData.currentCase + const resultElement = $('#tb-manual-table-test-result') + let inputCount = data.groups[0].inputs.length + resultElement.empty() + resultElement.append('Result') + while (inputCount--) { + resultElement.append(' - ') + } + + for (const output of result.keys()) { + const resultValue = result.get(output) + const expectedValue = data.groups[groupIndex].outputs.find( + (dataOutput) => dataOutput.label === output + ).values[caseIndex] + const color = resultValue === expectedValue ? '#17FC12' : '#FF1616' + resultElement.append( + `${escapeHtml(resultValue)}` + ) + } +} + +/** + * Use this function to navigate to test creator. This function starts the storage listener + * so the test is loaded directly into the simulator + * @param {string} type - 'create', 'edit' or 'result' + * @param {String} dataString - data in JSON string to load in case of 'edit' and 'result' + */ +function openCreator(type, dataString) { + const popupHeight = 800 + const popupWidth = 1200 + const popupTop = (window.height - popupHeight) / 2 + const popupLeft = (window.width - popupWidth) / 2 + const POPUP_STYLE_STRING = `height=${popupHeight},width=${popupWidth},top=${popupTop},left=${popupLeft}` + let popUp + + /* Listener to catch testData from pop up and load it onto the testbench */ + const dataListener = (message) => { + if ( + message.origin !== window.origin || + message.data.type !== 'testData' + ) + return + + // Check if the current scope requested the creator pop up + const data = JSON.parse(message.data.data) + + // Unbind event listener + window.removeEventListener('message', dataListener) + + // If scopeID does not match, do nothing and return + if (data.scopeID != globalScope.id) return + + // Load test data onto the scope + runTestBench(data.testData, globalScope, CONTEXT.CONTEXT_SIMULATOR) + + // Close the 'Pop up is open' dialog + $('#setTestbenchData').dialog('close') + } + + if (type === 'create') { + const url = `${TESTBENCH_CREATOR_PATH}?scopeID=${globalScope.id}&popUp=true` + popUp = window.open(url, 'popupWindow', POPUP_STYLE_STRING) + creatorOpenPrompt(popUp) + window.addEventListener('message', dataListener) + } + + if (type === 'edit') { + const url = `${TESTBENCH_CREATOR_PATH}?scopeID=${globalScope.id}&data=${dataString}&popUp=true` + popUp = window.open(url, 'popupWindow', POPUP_STYLE_STRING) + creatorOpenPrompt(popUp) + window.addEventListener('message', dataListener) + } + + if (type === 'result') { + const url = `${TESTBENCH_CREATOR_PATH}?scopeID=${globalScope.id}&result=${dataString}&popUp=true` + popUp = window.open(url, 'popupWindow', POPUP_STYLE_STRING) + } + + // Check if popup was closed (in case it was closed by window's X button), + // then close 'popup open' dialog + if (popUp && type !== 'result') { + const checkPopUp = setInterval(() => { + if (popUp.closed) { + // Close the dialog if it's open + if ($('#setTestbenchData').dialog('isOpen')) + $('#setTestbenchData').dialog('close') + + // Remove the event listener that listens for data from popup + window.removeEventListener('message', dataListener) + clearInterval(checkPopUp) + } + }, 1000) + } +} diff --git a/v1/src/simulator/src/testbench/ForceGate.js b/v1/src/simulator/src/testbench/ForceGate.js new file mode 100644 index 00000000..5da82c6f --- /dev/null +++ b/v1/src/simulator/src/testbench/ForceGate.js @@ -0,0 +1,92 @@ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { fillText4 } from '../canvasApi' +/** + * @class + * ForceGate + * @extends CircuitElement + * @param {number} x - x coordinate of element. + * @param {number} y - y coordinate of element. + * @param {Scope=} scope - Cirucit on which element is drawn + * @param {string=} dir - direction of element + * @param {number=} bitWidth - bit width per node. + * @category testbench + */ +export default class ForceGate extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT', bitWidth = 1) { + super(x, y, scope, dir, bitWidth) + this.setDimensions(20, 10) + this.objectType = 'ForceGate' + this.scope.ForceGate.push(this) + this.inp1 = new Node(-20, 0, 0, this) + this.inp2 = new Node(0, 0, 0, this) + this.output1 = new Node(20, 0, 1, this) + } + + /** + * @memberof ForceGate + * Checks if the element is resolvable + * @return {boolean} + */ + isResolvable() { + return this.inp1.value !== undefined || this.inp2.value !== undefined + } + + /** + * @memberof ForceGate + * fn to create save Json Data of object + * @return {JSON} + */ + customSave() { + const data = { + constructorParamaters: [this.direction, this.bitWidth], + nodes: { + output1: findNode(this.output1), + inp1: findNode(this.inp1), + inp2: findNode(this.inp2), + }, + } + return data + } + + /** + * @memberof ForceGate + * resolve output values based on inputData + */ + resolve() { + if (this.inp2.value !== undefined) { + this.output1.value = this.inp2.value + } else { + this.output1.value = this.inp1.value + } + simulationArea.simulationQueue.add(this.output1) + } + + /** + * @memberof ForceGate + * function to draw element + */ + customDraw() { + var ctx = simulationArea.context + const xx = this.x + const yy = this.y + + ctx.beginPath() + ctx.fillStyle = 'Black' + ctx.textAlign = 'center' + + fillText4(ctx, 'I', -10, 0, xx, yy, this.direction, 10) + fillText4(ctx, 'O', 10, 0, xx, yy, this.direction, 10) + ctx.fill() + } +} + +/** + * @memberof ForceGate + * Help Tip + * @type {string} + * @category testbench + */ +ForceGate.prototype.tooltipText = 'Force Gate ToolTip : ForceGate Selected.' +ForceGate.prototype.objectType = 'ForceGate' diff --git a/v1/src/simulator/src/testbench/testbenchInput.js b/v1/src/simulator/src/testbench/testbenchInput.js new file mode 100644 index 00000000..bdf7452b --- /dev/null +++ b/v1/src/simulator/src/testbench/testbenchInput.js @@ -0,0 +1,351 @@ +import CircuitElement from '../circuitElement' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, fillText } from '../canvasApi' +import Node, { findNode } from '../node' +import plotArea from '../plotArea' + +/** + * TestBench Input has a node for it's clock input. + * this.testData - the data of all test cases. + * Every testbench has a uniq identifier. + * @class + * @extends CircuitElement + * @param {number} x - the x coord of TB + * @param {number} y - the y coord of TB + * @param {Scope=} scope - the circuit on which TB is drawn + * @param {string} dir - direction + * @param {string} identifier - id to identify tests + * @param {JSON=} testData - input, output and number of tests + * @category testbench + */ +export default class TB_Input extends CircuitElement { + constructor( + x, + y, + scope = globalScope, + dir = 'RIGHT', + identifier, + testData + ) { + super(x, y, scope, dir, 1) + this.objectType = 'TB_Input' + this.scope.TB_Input.push(this) + this.setIdentifier(identifier || 'Test1') + this.testData = testData || { inputs: [], outputs: [], n: 0 } + this.clockInp = new Node(0, 20, 0, this, 1) + this.outputs = [] + this.running = false // if tests are undergo + this.iteration = 0 + this.setup() + } + + /** + * @memberof TB_Input + * Takes input when double clicked. For help on generation of input refer to TB_Input.helplink + */ + dblclick() { + this.testData = JSON.parse(prompt('Enter TestBench Json')) + this.setup() + } + + setDimensions() { + this.leftDimensionX = 0 + this.rightDimensionX = 120 + + this.upDimensionY = 0 + this.downDimensionY = 40 + this.testData.inputs.length * 20 + } + + /** + * @memberof TB_Input + * setups the Test by parsing through the testbench data. + */ + setup() { + this.iteration = 0 + this.running = false + this.nodeList.clean(this.clockInp) + this.deleteNodes() + this.nodeList = [] + this.nodeList.push(this.clockInp) + this.testData = this.testData || { inputs: [], outputs: [], n: 0 } + // this.clockInp = new Node(0,20, 0,this,1); + + this.setDimensions() + + this.prevClockState = 0 + this.outputs = [] + + for (var i = 0; i < this.testData.inputs.length; i++) { + this.outputs.push( + new Node( + this.rightDimensionX, + 30 + i * 20, + 1, + this, + this.testData.inputs[i].bitWidth, + this.testData.inputs[i].label + ) + ) + } + + for (var i = 0; i < this.scope.TB_Output.length; i++) { + if (this.scope.TB_Output[i].identifier == this.identifier) { + this.scope.TB_Output[i].setup() + } + } + } + + /** + * @memberof TB_Input + * toggles state by simply negating this.running so that test cases stop + */ + toggleState() { + this.running = !this.running + this.prevClockState = 0 + } + + /** + * @memberof TB_Input + * function to run from test case 0 again + */ + resetIterations() { + this.iteration = 0 + this.prevClockState = 0 + } + + /** + * @memberof TB_Input + * function to resolve the testbench input adds + */ + resolve() { + if (this.clockInp.value != this.prevClockState) { + this.prevClockState = this.clockInp.value + if (this.clockInp.value == 1 && this.running) { + if (this.iteration < this.testData.n) { + this.iteration++ + } else { + this.running = false + } + } + } + if (this.running && this.iteration) { + for (var i = 0; i < this.testData.inputs.length; i++) { + this.outputs[i].value = parseInt( + this.testData.inputs[i].values[this.iteration - 1], + 2 + ) + simulationArea.simulationQueue.add(this.outputs[i]) + } + } + } + + /** + * @memberof TB_Input + * was a function to plot values incase any flag used as output to this element + */ + setPlotValue() { + return + var time = plotArea.stopWatch.ElapsedMilliseconds + if ( + this.plotValues.length && + this.plotValues[this.plotValues.length - 1][0] == time + ) { + this.plotValues.pop() + } + + if (this.plotValues.length == 0) { + this.plotValues.push([time, this.inp1.value]) + return + } + + if (this.plotValues[this.plotValues.length - 1][1] == this.inp1.value) { + return + } + this.plotValues.push([time, this.inp1.value]) + } + + customSave() { + var data = { + constructorParamaters: [ + this.direction, + this.identifier, + this.testData, + ], + nodes: { + outputs: this.outputs.map(findNode), + clockInp: findNode(this.clockInp), + }, + } + return data + } + + /** + * This function is used to set a uniq identifier to every testbench + * @memberof TB_Input + */ + setIdentifier(id = '') { + if (id.length == 0 || id == this.identifier) return + + for (var i = 0; i < this.scope.TB_Output.length; i++) { + this.scope.TB_Output[i].checkPairing() + } + + for (var i = 0; i < this.scope.TB_Output.length; i++) { + if (this.scope.TB_Output[i].identifier == this.identifier) { + this.scope.TB_Output[i].identifier = id + } + } + + this.identifier = id + + this.checkPaired() + } + + /** + * Check if there is a output tester paired with input TB. + * @memberof TB_Input + */ + checkPaired() { + for (var i = 0; i < this.scope.TB_Output.length; i++) { + if (this.scope.TB_Output[i].identifier == this.identifier) { + this.scope.TB_Output[i].checkPairing() + } + } + } + + delete() { + super.delete() + this.checkPaired() + } + + customDraw() { + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = 'grey' + ctx.fillStyle = '#fcfcfc' + ctx.lineWidth = correctWidth(1) + var xx = this.x + var yy = this.y + + var xRotate = 0 + var yRotate = 0 + if (this.direction == 'LEFT') { + xRotate = 0 + yRotate = 0 + } else if (this.direction == 'RIGHT') { + xRotate = 120 - this.xSize + yRotate = 0 + } else if (this.direction == 'UP') { + xRotate = 60 - this.xSize / 2 + yRotate = -20 + } else { + xRotate = 60 - this.xSize / 2 + yRotate = 20 + } + + ctx.beginPath() + ctx.textAlign = 'center' + ctx.fillStyle = 'black' + fillText( + ctx, + `${this.identifier} [INPUT]`, + xx + this.rightDimensionX / 2, + yy + 14, + 10 + ) + + fillText( + ctx, + ['Not Running', 'Running'][+this.running], + xx + this.rightDimensionX / 2, + yy + 14 + 10 + 20 * this.testData.inputs.length, + 10 + ) + fillText( + ctx, + `Case: ${this.iteration}`, + xx + this.rightDimensionX / 2, + yy + 14 + 20 + 20 * this.testData.inputs.length, + 10 + ) + // fillText(ctx, "Case: "+this.iteration, xx , yy + 20+14, 10); + ctx.fill() + + ctx.font = '30px Raleway' + ctx.textAlign = 'right' + ctx.fillStyle = 'blue' + ctx.beginPath() + for (var i = 0; i < this.testData.inputs.length; i++) { + // ctx.beginPath(); + fillText( + ctx, + this.testData.inputs[i].label, + this.rightDimensionX - 5 + xx, + 30 + i * 20 + yy + 4, + 10 + ) + } + + ctx.fill() + if (this.running && this.iteration) { + ctx.font = '30px Raleway' + ctx.textAlign = 'left' + ctx.fillStyle = 'blue' + ctx.beginPath() + for (var i = 0; i < this.testData.inputs.length; i++) { + fillText( + ctx, + this.testData.inputs[i].values[this.iteration - 1], + 5 + xx, + 30 + i * 20 + yy + 4, + 10 + ) + } + + ctx.fill() + } + + ctx.beginPath() + ctx.strokeStyle = 'rgba(0,0,0,1)' + ctx.lineWidth = correctWidth(3) + var xx = this.x + var yy = this.y + // rect(ctx, xx - 20, yy - 20, 40, 40); + moveTo(ctx, 0, 15, xx, yy, this.direction) + lineTo(ctx, 5, 20, xx, yy, this.direction) + lineTo(ctx, 0, 25, xx, yy, this.direction) + + ctx.stroke() + } +} + +TB_Input.prototype.tooltipText = 'Test Bench Input Selected' + +/** + * @memberof TB_Input + * different algo for drawing center elements + * @category testbench + */ +TB_Input.prototype.centerElement = true + +TB_Input.prototype.helplink = 'https://docs.circuitverse.org/#/chapter7/3testcircuits' + +TB_Input.prototype.mutableProperties = { + identifier: { + name: 'TestBench Name:', + type: 'text', + maxlength: '10', + func: 'setIdentifier', + }, + iteration: { + name: 'Reset Iterations', + type: 'button', + func: 'resetIterations', + }, + toggleState: { + name: 'Toggle State', + type: 'button', + func: 'toggleState', + }, +} +TB_Input.prototype.objectType = 'TB_Input' diff --git a/v1/src/simulator/src/testbench/testbenchOutput.js b/v1/src/simulator/src/testbench/testbenchOutput.js new file mode 100644 index 00000000..865ec258 --- /dev/null +++ b/v1/src/simulator/src/testbench/testbenchOutput.js @@ -0,0 +1,324 @@ +import CircuitElement from '../circuitElement' +import simulationArea from '../simulationArea' +import { correctWidth, fillText } from '../canvasApi' +import Node, { findNode } from '../node' + +// helper function to convert decimal to binary +function dec2bin(dec, bitWidth = undefined) { + // only for positive nos + var bin = dec.toString(2) + if (bitWidth == undefined) return bin + return '0'.repeat(bitWidth - bin.length) + bin +} + +/** + * TestBench Output has a node for it's input which is + * compared to desired output according tp testData of + * input TB Every TB_output has a uniq identifier matching + * it's TB_Input + * @class + * @extends CircuitElement + * @param {number} x - the x coord of TB + * @param {number} y - the y coord of TB + * @param {Scope=} scope - the circuit on which TB is drawn + * @param {string} dir - direction + * @param {string} identifier - id to identify tests + * @category testbench + */ + +export default class TB_Output extends CircuitElement { + constructor(x, y, scope = globalScope, dir = 'RIGHT', identifier) { + super(x, y, scope, dir, 1) + // this.setDimensions(60,20); + this.objectType = 'TB_Output' + this.scope.TB_Output.push(this) + + // this.xSize=10; + + // this.plotValues = []; + // this.inp1 = new Node(0, 0, 0, this); + // this.inp1 = new Node(100, 100, 0, this); + this.setIdentifier(identifier || 'Test1') + this.inputs = [] + this.testBenchInput = undefined + + this.setup() + } + + // TB_Output.prototype.dblclick=function(){ + // this.testData=JSON.parse(prompt("Enter TestBench Json")); + // this.setup(); + // } + setDimensions() { + this.leftDimensionX = 0 + this.rightDimensionX = 160 + this.upDimensionY = 0 + this.downDimensionY = 40 + if (this.testBenchInput) { + this.downDimensionY = + 40 + this.testBenchInput.testData.outputs.length * 20 + } + } + + setup() { + // this.iteration = 0; + // this.running = false; + // this.nodeList.clean(this.clockInp); + this.deleteNodes() // deletes all nodes whenever setup is called. + this.nodeList = [] + + this.inputs = [] + this.testBenchInput = undefined + // find it's pair input + for (var i = 0; i < this.scope.TB_Input.length; i++) { + if (this.scope.TB_Input[i].identifier == this.identifier) { + this.testBenchInput = this.scope.TB_Input[i] + break + } + } + + this.setDimensions() + + if (this.testBenchInput) { + for ( + var i = 0; + i < this.testBenchInput.testData.outputs.length; + i++ + ) { + this.inputs.push( + new Node( + 0, + 30 + i * 20, + NODE_INPUT, + this, + this.testBenchInput.testData.outputs[i].bitWidth, + this.testBenchInput.testData.outputs[i].label + ) + ) + } + } + } + + customSave() { + var data = { + constructorParamaters: [this.direction, this.identifier], + nodes: { + inputs: this.inputs.map(findNode), + }, + } + return data + } + + /** + * @memberof TB_output + * set identifier for this testbench + */ + setIdentifier(id = '') { + if (id.length == 0 || id == this.identifier) return + this.identifier = id + this.setup() + } + + /** + * @memberof TB_output + * Function to check if the input for this TB exist + */ + checkPairing(id = '') { + if (this.testBenchInput) { + if ( + this.testBenchInput.deleted || + this.testBenchInput.identifier != this.identifier + ) { + this.setup() + } + } else { + this.setup() + } + } + + customDraw() { + var ctx = simulationArea.context + ctx.beginPath() + ctx.strokeStyle = 'grey' + ctx.fillStyle = '#fcfcfc' + ctx.lineWidth = correctWidth(1) + var xx = this.x + var yy = this.y + + var xRotate = 0 + var yRotate = 0 + if (this.direction == 'LEFT') { + xRotate = 0 + yRotate = 0 + } else if (this.direction == 'RIGHT') { + xRotate = 120 - this.xSize + yRotate = 0 + } else if (this.direction == 'UP') { + xRotate = 60 - this.xSize / 2 + yRotate = -20 + } else { + xRotate = 60 - this.xSize / 2 + yRotate = 20 + } + + // rect2(ctx, -120+xRotate+this.xSize, -20+yRotate, 120-this.xSize, 40, xx, yy, "RIGHT"); + // if ((this.hover && !simulationArea.shiftDown) || simulationArea.lastSelected == this || simulationArea.multipleObjectSelections.contains(this)) + // ctx.fillStyle = "rgba(255, 255, 32,0.8)"; + // ctx.fill(); + // ctx.stroke(); + // + // ctx.font = "14px Raleway"; + // this.xOff = ctx.measureText(this.identifier).width; + // ctx.beginPath(); + // rect2(ctx, -105+xRotate+this.xSize, -11+yRotate,this.xOff + 10, 23, xx, yy, "RIGHT"); + // ctx.fillStyle = "#eee" + // ctx.strokeStyle = "#ccc"; + // ctx.fill(); + // ctx.stroke(); + // + + ctx.beginPath() + ctx.textAlign = 'center' + ctx.fillStyle = 'black' + fillText( + ctx, + `${this.identifier} [OUTPUT]`, + xx + this.rightDimensionX / 2, + yy + 14, + 10 + ) + + // fillText(ctx, ["Not Running","Running"][+this.running], xx + this.rightDimensionX/ 2 , yy + 14 + 10 + 20*this.testData.inputs.length, 10); + // fillText(ctx, "Case: "+(this.iteration), xx + this.rightDimensionX/ 2 , yy + 14 + 20 + 20*this.testData.inputs.length, 10); + fillText( + ctx, + ['Unpaired', 'Paired'][+(this.testBenchInput != undefined)], + xx + this.rightDimensionX / 2, + yy + this.downDimensionY - 5, + 10 + ) + ctx.fill() + + if (this.testBenchInput) { + ctx.beginPath() + ctx.font = '30px Raleway' + ctx.textAlign = 'left' + ctx.fillStyle = 'blue' + for ( + var i = 0; + i < this.testBenchInput.testData.outputs.length; + i++ + ) { + // ctx.beginPath(); + fillText( + ctx, + this.testBenchInput.testData.outputs[i].label, + 5 + xx, + 30 + i * 20 + yy + 4, + 10 + ) + } + ctx.fill() + + if (this.testBenchInput.running && this.testBenchInput.iteration) { + ctx.beginPath() + ctx.font = '30px Raleway' + ctx.textAlign = 'right' + ctx.fillStyle = 'blue' + ctx.beginPath() + for ( + var i = 0; + i < this.testBenchInput.testData.outputs.length; + i++ + ) { + fillText( + ctx, + this.testBenchInput.testData.outputs[i].values[ + this.testBenchInput.iteration - 1 + ], + xx + this.rightDimensionX - 5, + 30 + i * 20 + yy + 4, + 10 + ) + } + + ctx.fill() + } + + if (this.testBenchInput.running && this.testBenchInput.iteration) { + ctx.beginPath() + ctx.font = '30px Raleway' + ctx.textAlign = 'center' + ctx.fillStyle = 'blue' + + for ( + var i = 0; + i < this.testBenchInput.testData.outputs.length; + i++ + ) { + if (this.inputs[i].value != undefined) { + ctx.beginPath() + if ( + this.testBenchInput.testData.outputs[i].values[ + this.testBenchInput.iteration - 1 + ] == 'x' || + parseInt( + this.testBenchInput.testData.outputs[i].values[ + this.testBenchInput.iteration - 1 + ], + 2 + ) == this.inputs[i].value + ) { + ctx.fillStyle = 'green' + } else { + ctx.fillStyle = 'red' + } + fillText( + ctx, + dec2bin( + this.inputs[i].value, + this.inputs[i].bitWidth + ), + xx + this.rightDimensionX / 2, + 30 + i * 20 + yy + 4, + 10 + ) + ctx.fill() + } else { + ctx.beginPath() + if ( + this.testBenchInput.testData.outputs[i].values[ + this.testBenchInput.iteration - 1 + ] == 'x' + ) { + ctx.fillStyle = 'green' + } else { + ctx.fillStyle = 'red' + } + fillText( + ctx, + 'X', + xx + this.rightDimensionX / 2, + 30 + i * 20 + yy + 4, + 10 + ) + ctx.fill() + } + } + } + } + } +} + +TB_Output.prototype.tooltipText = 'Test Bench Output Selected' +TB_Output.prototype.helplink = 'https://docs.circuitverse.org/#/chapter7/3testcircuits' +TB_Output.prototype.centerElement = true +TB_Output.prototype.mutableProperties = { + identifier: { + name: 'TestBench Name:', + type: 'text', + maxlength: '10', + func: 'setIdentifier', + }, +} +TB_Output.prototype.objectType = 'TB_Output' diff --git a/v1/src/simulator/src/themer/customThemeAbstraction.js b/v1/src/simulator/src/themer/customThemeAbstraction.js new file mode 100644 index 00000000..399134ea --- /dev/null +++ b/v1/src/simulator/src/themer/customThemeAbstraction.js @@ -0,0 +1,44 @@ +/** + * CreateAbstraction + * @param {*} themeOptions + * @returns an Object + */ +export const CreateAbstraction = (themeOptions) => { + return { + Navbar: { + color: themeOptions['--bg-navbar'], + description: 'navbar background', + ref: ['--bg-navbar'], + }, + Primary: { + color: themeOptions['--primary'], + description: 'modals background', + ref: ['--primary'], + }, + Secondary: { + color: themeOptions['--bg-tabs'], + description: 'tabBar background', + ref: ['--bg-tabs'], + }, + Canvas: { + color: themeOptions['--canvas-fill'], + description: 'canvas background', + ref: ['--canvas-fill'], + }, + Stroke: { + color: themeOptions['--canvas-stroke'], + description: 'canvas grid color', + ref: ['--canvas-stroke'], + }, + Text: { + color: themeOptions['--text-lite'], + description: 'text color', + ref: ['--text-lite', '--text-panel', '--text-dark'], + }, + Borders: { + color: themeOptions['--br-secondary'], + description: 'borders color', + ref: ['--br-secondary'], + }, + } +} diff --git a/v1/src/simulator/src/themer/customThemer.js b/v1/src/simulator/src/themer/customThemer.js new file mode 100644 index 00000000..51a7e0b2 --- /dev/null +++ b/v1/src/simulator/src/themer/customThemer.js @@ -0,0 +1,154 @@ +// /* eslint-disable import/prefer-default-export */ +// /* eslint-disable import/no-cycle */ +// import { dots } from '../canvasApi' +// import themeOptions from './themes' +// import { updateThemeForStyle } from './themer' +// import { CreateAbstraction } from './customThemeAbstraction' + +// /** +// * +// */ +// var customTheme = CreateAbstraction(themeOptions['Custom Theme']) + +// const updateBG = () => dots(true, false, true) + +// /** +// * Generates Custom theme card HTML +// * return Html Element Theme card html (properties_container) +// */ +// // const getCustomThemeCard = () => { +// // var propertiesContainer = document.createElement('form') +// // const keys = Object.keys(customTheme) +// // keys.forEach((key) => { +// // const property = document.createElement('div') +// // const newPropertyLabel = document.createElement('label') +// // newPropertyLabel.textContent = `${key} (${customTheme[key].description})` +// // newPropertyLabel.setAttribute('for', key) +// // const newPropertyInput = document.createElement('input') +// // newPropertyInput.setAttribute('type', 'color') +// // newPropertyInput.setAttribute('name', key) +// // newPropertyInput.setAttribute('value', customTheme[key].color) +// // newPropertyInput.classList.add('customColorInput') +// // property.append(newPropertyLabel) +// // property.append(newPropertyInput) +// // propertiesContainer.append(property) +// // }) +// // const downloadAnchor = document.createElement('a') +// // downloadAnchor.setAttribute('id', 'downloadThemeFile') +// // downloadAnchor.setAttribute('style', 'display:none') +// // propertiesContainer.appendChild(downloadAnchor) +// // return propertiesContainer +// // } + +// /** +// * Create Custom Color Themes Dialog +// */ +// // export const CustomColorThemes = () => { +// // $('#CustomColorThemesDialog').empty() +// // $('#CustomColorThemesDialog').append(getCustomThemeCard()) +// // $('#CustomColorThemesDialog').dialog({ +// // resizable: false, +// // close() { +// // themeOptions['Custom Theme'] = +// // JSON.parse(localStorage.getItem('Custom Theme')) || +// // themeOptions['Default Theme'] // hack for closing dialog box without saving +// // // Rollback to previous theme +// // updateThemeForStyle(localStorage.getItem('theme')) +// // updateBG() +// // }, +// // buttons: [ +// // { +// // text: 'Apply Theme', +// // click() { +// // // update theme to Custom Theme +// // localStorage.setItem('theme', 'Custom Theme') +// // // add Custom theme to custom theme object +// // localStorage.setItem( +// // 'Custom Theme', +// // JSON.stringify(themeOptions['Custom Theme']) +// // ) +// // $('.set').removeClass('set') +// // $('.selected').addClass('set') +// // $(this).dialog('close') +// // }, +// // }, +// // { +// // text: 'Import Theme', +// // click() { +// // $('#importThemeFile').click() +// // }, +// // }, +// // { +// // text: 'Export Theme', +// // click() { +// // const dlAnchorElem = +// // document.getElementById('downloadThemeFile') +// // dlAnchorElem.setAttribute( +// // 'href', +// // `data:text/json;charset=utf-8,${encodeURIComponent( +// // JSON.stringify(themeOptions['Custom Theme']) +// // )}` +// // ) +// // dlAnchorElem.setAttribute('download', 'CV_CustomTheme.json') +// // dlAnchorElem.click() +// // }, +// // }, +// // ], +// // }) + +// // $('#CustomColorThemesDialog').focus() + +// // /** +// // * To preview the changes +// // */ +// // // function setColorEvent() { +// // // $('.customColorInput').on('input', (e) => { +// // // customTheme[e.target.name].color = e.target.value +// // // customTheme[e.target.name].ref.forEach((property) => { +// // // themeOptions['Custom Theme'][property] = e.target.value +// // // }) +// // // updateThemeForStyle('Custom Theme') +// // // updateBG() +// // // }) +// // // } +// // // setColorEvent() + +// // // hack for updating current theme to the saved custom theme +// // setTimeout(() => { +// // updateThemeForStyle('Custom Theme') +// // updateBG() +// // }, 50) + +// // /** +// // * Read JSON file and +// // * set Custom theme to the Content of the JSON file +// // * */ +// // // function receivedText(e) { +// // // const lines = JSON.parse(e.target.result) +// // // customTheme = CreateAbstraction(lines) +// // // themeOptions['Custom Theme'] = lines +// // // // preview theme +// // // updateThemeForStyle('Custom Theme') +// // // updateBG() +// // // // update colors in dialog box +// // // $('#CustomColorThemesDialog').empty() +// // // $('#CustomColorThemesDialog').append(getCustomThemeCard()) +// // // setColorEvent() +// // // } + +// // /** +// // * Add listener for file input +// // * Read imported JSON file +// // */ +// // // $('#importThemeFile').on('change', (event) => { +// // // var File = event.target.files[0] +// // // if (File !== null && File.name.split('.')[1] === 'json') { +// // // var fr = new FileReader() +// // // fr.onload = receivedText +// // // fr.readAsText(File) +// // // $('#importThemeFile').val('') +// // // } else { +// // // alert('File Not Supported !') +// // // } +// // // }) +// // } diff --git a/v1/src/simulator/src/themer/themeCardSvg.js b/v1/src/simulator/src/themer/themeCardSvg.js new file mode 100644 index 00000000..5e1776cb --- /dev/null +++ b/v1/src/simulator/src/themer/themeCardSvg.js @@ -0,0 +1,102 @@ +export default ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +` diff --git a/v1/src/simulator/src/themer/themer.js b/v1/src/simulator/src/themer/themer.js new file mode 100644 index 00000000..2c61865e --- /dev/null +++ b/v1/src/simulator/src/themer/themer.js @@ -0,0 +1,223 @@ +import { dots } from '../canvasApi' +import themeOptions from './themes' +import themeCardSvg from './themeCardSvg' +import { SimulatorStore } from '#/store/SimulatorStore/SimulatorStore' + +/** + * Extracts canvas theme colors from CSS-Variables and returns a JSON Object + * @returns {object} + */ +const getCanvasColors = () => { + let colors = {} + colors['hover_select'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--hover-and-sel') + colors['fill'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--fill') + colors['mini_fill'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--mini-map') + colors['mini_stroke'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--mini-map-stroke') + colors['stroke'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--stroke') + colors['stroke_alt'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--secondary-stroke') + colors['input_text'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--input-text') + colors['color_wire_draw'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--wire-draw') + colors['color_wire_con'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--wire-cnt') + colors['color_wire_pow'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--wire-pow') + colors['color_wire_sel'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--wire-sel') + colors['color_wire_lose'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--wire-lose') + colors['color_wire'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--wire-norm') + colors['text'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--text') + colors['node'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--node') + colors['node_norm'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--node-norm') + colors['splitter'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--splitter') + colors['out_rect'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--output-rect') + colors['canvas_stroke'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--canvas-stroke') + colors['canvas_fill'] = getComputedStyle( + document.documentElement + ).getPropertyValue('--canvas-fill') + return colors +} + +/** + * Common canvas theme color object, used for rendering canvas elements + */ +export let colors = getCanvasColors() + +/** + * Updates theme + * 1) Sets CSS Variables for UI elements + * 2) Sets color variable for Canvas elements + */ +export function updateThemeForStyle(themeName) { + const selectedTheme = themeOptions[themeName] + if (selectedTheme === undefined) return + const html = document.getElementsByTagName('html')[0] + Object.keys(selectedTheme).forEach((property, i) => { + html.style.setProperty(property, selectedTheme[property]) + }) + colors = getCanvasColors() +} + +/** + * Theme Preview Card SVG + * Sets the SVG colors according to theme + * @param {string} themeName Name of theme + * @returns {SVG} + */ +export const getThemeCardSvg = (themeName) => { + const colors = themeOptions[themeName] + let svgIcon = $(themeCardSvg) + + // Dynamically set the colors according to the theme + $('.svgText', svgIcon).attr('fill', colors['--text-panel']) + + $('.svgNav', svgIcon).attr('fill', colors['--bg-tab']) + $('.svgNav', svgIcon).attr('stroke', colors['--br-primary']) + + $('.svgGridBG', svgIcon).attr('fill', colors['--canvas-fill']) + $('.svgGrid', svgIcon).attr('fill', colors['--canvas-stroke']) + + $('.svgPanel', svgIcon).attr('fill', colors['--primary']) + $('.svgPanel', svgIcon).attr('stroke', colors['--br-primary']) + + $('.svgChev', svgIcon).attr('stroke', colors['--br-secondary']) + + $('.svgHeader', svgIcon).attr('fill', colors['--primary']) + let temp = svgIcon.prop('outerHTML') + return svgIcon.prop('outerHTML') +} + +/** + * Generates theme card HTML + * @param {string} themeName Name of theme + * @param {boolean} selected Flag variable for currently selected theme + * @return {string} Theme card html + */ +export const getThemeCard = (themeName, selected) => { + if (themeName === 'Custom Theme') return '
' + let themeId = themeName.replace(' ', '') + let selectedClass = selected ? 'selected set' : '' + // themeSel is the hit area + return ` +
+
+ ${getThemeCardSvg(themeName)} + + + + +
+ ` +} + +/** + * Create Color Themes Dialog + */ +export const colorThemes = () => { + const simulatorStore = SimulatorStore() + simulatorStore.dialogBox.theme_dialog = true + + // const selectedTheme = localStorage.getItem('theme') + // $('#colorThemesDialog').empty() + // const themes = Object.keys(themeOptions) + // themes.forEach((theme) => { + // if (theme === selectedTheme) { + // $('#colorThemesDialog').append(getThemeCard(theme, true)) + // } else { + // $('#colorThemesDialog').append(getThemeCard(theme, false)) + // } + // }) + + // $('.selected label').trigger('click') + // $('#colorThemesDialog').dialog({ + // resizable: false, + // close() { + // // Rollback to previous theme + // updateThemeForStyle(localStorage.getItem('theme')) + // updateBG() + // }, + // buttons: [ + // { + // text: 'Apply Theme', + // click() { + // // check if any theme is selected or not + // if ($('.selected label').text()) { + // localStorage.removeItem('Custom Theme') + // localStorage.setItem( + // 'theme', + // $('.selected label').text() + // ) + // } + // $('.set').removeClass('set') + // $('.selected').addClass('set') + // $(this).dialog('close') + // }, + // }, + // { + // text: 'Custom Theme', + // click() { + // CustomColorThemes() + // $(this).dialog('close') + // }, + // }, + // ], + // }) + + $('#colorThemesDialog').focus() + $('.ui-dialog[aria-describedby="colorThemesDialog"]').on('click', () => + $('#colorThemesDialog').focus() + ) //hack for losing focus + + $('.themeSel').on('mousedown', (e) => { + e.preventDefault() + $('.selected').removeClass('selected') + let themeCard = $(e.target.parentElement) + themeCard.addClass('selected') + // Extract radio button + var radioButton = themeCard.find('input[type=radio]') + radioButton.trigger('click') // Mark as selected + updateThemeForStyle(themeCard.find('label').text()) // Extract theme name and set + updateBG() + }) +} + +export const updateBG = () => dots(true, false, true) +;(() => { + if (!localStorage.getItem('theme')) + localStorage.setItem('theme', 'Default Theme') + updateThemeForStyle(localStorage.getItem('theme')) +})() diff --git a/v1/src/simulator/src/themer/themes.js b/v1/src/simulator/src/themer/themes.js new file mode 100644 index 00000000..d4f8caa1 --- /dev/null +++ b/v1/src/simulator/src/themer/themes.js @@ -0,0 +1,382 @@ +export default { + 'Default Theme': { + '--text-navbar--alt': '#000', + '--br-secondary': '#7d7d7d', + '--br-circuit-cur': '#fff', + '--br-circuit': '#fff', + '--cus-radio_label': '#656565', + '--primary': '#454545', + '--text-lite': '#fff', + '--text-dark': '#000', + '--text-panel': 'white', + '--bg-navbar': '#454545', + '--qp-br-tl': '#333333', + '--qp-br-rd': '#535353', + '--qp-box-shadow-1': '#3b3b3b', + '--qp-box-shadow-2': '#4f4f4f', + '--bg-circuit': '#ddd', + '--br-circuit': '#454545', + '--br-primary': '#fff', + '--bg-primary-moz': '#454545e6', + '--bg-primary-chr': '#454545b3', + '--bg-tabs': '#8b8b8b', + '--bg-icons': '#7d7d7d', + '--bg-text': '#cacaca', + '--bg-secondary': '#bbbbbb', + '--canvas-stroke': '#eee', + '--canvas-fill': 'white', + '--context-text': 'white', + '--bg-toggle-btn-primary': '#42b983', + '--primary-btn-hov': '#3ca877', + '--btn-danger': '#dc5656', + '--btn-danger-darken': '#b03662', + '--disable': '#6c8b93', + '--cus-btn-hov--bg': '#ddd', + '--cus-btn-hov-text': '#000', + '--node': 'green', + '--stroke': 'black', + '--fill': 'white', + '--hover-and-sel': 'rgba(255, 255, 32, 0.8)', + '--wire-draw': 'black', + '--wire-cnt': 'green', + '--wire-pow': 'lightgreen', + '--wire-sel': 'blue', + '--wire-lose': 'red', + '--mini-map': 'green', + '--mini-map-stroke': 'darkgreen', + '--input-text': 'green', + '--secondary-stroke': 'red', + '--text': 'black', + '--wire-norm': 'black', + '--node-norm': 'green', + '--splitter': 'black', + '--output-rect': 'blue', + '--table-head-dark': '#3d3d3d', + }, + 'Night Sky': { + '--text-navbar--alt': '#fff', + '--br-secondary': '#665627', + '--cus-radio_label': '#0F111A', + '--primary': '#0F111A', //header bg, panels bg + '--text-lite': '#FFF', //normal state text + '--text-dark': 'white', //text state on hover, on drop down menu , context menu + '--text-panel': 'white', + '--bg-navbar': '#0F111A', + '--br-circuit': '#0F111A', //panel border, tabbar circuit border + '--br-primary': '#665627', //panel border, tabbar circuit border + '--br-circuit-cur': '#cccccc', + '--bg-circuit': '#bdc2ca', + '--bg-primary-moz': '#0f111ae6', //dialog bg + '--bg-primary-chr': '#0f111ab3', //dialog bg + '--bg-tabs': '#727d8d', //tabs bar primary bg, + '--bg-icons': '#4d647a', //ce icon bg + '--bg-text': '#727d8d', //drop down, content menu, text bg on hover + '--bg-secondary': '#536c84', //border color input button, + '--canvas-fill': '#1B2C33', //canvas bg + '--canvas-stroke': '#6A7980', //canvas stroke + '--context-text': 'white', + '--bg-toggle-btn-primary': '#48a69d', + '--primary-btn-hov': '#3f9189', + '--btn-danger': '#c33c6c', + '--btn-danger-darken': '#b03662', + '--qp-br-tl': '#282d46', //more ligthen than qp box shadow 1 + '--qp-br-rd': '#1d2132', + '--qp-box-shadow-1': '#1d2132', //lil lighten base + '--qp-box-shadow-2': '#0a0b11', //lil darken base + '--cus-btn-hov--bg': '#48a69d', + '--cus-btn-hov-text': '#fff', + '--node': '#285963', + '--stroke': '#35aea9', + '--fill': '#DEFFFE', + '--hover-and-sel': '#E3B924', + '--wire-draw': '#77878C', + '--wire-cnt': '#3B7F58', + '--wire-pow': '#75FFB0', + '--wire-sel': '#208CC9', + '--wire-lose': '#BF0426', + '--mini-map': '#3B7F58', + '--mini-map-stroke': '#607F6E', + '--input-text': '#3B7F58', + '--output-rect': '#0487D9', + '--secondary-stroke': '#BF0426', + '--text': '#E9FBF8', + '--wire-norm': '#277F7C', + '--node-norm': '#FFC231', + '--splitter': '#0284A8', + '--disable': '#4F74B0', + '--table-head-dark': '#000000', + }, + 'Lite-born Spring': { + '--text-navbar--alt': '#000', + '--br-secondary': '#6B6B6B', + '--cus-radio_label': '#6B6B6B', + '--primary': '#EAEAEB', //header bg + '--text-dark': '#6B6B6B', //normal state text + '--text-lite': 'white', //text state on hover, on drop down menu , context menu + '--text-panel': '#6B6B6B', + '--bg-navbar': '#6b6b6b', + '--qp-br-tl': '#969696', //more ligthen than qp box shadow 1 + '--qp-br-rd': '#545454', + '--qp-box-shadow-1': '#747474', //lil lighten base .. top left shadow + '--qp-box-shadow-2': '#5f5f5f', //lil darken base //down right shadow + // "--bg-tabs": "#EAEAEB", //tabs bar primary bg, + '--bg-tabs': '#A4A4A4', //tabs bar primary bg, + '--br-circuit-cur': '#42B983', + '--bg-circuit': '#D7D7D7', + '--br-circuit': '#42B983', + '--br-primary': '#42B983', //panel border, tabbar circuit border + '--context-text-hov': '#6B6B6B', + '--context-text': 'white', + '--bg-primary-moz': 'rgba(107, 107, 107, 0.904)', //dialog bg, navbar dropwdown //.9 opacity of nav + '--bg-primary-chr': 'rgba(107, 107, 107, 0.704)', //dialog bg navbar dropwdown // .7 opacity of nav + '--bg-icons': '#DDDDDD', //ce icon bg + '--bg-text': '#ddd', //drop down, content menu, text bg on hover + '--bg-secondary': '#6B6B6B', //border color input button, + '--bg-toggle-btn-primary': '#42B983', + '--primary-btn-hov': '#66C89C', + '--btn-danger': '#BF2424', + '--btn-danger-darken': '#BF414C', + '--cus-btn-hov--bg': '#42B983', + '--cus-btn-hov-text': '#fff', + '--canvas-fill': 'white', //canvas bg + '--canvas-stroke': '#BABABA', //canvas stroke + '--node': '#42B983', + '--stroke': '#6B6B6B', + '--fill': '#EAEAEB', + '--hover-and-sel': '#FFE99B', //yellow + '--wire-draw': '#6B6B6B', //black + '--wire-cnt': '#42B983', // + '--wire-pow': '#52E539', + '--wire-sel': '#0FB2F2', + '--wire-lose': '#F10530', + '--mini-map': '#42B983', + '--mini-map-stroke': '#0FB2F2', + '--input-text': '#42B983', + '--output-rect': '#0487D9', + '--secondary-stroke': '#F10530', + '--text': '#454545', + '--wire-norm': '#006839', + '--node-norm': '#FFC231', + '--splitter': '#00B462', + '--disable': '#656565', + '--table-head-dark': '#ffffff', + }, + 'G&W': { + '--text-navbar--alt': '#000', + '--br-secondary': '#6B6B6B', + '--cus-radio_label': '#6B6B6B', + '--primary': '#EAEAEB', //header bg + '--text-dark': '#6B6B6B', //normal state text + '--text-lite': 'white', //text state on hover, on drop down menu , context menu + '--text-panel': '#656565', + '--bg-navbar': '#6b6b6b', + '--qp-br-tl': '#969696', //more ligthen than qp box shadow 1 + '--qp-br-rd': '#545454', + '--qp-box-shadow-1': '#747474', //lil lighten base .. top left shadow + '--qp-box-shadow-2': '#5f5f5f', //lil darken base //down right shadow + // "--bg-tabs": "#EAEAEB", //tabs bar primary bg, + '--bg-tabs': '#A4A4A4', //tabs bar primary bg, + '--br-circuit-cur': '#6b6b6b', + '--bg-circuit': '#D7D7D7', + '--br-circuit': '#6b6b6b', + '--br-primary': '#6B6B6B', //panel border, tabbar circuit border + '--context-text-hov': '#6B6B6B', + '--context-text': 'white', + '--bg-primary-moz': 'rgba(107, 107, 107, 0.904)', //dialog bg, navbar dropwdown //.9 opacity of nav + '--bg-primary-chr': 'rgba(107, 107, 107, 0.704)', //dialog bg navbar dropwdown // .7 opacity of nav + '--bg-icons': '#DDDDDD', //ce icon bg + '--bg-text': '#ddd', //drop down, content menu, text bg on hover + '--bg-secondary': '#6B6B6B', //border color input button, + '--bg-toggle-btn-primary': '#3ac8a4', + '--primary-btn-hov': '#71D7BD', + '--btn-danger': '#fc8771', + '--btn-danger-darken': '#FDB2A4', + '--cus-btn-hov--bg': '#3ac8a4', + '--cus-btn-hov-text': '#fff', + '--canvas-fill': 'white', //canvas bg + '--canvas-stroke': '#BABABA', //canvas stroke + '--node': '#42B983', + '--stroke': '#6B6B6B', + '--fill': '#EAEAEB', + '--hover-and-sel': '#FFE99B', //yellow + '--wire-draw': '#6B6B6B', //black + '--wire-cnt': '#42B983', // + '--wire-pow': '#52E539', + '--wire-sel': '#0FB2F2', + '--wire-lose': '#F10530', + '--mini-map': '#42B983', + '--mini-map-stroke': '#0FB2F2', + '--input-text': '#42B983', + '--output-rect': '#0487D9', + '--secondary-stroke': '#F10530', + '--text': '#454545', + '--wire-norm': '#006839', + '--node-norm': '#FFC231', + '--splitter': '#00B462', + '--disable': '#656565', + '--table-head-dark': '#ffffff', + }, + 'High Contrast': { + '--text-navbar--alt': '#000', + '--br-secondary': '#F38518', + '--cus-radio_label': 'black', + '--primary': 'black', //header bg + '--text-dark': 'black', //normal state text + '--text-lite': 'white', //text state on hover, on drop down menu , context menu + '--text-panel': 'white', + '--bg-navbar': 'black', + '--qp-br-tl': '#F38518', //more ligthen than qp box shadow 1 + '--qp-br-rd': '#F38518', + '--qp-box-shadow-1': '#0D0D0D', //lil lighten base .. top left shadow + '--qp-box-shadow-2': '#0D0D0D', //lil darken base //down right shadow + '--bg-tabs': '#616161', //tabs bar primary bg, + '--text-circuit': 'black', + '--br-circuit-cur': '#F38518', + '--bg-circuit': '#B6B6B6', + '--br-circuit': '#F38518', + '--br-primary': '#F38518', //panel border, tabbar circuit border + '--context-text-hov': 'black', + '--context-text': 'white', + '--bg-primary-moz': 'rgba(0, 0, 0, 0.904)', //dialog bg, navbar dropwdown //.9 opacity of nav + '--bg-primary-chr': 'rgba(0, 0, 0, 0.704)', //dialog bg navbar dropwdown // .7 opacity of nav + '--bg-icons': '#262626', //ce icon bg + '--bg-text': '#ddd', //drop down, content menu, text bg on hover + '--bg-secondary': '#6B6B6B', //border color input button, + '--bg-toggle-btn-primary': '#009599', + '--primary-btn-hov': '#00b1b6', + '--btn-danger': '#E45605', + '--btn-danger-darken': '#fa792f', + '--cus-btn-hov--bg': '#009599', + '--cus-btn-hov-text': '#fff', + '--canvas-fill': 'black', //canvas bg + '--canvas-stroke': '#9ad5e9', //canvas stroke + '--node': '#26a841', + '--stroke': '#626262', + '--fill': '#d4d4d4', + '--hover-and-sel': '#ffdf6c', //yellow + '--wire-draw': '#909090', //black + '--wire-cnt': '#3bb15b', // + '--wire-pow': '#3ac53c', + '--wire-sel': '#049ffb', + '--wire-lose': '#f42f0b', + '--mini-map': '#1c2427', + '--mini-map-stroke': '#000', + '--input-text': '#42B983', + '--output-rect': '#005682', + '--secondary-stroke': '#f7081e', + '--text': '#fff', + '--wire-norm': '#00b965', + '--node-norm': '#F38518', + '--splitter': '#21de5a', + '--disable': '#262626', + '--table-head-dark': '#000000', + }, + 'Color Blind': { + '--text-navbar--alt': '#000', + '--br-secondary': '#e2dad1', + '--cus-radio_label': '#2e2b21', + '--primary': '#2e2b21', //header bg + '--text-dark': 'black', //normal state text + '--text-lite': 'white', //text state on hover, on drop down menu , context menu + '--text-panel': 'white', + '--bg-navbar': '#2e2b21', + '--qp-br-tl': '#716950', //more ligthen than qp box shadow 1 + '--qp-br-rd': '#6c654d', + '--qp-box-shadow-1': '#4f4a38', //lil lighten base .. top left shadow + '--qp-box-shadow-2': '#302d23', //lil darken base //down right shadow + '--bg-tabs': '#9a9a9a', //tabs bar primary bg, + '--text-circuit': 'black', + '--br-circuit-cur': '#e2dad1', + '--bg-circuit': '#d6d6d6', + '--br-circuit': '#e2dad1', + '--br-primary': '#e2dad1', //panel border, tabbar circuit border + '--context-text-hov': 'black', + '--context-text': 'white', + '--bg-primary-moz': 'rgba(46, 43, 33, 1)', //dialog bg, navbar dropwdown //.9 opacity of nav + '--bg-primary-chr': 'rgba(46, 43, 33, 1)', //dialog bg navbar dropwdown // .7 opacity of nav + '--bg-icons': '#9c7762', //ce icon bg + '--bg-text': '#ddd', //drop down, content menu, text bg on hover + '--bg-secondary': '#6B6B6B', //border color input button, + '--bg-toggle-btn-primary': '#bfaac1', + '--primary-btn-hov': '#ccbbcd', + '--btn-danger': '#b66e43', + '--btn-danger-darken': '#ba7144', + '--cus-btn-hov--bg': '#b66e43', + '--cus-btn-hov-text': '#fff', + '--canvas-fill': '#fff', //canvas bg + '--canvas-stroke': '#8d88ad', //canvas stroke + '--node': '#c59434', + '--stroke': '#342a1f', + '--fill': '#e0dcd3', + '--hover-and-sel': '#f4d4d4', //yellow + '--wire-draw': '#4c4c4c', //black + '--wire-cnt': '#908eb9', // + '--wire-pow': '#b3b1cf', + '--wire-sel': '#a7b8f8', + '--wire-lose': '#f42f0b', + '--mini-map': '#4b4636', + '--mini-map-stroke': '#000', + '--input-text': '#756d54', + '--output-rect': '#092c48', + '--secondary-stroke': '#cdb1ad', + '--text': '#000', + '--wire-norm': '#7f7cae', + '--node-norm': '#c59434', + '--splitter': '#836222', + '--disable': '#956c6a', + '--table-head-dark': '#2e2b21', + }, + 'Custom Theme': JSON.parse(localStorage.getItem('Custom Theme')) || { + '--text-navbar--alt': '#000', + '--br-secondary': '#7d7d7d', + '--br-circuit-cur': '#ffffff', + '--br-circuit': '#ffffff', + '--cus-radio_label': '#656565', + '--primary': '#454545', + '--text-lite': '#ffffff', + '--text-dark': '#000', + '--text-panel': '#ffffff', + '--bg-navbar': '#454545', + '--qp-br-tl': '#333333', + '--qp-br-rd': '#535353', + '--qp-box-shadow-1': '#3b3b3b', + '--qp-box-shadow-2': '#4f4f4f', + '--bg-circuit': '#ddd', + '--br-circuit': '#454545', + '--br-primary': '#ffffff', + '--bg-primary-moz': '#454545e6', + '--bg-primary-chr': '#454545b3', + '--bg-tabs': '#8b8b8b', + '--bg-icons': '#7d7d7d', + '--bg-text': '#cacaca', + '--bg-secondary': '#bbbbbb', + '--canvas-stroke': '#eee', + '--canvas-fill': '#ffffff', + '--context-text': '#ffffff', + '--bg-toggle-btn-primary': '#42b983', + '--primary-btn-hov': '#3ca877', + '--btn-danger': '#dc5656', + '--btn-danger-darken': '#b03662', + '--disable': '#6c8b93', + '--cus-btn-hov--bg': '#ddd', + '--cus-btn-hov-text': '#000', + '--node': '#008000', + '--stroke': '#000', + '--fill': '#ffffff', + '--hover-and-sel': '#ffff20cc', + '--wire-draw': '#000', + '--wire-cnt': '#008000', + '--wire-pow': '#90ee90', + '--wire-sel': '#0000ff', + '--wire-lose': '#ff0000', + '--mini-map': '#008000', + '--mini-map-stroke': '#006400', + '--input-text': '#008000', + '--secondary-stroke': '#ff0000', + '--text': '#000', + '--wire-norm': '#000', + '--node-norm': '#008000', + '--splitter': '#000', + '--output-rect': '#0000ff', + }, +} diff --git a/v1/src/simulator/src/tutorials.js b/v1/src/simulator/src/tutorials.js new file mode 100644 index 00000000..a1f10d67 --- /dev/null +++ b/v1/src/simulator/src/tutorials.js @@ -0,0 +1,143 @@ +import Driver from 'driver.js' + +export const tour = [ + { + element: '#guide_1', + className: 'guide_1', + popover: { + className: '', + title: 'Circuit Elements panel', + description: + 'This is where you can find all the circuit elements that are offered to build amazing circuits.', + position: 'right', + offset: 160, + }, + }, + { + element: '.guide_2', + popover: { + title: 'Properties Panel', + description: + 'This panel lets you change element properties as they are selected. When no elements are selected, the panel displays project properties.', + position: 'left', + offset: 200, + }, + }, + { + element: '.quick-btn', + popover: { + title: 'Quick Access Panel', + description: + 'This movable panel offers to perform some actions like Save Online, Open, Download quickly. Hover over the icons and see for yourself', + position: 'bottom', + // offset: 750, + }, + }, + // { + // element: '.forum-tab', + // popover: { + // className: "", + // title: 'Forum Tab', + // description: "The forums can help you report issues & bugs, feature requests, and discussing about circuits with the community!", + // position: 'right', + // // offset: -25, + // }, + // }, + { + element: '#tabsBar', + popover: { + title: 'Circuit Tabs', + description: + 'This section displays all the circuits you have in your project. You can easily add and delete circuits.', + position: 'bottom', + offset: 250, + }, + }, + { + element: '.timing-diagram-panel', + popover: { + title: 'Timing Diagram Panel (Waveform)', + description: + 'This panel displays the waveform created by circuits and can be used for resolving race conditions and debugging circuits.', + position: 'bottom', + offset: 0, + }, + }, + + // { + // element: '#delCirGuide', + // popover: { + // title: 'Delete sub-circuit button', + // description: "You can make delete sub-circuits by pressing the cross *Note that main circuit cannot be deleted.", + // position: 'right', + // // offset: 250, + // }, + // }, + { + element: '.report-sidebar a', + popover: { + className: 'bug-guide', + title: 'Report System', + description: + 'You can report any issues/bugs you face through this issue reporting button there and then quickly.', + position: 'left', + offset: -105, + }, + }, + { + element: '.tour-help', + popover: { + className: 'tourHelpStep', + title: 'Restart tutorial anytime', + description: + 'You can restart this tutorial anytime by clicking on "Tutorial Guide" under this dropdown.', + position: 'right', + offset: 0, + }, + }, +] + +// Not used currently +export const tutorialWrapper = () => { + const panelHighlight = new Driver() + document.querySelector('.panelHeader').addEventListener('click', (e) => { + if (localStorage.tutorials === 'next') { + panelHighlight.highlight({ + element: '#guide_1', + showButtons: false, + popover: { + title: 'Here are the elements', + description: + 'Select any element by clicking on it & then click anywhere on the grid to place the element.', + position: 'right', + offset: + e.target.nextElementSibling.offsetHeight + + e.target.offsetTop - + 45, + }, + }) + localStorage.setItem('tutorials', 'done') + } + }, { + once: true, + }) + document.querySelector('.icon').addEventListener('click', () => { + panelHighlight.reset(true) + }) +} + +const animatedTourDriver = new Driver({ + animate: true, + opacity: 0.8, + padding: 5, + showButtons: true, +}) + +export function showTourGuide() { + document.querySelector('.draggable-panel .maximize').click(); + animatedTourDriver.defineSteps(tour) + animatedTourDriver.start() + localStorage.setItem('tutorials_tour_done', true) +} + +export default showTourGuide diff --git a/v1/src/simulator/src/utils.js b/v1/src/simulator/src/utils.js new file mode 100644 index 00000000..9d6a32b9 --- /dev/null +++ b/v1/src/simulator/src/utils.js @@ -0,0 +1,277 @@ +import simulationArea from './simulationArea' +import { + scheduleUpdate, + play, + updateCanvasSet, + errorDetectedSet, + errorDetectedGet, +} from './engine' +import { layoutModeGet } from './layoutMode' +import plotArea from './plotArea' +import { SimulatorStore } from '#/store/SimulatorStore/SimulatorStore' + +window.globalScope = undefined +window.lightMode = false // To be deprecated +window.projectId = undefined +window.id = undefined +window.loading = false // Flag - all assets are loaded + +var prevErrorMessage // Global variable for error messages +var prevShowMessage // Global variable for error messages +export function generateId() { + var id = '' + var possible = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' + + for (var i = 0; i < 20; i++) { + id += possible.charAt(Math.floor(Math.random() * possible.length)) + } + + return id +} + +// To strip tags from input +export function stripTags(string = '') { + return string.replace(/(<([^>]+)>)/gi, '').trim() +} + +export function clockTick() { + if (!simulationArea.clockEnabled) return + if (errorDetectedGet()) return + if (layoutModeGet()) return + updateCanvasSet(true) + globalScope.clockTick() + plotArea.nextCycle() + play() + scheduleUpdate(0, 20) +} + +/** + * Helper function to show error + * @param {string} error -The error to be shown + * @category utils + */ +export function showError(error) { + errorDetectedSet(true) + // if error ha been shown return + if (error === prevErrorMessage) return + prevErrorMessage = error + var id = Math.floor(Math.random() * 10000) + $('#MessageDiv').append( + `` + ) + setTimeout(() => { + prevErrorMessage = undefined + $(`#${id}`).fadeOut() + }, 1500) +} + +// Helper function to show message +export function showMessage(mes) { + if (mes === prevShowMessage) return + prevShowMessage = mes + var id = Math.floor(Math.random() * 10000) + $('#MessageDiv').append( + `` + ) + setTimeout(() => { + prevShowMessage = undefined + $(`#${id}`).fadeOut() + }, 2500) +} + +export function distance(x1, y1, x2, y2) { + return Math.sqrt((x2 - x1) ** 2) + (y2 - y1) ** 2 +} + +/** + * Helper function to return unique list + * @param {Array} a - any array + * @category utils + */ +export function uniq(a) { + var seen = {} + const tmp = a.filter((item) => + seen.hasOwnProperty(item) ? false : (seen[item] = true) + ) + return tmp +} + +// Generates final verilog code for each element +// Gate = &/|/^ +// Invert is true for xNor, Nor, Nand +export function gateGenerateVerilog(gate, invert = false) { + var inputs = [] + var outputs = [] + + for (var i = 0; i < this.nodeList.length; i++) { + if (this.nodeList[i].type == NODE_INPUT) { + inputs.push(this.nodeList[i]) + } else { + if (this.nodeList[i].connections.length > 0) + outputs.push(this.nodeList[i]) + else outputs.push('') // Don't create a wire + } + } + + var res = 'assign ' + if (outputs.length == 1) res += outputs[0].verilogLabel + else res += `{${outputs.map((x) => x.verilogLabel).join(', ')}}` + + res += ' = ' + + var inputParams = inputs.map((x) => x.verilogLabel).join(` ${gate} `) + if (invert) { + res += `~(${inputParams});` + } else { + res += inputParams + ';' + } + return res +} + +// Helper function to download text +export function download(filename, text) { + var pom = document.createElement('a') + pom.setAttribute( + 'href', + 'data:text/plain;charset=utf-8,' + encodeURIComponent(text) + ) + pom.setAttribute('download', filename) + + if (document.createEvent) { + var event = document.createEvent('MouseEvents') + event.initEvent('click', true, true) + pom.dispatchEvent(event) + } else { + pom.click() + } +} + +// Helper function to open a new tab +export function openInNewTab(url) { + var win = window.open(url, '_blank') + win.focus() +} + +export function copyToClipboard(text) { + const textarea = document.createElement('textarea') + + // Move it off-screen. + textarea.style.cssText = 'position: absolute; left: -99999em' + + // Set to readonly to prevent mobile devices opening a keyboard when + // text is .select()'ed. + textarea.setAttribute('readonly', true) + + document.body.appendChild(textarea) + textarea.value = text + + // Check if there is any content selected previously. + const selected = + document.getSelection().rangeCount > 0 + ? document.getSelection().getRangeAt(0) + : false + + // iOS Safari blocks programmatic execCommand copying normally, without this hack. + // https://stackoverflow.com/questions/34045777/copy-to-clipboard-using-javascript-in-ios + if (navigator.userAgent.match(/ipad|ipod|iphone/i)) { + const editable = textarea.contentEditable + textarea.contentEditable = true + const range = document.createRange() + range.selectNodeContents(textarea) + const sel = window.getSelection() + sel.removeAllRanges() + sel.addRange(range) + textarea.setSelectionRange(0, 999999) + textarea.contentEditable = editable + } else { + textarea.select() + } + + try { + const result = document.execCommand('copy') + + // Restore previous selection. + if (selected) { + document.getSelection().removeAllRanges() + document.getSelection().addRange(selected) + } + textarea.remove() + return result + } catch (err) { + console.error(err) + textarea.remove() + return false + } +} + +export function truncateString(str, num) { + // If the length of str is less than or equal to num + // just return str--don't truncate it. + if (str.length <= num) { + return str + } + // Return str truncated with '...' concatenated to the end of str. + return str.slice(0, num) + '...' +} + +export function bitConverterDialog() { + const simulatorStore = SimulatorStore(); + simulatorStore.dialogBox.hex_bin_dec_converter_dialog = true; +} + +export function getImageDimensions(file) { + return new Promise(function (resolved, rejected) { + var i = new Image() + i.onload = function () { + resolved({ w: i.width, h: i.height }) + } + i.src = file + }) +} + +// convertors +export var convertors = { + dec2bin: (x) => '0b' + x.toString(2), + dec2hex: (x) => '0x' + x.toString(16), + dec2octal: (x) => '0' + x.toString(8), + dec2bcd: (x) => parseInt(x.toString(10), 16).toString(2), +} + +export function parseNumber(num) { + if (num instanceof Number) return num + if (num.slice(0, 2).toLocaleLowerCase() == '0b') + return parseInt(num.slice(2), 2) + if (num.slice(0, 2).toLocaleLowerCase() == '0x') + return parseInt(num.slice(2), 16) + if (num.slice(0, 1).toLocaleLowerCase() == '0') return parseInt(num, 8) + return parseInt(num) +} + +export function promptFile(contentType, multiple) { + var input = document.createElement('input') + input.type = 'file' + input.multiple = multiple + input.accept = contentType + return new Promise(function (resolve) { + document.activeElement.onfocus = function () { + document.activeElement.onfocus = null + setTimeout(resolve, 500) + } + input.onchange = function () { + var files = Array.from(input.files) + if (multiple) return resolve(files) + resolve(files[0]) + } + input.click() + }) +} + +export function escapeHtml(unsafe) { + return unsafe + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, ''') +} diff --git a/v1/src/simulator/src/ux.js b/v1/src/simulator/src/ux.js new file mode 100644 index 00000000..aaad33cd --- /dev/null +++ b/v1/src/simulator/src/ux.js @@ -0,0 +1,803 @@ +/* eslint-disable import/no-cycle */ +/* eslint-disable guard-for-in */ +/* eslint-disable no-restricted-syntax */ +/* eslint-disable no-restricted-syntax */ +/* eslint-disable guard-for-in */ + +import { layoutModeGet } from './layoutMode' +import { + scheduleUpdate, + wireToBeCheckedSet, + updateCanvasSet, + update, + updateSimulationSet, +} from './engine' +import simulationArea from './simulationArea' +import logixFunction from './data' +import { newCircuit, circuitProperty } from './circuit' +import modules from './modules' +import { updateRestrictedElementsInScope } from './restrictedElementDiv' +import { paste } from './events' +import { setProjectName, getProjectName } from './data/save' +import { changeScale } from './canvasApi' +import { generateImage, generateSaveData } from './data/save' +import { setupVerilogExportCodeWindow } from './verilog' +import { updateTestbenchUI, setupTestbenchUI } from './testbench' +import { applyVerilogTheme } from './Verilog2CV' +import { dragging } from './drag' + +export const uxvar = { + smartDropXX: 50, + smartDropYY: 80, +} +/** + * @type {number} - Is used to calculate the position where an element from sidebar is dropped + * @category ux + */ +uxvar.smartDropXX = 50 + +/** + * @type {number} - Is used to calculate the position where an element from sidebar is dropped + * @category ux + */ +uxvar.smartDropYY = 80 + +// ------------------------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------------------------ +/** + * @type {Object} - Object stores the position of context menu; + * @category ux + */ +var ctxPos = { + x: 0, + y: 0, + visible: false, +} +// FUNCITON TO SHOW AND HIDE CONTEXT MENU +function hideContextMenu() { + var el = document.getElementById('contextMenu') + el.style = 'opacity:0;' + setTimeout(() => { + el.style = 'visibility:hidden;' + ctxPos.visible = false + }, 200) // Hide after 2 sec +} +/** + * Function displays context menu + * @category ux + */ +function showContextMenu() { + if (layoutModeGet()) return false // Hide context menu when it is in Layout Mode + $('#contextMenu').css({ + visibility: 'visible', + opacity: 1, + }) + + var windowHeight = + $('#simulationArea').height() - $('#contextMenu').height() - 10 + var windowWidth = + $('#simulationArea').width() - $('#contextMenu').width() - 10 + // for top, left, right, bottom + var topPosition + var leftPosition + var rightPosition + var bottomPosition + if (ctxPos.y > windowHeight && ctxPos.x <= windowWidth) { + //When user click on bottom-left part of window + leftPosition = ctxPos.x + bottomPosition = $(window).height() - ctxPos.y + $('#contextMenu').css({ + left: `${leftPosition}px`, + bottom: `${bottomPosition}px`, + right: 'auto', + top: 'auto', + }) + } else if (ctxPos.y > windowHeight && ctxPos.x > windowWidth) { + //When user click on bottom-right part of window + bottomPosition = $(window).height() - ctxPos.y + rightPosition = $(window).width() - ctxPos.x + $('#contextMenu').css({ + left: 'auto', + bottom: `${bottomPosition}px`, + right: `${rightPosition}px`, + top: 'auto', + }) + } else if (ctxPos.y <= windowHeight && ctxPos.x <= windowWidth) { + //When user click on top-left part of window + leftPosition = ctxPos.x + topPosition = ctxPos.y + $('#contextMenu').css({ + left: `${leftPosition}px`, + bottom: 'auto', + right: 'auto', + top: `${topPosition}px`, + }) + } else { + //When user click on top-right part of window + rightPosition = $(window).width() - ctxPos.x + topPosition = ctxPos.y + $('#contextMenu').css({ + left: 'auto', + bottom: 'auto', + right: `${rightPosition}px`, + top: `${topPosition}px`, + }) + } + ctxPos.visible = true + return false +} + +/** + * adds some UI elements to side bar and + * menu also attaches listeners to sidebar + * @category ux + */ +export function setupUI() { + var ctxEl = document.getElementById('contextMenu') + document.addEventListener('mousedown', (e) => { + // Check if mouse is not inside the context menu and menu is visible + if ( + !( + e.clientX >= ctxPos.x && + e.clientX <= ctxPos.x + ctxEl.offsetWidth && + e.clientY >= ctxPos.y && + e.clientY <= ctxPos.y + ctxEl.offsetHeight + ) && + ctxPos.visible && + e.which !== 3 + ) { + hideContextMenu() + } + + // Change the position of context whenever mouse is clicked + ctxPos.x = e.clientX + ctxPos.y = e.clientY + }) + document.getElementById('canvasArea').oncontextmenu = showContextMenu + + // commenting jquery-ui (not working) + // $('#sideBar').resizable({ + // handles: 'e', + // // minWidth:270, + // }); + // $('#menu, #subcircuitMenu').accordion({ + // collapsible: true, + // active: false, + // heightStyle: 'content', + // }); + + $('.logixButton').on('click', function () { + logixFunction[this.id]() + }) + // var dummyCounter=0; + + // calling apply on select theme in dropdown + + // $('#saveAsImg').on('click',function(){ + // saveAsImg(); + // }); + // $('#Save').on('click',function(){ + // Save(); + // }); + // $('#moduleProperty').draggable(); + setupPanels() + // setupVerilogExportCodeWindow() +} + +/** + * Keeps in check which property is being displayed + * @category ux + */ +var prevPropertyObj + +export function prevPropertyObjSet(param) { + prevPropertyObj = param +} + +export function prevPropertyObjGet() { + return prevPropertyObj +} + +function checkValidBitWidth() { + const selector = $("[name='newBitWidth']") + if ( + selector === undefined || + selector.val() > 32 || + selector.val() < 1 || + !$.isNumeric(selector.val()) + ) { + // fallback to previously saves state + selector.val(selector.attr('old-val')) + } else { + selector.attr('old-val', selector.val()) + } +} + +export function objectPropertyAttributeUpdate() { + checkValidBitWidth() + scheduleUpdate() + updateCanvasSet(true) + wireToBeCheckedSet(1) + let { value } = this + if (this.type === 'number') { + value = parseFloat(value) + } + if (simulationArea.lastSelected && simulationArea.lastSelected[this.name]) { + simulationArea.lastSelected[this.name](value) + // Commented out due to property menu refresh bug + // prevPropertyObjSet(simulationArea.lastSelected[this.name](this.value)) || prevPropertyObjGet(); + } else { + circuitProperty[this.name](value) + } +} + +export function objectPropertyAttributeCheckedUpdate() { + if (this.name === 'toggleLabelInLayoutMode') return // Hack to prevent toggleLabelInLayoutMode from toggling twice + scheduleUpdate() + updateCanvasSet(true) + wireToBeCheckedSet(1) + if (simulationArea.lastSelected && simulationArea.lastSelected[this.name]) { + simulationArea.lastSelected[this.name](this.value) + // Commented out due to property menu refresh bug + // prevPropertyObjSet(simulationArea.lastSelected[this.name](this.value)) || prevPropertyObjGet(); + } else { + circuitProperty[this.name](this.checked) + } +} + +export function checkPropertiesUpdate(value = 0) { + $('.objectPropertyAttribute').off( + 'change keyup paste click', + objectPropertyAttributeUpdate + ) + $('.objectPropertyAttribute').on( + 'change keyup paste click', + objectPropertyAttributeUpdate + ) + + $('.objectPropertyAttributeChecked').off( + 'change keyup paste click', + objectPropertyAttributeCheckedUpdate + ) + $('.objectPropertyAttributeChecked').on( + 'change keyup paste click', + objectPropertyAttributeCheckedUpdate + ) +} + +/** + * show properties of an object. + * @param {CircuiElement} obj - the object whose properties we want to be shown in sidebar + * @category ux + */ +export function showProperties(obj) { + if (obj === prevPropertyObjGet()) return + + /* + hideProperties() + prevPropertyObjSet(obj) + if (layoutModeGet()) { + // if an element is selected, show its properties instead of the layout dialog + if ( + simulationArea.lastSelected === undefined || + ['Wire', 'CircuitElement', 'Node'].indexOf( + simulationArea.lastSelected.objectType + ) !== -1 + ) { + $('#moduleProperty').hide() + $('#layoutDialog').show() + return + } + + $('#moduleProperty').show() + $('#layoutDialog').hide() + $('#moduleProperty-inner').append( + "
" + obj.objectType + '
' + ) + + if (obj.subcircuitMutableProperties && obj.canShowInSubcircuit) { + for (let attr in obj.subcircuitMutableProperties) { + var prop = obj.subcircuitMutableProperties[attr] + if (obj.subcircuitMutableProperties[attr].type == 'number') { + var s = + '

' + + prop.name + + "

" + $('#moduleProperty-inner').append(s) + } + } + if (!obj.labelDirectionFixed) { + if (!obj.subcircuitMetadata.labelDirection) + obj.subcircuitMetadata.labelDirection = obj.labelDirection + var s = $( + "' + ) + s.val(obj.subcircuitMetadata.labelDirection) + $('#moduleProperty-inner').append( + '

Label Direction: ' + $(s).prop('outerHTML') + '

' + ) + } + } + } else if ( + simulationArea.lastSelected === undefined || + ['Wire', 'CircuitElement', 'Node'].indexOf( + simulationArea.lastSelected.objectType + ) !== -1 + ) { + $('#moduleProperty').show() + + $('#moduleProperty-inner').append( + `

Project:

` + ) + $('#moduleProperty-inner').append( + `

Circuit:

` + ) + $('#moduleProperty-inner').append( + `

Clock Time (ms):

` + ) + $('#moduleProperty-inner').append( + `

Clock Enabled:

` + ) + $('#moduleProperty-inner').append( + `

Lite Mode:

` + ) + $('#moduleProperty-inner').append( + "

" + ) + // $('#moduleProperty-inner').append("

"); + } else { + $('#moduleProperty').show() + + $('#moduleProperty-inner').append( + `

${obj.objectType}
` + ) + // $('#moduleProperty').append(""); + if (!obj.fixedBitWidth) { + $('#moduleProperty-inner').append( + `

BitWidth:

` + ) + } + + if (obj.changeInputSize) { + $('#moduleProperty-inner').append( + `

Input Size:

` + ) + } + + if (!obj.propagationDelayFixed) { + $('#moduleProperty-inner').append( + `

Delay:

` + ) + } + + if (!obj.disableLabel) + $('#moduleProperty-inner').append( + `

Label:

` + ) + + var s + if (!obj.labelDirectionFixed) { + s = $( + `${ + "' + ) + s.val(obj.labelDirection) + $('#moduleProperty-inner').append( + `

Label Direction: ${$(s).prop('outerHTML')}

` + ) + } + + if (!obj.directionFixed) { + s = $( + `${ + "' + ) + $('#moduleProperty-inner').append( + `

Direction: ${$(s).prop('outerHTML')}

` + ) + } else if (!obj.orientationFixed) { + s = $( + `${ + "' + ) + $('#moduleProperty-inner').append( + `

Orientation: ${$(s).prop('outerHTML')}

` + ) + } + + if (obj.mutableProperties) { + for (const attr in obj.mutableProperties) { + var prop = obj.mutableProperties[attr] + if (obj.mutableProperties[attr].type === 'number') { + s = `

${ + prop.name + }

` + $('#moduleProperty-inner').append(s) + } else if (obj.mutableProperties[attr].type === 'text') { + s = `

${ + prop.name + }

` + $('#moduleProperty-inner').append(s) + } else if (obj.mutableProperties[attr].type === 'button') { + s = `

` + $('#moduleProperty-inner').append(s) + } else if (obj.mutableProperties[attr].type === 'textarea') { + s = `

${prop.name}

` + $('#moduleProperty-inner').append(s) + } + } + } + } + + var helplink = obj && obj.helplink + if (helplink) { + $('#moduleProperty-inner').append( + '

' + ) + $('#HelpButton').on('click', () => { + window.open(helplink) + }) + } +*/ + checkPropertiesUpdate(this) + + // $(".moduleProperty input[type='number']").inputSpinner(); +} + +/** + * Hides the properties in sidebar. + * @category ux + */ +export function hideProperties() { + $('#moduleProperty-inner').empty() + $('#moduleProperty').hide() + prevPropertyObjSet(undefined) + $('.objectPropertyAttribute').unbind('change keyup paste click') +} +/** + * checkss the input is safe or not + * @param {HTML} unsafe - the html which we wants to escape + * @category ux + */ +function escapeHtml(unsafe) { + return unsafe + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, ''') +} + +export function deleteSelected() { + if ( + simulationArea.lastSelected && + !( + simulationArea.lastSelected.objectType === 'Node' && + simulationArea.lastSelected.type !== 2 + ) + ) { + simulationArea.lastSelected.delete() + } + + for (var i = 0; i < simulationArea.multipleObjectSelections.length; i++) { + if ( + !( + simulationArea.multipleObjectSelections[i].objectType === + 'Node' && + simulationArea.multipleObjectSelections[i].type !== 2 + ) + ) + simulationArea.multipleObjectSelections[i].cleanDelete() + } + + simulationArea.multipleObjectSelections = [] + simulationArea.lastSelected = undefined + showProperties(simulationArea.lastSelected) + // Updated restricted elements + updateCanvasSet(true) + scheduleUpdate() + updateRestrictedElementsInScope() +} + +export function setupPanels() { + // $('#dragQPanel') + // .on('mousedown', () => + // $('.quick-btn').draggable({ + // disabled: false, + // containment: 'window', + // }) + // ) + // .on('mouseup', () => $('.quick-btn').draggable({ disabled: true })) + + // let position = { x: 0, y: 0 } + // interact('.quick-btn').draggable({ + // allowFrom: '#dragQPanel', + // listeners: { + // move(event) { + // position.x = position.x + event.dx + // position.y = position.y + event.dy + // event.target.style.transform = `translate(${position.x}px, ${position.y}px)` + // }, + // }, + // }) + + dragging('#dragQPanel', '.quick-btn') + + setupPanelListeners('.elementPanel') + setupPanelListeners('.layoutElementPanel') + setupPanelListeners('#moduleProperty') + setupPanelListeners('#layoutDialog') + setupPanelListeners('#verilogEditorPanel') + setupPanelListeners('.timing-diagram-panel') + setupPanelListeners('.testbench-manual-panel') + + // Minimize Timing Diagram (takes too much space) + $('.timing-diagram-panel .minimize').trigger('click') + + // Update the Testbench Panel UI + updateTestbenchUI() + // Minimize Testbench UI + $('.testbench-manual-panel .minimize').trigger('click') + + // Hack because minimizing panel then maximizing sets visibility recursively + // updateTestbenchUI calls some hide()s which are undone by maximization + // TODO: Remove hack + $('.testbench-manual-panel .maximize').on('click', setupTestbenchUI) + + $('#projectName').on('click', () => { + $("input[name='setProjectName']").focus().select() + }) +} + +function setupPanelListeners(panelSelector) { + var headerSelector = `${panelSelector} .panel-header` + var minimizeSelector = `${panelSelector} .minimize` + var maximizeSelector = `${panelSelector} .maximize` + var bodySelector = `${panelSelector} > .panel-body` + + dragging(headerSelector, panelSelector) + // let position = { x: 0, y: 0 } + // Drag Start + // $(headerSelector).on('mousedown', () => + // $(panelSelector).draggable({ disabled: false, containment: 'window' }) + // interact(panelSelector).draggable({ + // allowFrom: headerSelector, + // listeners: { + // move(event) { + // position.x += event.dx + // position.y += event.dy + + // event.target.style.transform = `translate(${position.x}px, ${position.y}px)` + // }, + // }, + // }) + // ) + // // Drag End + // $(headerSelector).on('mouseup', () => + // $(panelSelector).draggable({ disabled: true }) + // ) + // Current Panel on Top + var minimized = false + $(headerSelector).on('dblclick', () => + minimized + ? $(maximizeSelector).trigger('click') + : $(minimizeSelector).trigger('click') + ) + // Minimize + $(minimizeSelector).on('click', () => { + $(bodySelector).hide() + $(minimizeSelector).hide() + $(maximizeSelector).show() + minimized = true + }) + // Maximize + $(maximizeSelector).on('click', () => { + $(bodySelector).show() + $(minimizeSelector).show() + $(maximizeSelector).hide() + minimized = false + }) +} + +export function exitFullView() { + const exitViewBtn = document.querySelector('#exitViewBtn') + if (exitViewBtn) exitViewBtn.remove() + + const elements = document.querySelectorAll( + '.navbar, .modules, .report-sidebar, #tabsBar, #moduleProperty, .timing-diagram-panel, .testbench-manual-panel, .quick-btn' + ) + elements.forEach((element) => { + if (element instanceof HTMLElement) { + element.style.display = '' + } + }) +} + +export function fullView() { + const app = document.querySelector('#app') + + const exitViewEl = document.createElement('button') + exitViewEl.id = 'exitViewBtn' + exitViewEl.textContent = 'Exit Full Preview' + + const elements = document.querySelectorAll( + '.navbar, .modules, .report-sidebar, #tabsBar, #moduleProperty, .timing-diagram-panel, .testbench-manual-panel, .quick-btn' + ) + elements.forEach((element) => { + if (element instanceof HTMLElement) { + element.style.display = 'none' + } + }) + + app.appendChild(exitViewEl) + exitViewEl.addEventListener('click', exitFullView) +} + +/** + Fills the elements that can be displayed in the subcircuit, in the subcircuit menu +**/ +export function fillSubcircuitElements() { + $('#subcircuitMenu').empty() + var subCircuitElementExists = false + for (let el of circuitElementList) { + if (globalScope[el].length === 0) continue + if (!globalScope[el][0].canShowInSubcircuit) continue + let tempHTML = '' + + // add a panel for each existing group + tempHTML += `
${el}s
` + tempHTML += `
` + + let available = false + + // add an SVG for each element + for (let i = 0; i < globalScope[el].length; i++) { + if (!globalScope[el][i].subcircuitMetadata.showInSubcircuit) { + tempHTML += `
` + tempHTML += `` + tempHTML += `

${ + globalScope[el][i].label !== '' + ? globalScope[el][i].label + : 'unlabeled' + }

` + tempHTML += '
' + available = true + } + } + tempHTML += '
' + subCircuitElementExists = subCircuitElementExists || available + if (available) $('#subcircuitMenu').append(tempHTML) + } + + if (subCircuitElementExists) { + // $('#subcircuitMenu').accordion('refresh') + } else { + $('#subcircuitMenu').append('

No layout elements available

') + } + + $('.subcircuitModule').mousedown(function () { + let elementName = this.dataset.elementName + let elementIndex = this.dataset.elementId + + let element = globalScope[elementName][elementIndex] + + element.subcircuitMetadata.showInSubcircuit = true + element.newElement = true + simulationArea.lastSelected = element + this.parentElement.removeChild(this) + }) +} diff --git a/v1/src/simulator/src/verilog.js b/v1/src/simulator/src/verilog.js new file mode 100644 index 00000000..d0811b2c --- /dev/null +++ b/v1/src/simulator/src/verilog.js @@ -0,0 +1,580 @@ +/* + # Primary Developers + 1) James H-J Yeh, Ph.D. + 2) Satvik Ramaprasad + + refer verilog_documentation.md +*/ +import { scopeList } from './circuit' +import { errorDetectedGet } from './engine' +import { download } from './utils' +import { getProjectName } from './data/save' +import modules from './modules' +import { sanitizeLabel } from './verilogHelpers' +import CodeMirror from 'codemirror/lib/codemirror.js' +import 'codemirror/lib/codemirror.css' +import 'codemirror/addon/hint/show-hint.css' +import 'codemirror/mode/verilog/verilog.js' +import 'codemirror/addon/edit/closebrackets.js' +import 'codemirror/addon/hint/anyword-hint.js' +import 'codemirror/addon/hint/show-hint.js' +import 'codemirror/addon/display/autorefresh.js' +import { openInNewTab, copyToClipboard, showMessage } from './utils' +import { SimulatorStore } from '#/store/SimulatorStore/SimulatorStore' +var editor + +export function generateVerilog() { + const simulatorStore = SimulatorStore() + simulatorStore.dialogBox.exportverilog_dialog = true + // var dialog = $('#verilog-export-code-window-div') + // var data = verilog.exportVerilog() + // editor.setValue(data) + // $('#verilog-export-code-window-div .CodeMirror').css( + // 'height', + // $(window).height() - 200 + // ) + // dialog.dialog({ + // resizable: false, + // width: '90%', + // height: 'auto', + // position: { my: 'center', at: 'center', of: window }, + // buttons: [ + // { + // text: 'Download Verilog File', + // click() { + // var fileName = getProjectName() || 'Untitled' + // download(fileName + '.v', editor.getValue()) + // }, + // }, + // { + // text: 'Copy to Clipboard', + // click() { + // copyToClipboard(editor.getValue()) + // showMessage('Code has been copied') + // }, + // }, + // { + // text: 'Try in EDA Playground', + // click() { + // copyToClipboard(editor.getValue()) + // openInNewTab('https://www.edaplayground.com/x/XZpY') + // }, + // }, + // ], + // }) +} + +export function setupVerilogExportCodeWindow() { + var myTextarea = document.getElementById('verilog-export-code-window') + editor = CodeMirror.fromTextArea(myTextarea, { + mode: 'verilog', + autoRefresh: true, + styleActiveLine: true, + lineNumbers: true, + autoCloseBrackets: true, + smartIndent: true, + indentWithTabs: true, + extraKeys: { 'Ctrl-Space': 'autocomplete' }, + }) +} + +export var verilog = { + // Entry point to verilog generation + // scope = undefined means export all circuits + exportVerilog: function (scope = undefined) { + var dependencyList = {} + // Reset Verilog Element State + for (var elem in modules) { + // Not sure if globalScope here is correct. + if (modules[elem].resetVerilog) { + modules[elem].resetVerilog() + } + } + + // List of devices under test for which testbench needs to be created + var DUTs = [] + var SubCircuitIds = new Set() + + // Generate SubCircuit Dependency Graph + for (id in scopeList) { + dependencyList[id] = scopeList[id].getDependencies() + for (var i = 0; i < scopeList[id].SubCircuit.length; i++) { + SubCircuitIds.add(scopeList[id].SubCircuit[i].id) + } + } + + for (id in scopeList) { + if (!SubCircuitIds.has(id)) DUTs.push(scopeList[id]) + } + + // DFS on SubCircuit Dependency Graph + var visited = {} + var elementTypesUsed = {} + var output = '' + if (scope) { + // generate verilog only for scope + output += this.exportVerilogScope( + scope.id, + visited, + dependencyList, + elementTypesUsed + ) + } else { + // generate verilog for everything + for (id in scopeList) { + output += this.exportVerilogScope( + id, + visited, + dependencyList, + elementTypesUsed + ) + } + } + // Add Circuit Element - Module Specific Verilog Code + for (var element in elementTypesUsed) { + // If element has custom verilog + if (modules[element] && modules[element].moduleVerilog) { + output += modules[element].moduleVerilog() + } + } + + var report = this.generateReport(elementTypesUsed) + '\n' + var testbench = this.generateTestBenchCode(DUTs) + + return report + testbench + output + }, + generateReport: function (elementTypesUsed) { + var output = '' + output += '/**\n' + output += + ' * This is an autogenerated netlist code from CircuitVerse. Verilog Code can be\n' + output += + ' * tested on https://www.edaplayground.com/ using Icarus Verilog 0.9.7. This is an\n' + output += + ' * experimental module and some manual changes make need to be done in order for\n' + output += ' * this to work.\n' + output += ' *\n' + output += + ' * If you have any ideas/suggestions or bug fixes, raise an issue\n' + output += + ' * on https://github.com/CircuitVerse/CircuitVerse/issues/new/choose\n' + output += ' */\n' + output += '\n' + output += '/*\n' + output += sp(1) + 'Element Usage Report\n' + for (var elem in elementTypesUsed) { + if (elem == 'Node') continue + output += `${sp(2)}${elem} - ${elementTypesUsed[elem]} times\n` + } + output += '*/\n' + output += '\n' + var instructions = '' + output += '/*\n' + output += sp(1) + 'Usage Instructions and Tips\n' + instructions += + sp(2) + + 'Labels - Ensure unique label names and avoid using verilog keywords\n' + instructions += + sp(2) + + 'Warnings - Connect all optional inputs to remove warnings\n' + for (var elem in elementTypesUsed) { + // If element has custom instructions + if (modules[elem] && modules[elem].verilogInstructions) { + instructions += indent(2, modules[elem].verilogInstructions()) + } + } + output += instructions + output += '*/\n' + return output + }, + generateTestBenchCode: function (DUTs) { + if (DUTs.length == 0) return '' + var output = '// Sample Testbench Code - Uncomment to use\n' + + output += '\n/*\n' + output += 'module TestBench();\n' + var registers = {} + var wires = {} + for (var i = 1; i <= 32; i++) registers[i] = new Set() + for (var i = 1; i <= 32; i++) wires[i] = new Set() + + var clocks = new Set() + var inputs = new Set() + var outputs = new Set() + var deviceInstantiations = '' + for (var i = 0; i < DUTs.length; i++) { + var DUT = DUTs[i] + for (var j = 0; j < DUT.Input.length; j++) { + var inp = DUT.Input[j] + registers[inp.bitWidth].add(inp.label) + inputs.add(inp.label) + } + for (var j = 0; j < DUT.Output.length; j++) { + var out = DUT.Output[j] + wires[out.bitWidth].add(out.label) + outputs.add(out.label) + } + for (var j = 0; j < DUT.Clock.length; j++) { + var inp = DUT.Clock[j] + registers[1].add(inp.label) + clocks.add(inp.label) + } + var circuitName = sanitizeLabel(DUT.name) + var dutHeader = this.generateHeaderHelper(DUT) + deviceInstantiations += `${sp( + 1 + )}${circuitName} DUT${i}${dutHeader}\n` + } + output += '\n' + // Generate Reg Initialization Code + for (var bitWidth = 1; bitWidth <= 32; bitWidth++) { + if (registers[bitWidth].size == 0) continue + var regArray = [...registers[bitWidth]] + if (bitWidth == 1) output += `${sp(1)}reg ${regArray.join(', ')};\n` + else + output += `${sp(1)}reg [${bitWidth - 1}:0] ${regArray.join( + ', ' + )};\n` + } + output += '\n' + // Generate Wire Initialization Code + for (var bitWidth = 1; bitWidth <= 32; bitWidth++) { + if (wires[bitWidth].size == 0) continue + var wireArray = [...wires[bitWidth]] + if (bitWidth == 1) + output += `${sp(1)}wire ${wireArray.join(', ')};\n` + else + output += `${sp(1)}wire [${bitWidth - 1}:0] ${wireArray.join( + ', ' + )};\n` + } + output += '\n' + + output += deviceInstantiations + + if (clocks.size) { + output += `${sp(1)}always begin\n` + output += `${sp(2)}#10\n` + for (var clk of clocks) output += `${sp(2)}${clk} = 0;\n` + output += `${sp(2)}#10\n` + for (var clk of clocks) output += `${sp(2)}${clk} = 1;\n` + output += `${sp(1)}end\n` + output += '\n' + } + + output += `${sp(1)}initial begin\n` + + // Reset inputs to 0 + for (var inp of inputs) { + output += `${sp(2)}${inp} = 0;\n` + } + output += '\n' + output += `${sp(2)}#15\n` + for (var out of outputs) { + output += `${sp(2)}$display("${out} = %b", ${out});\n` + } + output += '\n' + output += `${sp(2)}#10\n` + for (var out of outputs) { + output += `${sp(2)}$display("${out} = %b", ${out});\n` + } + output += '\n' + output += `${sp(2)}$finish;\n\n` + output += `${sp(1)}end\n` + + output += 'endmodule\n' + + output += '\n*/\n' + + return output + }, + // Recursive DFS function + exportVerilogScope: function ( + id, + visited, + dependencyList, + elementTypesUsed + ) { + // Already Visited + if (visited[id]) return '' + // Mark as Visited + visited[id] = true + + var output = '' + // DFS on dependencies + for (var i = 0; i < dependencyList[id].length; i++) + output += + this.exportVerilogScope( + dependencyList[id][i], + visited, + dependencyList, + elementTypesUsed + ) + '\n' + + var scope = scopeList[id] + // Initialize labels for all elements + this.resetLabels(scope) + this.setLabels(scope) + + output += this.generateHeader(scope) + output += this.generateOutputList(scope) // generate output first to be consistent + output += this.generateInputList(scope) + + // Note: processGraph function populates scope.verilogWireList + var res = this.processGraph(scope, elementTypesUsed) + + // Generate Wire Initialization Code + for (var bitWidth = 1; bitWidth <= 32; bitWidth++) { + var wireList = scope.verilogWireList[bitWidth] + // Hack for splitter + wireList = wireList.filter((x) => !x.includes('[')) + if (wireList.length == 0) continue + if (bitWidth == 1) output += ' wire ' + wireList.join(', ') + ';\n' + else + output += + ' wire [' + + (bitWidth - 1) + + ':0] ' + + wireList.join(', ') + + ';\n' + } + + // Append Wire connections and module instantiations + output += res + + // Append footer + output += 'endmodule\n' + + return output + }, + // Performs DFS on the graph and generates netlist of wires and connections + processGraph: function (scope, elementTypesUsed) { + // Initializations + var res = '' + scope.stack = [] + scope.verilogWireList = [] + for (var i = 0; i <= 32; i++) scope.verilogWireList.push(new Array()) + + var verilogResolvedSet = new Set() + + // Start DFS from inputs + for (var i = 0; i < inputList.length; i++) { + for (var j = 0; j < scope[inputList[i]].length; j++) { + scope.stack.push(scope[inputList[i]][j]) + } + } + + // Iterative DFS on circuit graph + while (scope.stack.length) { + if (errorDetectedGet()) return + var elem = scope.stack.pop() + + if (verilogResolvedSet.has(elem)) continue + + // Process verilog creates variable names and adds elements to DFS stack + elem.processVerilog() + + // Record usage of element type + if (elem.objectType != 'Node') { + if (elementTypesUsed[elem.objectType]) + elementTypesUsed[elem.objectType]++ + else elementTypesUsed[elem.objectType] = 1 + } + + if ( + elem.objectType != 'Node' && + elem.objectType != 'Input' && + elem.objectType != 'Clock' + ) { + verilogResolvedSet.add(elem) + } + } + + // Generate connection verilog code and module instantiations + for (var elem of verilogResolvedSet) { + res += ' ' + elem.generateVerilog() + '\n' + } + return res + }, + + resetLabels: function (scope) { + for (var i = 0; i < scope.allNodes.length; i++) { + scope.allNodes[i].verilogLabel = '' + } + }, + // Sets labels for all Circuit Elements elements + setLabels: function (scope = globalScope) { + /** + * Sets a name for each element. If element is already labeled, + * the element is used directly, otherwise an automated label is provided + * sanitizeLabel is a helper function to escape white spaces + */ + for (var i = 0; i < scope.Input.length; i++) { + if (scope.Input[i].label == '') scope.Input[i].label = 'inp_' + i + else scope.Input[i].label = sanitizeLabel(scope.Input[i].label) + // copy label to node + scope.Input[i].output1.verilogLabel = scope.Input[i].label + } + for (var i = 0; i < scope.ConstantVal.length; i++) { + if (scope.ConstantVal[i].label == '') + scope.ConstantVal[i].label = 'const_' + i + else + scope.ConstantVal[i].label = sanitizeLabel( + scope.ConstantVal[i].label + ) + // copy label to node + scope.ConstantVal[i].output1.verilogLabel = + scope.ConstantVal[i].label + } + + // copy label to clock + for (var i = 0; i < scope.Clock.length; i++) { + if (scope.Clock[i].label == '') scope.Clock[i].label = 'clk_' + i + else scope.Clock[i].label = sanitizeLabel(scope.Clock[i].label) + scope.Clock[i].output1.verilogLabel = scope.Clock[i].label + } + + for (var i = 0; i < scope.Output.length; i++) { + if (scope.Output[i].label == '') scope.Output[i].label = 'out_' + i + else scope.Output[i].label = sanitizeLabel(scope.Output[i].label) + } + for (var i = 0; i < scope.SubCircuit.length; i++) { + if (scope.SubCircuit[i].label == '') + scope.SubCircuit[i].label = + scope.SubCircuit[i].data.name + '_' + i + else + scope.SubCircuit[i].label = sanitizeLabel( + scope.SubCircuit[i].label + ) + } + for (var i = 0; i < moduleList.length; i++) { + var m = moduleList[i] + for (var j = 0; j < scope[m].length; j++) { + scope[m][j].verilogLabel = + sanitizeLabel(scope[m][j].label) || + scope[m][j].verilogName() + '_' + j + } + } + }, + generateHeader: function (scope = globalScope) { + // Example: module HalfAdder (a,b,s,c); + var res = '\nmodule ' + sanitizeLabel(scope.name) + res += this.generateHeaderHelper(scope) + return res + }, + generateHeaderHelper: function (scope = globalScope) { + // Example: (a,b,s,c); + var res = '(' + var pins = [] + for (var i = 0; i < scope.Output.length; i++) { + pins.push(scope.Output[i].label) + } + for (var i = 0; i < scope.Clock.length; i++) { + pins.push(scope.Clock[i].label) + } + for (var i = 0; i < scope.Input.length; i++) { + pins.push(scope.Input[i].label) + } + res += pins.join(', ') + res += ');\n' + return res + }, + generateInputList: function (scope = globalScope) { + var inputs = {} + for (var i = 1; i <= 32; i++) inputs[i] = [] + + for (var i = 0; i < scope.Input.length; i++) { + inputs[scope.Input[i].bitWidth].push(scope.Input[i].label) + } + + for (var i = 0; i < scope.Clock.length; i++) { + inputs[scope.Clock[i].bitWidth].push(scope.Clock[i].label) + } + + var res = '' + for (var bitWidth in inputs) { + if (inputs[bitWidth].length == 0) continue + if (bitWidth == 1) res += ' input ' + inputs[1].join(', ') + ';\n' + else + res += + ' input [' + + (bitWidth - 1) + + ':0] ' + + inputs[bitWidth].join(', ') + + ';\n' + } + + return res + }, + generateOutputList: function (scope = globalScope) { + // Example 1: output s,cout; + var outputs = {} + for (var i = 0; i < scope.Output.length; i++) { + if (outputs[scope.Output[i].bitWidth]) + outputs[scope.Output[i].bitWidth].push(scope.Output[i].label) + else outputs[scope.Output[i].bitWidth] = [scope.Output[i].label] + } + var res = '' + for (var bitWidth in outputs) { + if (bitWidth == 1) + res += ' output ' + outputs[1].join(', ') + ';\n' + else + res += + ' output [' + + (bitWidth - 1) + + ':0] ' + + outputs[bitWidth].join(', ') + + ';\n' + } + + return res + }, + /* + sanitizeLabel: function(name){ + // Replace spaces by "_" + name = name.replace(/ /g , "_"); + // Replace Hyphens by "_" + name = name.replace(/-/g , "_"); + // Replace Colons by "_" + name = name.replace(/:/g , "_"); + // replace ~ with inv_ + name = name.replace(/~/g , "inv_"); + // Shorten Inverse to inv + name = name.replace(/Inverse/g , "inv"); + + // If first character is a number + if(name.substring(0, 1).search(/[0-9]/g) > -1) { + name = "w_" + name; + } + + // if first character is not \ already + if (name[0] != '\\') { + //if there are non-alphanum_ character, add \ + if (name.search(/[\W]/g) > -1) + name = "\\" + name; + } + return name; + }, + */ +} + +/* + Helper function to generate spaces for indentation +*/ +function sp(indentation) { + return ' '.repeat(indentation * 2) +} + +/* + Helper function to indent paragraph +*/ +function indent(indentation, string) { + var result = string.split('\n') + if (result[result.length - 1] == '') { + result.pop() + result = result.map((x) => sp(indentation) + x).join('\n') + result += '\n' + return result + } + return result.map((x) => sp(indentation) + x).join('\n') +} diff --git a/v1/src/simulator/src/verilogHelpers.js b/v1/src/simulator/src/verilogHelpers.js new file mode 100644 index 00000000..3f9a4123 --- /dev/null +++ b/v1/src/simulator/src/verilogHelpers.js @@ -0,0 +1,41 @@ +export function sanitizeLabel(name) { + // return name.replace(/ Inverse/g, "_inv").replace(/ /g , "_"); + var temp = name + // if there is a space anywhere but the last place + // replace spaces by "_" + // last space is required for escaped id + if (temp.search(/ /g) < temp.length - 1 && temp.search(/ /g) >= 0) { + temp = temp.replace(/ Inverse/g, '_inv') + temp = temp.replace(/ /g, '_') + } + // if first character is not \ already + if (temp.substring(0, 1).search(/\\/g) < 0) { + // if there are non-alphanum_ character, or first character is num, add \ + if ( + temp.search(/[\W]/g) > -1 || + temp.substring(0, 1).search(/[0-9]/g) > -1 + ) + temp = '\\' + temp + ' ' + } + return temp +} + +export function generateNodeName(node, currentCount, totalCount) { + if (node.verilogLabel) return node.verilogLabel + var parentVerilogLabel = node.parent.verilogLabel + var nodeName + if (node.label) { + nodeName = sanitizeLabel(node.label) + } else { + nodeName = totalCount > 1 ? 'out_' + currentCount : 'out' + } + if (parentVerilogLabel.substring(0, 1).search(/\\/g) < 0) + return parentVerilogLabel + '_' + nodeName + else + return ( + parentVerilogLabel.substring(0, parentVerilogLabel.length - 1) + + '_' + + nodeName + + ' ' + ) +} diff --git a/v1/src/simulator/src/wire.js b/v1/src/simulator/src/wire.js new file mode 100644 index 00000000..7e06e7d2 --- /dev/null +++ b/v1/src/simulator/src/wire.js @@ -0,0 +1,240 @@ +/* eslint-disable no-multi-assign */ +// wire object +import { drawLine } from './canvasApi' +import simulationArea from './simulationArea' +import Node from './node' +import { updateSimulationSet, forceResetNodesSet } from './engine' +import { colors } from './themer/themer' + +/** + * Wire - To connect two nodes. + * @class + * @memberof module:wire + * @param {Node} node1 + * @param {Node} node2 + * @param {Scope} scope - The circuit in which wire has to be drawn + * @category wire + */ +export default class Wire { + constructor(node1, node2, scope) { + this.objectType = 'Wire' + this.node1 = node1 + this.scope = scope + this.node2 = node2 + this.type = 'horizontal' + + this.updateData() + this.scope.wires.push(this) + forceResetNodesSet(true) + } + + // if data changes + updateData() { + this.x1 = this.node1.absX() + this.y1 = this.node1.absY() + this.x2 = this.node2.absX() + this.y2 = this.node2.absY() + if (this.x1 === this.x2) this.type = 'vertical' + } + + updateScope(scope) { + this.scope = scope + this.checkConnections() + } + + // to check if nodes are disconnected + checkConnections() { + var check = + this.node1.deleted || + this.node2.deleted || + !this.node1.connections.contains(this.node2) || + !this.node2.connections.contains(this.node1) + if (check) this.delete() + return check + } + + dblclick() { + if ( + this.node1.parent == globalScope.root && + this.node2.parent == globalScope.root + ) { + simulationArea.multipleObjectSelections = [this.node1, this.node2] + simulationArea.lastSelected = undefined + } + } + + update() { + var updated = false + if (embed) return updated + + if (this.node1.absX() === this.node2.absX()) { + this.x1 = this.x2 = this.node1.absX() + this.type = 'vertical' + } else if (this.node1.absY() === this.node2.absY()) { + this.y1 = this.y2 = this.node1.absY() + this.type = 'horizontal' + } + + // if (wireToBeChecked && this.checkConnections()) { + // this.delete(); + // return updated; + // } // SLOW , REMOVE + if ( + simulationArea.shiftDown === false && + simulationArea.mouseDown === true && + simulationArea.selected === false && + this.checkWithin( + simulationArea.mouseDownX, + simulationArea.mouseDownY + ) + ) { + simulationArea.selected = true + simulationArea.lastSelected = this + updated = true + } else if ( + simulationArea.mouseDown && + simulationArea.lastSelected === this && + !this.checkWithin(simulationArea.mouseX, simulationArea.mouseY) + ) { + var n = new Node( + simulationArea.mouseDownX, + simulationArea.mouseDownY, + 2, + this.scope.root + ) + n.clicked = true + n.wasClicked = true + simulationArea.lastSelected = n + this.converge(n) + } + // eslint-disable-next-line no-empty + if (simulationArea.lastSelected === this) { + } + + if (this.node1.deleted || this.node2.deleted) { + this.delete() + return updated + } // if either of the nodes are deleted + + if (simulationArea.mouseDown === false) { + if (this.type === 'horizontal') { + if (this.node1.absY() !== this.y1) { + // if(this.checkConnections()){this.delete();return;} + n = new Node(this.node1.absX(), this.y1, 2, this.scope.root) + this.converge(n) + updated = true + } else if (this.node2.absY() !== this.y2) { + // if(this.checkConnections()){this.delete();return;} + n = new Node(this.node2.absX(), this.y2, 2, this.scope.root) + this.converge(n) + updated = true + } + } else if (this.type === 'vertical') { + if (this.node1.absX() !== this.x1) { + // if(this.checkConnections()){this.delete();return;} + n = new Node(this.x1, this.node1.absY(), 2, this.scope.root) + this.converge(n) + updated = true + } else if (this.node2.absX() !== this.x2) { + // if(this.checkConnections()){this.delete();return;} + n = new Node(this.x2, this.node2.absY(), 2, this.scope.root) + this.converge(n) + updated = true + } + } + } + return updated + } + + draw() { + // for calculating min-max Width,min-max Height + // + const ctx = simulationArea.context + + var color + if (simulationArea.lastSelected == this) { + color = colors['color_wire_sel'] + } else if ( + this.node1.value == undefined || + this.node2.value == undefined + ) { + color = colors['color_wire_lose'] + } else if (this.node1.bitWidth == 1) { + color = [ + colors['color_wire_lose'], + colors['color_wire_con'], + colors['color_wire_pow'], + ][this.node1.value + 1] + } else { + color = colors['color_wire'] + } + drawLine( + ctx, + this.node1.absX(), + this.node1.absY(), + this.node2.absX(), + this.node2.absY(), + color, + 3 + ) + } + + // checks if node lies on wire + checkConvergence(n) { + return this.checkWithin(n.absX(), n.absY()) + } + + // fn checks if coordinate lies on wire + checkWithin(x, y) { + if ( + this.type === 'horizontal' && + this.node1.absX() < this.node2.absX() && + x > this.node1.absX() && + x < this.node2.absX() && + y === this.node2.absY() + ) + return true + if ( + this.type === 'horizontal' && + this.node1.absX() > this.node2.absX() && + x < this.node1.absX() && + x > this.node2.absX() && + y === this.node2.absY() + ) + return true + if ( + this.type === 'vertical' && + this.node1.absY() < this.node2.absY() && + y > this.node1.absY() && + y < this.node2.absY() && + x === this.node2.absX() + ) + return true + if ( + this.type === 'vertical' && + this.node1.absY() > this.node2.absY() && + y < this.node1.absY() && + y > this.node2.absY() && + x === this.node2.absX() + ) + return true + return false + } + + // add intermediate node between these 2 nodes + converge(n) { + this.node1.connect(n) + this.node2.connect(n) + this.delete() + } + + delete() { + forceResetNodesSet(true) + updateSimulationSet(true) + this.node1.connections.clean(this.node2) + this.node2.connections.clean(this.node1) + this.scope.wires.clean(this) + this.node1.checkDeleted() + this.node2.checkDeleted() + } +} diff --git a/v1/src/simulator/vendor/canvas2svg.js b/v1/src/simulator/vendor/canvas2svg.js new file mode 100644 index 00000000..73dae81d --- /dev/null +++ b/v1/src/simulator/vendor/canvas2svg.js @@ -0,0 +1,1469 @@ +/*!! + * Canvas 2 Svg v1.0.19 + * A low level canvas to SVG converter. Uses a mock canvas context to build an SVG document. + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/mit-license.php + * + * Author: + * Kerry Liu + * + * Copyright (c) 2014 Gliffy Inc. + */ + +;(function () { + 'use strict' + + var STYLES, ctx, CanvasGradient, CanvasPattern, namedEntities + + //helper function to format a string + function format(str, args) { + var keys = Object.keys(args), + i + for (i = 0; i < keys.length; i++) { + str = str.replace( + new RegExp('\\{' + keys[i] + '\\}', 'gi'), + args[keys[i]] + ) + } + return str + } + + //helper function that generates a random string + function randomString(holder) { + var chars, randomstring, i + if (!holder) { + throw new Error( + 'cannot create a random attribute name for an undefined object' + ) + } + chars = 'ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz' + randomstring = '' + do { + randomstring = '' + for (i = 0; i < 12; i++) { + randomstring += chars[Math.floor(Math.random() * chars.length)] + } + } while (holder[randomstring]) + return randomstring + } + + //helper function to map named to numbered entities + function createNamedToNumberedLookup(items, radix) { + var i, + entity, + lookup = {}, + base10, + base16 + items = items.split(',') + radix = radix || 10 + // Map from named to numbered entities. + for (i = 0; i < items.length; i += 2) { + entity = '&' + items[i + 1] + ';' + base10 = parseInt(items[i], radix) + lookup[entity] = '&#' + base10 + ';' + } + //FF and IE need to create a regex from hex values ie   == \xa0 + lookup['\\xa0'] = ' ' + return lookup + } + + //helper function to map canvas-textAlign to svg-textAnchor + function getTextAnchor(textAlign) { + //TODO: support rtl languages + var mapping = { + left: 'start', + right: 'end', + center: 'middle', + start: 'start', + end: 'end', + } + return mapping[textAlign] || mapping.start + } + + //helper function to map canvas-textBaseline to svg-dominantBaseline + function getDominantBaseline(textBaseline) { + //INFO: not supported in all browsers + var mapping = { + alphabetic: 'alphabetic', + hanging: 'hanging', + top: 'text-before-edge', + bottom: 'text-after-edge', + middle: 'central', + } + return mapping[textBaseline] || mapping.alphabetic + } + + // Unpack entities lookup where the numbers are in radix 32 to reduce the size + // entity mapping courtesy of tinymce + namedEntities = createNamedToNumberedLookup( + '50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,' + + '5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,' + + '5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,' + + '5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,' + + '68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,' + + '6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,' + + '6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,' + + '75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,' + + '7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,' + + '7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,' + + 'sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,' + + 'st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,' + + 't9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,' + + 'tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,' + + 'u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,' + + '81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,' + + '8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,' + + '8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,' + + '8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,' + + '8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,' + + 'nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,' + + 'rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,' + + 'Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,' + + '80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,' + + '811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro', + 32 + ) + + //Some basic mappings for attributes and default values. + STYLES = { + strokeStyle: { + svgAttr: 'stroke', //corresponding svg attribute + canvas: '#000000', //canvas default + svg: 'none', //svg default + apply: 'stroke', //apply on stroke() or fill() + }, + fillStyle: { + svgAttr: 'fill', + canvas: '#000000', + svg: null, //svg default is black, but we need to special case this to handle canvas stroke without fill + apply: 'fill', + }, + lineCap: { + svgAttr: 'stroke-linecap', + canvas: 'butt', + svg: 'butt', + apply: 'stroke', + }, + lineJoin: { + svgAttr: 'stroke-linejoin', + canvas: 'miter', + svg: 'miter', + apply: 'stroke', + }, + miterLimit: { + svgAttr: 'stroke-miterlimit', + canvas: 10, + svg: 4, + apply: 'stroke', + }, + lineWidth: { + svgAttr: 'stroke-width', + canvas: 1, + svg: 1, + apply: 'stroke', + }, + globalAlpha: { + svgAttr: 'opacity', + canvas: 1, + svg: 1, + apply: 'fill stroke', + }, + font: { + //font converts to multiple svg attributes, there is custom logic for this + canvas: '10px sans-serif', + }, + shadowColor: { + canvas: '#000000', + }, + shadowOffsetX: { + canvas: 0, + }, + shadowOffsetY: { + canvas: 0, + }, + shadowBlur: { + canvas: 0, + }, + textAlign: { + canvas: 'start', + }, + textBaseline: { + canvas: 'alphabetic', + }, + lineDash: { + svgAttr: 'stroke-dasharray', + canvas: [], + svg: null, + apply: 'stroke', + }, + } + + /** + * + * @param gradientNode - reference to the gradient + * @constructor + */ + CanvasGradient = function (gradientNode, ctx) { + this.__root = gradientNode + this.__ctx = ctx + } + + /** + * Adds a color stop to the gradient root + */ + CanvasGradient.prototype.addColorStop = function (offset, color) { + var stop = this.__ctx.__createElement('stop'), + regex, + matches + stop.setAttribute('offset', offset) + if (color.indexOf('rgba') !== -1) { + //separate alpha value, since webkit can't handle it + regex = + /rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d?\.?\d*)\s*\)/gi + matches = regex.exec(color) + stop.setAttribute( + 'stop-color', + format('rgb({r},{g},{b})', { + r: matches[1], + g: matches[2], + b: matches[3], + }) + ) + stop.setAttribute('stop-opacity', matches[4]) + } else { + stop.setAttribute('stop-color', color) + } + this.__root.appendChild(stop) + } + + CanvasPattern = function (pattern, ctx) { + this.__root = pattern + this.__ctx = ctx + } + + /** + * The mock canvas context + * @param o - options include: + * ctx - existing Context2D to wrap around + * width - width of your canvas (defaults to 500) + * height - height of your canvas (defaults to 500) + * enableMirroring - enables canvas mirroring (get image data) (defaults to false) + * document - the document object (defaults to the current document) + */ + ctx = function (o) { + var defaultOptions = { + width: 500, + height: 500, + enableMirroring: false, + }, + options + + //keep support for this way of calling C2S: new C2S(width,height) + if (arguments.length > 1) { + options = defaultOptions + options.width = arguments[0] + options.height = arguments[1] + } else if (!o) { + options = defaultOptions + } else { + options = o + } + + if (!(this instanceof ctx)) { + //did someone call this without new? + return new ctx(options) + } + + //setup options + this.width = options.width || defaultOptions.width + this.height = options.height || defaultOptions.height + this.enableMirroring = + options.enableMirroring !== undefined + ? options.enableMirroring + : defaultOptions.enableMirroring + + this.canvas = this ///point back to this instance! + this.__document = options.document || document + + // allow passing in an existing context to wrap around + // if a context is passed in, we know a canvas already exist + if (options.ctx) { + this.__ctx = options.ctx + } else { + this.__canvas = this.__document.createElement('canvas') + this.__ctx = this.__canvas.getContext('2d') + } + + this.__setDefaultStyles() + this.__stack = [this.__getStyleState()] + this.__groupStack = [] + + //the root svg element + this.__root = this.__document.createElementNS( + 'http://www.w3.org/2000/svg', + 'svg' + ) + this.__root.setAttribute('version', 1.1) + this.__root.setAttribute('xmlns', 'http://www.w3.org/2000/svg') + this.__root.setAttributeNS( + 'http://www.w3.org/2000/xmlns/', + 'xmlns:xlink', + 'http://www.w3.org/1999/xlink' + ) + this.__root.setAttribute('width', this.width) + this.__root.setAttribute('height', this.height) + + //make sure we don't generate the same ids in defs + this.__ids = {} + + //defs tag + this.__defs = this.__document.createElementNS( + 'http://www.w3.org/2000/svg', + 'defs' + ) + this.__root.appendChild(this.__defs) + + //also add a group child. the svg element can't use the transform attribute + this.__currentElement = this.__document.createElementNS( + 'http://www.w3.org/2000/svg', + 'g' + ) + this.__root.appendChild(this.__currentElement) + } + + /** + * Creates the specified svg element + * @private + */ + ctx.prototype.__createElement = function ( + elementName, + properties, + resetFill + ) { + if (typeof properties === 'undefined') { + properties = {} + } + + var element = this.__document.createElementNS( + 'http://www.w3.org/2000/svg', + elementName + ), + keys = Object.keys(properties), + i, + key + if (resetFill) { + //if fill or stroke is not specified, the svg element should not display. By default SVG's fill is black. + element.setAttribute('fill', 'none') + element.setAttribute('stroke', 'none') + } + for (i = 0; i < keys.length; i++) { + key = keys[i] + element.setAttribute(key, properties[key]) + } + return element + } + + /** + * Applies default canvas styles to the context + * @private + */ + ctx.prototype.__setDefaultStyles = function () { + //default 2d canvas context properties see:http://www.w3.org/TR/2dcontext/ + var keys = Object.keys(STYLES), + i, + key + for (i = 0; i < keys.length; i++) { + key = keys[i] + this[key] = STYLES[key].canvas + } + } + + /** + * Applies styles on restore + * @param styleState + * @private + */ + ctx.prototype.__applyStyleState = function (styleState) { + var keys = Object.keys(styleState), + i, + key + for (i = 0; i < keys.length; i++) { + key = keys[i] + this[key] = styleState[key] + } + } + + /** + * Gets the current style state + * @return {Object} + * @private + */ + ctx.prototype.__getStyleState = function () { + var i, + styleState = {}, + keys = Object.keys(STYLES), + key + for (i = 0; i < keys.length; i++) { + key = keys[i] + styleState[key] = this[key] + } + return styleState + } + + /** + * Apples the current styles to the current SVG element. On "ctx.fill" or "ctx.stroke" + * @param type + * @private + */ + ctx.prototype.__applyStyleToCurrentElement = function (type) { + var currentElement = this.__currentElement + var currentStyleGroup = this.__currentElementsToStyle + if (currentStyleGroup) { + currentElement.setAttribute(type, '') + currentElement = currentStyleGroup.element + currentStyleGroup.children.forEach(function (node) { + node.setAttribute(type, '') + }) + } + + var keys = Object.keys(STYLES), + i, + style, + value, + id, + regex, + matches + for (i = 0; i < keys.length; i++) { + style = STYLES[keys[i]] + value = this[keys[i]] + if (style.apply) { + //is this a gradient or pattern? + if (value instanceof CanvasPattern) { + //pattern + if (value.__ctx) { + //copy over defs + while (value.__ctx.__defs.childNodes.length) { + id = + value.__ctx.__defs.childNodes[0].getAttribute( + 'id' + ) + this.__ids[id] = id + this.__defs.appendChild( + value.__ctx.__defs.childNodes[0] + ) + } + } + currentElement.setAttribute( + style.apply, + format('url(#{id})', { + id: value.__root.getAttribute('id'), + }) + ) + } else if (value instanceof CanvasGradient) { + //gradient + currentElement.setAttribute( + style.apply, + format('url(#{id})', { + id: value.__root.getAttribute('id'), + }) + ) + } else if ( + style.apply.indexOf(type) !== -1 && + style.svg !== value + ) { + if ( + (style.svgAttr === 'stroke' || + style.svgAttr === 'fill') && + value.indexOf('rgba') !== -1 + ) { + //separate alpha value, since illustrator can't handle it + regex = + /rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d?\.?\d*)\s*\)/gi + matches = regex.exec(value) + currentElement.setAttribute( + style.svgAttr, + format('rgb({r},{g},{b})', { + r: matches[1], + g: matches[2], + b: matches[3], + }) + ) + //should take globalAlpha here + var opacity = matches[4] + var globalAlpha = this.globalAlpha + if (globalAlpha != null) { + opacity *= globalAlpha + } + currentElement.setAttribute( + style.svgAttr + '-opacity', + opacity + ) + } else { + var attr = style.svgAttr + if (keys[i] === 'globalAlpha') { + attr = type + '-' + style.svgAttr + if (currentElement.getAttribute(attr)) { + //fill-opacity or stroke-opacity has already been set by stroke or fill. + continue + } + } + //otherwise only update attribute if right type, and not svg default + currentElement.setAttribute(attr, value) + } + } + } + } + } + + /** + * Will return the closest group or svg node. May return the current element. + * @private + */ + ctx.prototype.__closestGroupOrSvg = function (node) { + node = node || this.__currentElement + if (node.nodeName === 'g' || node.nodeName === 'svg') { + return node + } else { + return this.__closestGroupOrSvg(node.parentNode) + } + } + + /** + * Returns the serialized value of the svg so far + * @param fixNamedEntities - Standalone SVG doesn't support named entities, which document.createTextNode encodes. + * If true, we attempt to find all named entities and encode it as a numeric entity. + * @return serialized svg + */ + ctx.prototype.getSerializedSvg = function (fixNamedEntities) { + var serialized = new XMLSerializer().serializeToString(this.__root), + keys, + i, + key, + value, + regexp, + xmlns + + //IE search for a duplicate xmnls because they didn't implement setAttributeNS correctly + xmlns = + /xmlns="http:\/\/www\.w3\.org\/2000\/svg".+xmlns="http:\/\/www\.w3\.org\/2000\/svg/gi + if (xmlns.test(serialized)) { + serialized = serialized.replace( + 'xmlns="http://www.w3.org/2000/svg', + 'xmlns:xlink="http://www.w3.org/1999/xlink' + ) + } + + if (fixNamedEntities) { + keys = Object.keys(namedEntities) + //loop over each named entity and replace with the proper equivalent. + for (i = 0; i < keys.length; i++) { + key = keys[i] + value = namedEntities[key] + regexp = new RegExp(key, 'gi') + if (regexp.test(serialized)) { + serialized = serialized.replace(regexp, value) + } + } + } + + return serialized + } + + /** + * Returns the root svg + * @return + */ + ctx.prototype.getSvg = function () { + return this.__root + } + /** + * Will generate a group tag. + */ + ctx.prototype.save = function () { + var group = this.__createElement('g') + var parent = this.__closestGroupOrSvg() + this.__groupStack.push(parent) + parent.appendChild(group) + this.__currentElement = group + this.__stack.push(this.__getStyleState()) + } + /** + * Sets current element to parent, or just root if already root + */ + ctx.prototype.restore = function () { + this.__currentElement = this.__groupStack.pop() + this.__currentElementsToStyle = null + //Clearing canvas will make the poped group invalid, currentElement is set to the root group node. + if (!this.__currentElement) { + this.__currentElement = this.__root.childNodes[1] + } + var state = this.__stack.pop() + this.__applyStyleState(state) + } + + /** + * Helper method to add transform + * @private + */ + ctx.prototype.__addTransform = function (t) { + //if the current element has siblings, add another group + var parent = this.__closestGroupOrSvg() + if (parent.childNodes.length > 0) { + if (this.__currentElement.nodeName === 'path') { + if (!this.__currentElementsToStyle) + this.__currentElementsToStyle = { + element: parent, + children: [], + } + this.__currentElementsToStyle.children.push( + this.__currentElement + ) + this.__applyCurrentDefaultPath() + } + + var group = this.__createElement('g') + parent.appendChild(group) + this.__currentElement = group + } + + var transform = this.__currentElement.getAttribute('transform') + if (transform) { + transform += ' ' + } else { + transform = '' + } + transform += t + this.__currentElement.setAttribute('transform', transform) + } + + /** + * scales the current element + */ + ctx.prototype.scale = function (x, y) { + if (y === undefined) { + y = x + } + this.__addTransform(format('scale({x},{y})', { x: x, y: y })) + } + + /** + * rotates the current element + */ + ctx.prototype.rotate = function (angle) { + var degrees = (angle * 180) / Math.PI + this.__addTransform( + format('rotate({angle},{cx},{cy})', { + angle: degrees, + cx: 0, + cy: 0, + }) + ) + } + + /** + * translates the current element + */ + ctx.prototype.translate = function (x, y) { + this.__addTransform(format('translate({x},{y})', { x: x, y: y })) + } + + /** + * applies a transform to the current element + */ + ctx.prototype.transform = function (a, b, c, d, e, f) { + this.__addTransform( + format('matrix({a},{b},{c},{d},{e},{f})', { + a: a, + b: b, + c: c, + d: d, + e: e, + f: f, + }) + ) + } + + /** + * Create a new Path Element + */ + ctx.prototype.beginPath = function () { + var path, parent + + // Note that there is only one current default path, it is not part of the drawing state. + // See also: https://html.spec.whatwg.org/multipage/scripting.html#current-default-path + this.__currentDefaultPath = '' + this.__currentPosition = {} + + path = this.__createElement('path', {}, true) + parent = this.__closestGroupOrSvg() + parent.appendChild(path) + this.__currentElement = path + } + + /** + * Helper function to apply currentDefaultPath to current path element + * @private + */ + ctx.prototype.__applyCurrentDefaultPath = function () { + var currentElement = this.__currentElement + if (currentElement.nodeName === 'path') { + currentElement.setAttribute('d', this.__currentDefaultPath) + } else { + console.error( + 'Attempted to apply path command to node', + currentElement.nodeName + ) + } + } + + /** + * Helper function to add path command + * @private + */ + ctx.prototype.__addPathCommand = function (command) { + this.__currentDefaultPath += ' ' + this.__currentDefaultPath += command + } + + /** + * Adds the move command to the current path element, + * if the currentPathElement is not empty create a new path element + */ + ctx.prototype.moveTo = function (x, y) { + if (this.__currentElement.nodeName !== 'path') { + this.beginPath() + } + + // creates a new subpath with the given point + this.__currentPosition = { x: x, y: y } + this.__addPathCommand(format('M {x} {y}', { x: x, y: y })) + } + + /** + * Closes the current path + */ + ctx.prototype.closePath = function () { + if (this.__currentDefaultPath) { + this.__addPathCommand('Z') + } + } + + /** + * Adds a line to command + */ + ctx.prototype.lineTo = function (x, y) { + this.__currentPosition = { x: x, y: y } + if (this.__currentDefaultPath.indexOf('M') > -1) { + this.__addPathCommand(format('L {x} {y}', { x: x, y: y })) + } else { + this.__addPathCommand(format('M {x} {y}', { x: x, y: y })) + } + } + + /** + * Add a bezier command + */ + ctx.prototype.bezierCurveTo = function (cp1x, cp1y, cp2x, cp2y, x, y) { + this.__currentPosition = { x: x, y: y } + this.__addPathCommand( + format('C {cp1x} {cp1y} {cp2x} {cp2y} {x} {y}', { + cp1x: cp1x, + cp1y: cp1y, + cp2x: cp2x, + cp2y: cp2y, + x: x, + y: y, + }) + ) + } + + /** + * Adds a quadratic curve to command + */ + ctx.prototype.quadraticCurveTo = function (cpx, cpy, x, y) { + this.__currentPosition = { x: x, y: y } + this.__addPathCommand( + format('Q {cpx} {cpy} {x} {y}', { cpx: cpx, cpy: cpy, x: x, y: y }) + ) + } + + /** + * Return a new normalized vector of given vector + */ + var normalize = function (vector) { + var len = Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1]) + return [vector[0] / len, vector[1] / len] + } + + /** + * Adds the arcTo to the current path + * + * @see http://www.w3.org/TR/2015/WD-2dcontext-20150514/#dom-context-2d-arcto + */ + ctx.prototype.arcTo = function (x1, y1, x2, y2, radius) { + // Let the point (x0, y0) be the last point in the subpath. + var x0 = this.__currentPosition && this.__currentPosition.x + var y0 = this.__currentPosition && this.__currentPosition.y + + // First ensure there is a subpath for (x1, y1). + if (typeof x0 == 'undefined' || typeof y0 == 'undefined') { + return + } + + // Negative values for radius must cause the implementation to throw an IndexSizeError exception. + if (radius < 0) { + throw new Error( + 'IndexSizeError: The radius provided (' + + radius + + ') is negative.' + ) + } + + // If the point (x0, y0) is equal to the point (x1, y1), + // or if the point (x1, y1) is equal to the point (x2, y2), + // or if the radius radius is zero, + // then the method must add the point (x1, y1) to the subpath, + // and connect that point to the previous point (x0, y0) by a straight line. + if ( + (x0 === x1 && y0 === y1) || + (x1 === x2 && y1 === y2) || + radius === 0 + ) { + this.lineTo(x1, y1) + return + } + + // Otherwise, if the points (x0, y0), (x1, y1), and (x2, y2) all lie on a single straight line, + // then the method must add the point (x1, y1) to the subpath, + // and connect that point to the previous point (x0, y0) by a straight line. + var unit_vec_p1_p0 = normalize([x0 - x1, y0 - y1]) + var unit_vec_p1_p2 = normalize([x2 - x1, y2 - y1]) + if ( + unit_vec_p1_p0[0] * unit_vec_p1_p2[1] === + unit_vec_p1_p0[1] * unit_vec_p1_p2[0] + ) { + this.lineTo(x1, y1) + return + } + + // Otherwise, let The Arc be the shortest arc given by circumference of the circle that has radius radius, + // and that has one point tangent to the half-infinite line that crosses the point (x0, y0) and ends at the point (x1, y1), + // and that has a different point tangent to the half-infinite line that ends at the point (x1, y1), and crosses the point (x2, y2). + // The points at which this circle touches these two lines are called the start and end tangent points respectively. + + // note that both vectors are unit vectors, so the length is 1 + var cos = + unit_vec_p1_p0[0] * unit_vec_p1_p2[0] + + unit_vec_p1_p0[1] * unit_vec_p1_p2[1] + var theta = Math.acos(Math.abs(cos)) + + // Calculate origin + var unit_vec_p1_origin = normalize([ + unit_vec_p1_p0[0] + unit_vec_p1_p2[0], + unit_vec_p1_p0[1] + unit_vec_p1_p2[1], + ]) + var len_p1_origin = radius / Math.sin(theta / 2) + var x = x1 + len_p1_origin * unit_vec_p1_origin[0] + var y = y1 + len_p1_origin * unit_vec_p1_origin[1] + + // Calculate start angle and end angle + // rotate 90deg clockwise (note that y axis points to its down) + var unit_vec_origin_start_tangent = [ + -unit_vec_p1_p0[1], + unit_vec_p1_p0[0], + ] + // rotate 90deg counter clockwise (note that y axis points to its down) + var unit_vec_origin_end_tangent = [ + unit_vec_p1_p2[1], + -unit_vec_p1_p2[0], + ] + var getAngle = function (vector) { + // get angle (clockwise) between vector and (1, 0) + var x = vector[0] + var y = vector[1] + if (y >= 0) { + // note that y axis points to its down + return Math.acos(x) + } else { + return -Math.acos(x) + } + } + var startAngle = getAngle(unit_vec_origin_start_tangent) + var endAngle = getAngle(unit_vec_origin_end_tangent) + + // Connect the point (x0, y0) to the start tangent point by a straight line + this.lineTo( + x + unit_vec_origin_start_tangent[0] * radius, + y + unit_vec_origin_start_tangent[1] * radius + ) + + // Connect the start tangent point to the end tangent point by arc + // and adding the end tangent point to the subpath. + this.arc(x, y, radius, startAngle, endAngle) + } + + /** + * Sets the stroke property on the current element + */ + ctx.prototype.stroke = function () { + if (this.__currentElement.nodeName === 'path') { + this.__currentElement.setAttribute( + 'paint-order', + 'fill stroke markers' + ) + } + this.__applyCurrentDefaultPath() + this.__applyStyleToCurrentElement('stroke') + } + + /** + * Sets fill properties on the current element + */ + ctx.prototype.fill = function () { + if (this.__currentElement.nodeName === 'path') { + this.__currentElement.setAttribute( + 'paint-order', + 'stroke fill markers' + ) + } + this.__applyCurrentDefaultPath() + this.__applyStyleToCurrentElement('fill') + } + + /** + * Adds a rectangle to the path. + */ + ctx.prototype.rect = function (x, y, width, height) { + if (this.__currentElement.nodeName !== 'path') { + this.beginPath() + } + this.moveTo(x, y) + this.lineTo(x + width, y) + this.lineTo(x + width, y + height) + this.lineTo(x, y + height) + this.lineTo(x, y) + this.closePath() + } + + /** + * adds a rectangle element + */ + ctx.prototype.fillRect = function (x, y, width, height) { + var rect, parent + rect = this.__createElement( + 'rect', + { + x: x, + y: y, + width: width, + height: height, + }, + true + ) + parent = this.__closestGroupOrSvg() + parent.appendChild(rect) + this.__currentElement = rect + this.__applyStyleToCurrentElement('fill') + } + + /** + * Draws a rectangle with no fill + * @param x + * @param y + * @param width + * @param height + */ + ctx.prototype.strokeRect = function (x, y, width, height) { + var rect, parent + rect = this.__createElement( + 'rect', + { + x: x, + y: y, + width: width, + height: height, + }, + true + ) + parent = this.__closestGroupOrSvg() + parent.appendChild(rect) + this.__currentElement = rect + this.__applyStyleToCurrentElement('stroke') + } + + /** + * Clear entire canvas: + * 1. save current transforms + * 2. remove all the childNodes of the root g element + */ + ctx.prototype.__clearCanvas = function () { + var current = this.__closestGroupOrSvg(), + transform = current.getAttribute('transform') + var rootGroup = this.__root.childNodes[1] + var childNodes = rootGroup.childNodes + for (var i = childNodes.length - 1; i >= 0; i--) { + if (childNodes[i]) { + rootGroup.removeChild(childNodes[i]) + } + } + this.__currentElement = rootGroup + //reset __groupStack as all the child group nodes are all removed. + this.__groupStack = [] + if (transform) { + this.__addTransform(transform) + } + } + + /** + * "Clears" a canvas by just drawing a white rectangle in the current group. + */ + ctx.prototype.clearRect = function (x, y, width, height) { + //clear entire canvas + if ( + x === 0 && + y === 0 && + width === this.width && + height === this.height + ) { + this.__clearCanvas() + return + } + var rect, + parent = this.__closestGroupOrSvg() + rect = this.__createElement( + 'rect', + { + x: x, + y: y, + width: width, + height: height, + fill: '#FFFFFF', + }, + true + ) + parent.appendChild(rect) + } + + /** + * Adds a linear gradient to a defs tag. + * Returns a canvas gradient object that has a reference to it's parent def + */ + ctx.prototype.createLinearGradient = function (x1, y1, x2, y2) { + var grad = this.__createElement( + 'linearGradient', + { + id: randomString(this.__ids), + x1: x1 + 'px', + x2: x2 + 'px', + y1: y1 + 'px', + y2: y2 + 'px', + gradientUnits: 'userSpaceOnUse', + }, + false + ) + this.__defs.appendChild(grad) + return new CanvasGradient(grad, this) + } + + /** + * Adds a radial gradient to a defs tag. + * Returns a canvas gradient object that has a reference to it's parent def + */ + ctx.prototype.createRadialGradient = function (x0, y0, r0, x1, y1, r1) { + var grad = this.__createElement( + 'radialGradient', + { + id: randomString(this.__ids), + cx: x1 + 'px', + cy: y1 + 'px', + r: r1 + 'px', + fx: x0 + 'px', + fy: y0 + 'px', + gradientUnits: 'userSpaceOnUse', + }, + false + ) + this.__defs.appendChild(grad) + return new CanvasGradient(grad, this) + } + + /** + * Parses the font string and returns svg mapping + * @private + */ + ctx.prototype.__parseFont = function () { + var regex = + /^\s*(?=(?:(?:[-a-z]+\s*){0,2}(italic|oblique))?)(?=(?:(?:[-a-z]+\s*){0,2}(small-caps))?)(?=(?:(?:[-a-z]+\s*){0,2}(bold(?:er)?|lighter|[1-9]00))?)(?:(?:normal|\1|\2|\3)\s*){0,3}((?:xx?-)?(?:small|large)|medium|smaller|larger|[.\d]+(?:\%|in|[cem]m|ex|p[ctx]))(?:\s*\/\s*(normal|[.\d]+(?:\%|in|[cem]m|ex|p[ctx])))?\s*([-,\'\"\sa-z0-9]+?)\s*$/i + var fontPart = regex.exec(this.font) + var data = { + style: fontPart[1] || 'normal', + size: fontPart[4] || '10px', + family: fontPart[6] || 'sans-serif', + weight: fontPart[3] || 'normal', + decoration: fontPart[2] || 'normal', + href: null, + } + + //canvas doesn't support underline natively, but we can pass this attribute + if (this.__fontUnderline === 'underline') { + data.decoration = 'underline' + } + + //canvas also doesn't support linking, but we can pass this as well + if (this.__fontHref) { + data.href = this.__fontHref + } + + return data + } + + /** + * Helper to link text fragments + * @param font + * @param element + * @return {*} + * @private + */ + ctx.prototype.__wrapTextLink = function (font, element) { + if (font.href) { + var a = this.__createElement('a') + a.setAttributeNS( + 'http://www.w3.org/1999/xlink', + 'xlink:href', + font.href + ) + a.appendChild(element) + return a + } + return element + } + + /** + * Fills or strokes text + * @param text + * @param x + * @param y + * @param action - stroke or fill + * @private + */ + ctx.prototype.__applyText = function (text, x, y, action) { + var font = this.__parseFont(), + parent = this.__closestGroupOrSvg(), + textElement = this.__createElement( + 'text', + { + 'font-family': font.family, + 'font-size': font.size, + 'font-style': font.style, + 'font-weight': font.weight, + 'text-decoration': font.decoration, + x: x, + y: y, + 'text-anchor': getTextAnchor(this.textAlign), + 'dominant-baseline': getDominantBaseline(this.textBaseline), + }, + true + ) + + textElement.appendChild(this.__document.createTextNode(text)) + this.__currentElement = textElement + this.__applyStyleToCurrentElement(action) + parent.appendChild(this.__wrapTextLink(font, textElement)) + } + + /** + * Creates a text element + * @param text + * @param x + * @param y + */ + ctx.prototype.fillText = function (text, x, y) { + this.__applyText(text, x, y, 'fill') + } + + /** + * Strokes text + * @param text + * @param x + * @param y + */ + ctx.prototype.strokeText = function (text, x, y) { + this.__applyText(text, x, y, 'stroke') + } + + /** + * No need to implement this for svg. + * @param text + * @return {TextMetrics} + */ + ctx.prototype.measureText = function (text) { + this.__ctx.font = this.font + return this.__ctx.measureText(text) + } + + /** + * Arc command! + */ + ctx.prototype.arc = function ( + x, + y, + radius, + startAngle, + endAngle, + counterClockwise + ) { + // in canvas no circle is drawn if no angle is provided. + if (startAngle === endAngle) { + return + } + startAngle = startAngle % (2 * Math.PI) + endAngle = endAngle % (2 * Math.PI) + if (startAngle === endAngle) { + //circle time! subtract some of the angle so svg is happy (svg elliptical arc can't draw a full circle) + endAngle = + (endAngle + 2 * Math.PI - 0.001 * (counterClockwise ? -1 : 1)) % + (2 * Math.PI) + } + var endX = x + radius * Math.cos(endAngle), + endY = y + radius * Math.sin(endAngle), + startX = x + radius * Math.cos(startAngle), + startY = y + radius * Math.sin(startAngle), + sweepFlag = counterClockwise ? 0 : 1, + largeArcFlag = 0, + diff = endAngle - startAngle + + // https://github.com/gliffy/canvas2svg/issues/4 + if (diff < 0) { + diff += 2 * Math.PI + } + + if (counterClockwise) { + largeArcFlag = diff > Math.PI ? 0 : 1 + } else { + largeArcFlag = diff > Math.PI ? 1 : 0 + } + + this.lineTo(startX, startY) + this.__addPathCommand( + format( + 'A {rx} {ry} {xAxisRotation} {largeArcFlag} {sweepFlag} {endX} {endY}', + { + rx: radius, + ry: radius, + xAxisRotation: 0, + largeArcFlag: largeArcFlag, + sweepFlag: sweepFlag, + endX: endX, + endY: endY, + } + ) + ) + + this.__currentPosition = { x: endX, y: endY } + } + + /** + * Generates a ClipPath from the clip command. + */ + ctx.prototype.clip = function () { + var group = this.__closestGroupOrSvg(), + clipPath = this.__createElement('clipPath'), + id = randomString(this.__ids), + newGroup = this.__createElement('g') + + this.__applyCurrentDefaultPath() + group.removeChild(this.__currentElement) + clipPath.setAttribute('id', id) + clipPath.appendChild(this.__currentElement) + + this.__defs.appendChild(clipPath) + + //set the clip path to this group + group.setAttribute('clip-path', format('url(#{id})', { id: id })) + + //clip paths can be scaled and transformed, we need to add another wrapper group to avoid later transformations + // to this path + group.appendChild(newGroup) + + this.__currentElement = newGroup + } + + /** + * Draws a canvas, image or mock context to this canvas. + * Note that all svg dom manipulation uses node.childNodes rather than node.children for IE support. + * http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-drawimage + */ + ctx.prototype.drawImage = function () { + //convert arguments to a real array + var args = Array.prototype.slice.call(arguments), + image = args[0], + dx, + dy, + dw, + dh, + sx = 0, + sy = 0, + sw, + sh, + parent, + svg, + defs, + group, + currentElement, + svgImage, + canvas, + context, + id + + if (args.length === 3) { + dx = args[1] + dy = args[2] + sw = image.width + sh = image.height + dw = sw + dh = sh + } else if (args.length === 5) { + dx = args[1] + dy = args[2] + dw = args[3] + dh = args[4] + sw = image.width + sh = image.height + } else if (args.length === 9) { + sx = args[1] + sy = args[2] + sw = args[3] + sh = args[4] + dx = args[5] + dy = args[6] + dw = args[7] + dh = args[8] + } else { + throw new Error( + 'Invalid number of arguments passed to drawImage: ' + + arguments.length + ) + } + + parent = this.__closestGroupOrSvg() + currentElement = this.__currentElement + var translateDirective = 'translate(' + dx + ', ' + dy + ')' + if (image instanceof ctx) { + //canvas2svg mock canvas context. In the future we may want to clone nodes instead. + //also I'm currently ignoring dw, dh, sw, sh, sx, sy for a mock context. + svg = image.getSvg().cloneNode(true) + if (svg.childNodes && svg.childNodes.length > 1) { + defs = svg.childNodes[0] + while (defs.childNodes.length) { + id = defs.childNodes[0].getAttribute('id') + this.__ids[id] = id + this.__defs.appendChild(defs.childNodes[0]) + } + group = svg.childNodes[1] + if (group) { + //save original transform + var originTransform = group.getAttribute('transform') + var transformDirective + if (originTransform) { + transformDirective = + originTransform + ' ' + translateDirective + } else { + transformDirective = translateDirective + } + group.setAttribute('transform', transformDirective) + parent.appendChild(group) + } + } + } else if (image.nodeName === 'CANVAS' || image.nodeName === 'IMG') { + //canvas or image + svgImage = this.__createElement('image') + svgImage.setAttribute('width', dw) + svgImage.setAttribute('height', dh) + svgImage.setAttribute('preserveAspectRatio', 'none') + + if (sx || sy || sw !== image.width || sh !== image.height) { + //crop the image using a temporary canvas + canvas = this.__document.createElement('canvas') + canvas.width = dw + canvas.height = dh + context = canvas.getContext('2d') + context.drawImage(image, sx, sy, sw, sh, 0, 0, dw, dh) + image = canvas + } + svgImage.setAttribute('transform', translateDirective) + svgImage.setAttributeNS( + 'http://www.w3.org/1999/xlink', + 'xlink:href', + image.nodeName === 'CANVAS' + ? image.toDataURL() + : image.getAttribute('src') + ) + parent.appendChild(svgImage) + } + } + + /** + * Generates a pattern tag + */ + ctx.prototype.createPattern = function (image, repetition) { + var pattern = this.__document.createElementNS( + 'http://www.w3.org/2000/svg', + 'pattern' + ), + id = randomString(this.__ids), + img + pattern.setAttribute('id', id) + pattern.setAttribute('width', image.width) + pattern.setAttribute('height', image.height) + if (image.nodeName === 'CANVAS' || image.nodeName === 'IMG') { + img = this.__document.createElementNS( + 'http://www.w3.org/2000/svg', + 'image' + ) + img.setAttribute('width', image.width) + img.setAttribute('height', image.height) + img.setAttributeNS( + 'http://www.w3.org/1999/xlink', + 'xlink:href', + image.nodeName === 'CANVAS' + ? image.toDataURL() + : image.getAttribute('src') + ) + pattern.appendChild(img) + this.__defs.appendChild(pattern) + } else if (image instanceof ctx) { + pattern.appendChild(image.__root.childNodes[1]) + this.__defs.appendChild(pattern) + } + return new CanvasPattern(pattern, this) + } + + ctx.prototype.setLineDash = function (dashArray) { + if (dashArray && dashArray.length > 0) { + this.lineDash = dashArray.join(',') + } else { + this.lineDash = null + } + } + + /** + * Not yet implemented + */ + ctx.prototype.drawFocusRing = function () {} + ctx.prototype.createImageData = function () {} + ctx.prototype.getImageData = function () {} + ctx.prototype.putImageData = function () {} + ctx.prototype.globalCompositeOperation = function () {} + ctx.prototype.setTransform = function () {} + + //add options for alternative namespace + if (typeof window === 'object') { + window.C2S = ctx + } + + // CommonJS/Browserify + if (typeof module === 'object' && typeof module.exports === 'object') { + module.exports = ctx + } +})() diff --git a/v1/src/simulator/vendor/images/ui-icons_444444_256x240.png b/v1/src/simulator/vendor/images/ui-icons_444444_256x240.png new file mode 100644 index 00000000..8cc6f5db Binary files /dev/null and b/v1/src/simulator/vendor/images/ui-icons_444444_256x240.png differ diff --git a/v1/src/simulator/vendor/images/ui-icons_555555_256x240.png b/v1/src/simulator/vendor/images/ui-icons_555555_256x240.png new file mode 100644 index 00000000..c852a056 Binary files /dev/null and b/v1/src/simulator/vendor/images/ui-icons_555555_256x240.png differ diff --git a/v1/src/simulator/vendor/images/ui-icons_777620_256x240.png b/v1/src/simulator/vendor/images/ui-icons_777620_256x240.png new file mode 100644 index 00000000..3136fd07 Binary files /dev/null and b/v1/src/simulator/vendor/images/ui-icons_777620_256x240.png differ diff --git a/v1/src/simulator/vendor/images/ui-icons_777777_256x240.png b/v1/src/simulator/vendor/images/ui-icons_777777_256x240.png new file mode 100644 index 00000000..e055acb9 Binary files /dev/null and b/v1/src/simulator/vendor/images/ui-icons_777777_256x240.png differ diff --git a/v1/src/simulator/vendor/images/ui-icons_cc0000_256x240.png b/v1/src/simulator/vendor/images/ui-icons_cc0000_256x240.png new file mode 100644 index 00000000..acb51425 Binary files /dev/null and b/v1/src/simulator/vendor/images/ui-icons_cc0000_256x240.png differ diff --git a/v1/src/simulator/vendor/images/ui-icons_ffffff_256x240.png b/v1/src/simulator/vendor/images/ui-icons_ffffff_256x240.png new file mode 100644 index 00000000..7e4d1d08 Binary files /dev/null and b/v1/src/simulator/vendor/images/ui-icons_ffffff_256x240.png differ diff --git a/v1/src/simulator/vendor/jquery-ui.min.css b/v1/src/simulator/vendor/jquery-ui.min.css new file mode 100644 index 00000000..4c702d0f --- /dev/null +++ b/v1/src/simulator/vendor/jquery-ui.min.css @@ -0,0 +1,1137 @@ +/*! jQuery UI - v1.13.1 - 2022-01-22 +* http://jqueryui.com +* Includes: draggable.css, core.css, resizable.css, sortable.css, accordion.css, button.css, controlgroup.css, checkboxradio.css, dialog.css, theme.css +* To view and modify this theme, visit http://jqueryui.com/themeroller/?scope=&folderName=base&cornerRadiusShadow=8px&offsetLeftShadow=0px&offsetTopShadow=0px&thicknessShadow=5px&opacityShadow=30&bgImgOpacityShadow=0&bgTextureShadow=flat&bgColorShadow=666666&opacityOverlay=30&bgImgOpacityOverlay=0&bgTextureOverlay=flat&bgColorOverlay=aaaaaa&iconColorError=cc0000&fcError=5f3f3f&borderColorError=f1a899&bgTextureError=flat&bgColorError=fddfdf&iconColorHighlight=777620&fcHighlight=777620&borderColorHighlight=dad55e&bgTextureHighlight=flat&bgColorHighlight=fffa90&iconColorActive=ffffff&fcActive=ffffff&borderColorActive=003eff&bgTextureActive=flat&bgColorActive=007fff&iconColorHover=555555&fcHover=2b2b2b&borderColorHover=cccccc&bgTextureHover=flat&bgColorHover=ededed&iconColorDefault=777777&fcDefault=454545&borderColorDefault=c5c5c5&bgTextureDefault=flat&bgColorDefault=f6f6f6&iconColorContent=444444&fcContent=333333&borderColorContent=dddddd&bgTextureContent=flat&bgColorContent=ffffff&iconColorHeader=444444&fcHeader=333333&borderColorHeader=dddddd&bgTextureHeader=flat&bgColorHeader=e9e9e9&cornerRadius=3px&fwDefault=normal&fsDefault=1em&ffDefault=Arial%2CHelvetica%2Csans-serif +* Copyright jQuery Foundation and other contributors; Licensed MIT */ + +.ui-draggable-handle { + -ms-touch-action: none; + touch-action: none; +} +.ui-helper-hidden { + display: none; +} +.ui-helper-hidden-accessible { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} +.ui-helper-reset { + margin: 0; + padding: 0; + border: 0; + outline: 0; + line-height: 1.3; + text-decoration: none; + font-size: 100%; + list-style: none; +} +.ui-helper-clearfix:before, +.ui-helper-clearfix:after { + content: ''; + display: table; + border-collapse: collapse; +} +.ui-helper-clearfix:after { + clear: both; +} +.ui-helper-zfix { + width: 100%; + height: 100%; + top: 0; + left: 0; + position: absolute; + opacity: 0; + -ms-filter: 'alpha(opacity=0)'; +} +.ui-front { + z-index: 100; +} +.ui-state-disabled { + cursor: default !important; + pointer-events: none; +} +.ui-icon { + display: inline-block; + vertical-align: middle; + margin-top: -0.25em; + position: relative; + text-indent: -99999px; + overflow: hidden; + background-repeat: no-repeat; +} +.ui-widget-icon-block { + left: 50%; + margin-left: -8px; + display: block; +} +.ui-widget-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +.ui-resizable { + position: relative; +} +.ui-resizable-handle { + position: absolute; + font-size: 0.1px; + display: block; + -ms-touch-action: none; + touch-action: none; +} +.ui-resizable-disabled .ui-resizable-handle, +.ui-resizable-autohide .ui-resizable-handle { + display: none; +} +.ui-resizable-n { + cursor: n-resize; + height: 7px; + width: 100%; + top: -5px; + left: 0; +} +.ui-resizable-s { + cursor: s-resize; + height: 7px; + width: 100%; + bottom: -5px; + left: 0; +} +.ui-resizable-e { + cursor: e-resize; + width: 7px; + right: -5px; + top: 0; + height: 100%; +} +.ui-resizable-w { + cursor: w-resize; + width: 7px; + left: -5px; + top: 0; + height: 100%; +} +.ui-resizable-se { + cursor: se-resize; + width: 12px; + height: 12px; + right: 1px; + bottom: 1px; +} +.ui-resizable-sw { + cursor: sw-resize; + width: 9px; + height: 9px; + left: -5px; + bottom: -5px; +} +.ui-resizable-nw { + cursor: nw-resize; + width: 9px; + height: 9px; + left: -5px; + top: -5px; +} +.ui-resizable-ne { + cursor: ne-resize; + width: 9px; + height: 9px; + right: -5px; + top: -5px; +} +.ui-sortable-handle { + -ms-touch-action: none; + touch-action: none; +} +.ui-accordion .ui-accordion-header { + display: block; + cursor: pointer; + position: relative; + margin: 2px 0 0 0; + padding: 0.5em 0.5em 0.5em 0.7em; + font-size: 100%; +} +.ui-accordion .ui-accordion-content { + padding: 1em 2.2em; + border-top: 0; + overflow: auto; +} +.ui-button { + padding: 0.4em 1em; + display: inline-block; + position: relative; + line-height: normal; + margin-right: 0.1em; + cursor: pointer; + vertical-align: middle; + text-align: center; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + overflow: visible; +} +.ui-button, +.ui-button:link, +.ui-button:visited, +.ui-button:hover, +.ui-button:active { + text-decoration: none; +} +.ui-button-icon-only { + width: 2em; + box-sizing: border-box; + text-indent: -9999px; + white-space: nowrap; +} +input.ui-button.ui-button-icon-only { + text-indent: 0; +} +.ui-button-icon-only .ui-icon { + position: absolute; + top: 50%; + left: 50%; + margin-top: -8px; + margin-left: -8px; +} +.ui-button.ui-icon-notext .ui-icon { + padding: 0; + width: 2.1em; + height: 2.1em; + text-indent: -9999px; + white-space: nowrap; +} +input.ui-button.ui-icon-notext .ui-icon { + width: auto; + height: auto; + text-indent: 0; + white-space: normal; + padding: 0.4em 1em; +} +input.ui-button::-moz-focus-inner, +button.ui-button::-moz-focus-inner { + border: 0; + padding: 0; +} +.ui-controlgroup { + vertical-align: middle; + display: inline-block; +} +.ui-controlgroup > .ui-controlgroup-item { + float: left; + margin-left: 0; + margin-right: 0; +} +.ui-controlgroup > .ui-controlgroup-item:focus, +.ui-controlgroup > .ui-controlgroup-item.ui-visual-focus { + z-index: 9999; +} +.ui-controlgroup-vertical > .ui-controlgroup-item { + display: block; + float: none; + width: 100%; + margin-top: 0; + margin-bottom: 0; + text-align: left; +} +.ui-controlgroup-vertical .ui-controlgroup-item { + box-sizing: border-box; +} +.ui-controlgroup .ui-controlgroup-label { + padding: 0.4em 1em; +} +.ui-controlgroup .ui-controlgroup-label span { + font-size: 80%; +} +.ui-controlgroup-horizontal .ui-controlgroup-label + .ui-controlgroup-item { + border-left: none; +} +.ui-controlgroup-vertical .ui-controlgroup-label + .ui-controlgroup-item { + border-top: none; +} +.ui-controlgroup-horizontal .ui-controlgroup-label.ui-widget-content { + border-right: none; +} +.ui-controlgroup-vertical .ui-controlgroup-label.ui-widget-content { + border-bottom: none; +} +.ui-controlgroup-vertical .ui-spinner-input { + width: 75%; + width: calc(100% - 2.4em); +} +.ui-controlgroup-vertical .ui-spinner .ui-spinner-up { + border-top-style: solid; +} +.ui-checkboxradio-label .ui-icon-background { + box-shadow: inset 1px 1px 1px #ccc; + border-radius: 0.12em; + border: none; +} +.ui-checkboxradio-radio-label .ui-icon-background { + width: 16px; + height: 16px; + border-radius: 1em; + overflow: visible; + border: none; +} +.ui-checkboxradio-radio-label.ui-checkboxradio-checked .ui-icon, +.ui-checkboxradio-radio-label.ui-checkboxradio-checked:hover .ui-icon { + background-image: none; + width: 8px; + height: 8px; + border-width: 4px; + border-style: solid; +} +.ui-checkboxradio-disabled { + pointer-events: none; +} +.ui-dialog { + position: absolute; + top: 0; + left: 0; + padding: 0.2em; + outline: 0; +} +.ui-dialog .ui-dialog-titlebar { + padding: 0.4em 1em; + position: relative; +} +.ui-dialog .ui-dialog-title { + float: left; + margin: 0.1em 0; + white-space: nowrap; + width: 90%; + overflow: hidden; + text-overflow: ellipsis; +} +.ui-dialog .ui-dialog-titlebar-close { + position: absolute; + right: 0.3em; + top: 50%; + width: 20px; + margin: -10px 0 0 0; + padding: 1px; + height: 20px; +} +.ui-dialog .ui-dialog-content { + position: relative; + border: 0; + padding: 0.5em 1em; + background: none; + overflow: auto; +} +.ui-dialog .ui-dialog-buttonpane { + text-align: left; + border-width: 1px 0 0 0; + background-image: none; + margin-top: 0.5em; + padding: 0.3em 1em 0.5em 0.4em; +} +.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { + float: right; +} +.ui-dialog .ui-dialog-buttonpane button { + margin: 0.5em 0.4em 0.5em 0; + cursor: pointer; +} +.ui-dialog .ui-resizable-n { + height: 2px; + top: 0; +} +.ui-dialog .ui-resizable-e { + width: 2px; + right: 0; +} +.ui-dialog .ui-resizable-s { + height: 2px; + bottom: 0; +} +.ui-dialog .ui-resizable-w { + width: 2px; + left: 0; +} +.ui-dialog .ui-resizable-se, +.ui-dialog .ui-resizable-sw, +.ui-dialog .ui-resizable-ne, +.ui-dialog .ui-resizable-nw { + width: 7px; + height: 7px; +} +.ui-dialog .ui-resizable-se { + right: 0; + bottom: 0; +} +.ui-dialog .ui-resizable-sw { + left: 0; + bottom: 0; +} +.ui-dialog .ui-resizable-ne { + right: 0; + top: 0; +} +.ui-dialog .ui-resizable-nw { + left: 0; + top: 0; +} +.ui-draggable .ui-dialog-titlebar { + cursor: move; +} +.ui-widget { + font-family: Arial, Helvetica, sans-serif; + font-size: 1em; +} +.ui-widget .ui-widget { + font-size: 1em; +} +.ui-widget input, +.ui-widget select, +.ui-widget textarea, +.ui-widget button { + font-family: Arial, Helvetica, sans-serif; + font-size: 1em; +} +.ui-widget.ui-widget-content { + border: 1px solid #c5c5c5; +} +.ui-widget-content { + border: 1px solid #ddd; + background: #fff; + color: #333; +} +.ui-widget-content a { + color: #333; +} +.ui-widget-header { + border: 1px solid #ddd; + background: #e9e9e9; + color: #333; + font-weight: bold; +} +.ui-widget-header a { + color: #333; +} +.ui-state-default, +.ui-widget-content .ui-state-default, +.ui-widget-header .ui-state-default, +.ui-button, +html .ui-button.ui-state-disabled:hover, +html .ui-button.ui-state-disabled:active { + border: 1px solid #c5c5c5; + background: #f6f6f6; + font-weight: normal; + color: #454545; +} +.ui-state-default a, +.ui-state-default a:link, +.ui-state-default a:visited, +a.ui-button, +a:link.ui-button, +a:visited.ui-button, +.ui-button { + color: #454545; + text-decoration: none; +} +.ui-state-hover, +.ui-widget-content .ui-state-hover, +.ui-widget-header .ui-state-hover, +.ui-state-focus, +.ui-widget-content .ui-state-focus, +.ui-widget-header .ui-state-focus, +.ui-button:hover, +.ui-button:focus { + border: 1px solid #ccc; + background: #ededed; + font-weight: normal; + color: #2b2b2b; +} +.ui-state-hover a, +.ui-state-hover a:hover, +.ui-state-hover a:link, +.ui-state-hover a:visited, +.ui-state-focus a, +.ui-state-focus a:hover, +.ui-state-focus a:link, +.ui-state-focus a:visited, +a.ui-button:hover, +a.ui-button:focus { + color: #2b2b2b; + text-decoration: none; +} +.ui-visual-focus { + box-shadow: 0 0 3px 1px rgb(94, 158, 214); +} +.ui-state-active, +.ui-widget-content .ui-state-active, +.ui-widget-header .ui-state-active, +a.ui-button:active, +.ui-button:active, +.ui-button.ui-state-active:hover { + border: 1px solid #003eff; + background: #007fff; + font-weight: normal; + color: #fff; +} +.ui-icon-background, +.ui-state-active .ui-icon-background { + border: #003eff; + background-color: #fff; +} +.ui-state-active a, +.ui-state-active a:link, +.ui-state-active a:visited { + color: #fff; + text-decoration: none; +} +.ui-state-highlight, +.ui-widget-content .ui-state-highlight, +.ui-widget-header .ui-state-highlight { + border: 1px solid #dad55e; + background: #fffa90; + color: #777620; +} +.ui-state-checked { + border: 1px solid #dad55e; + background: #fffa90; +} +.ui-state-highlight a, +.ui-widget-content .ui-state-highlight a, +.ui-widget-header .ui-state-highlight a { + color: #777620; +} +.ui-state-error, +.ui-widget-content .ui-state-error, +.ui-widget-header .ui-state-error { + border: 1px solid #f1a899; + background: #fddfdf; + color: #5f3f3f; +} +.ui-state-error a, +.ui-widget-content .ui-state-error a, +.ui-widget-header .ui-state-error a { + color: #5f3f3f; +} +.ui-state-error-text, +.ui-widget-content .ui-state-error-text, +.ui-widget-header .ui-state-error-text { + color: #5f3f3f; +} +.ui-priority-primary, +.ui-widget-content .ui-priority-primary, +.ui-widget-header .ui-priority-primary { + font-weight: bold; +} +.ui-priority-secondary, +.ui-widget-content .ui-priority-secondary, +.ui-widget-header .ui-priority-secondary { + opacity: 0.7; + -ms-filter: 'alpha(opacity=70)'; + font-weight: normal; +} +.ui-state-disabled, +.ui-widget-content .ui-state-disabled, +.ui-widget-header .ui-state-disabled { + opacity: 0.35; + -ms-filter: 'alpha(opacity=35)'; + background-image: none; +} +.ui-state-disabled .ui-icon { + -ms-filter: 'alpha(opacity=35)'; +} +.ui-icon { + width: 16px; + height: 16px; +} +.ui-icon, +.ui-widget-content .ui-icon { + background-image: url('images/ui-icons_444444_256x240.png'); +} +.ui-widget-header .ui-icon { + background-image: url('images/ui-icons_444444_256x240.png'); +} +.ui-state-hover .ui-icon, +.ui-state-focus .ui-icon, +.ui-button:hover .ui-icon, +.ui-button:focus .ui-icon { + background-image: url('images/ui-icons_555555_256x240.png'); +} +.ui-state-active .ui-icon, +.ui-button:active .ui-icon { + background-image: url('images/ui-icons_ffffff_256x240.png'); +} +.ui-state-highlight .ui-icon, +.ui-button .ui-state-highlight.ui-icon { + background-image: url('images/ui-icons_777620_256x240.png'); +} +.ui-state-error .ui-icon, +.ui-state-error-text .ui-icon { + background-image: url('images/ui-icons_cc0000_256x240.png'); +} +.ui-button .ui-icon { + background-image: url('images/ui-icons_777777_256x240.png'); +} +.ui-icon-blank.ui-icon-blank.ui-icon-blank { + background-image: none; +} +.ui-icon-caret-1-n { + background-position: 0 0; +} +.ui-icon-caret-1-ne { + background-position: -16px 0; +} +.ui-icon-caret-1-e { + background-position: -32px 0; +} +.ui-icon-caret-1-se { + background-position: -48px 0; +} +.ui-icon-caret-1-s { + background-position: -65px 0; +} +.ui-icon-caret-1-sw { + background-position: -80px 0; +} +.ui-icon-caret-1-w { + background-position: -96px 0; +} +.ui-icon-caret-1-nw { + background-position: -112px 0; +} +.ui-icon-caret-2-n-s { + background-position: -128px 0; +} +.ui-icon-caret-2-e-w { + background-position: -144px 0; +} +.ui-icon-triangle-1-n { + background-position: 0 -16px; +} +.ui-icon-triangle-1-ne { + background-position: -16px -16px; +} +.ui-icon-triangle-1-e { + background-position: -32px -16px; +} +.ui-icon-triangle-1-se { + background-position: -48px -16px; +} +.ui-icon-triangle-1-s { + background-position: -65px -16px; +} +.ui-icon-triangle-1-sw { + background-position: -80px -16px; +} +.ui-icon-triangle-1-w { + background-position: -96px -16px; +} +.ui-icon-triangle-1-nw { + background-position: -112px -16px; +} +.ui-icon-triangle-2-n-s { + background-position: -128px -16px; +} +.ui-icon-triangle-2-e-w { + background-position: -144px -16px; +} +.ui-icon-arrow-1-n { + background-position: 0 -32px; +} +.ui-icon-arrow-1-ne { + background-position: -16px -32px; +} +.ui-icon-arrow-1-e { + background-position: -32px -32px; +} +.ui-icon-arrow-1-se { + background-position: -48px -32px; +} +.ui-icon-arrow-1-s { + background-position: -65px -32px; +} +.ui-icon-arrow-1-sw { + background-position: -80px -32px; +} +.ui-icon-arrow-1-w { + background-position: -96px -32px; +} +.ui-icon-arrow-1-nw { + background-position: -112px -32px; +} +.ui-icon-arrow-2-n-s { + background-position: -128px -32px; +} +.ui-icon-arrow-2-ne-sw { + background-position: -144px -32px; +} +.ui-icon-arrow-2-e-w { + background-position: -160px -32px; +} +.ui-icon-arrow-2-se-nw { + background-position: -176px -32px; +} +.ui-icon-arrowstop-1-n { + background-position: -192px -32px; +} +.ui-icon-arrowstop-1-e { + background-position: -208px -32px; +} +.ui-icon-arrowstop-1-s { + background-position: -224px -32px; +} +.ui-icon-arrowstop-1-w { + background-position: -240px -32px; +} +.ui-icon-arrowthick-1-n { + background-position: 1px -48px; +} +.ui-icon-arrowthick-1-ne { + background-position: -16px -48px; +} +.ui-icon-arrowthick-1-e { + background-position: -32px -48px; +} +.ui-icon-arrowthick-1-se { + background-position: -48px -48px; +} +.ui-icon-arrowthick-1-s { + background-position: -64px -48px; +} +.ui-icon-arrowthick-1-sw { + background-position: -80px -48px; +} +.ui-icon-arrowthick-1-w { + background-position: -96px -48px; +} +.ui-icon-arrowthick-1-nw { + background-position: -112px -48px; +} +.ui-icon-arrowthick-2-n-s { + background-position: -128px -48px; +} +.ui-icon-arrowthick-2-ne-sw { + background-position: -144px -48px; +} +.ui-icon-arrowthick-2-e-w { + background-position: -160px -48px; +} +.ui-icon-arrowthick-2-se-nw { + background-position: -176px -48px; +} +.ui-icon-arrowthickstop-1-n { + background-position: -192px -48px; +} +.ui-icon-arrowthickstop-1-e { + background-position: -208px -48px; +} +.ui-icon-arrowthickstop-1-s { + background-position: -224px -48px; +} +.ui-icon-arrowthickstop-1-w { + background-position: -240px -48px; +} +.ui-icon-arrowreturnthick-1-w { + background-position: 0 -64px; +} +.ui-icon-arrowreturnthick-1-n { + background-position: -16px -64px; +} +.ui-icon-arrowreturnthick-1-e { + background-position: -32px -64px; +} +.ui-icon-arrowreturnthick-1-s { + background-position: -48px -64px; +} +.ui-icon-arrowreturn-1-w { + background-position: -64px -64px; +} +.ui-icon-arrowreturn-1-n { + background-position: -80px -64px; +} +.ui-icon-arrowreturn-1-e { + background-position: -96px -64px; +} +.ui-icon-arrowreturn-1-s { + background-position: -112px -64px; +} +.ui-icon-arrowrefresh-1-w { + background-position: -128px -64px; +} +.ui-icon-arrowrefresh-1-n { + background-position: -144px -64px; +} +.ui-icon-arrowrefresh-1-e { + background-position: -160px -64px; +} +.ui-icon-arrowrefresh-1-s { + background-position: -176px -64px; +} +.ui-icon-arrow-4 { + background-position: 0 -80px; +} +.ui-icon-arrow-4-diag { + background-position: -16px -80px; +} +.ui-icon-extlink { + background-position: -32px -80px; +} +.ui-icon-newwin { + background-position: -48px -80px; +} +.ui-icon-refresh { + background-position: -64px -80px; +} +.ui-icon-shuffle { + background-position: -80px -80px; +} +.ui-icon-transfer-e-w { + background-position: -96px -80px; +} +.ui-icon-transferthick-e-w { + background-position: -112px -80px; +} +.ui-icon-folder-collapsed { + background-position: 0 -96px; +} +.ui-icon-folder-open { + background-position: -16px -96px; +} +.ui-icon-document { + background-position: -32px -96px; +} +.ui-icon-document-b { + background-position: -48px -96px; +} +.ui-icon-note { + background-position: -64px -96px; +} +.ui-icon-mail-closed { + background-position: -80px -96px; +} +.ui-icon-mail-open { + background-position: -96px -96px; +} +.ui-icon-suitcase { + background-position: -112px -96px; +} +.ui-icon-comment { + background-position: -128px -96px; +} +.ui-icon-person { + background-position: -144px -96px; +} +.ui-icon-print { + background-position: -160px -96px; +} +.ui-icon-trash { + background-position: -176px -96px; +} +.ui-icon-locked { + background-position: -192px -96px; +} +.ui-icon-unlocked { + background-position: -208px -96px; +} +.ui-icon-bookmark { + background-position: -224px -96px; +} +.ui-icon-tag { + background-position: -240px -96px; +} +.ui-icon-home { + background-position: 0 -112px; +} +.ui-icon-flag { + background-position: -16px -112px; +} +.ui-icon-calendar { + background-position: -32px -112px; +} +.ui-icon-cart { + background-position: -48px -112px; +} +.ui-icon-pencil { + background-position: -64px -112px; +} +.ui-icon-clock { + background-position: -80px -112px; +} +.ui-icon-disk { + background-position: -96px -112px; +} +.ui-icon-calculator { + background-position: -112px -112px; +} +.ui-icon-zoomin { + background-position: -128px -112px; +} +.ui-icon-zoomout { + background-position: -144px -112px; +} +.ui-icon-search { + background-position: -160px -112px; +} +.ui-icon-wrench { + background-position: -176px -112px; +} +.ui-icon-gear { + background-position: -192px -112px; +} +.ui-icon-heart { + background-position: -208px -112px; +} +.ui-icon-star { + background-position: -224px -112px; +} +.ui-icon-link { + background-position: -240px -112px; +} +.ui-icon-cancel { + background-position: 0 -128px; +} +.ui-icon-plus { + background-position: -16px -128px; +} +.ui-icon-plusthick { + background-position: -32px -128px; +} +.ui-icon-minus { + background-position: -48px -128px; +} +.ui-icon-minusthick { + background-position: -64px -128px; +} +.ui-icon-close { + background-position: -80px -128px; +} +.ui-icon-closethick { + background-position: -96px -128px; +} +.ui-icon-key { + background-position: -112px -128px; +} +.ui-icon-lightbulb { + background-position: -128px -128px; +} +.ui-icon-scissors { + background-position: -144px -128px; +} +.ui-icon-clipboard { + background-position: -160px -128px; +} +.ui-icon-copy { + background-position: -176px -128px; +} +.ui-icon-contact { + background-position: -192px -128px; +} +.ui-icon-image { + background-position: -208px -128px; +} +.ui-icon-video { + background-position: -224px -128px; +} +.ui-icon-script { + background-position: -240px -128px; +} +.ui-icon-alert { + background-position: 0 -144px; +} +.ui-icon-info { + background-position: -16px -144px; +} +.ui-icon-notice { + background-position: -32px -144px; +} +.ui-icon-help { + background-position: -48px -144px; +} +.ui-icon-check { + background-position: -64px -144px; +} +.ui-icon-bullet { + background-position: -80px -144px; +} +.ui-icon-radio-on { + background-position: -96px -144px; +} +.ui-icon-radio-off { + background-position: -112px -144px; +} +.ui-icon-pin-w { + background-position: -128px -144px; +} +.ui-icon-pin-s { + background-position: -144px -144px; +} +.ui-icon-play { + background-position: 0 -160px; +} +.ui-icon-pause { + background-position: -16px -160px; +} +.ui-icon-seek-next { + background-position: -32px -160px; +} +.ui-icon-seek-prev { + background-position: -48px -160px; +} +.ui-icon-seek-end { + background-position: -64px -160px; +} +.ui-icon-seek-start { + background-position: -80px -160px; +} +.ui-icon-seek-first { + background-position: -80px -160px; +} +.ui-icon-stop { + background-position: -96px -160px; +} +.ui-icon-eject { + background-position: -112px -160px; +} +.ui-icon-volume-off { + background-position: -128px -160px; +} +.ui-icon-volume-on { + background-position: -144px -160px; +} +.ui-icon-power { + background-position: 0 -176px; +} +.ui-icon-signal-diag { + background-position: -16px -176px; +} +.ui-icon-signal { + background-position: -32px -176px; +} +.ui-icon-battery-0 { + background-position: -48px -176px; +} +.ui-icon-battery-1 { + background-position: -64px -176px; +} +.ui-icon-battery-2 { + background-position: -80px -176px; +} +.ui-icon-battery-3 { + background-position: -96px -176px; +} +.ui-icon-circle-plus { + background-position: 0 -192px; +} +.ui-icon-circle-minus { + background-position: -16px -192px; +} +.ui-icon-circle-close { + background-position: -32px -192px; +} +.ui-icon-circle-triangle-e { + background-position: -48px -192px; +} +.ui-icon-circle-triangle-s { + background-position: -64px -192px; +} +.ui-icon-circle-triangle-w { + background-position: -80px -192px; +} +.ui-icon-circle-triangle-n { + background-position: -96px -192px; +} +.ui-icon-circle-arrow-e { + background-position: -112px -192px; +} +.ui-icon-circle-arrow-s { + background-position: -128px -192px; +} +.ui-icon-circle-arrow-w { + background-position: -144px -192px; +} +.ui-icon-circle-arrow-n { + background-position: -160px -192px; +} +.ui-icon-circle-zoomin { + background-position: -176px -192px; +} +.ui-icon-circle-zoomout { + background-position: -192px -192px; +} +.ui-icon-circle-check { + background-position: -208px -192px; +} +.ui-icon-circlesmall-plus { + background-position: 0 -208px; +} +.ui-icon-circlesmall-minus { + background-position: -16px -208px; +} +.ui-icon-circlesmall-close { + background-position: -32px -208px; +} +.ui-icon-squaresmall-plus { + background-position: -48px -208px; +} +.ui-icon-squaresmall-minus { + background-position: -64px -208px; +} +.ui-icon-squaresmall-close { + background-position: -80px -208px; +} +.ui-icon-grip-dotted-vertical { + background-position: 0 -224px; +} +.ui-icon-grip-dotted-horizontal { + background-position: -16px -224px; +} +.ui-icon-grip-solid-vertical { + background-position: -32px -224px; +} +.ui-icon-grip-solid-horizontal { + background-position: -48px -224px; +} +.ui-icon-gripsmall-diagonal-se { + background-position: -64px -224px; +} +.ui-icon-grip-diagonal-se { + background-position: -80px -224px; +} +.ui-corner-all, +.ui-corner-top, +.ui-corner-left, +.ui-corner-tl { + border-top-left-radius: 3px; +} +.ui-corner-all, +.ui-corner-top, +.ui-corner-right, +.ui-corner-tr { + border-top-right-radius: 3px; +} +.ui-corner-all, +.ui-corner-bottom, +.ui-corner-left, +.ui-corner-bl { + border-bottom-left-radius: 3px; +} +.ui-corner-all, +.ui-corner-bottom, +.ui-corner-right, +.ui-corner-br { + border-bottom-right-radius: 3px; +} +.ui-widget-overlay { + background: #aaa; + opacity: 0.3; + -ms-filter: Alpha(Opacity=30); +} +.ui-widget-shadow { + -webkit-box-shadow: 0 0 5px #666; + box-shadow: 0 0 5px #666; +} diff --git a/v1/src/simulator/vendor/jquery-ui.min.js b/v1/src/simulator/vendor/jquery-ui.min.js new file mode 100644 index 00000000..8453fa2f --- /dev/null +++ b/v1/src/simulator/vendor/jquery-ui.min.js @@ -0,0 +1,7887 @@ +/*! jQuery UI - v1.13.1 - 2022-01-22 + * http://jqueryui.com + * Includes: widget.js, position.js, data.js, disable-selection.js, focusable.js, form-reset-mixin.js, jquery-patch.js, keycode.js, labels.js, scroll-parent.js, tabbable.js, unique-id.js, widgets/draggable.js, widgets/resizable.js, widgets/sortable.js, widgets/accordion.js, widgets/button.js, widgets/checkboxradio.js, widgets/controlgroup.js, widgets/dialog.js, widgets/mouse.js, effect.js, effects/effect-blind.js, effects/effect-bounce.js, effects/effect-clip.js, effects/effect-drop.js, effects/effect-explode.js, effects/effect-fade.js, effects/effect-fold.js, effects/effect-highlight.js, effects/effect-puff.js, effects/effect-pulsate.js, effects/effect-scale.js, effects/effect-shake.js, effects/effect-size.js, effects/effect-slide.js, effects/effect-transfer.js + * Copyright jQuery Foundation and other contributors; Licensed MIT */ + +!(function (t) { + 'use strict' + 'function' == typeof define && define.amd + ? define(['jquery'], t) + : t(jQuery) +})(function (x) { + 'use strict' + x.ui = x.ui || {} + x.ui.version = '1.13.1' + var o, + i = 0, + r = Array.prototype.hasOwnProperty, + a = Array.prototype.slice + ;(x.cleanData = + ((o = x.cleanData), + function (t) { + for (var e, i, s = 0; null != (i = t[s]); s++) + (e = x._data(i, 'events')) && + e.remove && + x(i).triggerHandler('remove') + o(t) + })), + (x.widget = function (t, i, e) { + var s, + o, + n, + r = {}, + a = t.split('.')[0], + h = a + '-' + (t = t.split('.')[1]) + return ( + e || ((e = i), (i = x.Widget)), + Array.isArray(e) && (e = x.extend.apply(null, [{}].concat(e))), + (x.expr.pseudos[h.toLowerCase()] = function (t) { + return !!x.data(t, h) + }), + (x[a] = x[a] || {}), + (s = x[a][t]), + (o = x[a][t] = + function (t, e) { + if (!this || !this._createWidget) return new o(t, e) + arguments.length && this._createWidget(t, e) + }), + x.extend(o, s, { + version: e.version, + _proto: x.extend({}, e), + _childConstructors: [], + }), + ((n = new i()).options = x.widget.extend({}, n.options)), + x.each(e, function (e, s) { + function o() { + return i.prototype[e].apply(this, arguments) + } + function n(t) { + return i.prototype[e].apply(this, t) + } + r[e] = + 'function' == typeof s + ? function () { + var t, + e = this._super, + i = this._superApply + return ( + (this._super = o), + (this._superApply = n), + (t = s.apply(this, arguments)), + (this._super = e), + (this._superApply = i), + t + ) + } + : s + }), + (o.prototype = x.widget.extend( + n, + { widgetEventPrefix: (s && n.widgetEventPrefix) || t }, + r, + { + constructor: o, + namespace: a, + widgetName: t, + widgetFullName: h, + } + )), + s + ? (x.each(s._childConstructors, function (t, e) { + var i = e.prototype + x.widget( + i.namespace + '.' + i.widgetName, + o, + e._proto + ) + }), + delete s._childConstructors) + : i._childConstructors.push(o), + x.widget.bridge(t, o), + o + ) + }), + (x.widget.extend = function (t) { + for ( + var e, i, s = a.call(arguments, 1), o = 0, n = s.length; + o < n; + o++ + ) + for (e in s[o]) + (i = s[o][e]), + r.call(s[o], e) && + void 0 !== i && + (x.isPlainObject(i) + ? (t[e] = x.isPlainObject(t[e]) + ? x.widget.extend({}, t[e], i) + : x.widget.extend({}, i)) + : (t[e] = i)) + return t + }), + (x.widget.bridge = function (n, e) { + var r = e.prototype.widgetFullName || n + x.fn[n] = function (i) { + var t = 'string' == typeof i, + s = a.call(arguments, 1), + o = this + return ( + t + ? this.length || 'instance' !== i + ? this.each(function () { + var t, + e = x.data(this, r) + return 'instance' === i + ? ((o = e), !1) + : e + ? 'function' != typeof e[i] || + '_' === i.charAt(0) + ? x.error( + "no such method '" + + i + + "' for " + + n + + ' widget instance' + ) + : (t = e[i].apply(e, s)) !== e && + void 0 !== t + ? ((o = + t && t.jquery + ? o.pushStack(t.get()) + : t), + !1) + : void 0 + : x.error( + 'cannot call methods on ' + + n + + " prior to initialization; attempted to call method '" + + i + + "'" + ) + }) + : (o = void 0) + : (s.length && + (i = x.widget.extend.apply(null, [i].concat(s))), + this.each(function () { + var t = x.data(this, r) + t + ? (t.option(i || {}), t._init && t._init()) + : x.data(this, r, new e(i, this)) + })), + o + ) + } + }), + (x.Widget = function () {}), + (x.Widget._childConstructors = []), + (x.Widget.prototype = { + widgetName: 'widget', + widgetEventPrefix: '', + defaultElement: '
', + options: { classes: {}, disabled: !1, create: null }, + _createWidget: function (t, e) { + ;(e = x(e || this.defaultElement || this)[0]), + (this.element = x(e)), + (this.uuid = i++), + (this.eventNamespace = '.' + this.widgetName + this.uuid), + (this.bindings = x()), + (this.hoverable = x()), + (this.focusable = x()), + (this.classesElementLookup = {}), + e !== this && + (x.data(e, this.widgetFullName, this), + this._on(!0, this.element, { + remove: function (t) { + t.target === e && this.destroy() + }, + }), + (this.document = x( + e.style ? e.ownerDocument : e.document || e + )), + (this.window = x( + this.document[0].defaultView || + this.document[0].parentWindow + ))), + (this.options = x.widget.extend( + {}, + this.options, + this._getCreateOptions(), + t + )), + this._create(), + this.options.disabled && + this._setOptionDisabled(this.options.disabled), + this._trigger('create', null, this._getCreateEventData()), + this._init() + }, + _getCreateOptions: function () { + return {} + }, + _getCreateEventData: x.noop, + _create: x.noop, + _init: x.noop, + destroy: function () { + var i = this + this._destroy(), + x.each(this.classesElementLookup, function (t, e) { + i._removeClass(e, t) + }), + this.element + .off(this.eventNamespace) + .removeData(this.widgetFullName), + this.widget() + .off(this.eventNamespace) + .removeAttr('aria-disabled'), + this.bindings.off(this.eventNamespace) + }, + _destroy: x.noop, + widget: function () { + return this.element + }, + option: function (t, e) { + var i, + s, + o, + n = t + if (0 === arguments.length) + return x.widget.extend({}, this.options) + if ('string' == typeof t) + if ( + ((n = {}), (t = (i = t.split('.')).shift()), i.length) + ) { + for ( + s = n[t] = x.widget.extend({}, this.options[t]), + o = 0; + o < i.length - 1; + o++ + ) + (s[i[o]] = s[i[o]] || {}), (s = s[i[o]]) + if (((t = i.pop()), 1 === arguments.length)) + return void 0 === s[t] ? null : s[t] + s[t] = e + } else { + if (1 === arguments.length) + return void 0 === this.options[t] + ? null + : this.options[t] + n[t] = e + } + return this._setOptions(n), this + }, + _setOptions: function (t) { + for (var e in t) this._setOption(e, t[e]) + return this + }, + _setOption: function (t, e) { + return ( + 'classes' === t && this._setOptionClasses(e), + (this.options[t] = e), + 'disabled' === t && this._setOptionDisabled(e), + this + ) + }, + _setOptionClasses: function (t) { + var e, i, s + for (e in t) + (s = this.classesElementLookup[e]), + t[e] !== this.options.classes[e] && + s && + s.length && + ((i = x(s.get())), + this._removeClass(s, e), + i.addClass( + this._classes({ + element: i, + keys: e, + classes: t, + add: !0, + }) + )) + }, + _setOptionDisabled: function (t) { + this._toggleClass( + this.widget(), + this.widgetFullName + '-disabled', + null, + !!t + ), + t && + (this._removeClass( + this.hoverable, + null, + 'ui-state-hover' + ), + this._removeClass( + this.focusable, + null, + 'ui-state-focus' + )) + }, + enable: function () { + return this._setOptions({ disabled: !1 }) + }, + disable: function () { + return this._setOptions({ disabled: !0 }) + }, + _classes: function (o) { + var n = [], + r = this + function t(t, e) { + for (var i, s = 0; s < t.length; s++) + (i = r.classesElementLookup[t[s]] || x()), + (i = o.add + ? ((function () { + var i = [] + o.element.each(function (t, e) { + x + .map( + r.classesElementLookup, + function (t) { + return t + } + ) + .some(function (t) { + return t.is(e) + }) || i.push(e) + }), + r._on(x(i), { + remove: '_untrackClassesElement', + }) + })(), + x( + x.uniqueSort( + i.get().concat(o.element.get()) + ) + )) + : x(i.not(o.element).get())), + (r.classesElementLookup[t[s]] = i), + n.push(t[s]), + e && o.classes[t[s]] && n.push(o.classes[t[s]]) + } + return ( + (o = x.extend( + { + element: this.element, + classes: this.options.classes || {}, + }, + o + )).keys && t(o.keys.match(/\S+/g) || [], !0), + o.extra && t(o.extra.match(/\S+/g) || []), + n.join(' ') + ) + }, + _untrackClassesElement: function (i) { + var s = this + x.each(s.classesElementLookup, function (t, e) { + ;-1 !== x.inArray(i.target, e) && + (s.classesElementLookup[t] = x(e.not(i.target).get())) + }), + this._off(x(i.target)) + }, + _removeClass: function (t, e, i) { + return this._toggleClass(t, e, i, !1) + }, + _addClass: function (t, e, i) { + return this._toggleClass(t, e, i, !0) + }, + _toggleClass: function (t, e, i, s) { + var o = 'string' == typeof t || null === t, + i = { + extra: o ? e : i, + keys: o ? t : e, + element: o ? this.element : t, + add: (s = 'boolean' == typeof s ? s : i), + } + return i.element.toggleClass(this._classes(i), s), this + }, + _on: function (o, n, t) { + var r, + a = this + 'boolean' != typeof o && ((t = n), (n = o), (o = !1)), + t + ? ((n = r = x(n)), + (this.bindings = this.bindings.add(n))) + : ((t = n), (n = this.element), (r = this.widget())), + x.each(t, function (t, e) { + function i() { + if ( + o || + (!0 !== a.options.disabled && + !x(this).hasClass('ui-state-disabled')) + ) + return ('string' == typeof e ? a[e] : e).apply( + a, + arguments + ) + } + 'string' != typeof e && + (i.guid = e.guid = e.guid || i.guid || x.guid++) + var s = t.match(/^([\w:-]*)\s*(.*)$/), + t = s[1] + a.eventNamespace, + s = s[2] + s ? r.on(t, s, i) : n.on(t, i) + }) + }, + _off: function (t, e) { + ;(e = + (e || '').split(' ').join(this.eventNamespace + ' ') + + this.eventNamespace), + t.off(e), + (this.bindings = x(this.bindings.not(t).get())), + (this.focusable = x(this.focusable.not(t).get())), + (this.hoverable = x(this.hoverable.not(t).get())) + }, + _delay: function (t, e) { + var i = this + return setTimeout(function () { + return ('string' == typeof t ? i[t] : t).apply(i, arguments) + }, e || 0) + }, + _hoverable: function (t) { + ;(this.hoverable = this.hoverable.add(t)), + this._on(t, { + mouseenter: function (t) { + this._addClass( + x(t.currentTarget), + null, + 'ui-state-hover' + ) + }, + mouseleave: function (t) { + this._removeClass( + x(t.currentTarget), + null, + 'ui-state-hover' + ) + }, + }) + }, + _focusable: function (t) { + ;(this.focusable = this.focusable.add(t)), + this._on(t, { + focusin: function (t) { + this._addClass( + x(t.currentTarget), + null, + 'ui-state-focus' + ) + }, + focusout: function (t) { + this._removeClass( + x(t.currentTarget), + null, + 'ui-state-focus' + ) + }, + }) + }, + _trigger: function (t, e, i) { + var s, + o, + n = this.options[t] + if ( + ((i = i || {}), + ((e = x.Event(e)).type = ( + t === this.widgetEventPrefix + ? t + : this.widgetEventPrefix + t + ).toLowerCase()), + (e.target = this.element[0]), + (o = e.originalEvent)) + ) + for (s in o) s in e || (e[s] = o[s]) + return ( + this.element.trigger(e, i), + !( + ('function' == typeof n && + !1 === n.apply(this.element[0], [e].concat(i))) || + e.isDefaultPrevented() + ) + ) + }, + }), + x.each({ show: 'fadeIn', hide: 'fadeOut' }, function (n, r) { + x.Widget.prototype['_' + n] = function (e, t, i) { + var s, + o = (t = 'string' == typeof t ? { effect: t } : t) + ? (!0 !== t && 'number' != typeof t && t.effect) || r + : n + 'number' == typeof (t = t || {}) + ? (t = { duration: t }) + : !0 === t && (t = {}), + (s = !x.isEmptyObject(t)), + (t.complete = i), + t.delay && e.delay(t.delay), + s && x.effects && x.effects.effect[o] + ? e[n](t) + : o !== n && e[o] + ? e[o](t.duration, t.easing, i) + : e.queue(function (t) { + x(this)[n](), i && i.call(e[0]), t() + }) + } + }) + var s, P, C, n, h, l, c, p, z + x.widget + function T(t, e, i) { + return [ + parseFloat(t[0]) * (p.test(t[0]) ? e / 100 : 1), + parseFloat(t[1]) * (p.test(t[1]) ? i / 100 : 1), + ] + } + function k(t, e) { + return parseInt(x.css(t, e), 10) || 0 + } + function H(t) { + return null != t && t === t.window + } + ;(P = Math.max), + (C = Math.abs), + (n = /left|center|right/), + (h = /top|center|bottom/), + (l = /[\+\-]\d+(\.[\d]+)?%?/), + (c = /^\w+/), + (p = /%$/), + (z = x.fn.position), + (x.position = { + scrollbarWidth: function () { + if (void 0 !== s) return s + var t, + e = x( + "
" + ), + i = e.children()[0] + return ( + x('body').append(e), + (t = i.offsetWidth), + e.css('overflow', 'scroll'), + t === (i = i.offsetWidth) && (i = e[0].clientWidth), + e.remove(), + (s = t - i) + ) + }, + getScrollInfo: function (t) { + var e = + t.isWindow || t.isDocument + ? '' + : t.element.css('overflow-x'), + i = + t.isWindow || t.isDocument + ? '' + : t.element.css('overflow-y'), + e = + 'scroll' === e || + ('auto' === e && t.width < t.element[0].scrollWidth) + return { + width: + 'scroll' === i || + ('auto' === i && t.height < t.element[0].scrollHeight) + ? x.position.scrollbarWidth() + : 0, + height: e ? x.position.scrollbarWidth() : 0, + } + }, + getWithinInfo: function (t) { + var e = x(t || window), + i = H(e[0]), + s = !!e[0] && 9 === e[0].nodeType + return { + element: e, + isWindow: i, + isDocument: s, + offset: !i && !s ? x(t).offset() : { left: 0, top: 0 }, + scrollLeft: e.scrollLeft(), + scrollTop: e.scrollTop(), + width: e.outerWidth(), + height: e.outerHeight(), + } + }, + }), + (x.fn.position = function (p) { + if (!p || !p.of) return z.apply(this, arguments) + var u, + d, + f, + g, + m, + t, + v = + 'string' == typeof (p = x.extend({}, p)).of + ? x(document).find(p.of) + : x(p.of), + _ = x.position.getWithinInfo(p.within), + b = x.position.getScrollInfo(_), + y = (p.collision || 'flip').split(' '), + w = {}, + e = + 9 === (t = (e = v)[0]).nodeType + ? { + width: e.width(), + height: e.height(), + offset: { top: 0, left: 0 }, + } + : H(t) + ? { + width: e.width(), + height: e.height(), + offset: { + top: e.scrollTop(), + left: e.scrollLeft(), + }, + } + : t.preventDefault + ? { + width: 0, + height: 0, + offset: { top: t.pageY, left: t.pageX }, + } + : { + width: e.outerWidth(), + height: e.outerHeight(), + offset: e.offset(), + } + return ( + v[0].preventDefault && (p.at = 'left top'), + (d = e.width), + (f = e.height), + (m = x.extend({}, (g = e.offset))), + x.each(['my', 'at'], function () { + var t, + e, + i = (p[this] || '').split(' ') + ;((i = + 1 === i.length + ? n.test(i[0]) + ? i.concat(['center']) + : h.test(i[0]) + ? ['center'].concat(i) + : ['center', 'center'] + : i)[0] = n.test(i[0]) ? i[0] : 'center'), + (i[1] = h.test(i[1]) ? i[1] : 'center'), + (t = l.exec(i[0])), + (e = l.exec(i[1])), + (w[this] = [t ? t[0] : 0, e ? e[0] : 0]), + (p[this] = [c.exec(i[0])[0], c.exec(i[1])[0]]) + }), + 1 === y.length && (y[1] = y[0]), + 'right' === p.at[0] + ? (m.left += d) + : 'center' === p.at[0] && (m.left += d / 2), + 'bottom' === p.at[1] + ? (m.top += f) + : 'center' === p.at[1] && (m.top += f / 2), + (u = T(w.at, d, f)), + (m.left += u[0]), + (m.top += u[1]), + this.each(function () { + var i, + t, + r = x(this), + a = r.outerWidth(), + h = r.outerHeight(), + e = k(this, 'marginLeft'), + s = k(this, 'marginTop'), + o = a + e + k(this, 'marginRight') + b.width, + n = h + s + k(this, 'marginBottom') + b.height, + l = x.extend({}, m), + c = T(w.my, r.outerWidth(), r.outerHeight()) + 'right' === p.my[0] + ? (l.left -= a) + : 'center' === p.my[0] && (l.left -= a / 2), + 'bottom' === p.my[1] + ? (l.top -= h) + : 'center' === p.my[1] && (l.top -= h / 2), + (l.left += c[0]), + (l.top += c[1]), + (i = { marginLeft: e, marginTop: s }), + x.each(['left', 'top'], function (t, e) { + x.ui.position[y[t]] && + x.ui.position[y[t]][e](l, { + targetWidth: d, + targetHeight: f, + elemWidth: a, + elemHeight: h, + collisionPosition: i, + collisionWidth: o, + collisionHeight: n, + offset: [u[0] + c[0], u[1] + c[1]], + my: p.my, + at: p.at, + within: _, + elem: r, + }) + }), + p.using && + (t = function (t) { + var e = g.left - l.left, + i = e + d - a, + s = g.top - l.top, + o = s + f - h, + n = { + target: { + element: v, + left: g.left, + top: g.top, + width: d, + height: f, + }, + element: { + element: r, + left: l.left, + top: l.top, + width: a, + height: h, + }, + horizontal: + i < 0 + ? 'left' + : 0 < e + ? 'right' + : 'center', + vertical: + o < 0 + ? 'top' + : 0 < s + ? 'bottom' + : 'middle', + } + d < a && + C(e + i) < d && + (n.horizontal = 'center'), + f < h && + C(s + o) < f && + (n.vertical = 'middle'), + P(C(e), C(i)) > P(C(s), C(o)) + ? (n.important = 'horizontal') + : (n.important = 'vertical'), + p.using.call(this, t, n) + }), + r.offset(x.extend(l, { using: t })) + }) + ) + }), + (x.ui.position = { + fit: { + left: function (t, e) { + var i = e.within, + s = i.isWindow ? i.scrollLeft : i.offset.left, + o = i.width, + n = t.left - e.collisionPosition.marginLeft, + r = s - n, + a = n + e.collisionWidth - o - s + e.collisionWidth > o + ? 0 < r && a <= 0 + ? ((i = t.left + r + e.collisionWidth - o - s), + (t.left += r - i)) + : (t.left = + !(0 < a && r <= 0) && a < r + ? s + o - e.collisionWidth + : s) + : 0 < r + ? (t.left += r) + : 0 < a + ? (t.left -= a) + : (t.left = P(t.left - n, t.left)) + }, + top: function (t, e) { + var i = e.within, + s = i.isWindow ? i.scrollTop : i.offset.top, + o = e.within.height, + n = t.top - e.collisionPosition.marginTop, + r = s - n, + a = n + e.collisionHeight - o - s + e.collisionHeight > o + ? 0 < r && a <= 0 + ? ((i = t.top + r + e.collisionHeight - o - s), + (t.top += r - i)) + : (t.top = + !(0 < a && r <= 0) && a < r + ? s + o - e.collisionHeight + : s) + : 0 < r + ? (t.top += r) + : 0 < a + ? (t.top -= a) + : (t.top = P(t.top - n, t.top)) + }, + }, + flip: { + left: function (t, e) { + var i = e.within, + s = i.offset.left + i.scrollLeft, + o = i.width, + n = i.isWindow ? i.scrollLeft : i.offset.left, + r = t.left - e.collisionPosition.marginLeft, + a = r - n, + h = r + e.collisionWidth - o - n, + l = + 'left' === e.my[0] + ? -e.elemWidth + : 'right' === e.my[0] + ? e.elemWidth + : 0, + i = + 'left' === e.at[0] + ? e.targetWidth + : 'right' === e.at[0] + ? -e.targetWidth + : 0, + r = -2 * e.offset[0] + a < 0 + ? ((s = t.left + l + i + r + e.collisionWidth - o - s) < + 0 || + s < C(a)) && + (t.left += l + i + r) + : 0 < h && + (0 < + (n = + t.left - + e.collisionPosition.marginLeft + + l + + i + + r - + n) || + C(n) < h) && + (t.left += l + i + r) + }, + top: function (t, e) { + var i = e.within, + s = i.offset.top + i.scrollTop, + o = i.height, + n = i.isWindow ? i.scrollTop : i.offset.top, + r = t.top - e.collisionPosition.marginTop, + a = r - n, + h = r + e.collisionHeight - o - n, + l = + 'top' === e.my[1] + ? -e.elemHeight + : 'bottom' === e.my[1] + ? e.elemHeight + : 0, + i = + 'top' === e.at[1] + ? e.targetHeight + : 'bottom' === e.at[1] + ? -e.targetHeight + : 0, + r = -2 * e.offset[1] + a < 0 + ? ((s = t.top + l + i + r + e.collisionHeight - o - s) < + 0 || + s < C(a)) && + (t.top += l + i + r) + : 0 < h && + (0 < + (n = + t.top - + e.collisionPosition.marginTop + + l + + i + + r - + n) || + C(n) < h) && + (t.top += l + i + r) + }, + }, + flipfit: { + left: function () { + x.ui.position.flip.left.apply(this, arguments), + x.ui.position.fit.left.apply(this, arguments) + }, + top: function () { + x.ui.position.flip.top.apply(this, arguments), + x.ui.position.fit.top.apply(this, arguments) + }, + }, + }) + var t + x.ui.position, + x.extend(x.expr.pseudos, { + data: x.expr.createPseudo + ? x.expr.createPseudo(function (e) { + return function (t) { + return !!x.data(t, e) + } + }) + : function (t, e, i) { + return !!x.data(t, i[3]) + }, + }), + x.fn.extend({ + disableSelection: + ((t = + 'onselectstart' in document.createElement('div') + ? 'selectstart' + : 'mousedown'), + function () { + return this.on(t + '.ui-disableSelection', function (t) { + t.preventDefault() + }) + }), + enableSelection: function () { + return this.off('.ui-disableSelection') + }, + }) + ;(x.ui.focusable = function (t, e) { + var i, + s, + o, + n, + r = t.nodeName.toLowerCase() + return 'area' === r + ? ((s = (i = t.parentNode).name), + !(!t.href || !s || 'map' !== i.nodeName.toLowerCase()) && + 0 < (s = x("img[usemap='#" + s + "']")).length && + s.is(':visible')) + : (/^(input|select|textarea|button|object)$/.test(r) + ? (o = !t.disabled) && + (n = x(t).closest('fieldset')[0]) && + (o = !n.disabled) + : (o = ('a' === r && t.href) || e), + o && + x(t).is(':visible') && + (function (t) { + var e = t.css('visibility') + for (; 'inherit' === e; ) + (t = t.parent()), (e = t.css('visibility')) + return 'visible' === e + })(x(t))) + }), + x.extend(x.expr.pseudos, { + focusable: function (t) { + return x.ui.focusable(t, null != x.attr(t, 'tabindex')) + }, + }) + var e, u + x.ui.focusable, + (x.fn._form = function () { + return 'string' == typeof this[0].form + ? this.closest('form') + : x(this[0].form) + }), + (x.ui.formResetMixin = { + _formResetHandler: function () { + var e = x(this) + setTimeout(function () { + var t = e.data('ui-form-reset-instances') + x.each(t, function () { + this.refresh() + }) + }) + }, + _bindFormResetHandler: function () { + var t + ;(this.form = this.element._form()), + this.form.length && + ((t = this.form.data('ui-form-reset-instances') || []) + .length || + this.form.on( + 'reset.ui-form-reset', + this._formResetHandler + ), + t.push(this), + this.form.data('ui-form-reset-instances', t)) + }, + _unbindFormResetHandler: function () { + var t + this.form.length && + ((t = this.form.data('ui-form-reset-instances')).splice( + x.inArray(this, t), + 1 + ), + t.length + ? this.form.data('ui-form-reset-instances', t) + : this.form + .removeData('ui-form-reset-instances') + .off('reset.ui-form-reset')) + }, + }) + x.expr.pseudos || (x.expr.pseudos = x.expr[':']), + x.uniqueSort || (x.uniqueSort = x.unique), + x.escapeSelector || + ((e = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g), + (u = function (t, e) { + return e + ? '\0' === t + ? '�' + : t.slice(0, -1) + + '\\' + + t.charCodeAt(t.length - 1).toString(16) + + ' ' + : '\\' + t + }), + (x.escapeSelector = function (t) { + return (t + '').replace(e, u) + })), + (x.fn.even && x.fn.odd) || + x.fn.extend({ + even: function () { + return this.filter(function (t) { + return t % 2 == 0 + }) + }, + odd: function () { + return this.filter(function (t) { + return t % 2 == 1 + }) + }, + }) + ;(x.ui.keyCode = { + BACKSPACE: 8, + COMMA: 188, + DELETE: 46, + DOWN: 40, + END: 35, + ENTER: 13, + ESCAPE: 27, + HOME: 36, + LEFT: 37, + PAGE_DOWN: 34, + PAGE_UP: 33, + PERIOD: 190, + RIGHT: 39, + SPACE: 32, + TAB: 9, + UP: 38, + }), + (x.fn.labels = function () { + var t, e, i + return this.length + ? this[0].labels && this[0].labels.length + ? this.pushStack(this[0].labels) + : ((e = this.eq(0).parents('label')), + (t = this.attr('id')) && + ((i = (i = this.eq(0).parents().last()).add( + (i.length ? i : this).siblings() + )), + (t = "label[for='" + x.escapeSelector(t) + "']"), + (e = e.add(i.find(t).addBack(t)))), + this.pushStack(e)) + : this.pushStack([]) + }), + (x.fn.scrollParent = function (t) { + var e = this.css('position'), + i = 'absolute' === e, + s = t ? /(auto|scroll|hidden)/ : /(auto|scroll)/, + t = this.parents() + .filter(function () { + var t = x(this) + return ( + (!i || 'static' !== t.css('position')) && + s.test( + t.css('overflow') + + t.css('overflow-y') + + t.css('overflow-x') + ) + ) + }) + .eq(0) + return 'fixed' !== e && t.length + ? t + : x(this[0].ownerDocument || document) + }), + x.extend(x.expr.pseudos, { + tabbable: function (t) { + var e = x.attr(t, 'tabindex'), + i = null != e + return (!i || 0 <= e) && x.ui.focusable(t, i) + }, + }), + x.fn.extend({ + uniqueId: + ((d = 0), + function () { + return this.each(function () { + this.id || (this.id = 'ui-id-' + ++d) + }) + }), + removeUniqueId: function () { + return this.each(function () { + ;/^ui-id-\d+$/.test(this.id) && x(this).removeAttr('id') + }) + }, + }), + (x.ui.ie = !!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase())) + var d, + f = !1 + x(document).on('mouseup', function () { + f = !1 + }) + x.widget('ui.mouse', { + version: '1.13.1', + options: { + cancel: 'input, textarea, button, select, option', + distance: 1, + delay: 0, + }, + _mouseInit: function () { + var e = this + this.element + .on('mousedown.' + this.widgetName, function (t) { + return e._mouseDown(t) + }) + .on('click.' + this.widgetName, function (t) { + if ( + !0 === + x.data(t.target, e.widgetName + '.preventClickEvent') + ) + return ( + x.removeData( + t.target, + e.widgetName + '.preventClickEvent' + ), + t.stopImmediatePropagation(), + !1 + ) + }), + (this.started = !1) + }, + _mouseDestroy: function () { + this.element.off('.' + this.widgetName), + this._mouseMoveDelegate && + this.document + .off( + 'mousemove.' + this.widgetName, + this._mouseMoveDelegate + ) + .off( + 'mouseup.' + this.widgetName, + this._mouseUpDelegate + ) + }, + _mouseDown: function (t) { + if (!f) { + ;(this._mouseMoved = !1), + this._mouseStarted && this._mouseUp(t), + (this._mouseDownEvent = t) + var e = this, + i = 1 === t.which, + s = + !( + 'string' != typeof this.options.cancel || + !t.target.nodeName + ) && x(t.target).closest(this.options.cancel).length + return i && !s && this._mouseCapture(t) + ? ((this.mouseDelayMet = !this.options.delay), + this.mouseDelayMet || + (this._mouseDelayTimer = setTimeout(function () { + e.mouseDelayMet = !0 + }, this.options.delay)), + this._mouseDistanceMet(t) && + this._mouseDelayMet(t) && + ((this._mouseStarted = !1 !== this._mouseStart(t)), + !this._mouseStarted) + ? (t.preventDefault(), !0) + : (!0 === + x.data( + t.target, + this.widgetName + '.preventClickEvent' + ) && + x.removeData( + t.target, + this.widgetName + '.preventClickEvent' + ), + (this._mouseMoveDelegate = function (t) { + return e._mouseMove(t) + }), + (this._mouseUpDelegate = function (t) { + return e._mouseUp(t) + }), + this.document + .on( + 'mousemove.' + this.widgetName, + this._mouseMoveDelegate + ) + .on( + 'mouseup.' + this.widgetName, + this._mouseUpDelegate + ), + t.preventDefault(), + (f = !0))) + : !0 + } + }, + _mouseMove: function (t) { + if (this._mouseMoved) { + if ( + x.ui.ie && + (!document.documentMode || document.documentMode < 9) && + !t.button + ) + return this._mouseUp(t) + if (!t.which) + if ( + t.originalEvent.altKey || + t.originalEvent.ctrlKey || + t.originalEvent.metaKey || + t.originalEvent.shiftKey + ) + this.ignoreMissingWhich = !0 + else if (!this.ignoreMissingWhich) return this._mouseUp(t) + } + return ( + (t.which || t.button) && (this._mouseMoved = !0), + this._mouseStarted + ? (this._mouseDrag(t), t.preventDefault()) + : (this._mouseDistanceMet(t) && + this._mouseDelayMet(t) && + ((this._mouseStarted = + !1 !== this._mouseStart(this._mouseDownEvent, t)), + this._mouseStarted + ? this._mouseDrag(t) + : this._mouseUp(t)), + !this._mouseStarted) + ) + }, + _mouseUp: function (t) { + this.document + .off('mousemove.' + this.widgetName, this._mouseMoveDelegate) + .off('mouseup.' + this.widgetName, this._mouseUpDelegate), + this._mouseStarted && + ((this._mouseStarted = !1), + t.target === this._mouseDownEvent.target && + x.data( + t.target, + this.widgetName + '.preventClickEvent', + !0 + ), + this._mouseStop(t)), + this._mouseDelayTimer && + (clearTimeout(this._mouseDelayTimer), + delete this._mouseDelayTimer), + (this.ignoreMissingWhich = !1), + (f = !1), + t.preventDefault() + }, + _mouseDistanceMet: function (t) { + return ( + Math.max( + Math.abs(this._mouseDownEvent.pageX - t.pageX), + Math.abs(this._mouseDownEvent.pageY - t.pageY) + ) >= this.options.distance + ) + }, + _mouseDelayMet: function () { + return this.mouseDelayMet + }, + _mouseStart: function () {}, + _mouseDrag: function () {}, + _mouseStop: function () {}, + _mouseCapture: function () { + return !0 + }, + }), + (x.ui.plugin = { + add: function (t, e, i) { + var s, + o = x.ui[t].prototype + for (s in i) + (o.plugins[s] = o.plugins[s] || []), + o.plugins[s].push([e, i[s]]) + }, + call: function (t, e, i, s) { + var o, + n = t.plugins[e] + if ( + n && + (s || + (t.element[0].parentNode && + 11 !== t.element[0].parentNode.nodeType)) + ) + for (o = 0; o < n.length; o++) + t.options[n[o][0]] && n[o][1].apply(t.element, i) + }, + }), + (x.ui.safeActiveElement = function (e) { + var i + try { + i = e.activeElement + } catch (t) { + i = e.body + } + return (i = !(i = i || e.body).nodeName ? e.body : i) + }), + (x.ui.safeBlur = function (t) { + t && 'body' !== t.nodeName.toLowerCase() && x(t).trigger('blur') + }) + x.widget('ui.draggable', x.ui.mouse, { + version: '1.13.1', + widgetEventPrefix: 'drag', + options: { + addClasses: !0, + appendTo: 'parent', + axis: !1, + connectToSortable: !1, + containment: !1, + cursor: 'auto', + cursorAt: !1, + grid: !1, + handle: !1, + helper: 'original', + iframeFix: !1, + opacity: !1, + refreshPositions: !1, + revert: !1, + revertDuration: 500, + scope: 'default', + scroll: !0, + scrollSensitivity: 20, + scrollSpeed: 20, + snap: !1, + snapMode: 'both', + snapTolerance: 20, + stack: !1, + zIndex: !1, + drag: null, + start: null, + stop: null, + }, + _create: function () { + 'original' === this.options.helper && this._setPositionRelative(), + this.options.addClasses && this._addClass('ui-draggable'), + this._setHandleClassName(), + this._mouseInit() + }, + _setOption: function (t, e) { + this._super(t, e), + 'handle' === t && + (this._removeHandleClassName(), this._setHandleClassName()) + }, + _destroy: function () { + ;(this.helper || this.element).is('.ui-draggable-dragging') + ? (this.destroyOnClear = !0) + : (this._removeHandleClassName(), this._mouseDestroy()) + }, + _mouseCapture: function (t) { + var e = this.options + return ( + !( + this.helper || + e.disabled || + 0 < x(t.target).closest('.ui-resizable-handle').length + ) && + ((this.handle = this._getHandle(t)), + !!this.handle && + (this._blurActiveElement(t), + this._blockFrames( + !0 === e.iframeFix ? 'iframe' : e.iframeFix + ), + !0)) + ) + }, + _blockFrames: function (t) { + this.iframeBlocks = this.document.find(t).map(function () { + var t = x(this) + return x('
') + .css('position', 'absolute') + .appendTo(t.parent()) + .outerWidth(t.outerWidth()) + .outerHeight(t.outerHeight()) + .offset(t.offset())[0] + }) + }, + _unblockFrames: function () { + this.iframeBlocks && + (this.iframeBlocks.remove(), delete this.iframeBlocks) + }, + _blurActiveElement: function (t) { + var e = x.ui.safeActiveElement(this.document[0]) + x(t.target).closest(e).length || x.ui.safeBlur(e) + }, + _mouseStart: function (t) { + var e = this.options + return ( + (this.helper = this._createHelper(t)), + this._addClass(this.helper, 'ui-draggable-dragging'), + this._cacheHelperProportions(), + x.ui.ddmanager && (x.ui.ddmanager.current = this), + this._cacheMargins(), + (this.cssPosition = this.helper.css('position')), + (this.scrollParent = this.helper.scrollParent(!0)), + (this.offsetParent = this.helper.offsetParent()), + (this.hasFixedAncestor = + 0 < + this.helper.parents().filter(function () { + return 'fixed' === x(this).css('position') + }).length), + (this.positionAbs = this.element.offset()), + this._refreshOffsets(t), + (this.originalPosition = this.position = + this._generatePosition(t, !1)), + (this.originalPageX = t.pageX), + (this.originalPageY = t.pageY), + e.cursorAt && this._adjustOffsetFromHelper(e.cursorAt), + this._setContainment(), + !1 === this._trigger('start', t) + ? (this._clear(), !1) + : (this._cacheHelperProportions(), + x.ui.ddmanager && + !e.dropBehaviour && + x.ui.ddmanager.prepareOffsets(this, t), + this._mouseDrag(t, !0), + x.ui.ddmanager && x.ui.ddmanager.dragStart(this, t), + !0) + ) + }, + _refreshOffsets: function (t) { + ;(this.offset = { + top: this.positionAbs.top - this.margins.top, + left: this.positionAbs.left - this.margins.left, + scroll: !1, + parent: this._getParentOffset(), + relative: this._getRelativeOffset(), + }), + (this.offset.click = { + left: t.pageX - this.offset.left, + top: t.pageY - this.offset.top, + }) + }, + _mouseDrag: function (t, e) { + if ( + (this.hasFixedAncestor && + (this.offset.parent = this._getParentOffset()), + (this.position = this._generatePosition(t, !0)), + (this.positionAbs = this._convertPositionTo('absolute')), + !e) + ) { + e = this._uiHash() + if (!1 === this._trigger('drag', t, e)) + return this._mouseUp(new x.Event('mouseup', t)), !1 + this.position = e.position + } + return ( + (this.helper[0].style.left = this.position.left + 'px'), + (this.helper[0].style.top = this.position.top + 'px'), + x.ui.ddmanager && x.ui.ddmanager.drag(this, t), + !1 + ) + }, + _mouseStop: function (t) { + var e = this, + i = !1 + return ( + x.ui.ddmanager && + !this.options.dropBehaviour && + (i = x.ui.ddmanager.drop(this, t)), + this.dropped && ((i = this.dropped), (this.dropped = !1)), + ('invalid' === this.options.revert && !i) || + ('valid' === this.options.revert && i) || + !0 === this.options.revert || + ('function' == typeof this.options.revert && + this.options.revert.call(this.element, i)) + ? x(this.helper).animate( + this.originalPosition, + parseInt(this.options.revertDuration, 10), + function () { + !1 !== e._trigger('stop', t) && e._clear() + } + ) + : !1 !== this._trigger('stop', t) && this._clear(), + !1 + ) + }, + _mouseUp: function (t) { + return ( + this._unblockFrames(), + x.ui.ddmanager && x.ui.ddmanager.dragStop(this, t), + this.handleElement.is(t.target) && + this.element.trigger('focus'), + x.ui.mouse.prototype._mouseUp.call(this, t) + ) + }, + cancel: function () { + return ( + this.helper.is('.ui-draggable-dragging') + ? this._mouseUp( + new x.Event('mouseup', { target: this.element[0] }) + ) + : this._clear(), + this + ) + }, + _getHandle: function (t) { + return ( + !this.options.handle || + !!x(t.target).closest(this.element.find(this.options.handle)) + .length + ) + }, + _setHandleClassName: function () { + ;(this.handleElement = this.options.handle + ? this.element.find(this.options.handle) + : this.element), + this._addClass(this.handleElement, 'ui-draggable-handle') + }, + _removeHandleClassName: function () { + this._removeClass(this.handleElement, 'ui-draggable-handle') + }, + _createHelper: function (t) { + var e = this.options, + i = 'function' == typeof e.helper, + t = i + ? x(e.helper.apply(this.element[0], [t])) + : 'clone' === e.helper + ? this.element.clone().removeAttr('id') + : this.element + return ( + t.parents('body').length || + t.appendTo( + 'parent' === e.appendTo + ? this.element[0].parentNode + : e.appendTo + ), + i && t[0] === this.element[0] && this._setPositionRelative(), + t[0] === this.element[0] || + /(fixed|absolute)/.test(t.css('position')) || + t.css('position', 'absolute'), + t + ) + }, + _setPositionRelative: function () { + ;/^(?:r|a|f)/.test(this.element.css('position')) || + (this.element[0].style.position = 'relative') + }, + _adjustOffsetFromHelper: function (t) { + 'string' == typeof t && (t = t.split(' ')), + 'left' in + (t = Array.isArray(t) + ? { left: +t[0], top: +t[1] || 0 } + : t) && + (this.offset.click.left = t.left + this.margins.left), + 'right' in t && + (this.offset.click.left = + this.helperProportions.width - + t.right + + this.margins.left), + 'top' in t && + (this.offset.click.top = t.top + this.margins.top), + 'bottom' in t && + (this.offset.click.top = + this.helperProportions.height - + t.bottom + + this.margins.top) + }, + _isRootNode: function (t) { + return /(html|body)/i.test(t.tagName) || t === this.document[0] + }, + _getParentOffset: function () { + var t = this.offsetParent.offset(), + e = this.document[0] + return ( + 'absolute' === this.cssPosition && + this.scrollParent[0] !== e && + x.contains(this.scrollParent[0], this.offsetParent[0]) && + ((t.left += this.scrollParent.scrollLeft()), + (t.top += this.scrollParent.scrollTop())), + { + top: + (t = this._isRootNode(this.offsetParent[0]) + ? { top: 0, left: 0 } + : t).top + + (parseInt( + this.offsetParent.css('borderTopWidth'), + 10 + ) || 0), + left: + t.left + + (parseInt( + this.offsetParent.css('borderLeftWidth'), + 10 + ) || 0), + } + ) + }, + _getRelativeOffset: function () { + if ('relative' !== this.cssPosition) return { top: 0, left: 0 } + var t = this.element.position(), + e = this._isRootNode(this.scrollParent[0]) + return { + top: + t.top - + (parseInt(this.helper.css('top'), 10) || 0) + + (e ? 0 : this.scrollParent.scrollTop()), + left: + t.left - + (parseInt(this.helper.css('left'), 10) || 0) + + (e ? 0 : this.scrollParent.scrollLeft()), + } + }, + _cacheMargins: function () { + this.margins = { + left: parseInt(this.element.css('marginLeft'), 10) || 0, + top: parseInt(this.element.css('marginTop'), 10) || 0, + right: parseInt(this.element.css('marginRight'), 10) || 0, + bottom: parseInt(this.element.css('marginBottom'), 10) || 0, + } + }, + _cacheHelperProportions: function () { + this.helperProportions = { + width: this.helper.outerWidth(), + height: this.helper.outerHeight(), + } + }, + _setContainment: function () { + var t, + e, + i, + s = this.options, + o = this.document[0] + ;(this.relativeContainer = null), + s.containment + ? 'window' !== s.containment + ? 'document' !== s.containment + ? s.containment.constructor !== Array + ? ('parent' === s.containment && + (s.containment = + this.helper[0].parentNode), + (i = (e = x(s.containment))[0]) && + ((t = /(scroll|auto)/.test( + e.css('overflow') + )), + (this.containment = [ + (parseInt( + e.css('borderLeftWidth'), + 10 + ) || 0) + + (parseInt( + e.css('paddingLeft'), + 10 + ) || 0), + (parseInt( + e.css('borderTopWidth'), + 10 + ) || 0) + + (parseInt( + e.css('paddingTop'), + 10 + ) || 0), + (t + ? Math.max( + i.scrollWidth, + i.offsetWidth + ) + : i.offsetWidth) - + (parseInt( + e.css('borderRightWidth'), + 10 + ) || 0) - + (parseInt( + e.css('paddingRight'), + 10 + ) || 0) - + this.helperProportions.width - + this.margins.left - + this.margins.right, + (t + ? Math.max( + i.scrollHeight, + i.offsetHeight + ) + : i.offsetHeight) - + (parseInt( + e.css('borderBottomWidth'), + 10 + ) || 0) - + (parseInt( + e.css('paddingBottom'), + 10 + ) || 0) - + this.helperProportions.height - + this.margins.top - + this.margins.bottom, + ]), + (this.relativeContainer = e))) + : (this.containment = s.containment) + : (this.containment = [ + 0, + 0, + x(o).width() - + this.helperProportions.width - + this.margins.left, + (x(o).height() || + o.body.parentNode.scrollHeight) - + this.helperProportions.height - + this.margins.top, + ]) + : (this.containment = [ + x(window).scrollLeft() - + this.offset.relative.left - + this.offset.parent.left, + x(window).scrollTop() - + this.offset.relative.top - + this.offset.parent.top, + x(window).scrollLeft() + + x(window).width() - + this.helperProportions.width - + this.margins.left, + x(window).scrollTop() + + (x(window).height() || + o.body.parentNode.scrollHeight) - + this.helperProportions.height - + this.margins.top, + ]) + : (this.containment = null) + }, + _convertPositionTo: function (t, e) { + e = e || this.position + var i = 'absolute' === t ? 1 : -1, + t = this._isRootNode(this.scrollParent[0]) + return { + top: + e.top + + this.offset.relative.top * i + + this.offset.parent.top * i - + ('fixed' === this.cssPosition + ? -this.offset.scroll.top + : t + ? 0 + : this.offset.scroll.top) * + i, + left: + e.left + + this.offset.relative.left * i + + this.offset.parent.left * i - + ('fixed' === this.cssPosition + ? -this.offset.scroll.left + : t + ? 0 + : this.offset.scroll.left) * + i, + } + }, + _generatePosition: function (t, e) { + var i, + s = this.options, + o = this._isRootNode(this.scrollParent[0]), + n = t.pageX, + r = t.pageY + return ( + (o && this.offset.scroll) || + (this.offset.scroll = { + top: this.scrollParent.scrollTop(), + left: this.scrollParent.scrollLeft(), + }), + e && + (this.containment && + ((i = this.relativeContainer + ? ((i = this.relativeContainer.offset()), + [ + this.containment[0] + i.left, + this.containment[1] + i.top, + this.containment[2] + i.left, + this.containment[3] + i.top, + ]) + : this.containment), + t.pageX - this.offset.click.left < i[0] && + (n = i[0] + this.offset.click.left), + t.pageY - this.offset.click.top < i[1] && + (r = i[1] + this.offset.click.top), + t.pageX - this.offset.click.left > i[2] && + (n = i[2] + this.offset.click.left), + t.pageY - this.offset.click.top > i[3] && + (r = i[3] + this.offset.click.top)), + s.grid && + ((t = s.grid[1] + ? this.originalPageY + + Math.round((r - this.originalPageY) / s.grid[1]) * + s.grid[1] + : this.originalPageY), + (r = + !i || + t - this.offset.click.top >= i[1] || + t - this.offset.click.top > i[3] + ? t + : t - this.offset.click.top >= i[1] + ? t - s.grid[1] + : t + s.grid[1]), + (t = s.grid[0] + ? this.originalPageX + + Math.round((n - this.originalPageX) / s.grid[0]) * + s.grid[0] + : this.originalPageX), + (n = + !i || + t - this.offset.click.left >= i[0] || + t - this.offset.click.left > i[2] + ? t + : t - this.offset.click.left >= i[0] + ? t - s.grid[0] + : t + s.grid[0])), + 'y' === s.axis && (n = this.originalPageX), + 'x' === s.axis && (r = this.originalPageY)), + { + top: + r - + this.offset.click.top - + this.offset.relative.top - + this.offset.parent.top + + ('fixed' === this.cssPosition + ? -this.offset.scroll.top + : o + ? 0 + : this.offset.scroll.top), + left: + n - + this.offset.click.left - + this.offset.relative.left - + this.offset.parent.left + + ('fixed' === this.cssPosition + ? -this.offset.scroll.left + : o + ? 0 + : this.offset.scroll.left), + } + ) + }, + _clear: function () { + this._removeClass(this.helper, 'ui-draggable-dragging'), + this.helper[0] === this.element[0] || + this.cancelHelperRemoval || + this.helper.remove(), + (this.helper = null), + (this.cancelHelperRemoval = !1), + this.destroyOnClear && this.destroy() + }, + _trigger: function (t, e, i) { + return ( + (i = i || this._uiHash()), + x.ui.plugin.call(this, t, [e, i, this], !0), + /^(drag|start|stop)/.test(t) && + ((this.positionAbs = this._convertPositionTo('absolute')), + (i.offset = this.positionAbs)), + x.Widget.prototype._trigger.call(this, t, e, i) + ) + }, + plugins: {}, + _uiHash: function () { + return { + helper: this.helper, + position: this.position, + originalPosition: this.originalPosition, + offset: this.positionAbs, + } + }, + }), + x.ui.plugin.add('draggable', 'connectToSortable', { + start: function (e, t, i) { + var s = x.extend({}, t, { item: i.element }) + ;(i.sortables = []), + x(i.options.connectToSortable).each(function () { + var t = x(this).sortable('instance') + t && + !t.options.disabled && + (i.sortables.push(t), + t.refreshPositions(), + t._trigger('activate', e, s)) + }) + }, + stop: function (e, t, i) { + var s = x.extend({}, t, { item: i.element }) + ;(i.cancelHelperRemoval = !1), + x.each(i.sortables, function () { + var t = this + t.isOver + ? ((t.isOver = 0), + (i.cancelHelperRemoval = !0), + (t.cancelHelperRemoval = !1), + (t._storedCSS = { + position: t.placeholder.css('position'), + top: t.placeholder.css('top'), + left: t.placeholder.css('left'), + }), + t._mouseStop(e), + (t.options.helper = t.options._helper)) + : ((t.cancelHelperRemoval = !0), + t._trigger('deactivate', e, s)) + }) + }, + drag: function (i, s, o) { + x.each(o.sortables, function () { + var t = !1, + e = this + ;(e.positionAbs = o.positionAbs), + (e.helperProportions = o.helperProportions), + (e.offset.click = o.offset.click), + e._intersectsWith(e.containerCache) && + ((t = !0), + x.each(o.sortables, function () { + return ( + (this.positionAbs = o.positionAbs), + (this.helperProportions = + o.helperProportions), + (this.offset.click = o.offset.click), + (t = + this !== e && + this._intersectsWith( + this.containerCache + ) && + x.contains( + e.element[0], + this.element[0] + ) + ? !1 + : t) + ) + })), + t + ? (e.isOver || + ((e.isOver = 1), + (o._parent = s.helper.parent()), + (e.currentItem = s.helper + .appendTo(e.element) + .data('ui-sortable-item', !0)), + (e.options._helper = e.options.helper), + (e.options.helper = function () { + return s.helper[0] + }), + (i.target = e.currentItem[0]), + e._mouseCapture(i, !0), + e._mouseStart(i, !0, !0), + (e.offset.click.top = o.offset.click.top), + (e.offset.click.left = o.offset.click.left), + (e.offset.parent.left -= + o.offset.parent.left - + e.offset.parent.left), + (e.offset.parent.top -= + o.offset.parent.top - + e.offset.parent.top), + o._trigger('toSortable', i), + (o.dropped = e.element), + x.each(o.sortables, function () { + this.refreshPositions() + }), + (o.currentItem = o.element), + (e.fromOutside = o)), + e.currentItem && + (e._mouseDrag(i), (s.position = e.position))) + : e.isOver && + ((e.isOver = 0), + (e.cancelHelperRemoval = !0), + (e.options._revert = e.options.revert), + (e.options.revert = !1), + e._trigger('out', i, e._uiHash(e)), + e._mouseStop(i, !0), + (e.options.revert = e.options._revert), + (e.options.helper = e.options._helper), + e.placeholder && e.placeholder.remove(), + s.helper.appendTo(o._parent), + o._refreshOffsets(i), + (s.position = o._generatePosition(i, !0)), + o._trigger('fromSortable', i), + (o.dropped = !1), + x.each(o.sortables, function () { + this.refreshPositions() + })) + }) + }, + }), + x.ui.plugin.add('draggable', 'cursor', { + start: function (t, e, i) { + var s = x('body'), + i = i.options + s.css('cursor') && (i._cursor = s.css('cursor')), + s.css('cursor', i.cursor) + }, + stop: function (t, e, i) { + i = i.options + i._cursor && x('body').css('cursor', i._cursor) + }, + }), + x.ui.plugin.add('draggable', 'opacity', { + start: function (t, e, i) { + ;(e = x(e.helper)), (i = i.options) + e.css('opacity') && (i._opacity = e.css('opacity')), + e.css('opacity', i.opacity) + }, + stop: function (t, e, i) { + i = i.options + i._opacity && x(e.helper).css('opacity', i._opacity) + }, + }), + x.ui.plugin.add('draggable', 'scroll', { + start: function (t, e, i) { + i.scrollParentNotHidden || + (i.scrollParentNotHidden = i.helper.scrollParent(!1)), + i.scrollParentNotHidden[0] !== i.document[0] && + 'HTML' !== i.scrollParentNotHidden[0].tagName && + (i.overflowOffset = i.scrollParentNotHidden.offset()) + }, + drag: function (t, e, i) { + var s = i.options, + o = !1, + n = i.scrollParentNotHidden[0], + r = i.document[0] + n !== r && 'HTML' !== n.tagName + ? ((s.axis && 'x' === s.axis) || + (i.overflowOffset.top + n.offsetHeight - t.pageY < + s.scrollSensitivity + ? (n.scrollTop = o = n.scrollTop + s.scrollSpeed) + : t.pageY - i.overflowOffset.top < + s.scrollSensitivity && + (n.scrollTop = o = + n.scrollTop - s.scrollSpeed)), + (s.axis && 'y' === s.axis) || + (i.overflowOffset.left + n.offsetWidth - t.pageX < + s.scrollSensitivity + ? (n.scrollLeft = o = + n.scrollLeft + s.scrollSpeed) + : t.pageX - i.overflowOffset.left < + s.scrollSensitivity && + (n.scrollLeft = o = + n.scrollLeft - s.scrollSpeed))) + : ((s.axis && 'x' === s.axis) || + (t.pageY - x(r).scrollTop() < s.scrollSensitivity + ? (o = x(r).scrollTop( + x(r).scrollTop() - s.scrollSpeed + )) + : x(window).height() - + (t.pageY - x(r).scrollTop()) < + s.scrollSensitivity && + (o = x(r).scrollTop( + x(r).scrollTop() + s.scrollSpeed + ))), + (s.axis && 'y' === s.axis) || + (t.pageX - x(r).scrollLeft() < s.scrollSensitivity + ? (o = x(r).scrollLeft( + x(r).scrollLeft() - s.scrollSpeed + )) + : x(window).width() - + (t.pageX - x(r).scrollLeft()) < + s.scrollSensitivity && + (o = x(r).scrollLeft( + x(r).scrollLeft() + s.scrollSpeed + )))), + !1 !== o && + x.ui.ddmanager && + !s.dropBehaviour && + x.ui.ddmanager.prepareOffsets(i, t) + }, + }), + x.ui.plugin.add('draggable', 'snap', { + start: function (t, e, i) { + var s = i.options + ;(i.snapElements = []), + x( + s.snap.constructor !== String + ? s.snap.items || ':data(ui-draggable)' + : s.snap + ).each(function () { + var t = x(this), + e = t.offset() + this !== i.element[0] && + i.snapElements.push({ + item: this, + width: t.outerWidth(), + height: t.outerHeight(), + top: e.top, + left: e.left, + }) + }) + }, + drag: function (t, e, i) { + for ( + var s, + o, + n, + r, + a, + h, + l, + c, + p, + u = i.options, + d = u.snapTolerance, + f = e.offset.left, + g = f + i.helperProportions.width, + m = e.offset.top, + v = m + i.helperProportions.height, + _ = i.snapElements.length - 1; + 0 <= _; + _-- + ) + (h = + (a = i.snapElements[_].left - i.margins.left) + + i.snapElements[_].width), + (c = + (l = i.snapElements[_].top - i.margins.top) + + i.snapElements[_].height), + g < a - d || + h + d < f || + v < l - d || + c + d < m || + !x.contains( + i.snapElements[_].item.ownerDocument, + i.snapElements[_].item + ) + ? (i.snapElements[_].snapping && + i.options.snap.release && + i.options.snap.release.call( + i.element, + t, + x.extend(i._uiHash(), { + snapItem: i.snapElements[_].item, + }) + ), + (i.snapElements[_].snapping = !1)) + : ('inner' !== u.snapMode && + ((s = Math.abs(l - v) <= d), + (o = Math.abs(c - m) <= d), + (n = Math.abs(a - g) <= d), + (r = Math.abs(h - f) <= d), + s && + (e.position.top = i._convertPositionTo( + 'relative', + { + top: + l - + i.helperProportions.height, + left: 0, + } + ).top), + o && + (e.position.top = i._convertPositionTo( + 'relative', + { top: c, left: 0 } + ).top), + n && + (e.position.left = i._convertPositionTo( + 'relative', + { + top: 0, + left: + a - i.helperProportions.width, + } + ).left), + r && + (e.position.left = i._convertPositionTo( + 'relative', + { top: 0, left: h } + ).left)), + (p = s || o || n || r), + 'outer' !== u.snapMode && + ((s = Math.abs(l - m) <= d), + (o = Math.abs(c - v) <= d), + (n = Math.abs(a - f) <= d), + (r = Math.abs(h - g) <= d), + s && + (e.position.top = i._convertPositionTo( + 'relative', + { top: l, left: 0 } + ).top), + o && + (e.position.top = i._convertPositionTo( + 'relative', + { + top: + c - + i.helperProportions.height, + left: 0, + } + ).top), + n && + (e.position.left = i._convertPositionTo( + 'relative', + { top: 0, left: a } + ).left), + r && + (e.position.left = i._convertPositionTo( + 'relative', + { + top: 0, + left: + h - i.helperProportions.width, + } + ).left)), + !i.snapElements[_].snapping && + (s || o || n || r || p) && + i.options.snap.snap && + i.options.snap.snap.call( + i.element, + t, + x.extend(i._uiHash(), { + snapItem: i.snapElements[_].item, + }) + ), + (i.snapElements[_].snapping = + s || o || n || r || p)) + }, + }), + x.ui.plugin.add('draggable', 'stack', { + start: function (t, e, i) { + var s, + i = i.options, + i = x.makeArray(x(i.stack)).sort(function (t, e) { + return ( + (parseInt(x(t).css('zIndex'), 10) || 0) - + (parseInt(x(e).css('zIndex'), 10) || 0) + ) + }) + i.length && + ((s = parseInt(x(i[0]).css('zIndex'), 10) || 0), + x(i).each(function (t) { + x(this).css('zIndex', s + t) + }), + this.css('zIndex', s + i.length)) + }, + }), + x.ui.plugin.add('draggable', 'zIndex', { + start: function (t, e, i) { + ;(e = x(e.helper)), (i = i.options) + e.css('zIndex') && (i._zIndex = e.css('zIndex')), + e.css('zIndex', i.zIndex) + }, + stop: function (t, e, i) { + i = i.options + i._zIndex && x(e.helper).css('zIndex', i._zIndex) + }, + }) + x.ui.draggable + x.widget('ui.resizable', x.ui.mouse, { + version: '1.13.1', + widgetEventPrefix: 'resize', + options: { + alsoResize: !1, + animate: !1, + animateDuration: 'slow', + animateEasing: 'swing', + aspectRatio: !1, + autoHide: !1, + classes: { + 'ui-resizable-se': 'ui-icon ui-icon-gripsmall-diagonal-se', + }, + containment: !1, + ghost: !1, + grid: !1, + handles: 'e,s,se', + helper: !1, + maxHeight: null, + maxWidth: null, + minHeight: 10, + minWidth: 10, + zIndex: 90, + resize: null, + start: null, + stop: null, + }, + _num: function (t) { + return parseFloat(t) || 0 + }, + _isNumber: function (t) { + return !isNaN(parseFloat(t)) + }, + _hasScroll: function (t, e) { + if ('hidden' === x(t).css('overflow')) return !1 + var i = e && 'left' === e ? 'scrollLeft' : 'scrollTop', + e = !1 + if (0 < t[i]) return !0 + try { + ;(t[i] = 1), (e = 0 < t[i]), (t[i] = 0) + } catch (t) {} + return e + }, + _create: function () { + var t, + e = this.options, + i = this + this._addClass('ui-resizable'), + x.extend(this, { + _aspectRatio: !!e.aspectRatio, + aspectRatio: e.aspectRatio, + originalElement: this.element, + _proportionallyResizeElements: [], + _helper: + e.helper || e.ghost || e.animate + ? e.helper || 'ui-resizable-helper' + : null, + }), + this.element[0].nodeName.match( + /^(canvas|textarea|input|select|button|img)$/i + ) && + (this.element.wrap( + x("
").css({ + overflow: 'hidden', + position: this.element.css('position'), + width: this.element.outerWidth(), + height: this.element.outerHeight(), + top: this.element.css('top'), + left: this.element.css('left'), + }) + ), + (this.element = this.element + .parent() + .data( + 'ui-resizable', + this.element.resizable('instance') + )), + (this.elementIsWrapper = !0), + (t = { + marginTop: this.originalElement.css('marginTop'), + marginRight: this.originalElement.css('marginRight'), + marginBottom: this.originalElement.css('marginBottom'), + marginLeft: this.originalElement.css('marginLeft'), + }), + this.element.css(t), + this.originalElement.css('margin', 0), + (this.originalResizeStyle = + this.originalElement.css('resize')), + this.originalElement.css('resize', 'none'), + this._proportionallyResizeElements.push( + this.originalElement.css({ + position: 'static', + zoom: 1, + display: 'block', + }) + ), + this.originalElement.css(t), + this._proportionallyResize()), + this._setupHandles(), + e.autoHide && + x(this.element) + .on('mouseenter', function () { + e.disabled || + (i._removeClass('ui-resizable-autohide'), + i._handles.show()) + }) + .on('mouseleave', function () { + e.disabled || + i.resizing || + (i._addClass('ui-resizable-autohide'), + i._handles.hide()) + }), + this._mouseInit() + }, + _destroy: function () { + this._mouseDestroy(), this._addedHandles.remove() + function t(t) { + x(t) + .removeData('resizable') + .removeData('ui-resizable') + .off('.resizable') + } + var e + return ( + this.elementIsWrapper && + (t(this.element), + (e = this.element), + this.originalElement + .css({ + position: e.css('position'), + width: e.outerWidth(), + height: e.outerHeight(), + top: e.css('top'), + left: e.css('left'), + }) + .insertAfter(e), + e.remove()), + this.originalElement.css('resize', this.originalResizeStyle), + t(this.originalElement), + this + ) + }, + _setOption: function (t, e) { + switch ((this._super(t, e), t)) { + case 'handles': + this._removeHandles(), this._setupHandles() + break + case 'aspectRatio': + this._aspectRatio = !!e + } + }, + _setupHandles: function () { + var t, + e, + i, + s, + o, + n = this.options, + r = this + if ( + ((this.handles = + n.handles || + (x('.ui-resizable-handle', this.element).length + ? { + n: '.ui-resizable-n', + e: '.ui-resizable-e', + s: '.ui-resizable-s', + w: '.ui-resizable-w', + se: '.ui-resizable-se', + sw: '.ui-resizable-sw', + ne: '.ui-resizable-ne', + nw: '.ui-resizable-nw', + } + : 'e,s,se')), + (this._handles = x()), + (this._addedHandles = x()), + this.handles.constructor === String) + ) + for ( + 'all' === this.handles && + (this.handles = 'n,e,s,w,se,sw,ne,nw'), + i = this.handles.split(','), + this.handles = {}, + e = 0; + e < i.length; + e++ + ) + (s = + 'ui-resizable-' + + (t = String.prototype.trim.call(i[e]))), + (o = x('
')), + this._addClass(o, 'ui-resizable-handle ' + s), + o.css({ zIndex: n.zIndex }), + (this.handles[t] = '.ui-resizable-' + t), + this.element.children(this.handles[t]).length || + (this.element.append(o), + (this._addedHandles = this._addedHandles.add(o))) + ;(this._renderAxis = function (t) { + var e, i, s + for (e in ((t = t || this.element), this.handles)) + this.handles[e].constructor === String + ? (this.handles[e] = this.element + .children(this.handles[e]) + .first() + .show()) + : (this.handles[e].jquery || + this.handles[e].nodeType) && + ((this.handles[e] = x(this.handles[e])), + this._on(this.handles[e], { + mousedown: r._mouseDown, + })), + this.elementIsWrapper && + this.originalElement[0].nodeName.match( + /^(textarea|input|select|button)$/i + ) && + ((i = x(this.handles[e], this.element)), + (s = /sw|ne|nw|se|n|s/.test(e) + ? i.outerHeight() + : i.outerWidth()), + (i = [ + 'padding', + /ne|nw|n/.test(e) + ? 'Top' + : /se|sw|s/.test(e) + ? 'Bottom' + : /^e$/.test(e) + ? 'Right' + : 'Left', + ].join('')), + t.css(i, s), + this._proportionallyResize()), + (this._handles = this._handles.add(this.handles[e])) + }), + this._renderAxis(this.element), + (this._handles = this._handles.add( + this.element.find('.ui-resizable-handle') + )), + this._handles.disableSelection(), + this._handles.on('mouseover', function () { + r.resizing || + (this.className && + (o = this.className.match( + /ui-resizable-(se|sw|ne|nw|n|e|s|w)/i + )), + (r.axis = o && o[1] ? o[1] : 'se')) + }), + n.autoHide && + (this._handles.hide(), + this._addClass('ui-resizable-autohide')) + }, + _removeHandles: function () { + this._addedHandles.remove() + }, + _mouseCapture: function (t) { + var e, + i, + s = !1 + for (e in this.handles) + ((i = x(this.handles[e])[0]) !== t.target && + !x.contains(i, t.target)) || + (s = !0) + return !this.options.disabled && s + }, + _mouseStart: function (t) { + var e, + i, + s = this.options, + o = this.element + return ( + (this.resizing = !0), + this._renderProxy(), + (e = this._num(this.helper.css('left'))), + (i = this._num(this.helper.css('top'))), + s.containment && + ((e += x(s.containment).scrollLeft() || 0), + (i += x(s.containment).scrollTop() || 0)), + (this.offset = this.helper.offset()), + (this.position = { left: e, top: i }), + (this.size = this._helper + ? { + width: this.helper.width(), + height: this.helper.height(), + } + : { width: o.width(), height: o.height() }), + (this.originalSize = this._helper + ? { width: o.outerWidth(), height: o.outerHeight() } + : { width: o.width(), height: o.height() }), + (this.sizeDiff = { + width: o.outerWidth() - o.width(), + height: o.outerHeight() - o.height(), + }), + (this.originalPosition = { left: e, top: i }), + (this.originalMousePosition = { left: t.pageX, top: t.pageY }), + (this.aspectRatio = + 'number' == typeof s.aspectRatio + ? s.aspectRatio + : this.originalSize.width / this.originalSize.height || + 1), + (s = x('.ui-resizable-' + this.axis).css('cursor')), + x('body').css( + 'cursor', + 'auto' === s ? this.axis + '-resize' : s + ), + this._addClass('ui-resizable-resizing'), + this._propagate('start', t), + !0 + ) + }, + _mouseDrag: function (t) { + var e = this.originalMousePosition, + i = this.axis, + s = t.pageX - e.left || 0, + e = t.pageY - e.top || 0, + i = this._change[i] + return ( + this._updatePrevProperties(), + i && + ((e = i.apply(this, [t, s, e])), + this._updateVirtualBoundaries(t.shiftKey), + (this._aspectRatio || t.shiftKey) && + (e = this._updateRatio(e, t)), + (e = this._respectSize(e, t)), + this._updateCache(e), + this._propagate('resize', t), + (e = this._applyChanges()), + !this._helper && + this._proportionallyResizeElements.length && + this._proportionallyResize(), + x.isEmptyObject(e) || + (this._updatePrevProperties(), + this._trigger('resize', t, this.ui()), + this._applyChanges())), + !1 + ) + }, + _mouseStop: function (t) { + this.resizing = !1 + var e, + i, + s, + o = this.options, + n = this + return ( + this._helper && + ((s = + (e = + (i = this._proportionallyResizeElements).length && + /textarea/i.test(i[0].nodeName)) && + this._hasScroll(i[0], 'left') + ? 0 + : n.sizeDiff.height), + (i = e ? 0 : n.sizeDiff.width), + (e = { + width: n.helper.width() - i, + height: n.helper.height() - s, + }), + (i = + parseFloat(n.element.css('left')) + + (n.position.left - n.originalPosition.left) || + null), + (s = + parseFloat(n.element.css('top')) + + (n.position.top - n.originalPosition.top) || null), + o.animate || + this.element.css(x.extend(e, { top: s, left: i })), + n.helper.height(n.size.height), + n.helper.width(n.size.width), + this._helper && !o.animate && this._proportionallyResize()), + x('body').css('cursor', 'auto'), + this._removeClass('ui-resizable-resizing'), + this._propagate('stop', t), + this._helper && this.helper.remove(), + !1 + ) + }, + _updatePrevProperties: function () { + ;(this.prevPosition = { + top: this.position.top, + left: this.position.left, + }), + (this.prevSize = { + width: this.size.width, + height: this.size.height, + }) + }, + _applyChanges: function () { + var t = {} + return ( + this.position.top !== this.prevPosition.top && + (t.top = this.position.top + 'px'), + this.position.left !== this.prevPosition.left && + (t.left = this.position.left + 'px'), + this.size.width !== this.prevSize.width && + (t.width = this.size.width + 'px'), + this.size.height !== this.prevSize.height && + (t.height = this.size.height + 'px'), + this.helper.css(t), + t + ) + }, + _updateVirtualBoundaries: function (t) { + var e, + i, + s = this.options, + o = { + minWidth: this._isNumber(s.minWidth) ? s.minWidth : 0, + maxWidth: this._isNumber(s.maxWidth) ? s.maxWidth : 1 / 0, + minHeight: this._isNumber(s.minHeight) ? s.minHeight : 0, + maxHeight: this._isNumber(s.maxHeight) + ? s.maxHeight + : 1 / 0, + } + ;(this._aspectRatio || t) && + ((e = o.minHeight * this.aspectRatio), + (i = o.minWidth / this.aspectRatio), + (s = o.maxHeight * this.aspectRatio), + (t = o.maxWidth / this.aspectRatio), + e > o.minWidth && (o.minWidth = e), + i > o.minHeight && (o.minHeight = i), + s < o.maxWidth && (o.maxWidth = s), + t < o.maxHeight && (o.maxHeight = t)), + (this._vBoundaries = o) + }, + _updateCache: function (t) { + ;(this.offset = this.helper.offset()), + this._isNumber(t.left) && (this.position.left = t.left), + this._isNumber(t.top) && (this.position.top = t.top), + this._isNumber(t.height) && (this.size.height = t.height), + this._isNumber(t.width) && (this.size.width = t.width) + }, + _updateRatio: function (t) { + var e = this.position, + i = this.size, + s = this.axis + return ( + this._isNumber(t.height) + ? (t.width = t.height * this.aspectRatio) + : this._isNumber(t.width) && + (t.height = t.width / this.aspectRatio), + 'sw' === s && + ((t.left = e.left + (i.width - t.width)), (t.top = null)), + 'nw' === s && + ((t.top = e.top + (i.height - t.height)), + (t.left = e.left + (i.width - t.width))), + t + ) + }, + _respectSize: function (t) { + var e = this._vBoundaries, + i = this.axis, + s = + this._isNumber(t.width) && + e.maxWidth && + e.maxWidth < t.width, + o = + this._isNumber(t.height) && + e.maxHeight && + e.maxHeight < t.height, + n = + this._isNumber(t.width) && + e.minWidth && + e.minWidth > t.width, + r = + this._isNumber(t.height) && + e.minHeight && + e.minHeight > t.height, + a = this.originalPosition.left + this.originalSize.width, + h = this.originalPosition.top + this.originalSize.height, + l = /sw|nw|w/.test(i), + i = /nw|ne|n/.test(i) + return ( + n && (t.width = e.minWidth), + r && (t.height = e.minHeight), + s && (t.width = e.maxWidth), + o && (t.height = e.maxHeight), + n && l && (t.left = a - e.minWidth), + s && l && (t.left = a - e.maxWidth), + r && i && (t.top = h - e.minHeight), + o && i && (t.top = h - e.maxHeight), + t.width || t.height || t.left || !t.top + ? t.width || t.height || t.top || !t.left || (t.left = null) + : (t.top = null), + t + ) + }, + _getPaddingPlusBorderDimensions: function (t) { + for ( + var e = 0, + i = [], + s = [ + t.css('borderTopWidth'), + t.css('borderRightWidth'), + t.css('borderBottomWidth'), + t.css('borderLeftWidth'), + ], + o = [ + t.css('paddingTop'), + t.css('paddingRight'), + t.css('paddingBottom'), + t.css('paddingLeft'), + ]; + e < 4; + e++ + ) + (i[e] = parseFloat(s[e]) || 0), (i[e] += parseFloat(o[e]) || 0) + return { height: i[0] + i[2], width: i[1] + i[3] } + }, + _proportionallyResize: function () { + if (this._proportionallyResizeElements.length) + for ( + var t, e = 0, i = this.helper || this.element; + e < this._proportionallyResizeElements.length; + e++ + ) + (t = this._proportionallyResizeElements[e]), + this.outerDimensions || + (this.outerDimensions = + this._getPaddingPlusBorderDimensions(t)), + t.css({ + height: + i.height() - this.outerDimensions.height || 0, + width: i.width() - this.outerDimensions.width || 0, + }) + }, + _renderProxy: function () { + var t = this.element, + e = this.options + ;(this.elementOffset = t.offset()), + this._helper + ? ((this.helper = + this.helper || + x('
').css({ overflow: 'hidden' })), + this._addClass(this.helper, this._helper), + this.helper.css({ + width: this.element.outerWidth(), + height: this.element.outerHeight(), + position: 'absolute', + left: this.elementOffset.left + 'px', + top: this.elementOffset.top + 'px', + zIndex: ++e.zIndex, + }), + this.helper.appendTo('body').disableSelection()) + : (this.helper = this.element) + }, + _change: { + e: function (t, e) { + return { width: this.originalSize.width + e } + }, + w: function (t, e) { + var i = this.originalSize + return { + left: this.originalPosition.left + e, + width: i.width - e, + } + }, + n: function (t, e, i) { + var s = this.originalSize + return { + top: this.originalPosition.top + i, + height: s.height - i, + } + }, + s: function (t, e, i) { + return { height: this.originalSize.height + i } + }, + se: function (t, e, i) { + return x.extend( + this._change.s.apply(this, arguments), + this._change.e.apply(this, [t, e, i]) + ) + }, + sw: function (t, e, i) { + return x.extend( + this._change.s.apply(this, arguments), + this._change.w.apply(this, [t, e, i]) + ) + }, + ne: function (t, e, i) { + return x.extend( + this._change.n.apply(this, arguments), + this._change.e.apply(this, [t, e, i]) + ) + }, + nw: function (t, e, i) { + return x.extend( + this._change.n.apply(this, arguments), + this._change.w.apply(this, [t, e, i]) + ) + }, + }, + _propagate: function (t, e) { + x.ui.plugin.call(this, t, [e, this.ui()]), + 'resize' !== t && this._trigger(t, e, this.ui()) + }, + plugins: {}, + ui: function () { + return { + originalElement: this.originalElement, + element: this.element, + helper: this.helper, + position: this.position, + size: this.size, + originalSize: this.originalSize, + originalPosition: this.originalPosition, + } + }, + }), + x.ui.plugin.add('resizable', 'animate', { + stop: function (e) { + var i = x(this).resizable('instance'), + t = i.options, + s = i._proportionallyResizeElements, + o = s.length && /textarea/i.test(s[0].nodeName), + n = o && i._hasScroll(s[0], 'left') ? 0 : i.sizeDiff.height, + r = o ? 0 : i.sizeDiff.width, + o = { width: i.size.width - r, height: i.size.height - n }, + r = + parseFloat(i.element.css('left')) + + (i.position.left - i.originalPosition.left) || null, + n = + parseFloat(i.element.css('top')) + + (i.position.top - i.originalPosition.top) || null + i.element.animate( + x.extend(o, n && r ? { top: n, left: r } : {}), + { + duration: t.animateDuration, + easing: t.animateEasing, + step: function () { + var t = { + width: parseFloat(i.element.css('width')), + height: parseFloat(i.element.css('height')), + top: parseFloat(i.element.css('top')), + left: parseFloat(i.element.css('left')), + } + s && + s.length && + x(s[0]).css({ + width: t.width, + height: t.height, + }), + i._updateCache(t), + i._propagate('resize', e) + }, + } + ) + }, + }), + x.ui.plugin.add('resizable', 'containment', { + start: function () { + var i, + s, + o = x(this).resizable('instance'), + t = o.options, + e = o.element, + n = t.containment, + r = + n instanceof x + ? n.get(0) + : /parent/.test(n) + ? e.parent().get(0) + : n + r && + ((o.containerElement = x(r)), + /document/.test(n) || n === document + ? ((o.containerOffset = { left: 0, top: 0 }), + (o.containerPosition = { left: 0, top: 0 }), + (o.parentData = { + element: x(document), + left: 0, + top: 0, + width: x(document).width(), + height: + x(document).height() || + document.body.parentNode.scrollHeight, + })) + : ((i = x(r)), + (s = []), + x(['Top', 'Right', 'Left', 'Bottom']).each(function ( + t, + e + ) { + s[t] = o._num(i.css('padding' + e)) + }), + (o.containerOffset = i.offset()), + (o.containerPosition = i.position()), + (o.containerSize = { + height: i.innerHeight() - s[3], + width: i.innerWidth() - s[1], + }), + (t = o.containerOffset), + (e = o.containerSize.height), + (n = o.containerSize.width), + (n = o._hasScroll(r, 'left') ? r.scrollWidth : n), + (e = o._hasScroll(r) ? r.scrollHeight : e), + (o.parentData = { + element: r, + left: t.left, + top: t.top, + width: n, + height: e, + }))) + }, + resize: function (t) { + var e = x(this).resizable('instance'), + i = e.options, + s = e.containerOffset, + o = e.position, + n = e._aspectRatio || t.shiftKey, + r = { top: 0, left: 0 }, + a = e.containerElement, + t = !0 + a[0] !== document && + /static/.test(a.css('position')) && + (r = s), + o.left < (e._helper ? s.left : 0) && + ((e.size.width = + e.size.width + + (e._helper + ? e.position.left - s.left + : e.position.left - r.left)), + n && + ((e.size.height = e.size.width / e.aspectRatio), + (t = !1)), + (e.position.left = i.helper ? s.left : 0)), + o.top < (e._helper ? s.top : 0) && + ((e.size.height = + e.size.height + + (e._helper + ? e.position.top - s.top + : e.position.top)), + n && + ((e.size.width = e.size.height * e.aspectRatio), + (t = !1)), + (e.position.top = e._helper ? s.top : 0)), + (i = + e.containerElement.get(0) === + e.element.parent().get(0)), + (o = /relative|absolute/.test( + e.containerElement.css('position') + )), + i && o + ? ((e.offset.left = + e.parentData.left + e.position.left), + (e.offset.top = e.parentData.top + e.position.top)) + : ((e.offset.left = e.element.offset().left), + (e.offset.top = e.element.offset().top)), + (o = Math.abs( + e.sizeDiff.width + + (e._helper + ? e.offset.left - r.left + : e.offset.left - s.left) + )), + (s = Math.abs( + e.sizeDiff.height + + (e._helper + ? e.offset.top - r.top + : e.offset.top - s.top) + )), + o + e.size.width >= e.parentData.width && + ((e.size.width = e.parentData.width - o), + n && + ((e.size.height = e.size.width / e.aspectRatio), + (t = !1))), + s + e.size.height >= e.parentData.height && + ((e.size.height = e.parentData.height - s), + n && + ((e.size.width = e.size.height * e.aspectRatio), + (t = !1))), + t || + ((e.position.left = e.prevPosition.left), + (e.position.top = e.prevPosition.top), + (e.size.width = e.prevSize.width), + (e.size.height = e.prevSize.height)) + }, + stop: function () { + var t = x(this).resizable('instance'), + e = t.options, + i = t.containerOffset, + s = t.containerPosition, + o = t.containerElement, + n = x(t.helper), + r = n.offset(), + a = n.outerWidth() - t.sizeDiff.width, + n = n.outerHeight() - t.sizeDiff.height + t._helper && + !e.animate && + /relative/.test(o.css('position')) && + x(this).css({ + left: r.left - s.left - i.left, + width: a, + height: n, + }), + t._helper && + !e.animate && + /static/.test(o.css('position')) && + x(this).css({ + left: r.left - s.left - i.left, + width: a, + height: n, + }) + }, + }), + x.ui.plugin.add('resizable', 'alsoResize', { + start: function () { + var t = x(this).resizable('instance').options + x(t.alsoResize).each(function () { + var t = x(this) + t.data('ui-resizable-alsoresize', { + width: parseFloat(t.width()), + height: parseFloat(t.height()), + left: parseFloat(t.css('left')), + top: parseFloat(t.css('top')), + }) + }) + }, + resize: function (t, i) { + var e = x(this).resizable('instance'), + s = e.options, + o = e.originalSize, + n = e.originalPosition, + r = { + height: e.size.height - o.height || 0, + width: e.size.width - o.width || 0, + top: e.position.top - n.top || 0, + left: e.position.left - n.left || 0, + } + x(s.alsoResize).each(function () { + var t = x(this), + s = x(this).data('ui-resizable-alsoresize'), + o = {}, + e = t.parents(i.originalElement[0]).length + ? ['width', 'height'] + : ['width', 'height', 'top', 'left'] + x.each(e, function (t, e) { + var i = (s[e] || 0) + (r[e] || 0) + i && 0 <= i && (o[e] = i || null) + }), + t.css(o) + }) + }, + stop: function () { + x(this).removeData('ui-resizable-alsoresize') + }, + }), + x.ui.plugin.add('resizable', 'ghost', { + start: function () { + var t = x(this).resizable('instance'), + e = t.size + ;(t.ghost = t.originalElement.clone()), + t.ghost.css({ + opacity: 0.25, + display: 'block', + position: 'relative', + height: e.height, + width: e.width, + margin: 0, + left: 0, + top: 0, + }), + t._addClass(t.ghost, 'ui-resizable-ghost'), + !1 !== x.uiBackCompat && + 'string' == typeof t.options.ghost && + t.ghost.addClass(this.options.ghost), + t.ghost.appendTo(t.helper) + }, + resize: function () { + var t = x(this).resizable('instance') + t.ghost && + t.ghost.css({ + position: 'relative', + height: t.size.height, + width: t.size.width, + }) + }, + stop: function () { + var t = x(this).resizable('instance') + t.ghost && + t.helper && + t.helper.get(0).removeChild(t.ghost.get(0)) + }, + }), + x.ui.plugin.add('resizable', 'grid', { + resize: function () { + var t, + e = x(this).resizable('instance'), + i = e.options, + s = e.size, + o = e.originalSize, + n = e.originalPosition, + r = e.axis, + a = 'number' == typeof i.grid ? [i.grid, i.grid] : i.grid, + h = a[0] || 1, + l = a[1] || 1, + c = Math.round((s.width - o.width) / h) * h, + p = Math.round((s.height - o.height) / l) * l, + u = o.width + c, + d = o.height + p, + f = i.maxWidth && i.maxWidth < u, + g = i.maxHeight && i.maxHeight < d, + m = i.minWidth && i.minWidth > u, + s = i.minHeight && i.minHeight > d + ;(i.grid = a), + m && (u += h), + s && (d += l), + f && (u -= h), + g && (d -= l), + /^(se|s|e)$/.test(r) + ? ((e.size.width = u), (e.size.height = d)) + : /^(ne)$/.test(r) + ? ((e.size.width = u), + (e.size.height = d), + (e.position.top = n.top - p)) + : /^(sw)$/.test(r) + ? ((e.size.width = u), + (e.size.height = d), + (e.position.left = n.left - c)) + : ((d - l <= 0 || u - h <= 0) && + (t = e._getPaddingPlusBorderDimensions(this)), + 0 < d - l + ? ((e.size.height = d), + (e.position.top = n.top - p)) + : ((d = l - t.height), + (e.size.height = d), + (e.position.top = n.top + o.height - d)), + 0 < u - h + ? ((e.size.width = u), + (e.position.left = n.left - c)) + : ((u = h - t.width), + (e.size.width = u), + (e.position.left = n.left + o.width - u))) + }, + }) + x.ui.resizable, + x.widget('ui.sortable', x.ui.mouse, { + version: '1.13.1', + widgetEventPrefix: 'sort', + ready: !1, + options: { + appendTo: 'parent', + axis: !1, + connectWith: !1, + containment: !1, + cursor: 'auto', + cursorAt: !1, + dropOnEmpty: !0, + forcePlaceholderSize: !1, + forceHelperSize: !1, + grid: !1, + handle: !1, + helper: 'original', + items: '> *', + opacity: !1, + placeholder: !1, + revert: !1, + scroll: !0, + scrollSensitivity: 20, + scrollSpeed: 20, + scope: 'default', + tolerance: 'intersect', + zIndex: 1e3, + activate: null, + beforeStop: null, + change: null, + deactivate: null, + out: null, + over: null, + receive: null, + remove: null, + sort: null, + start: null, + stop: null, + update: null, + }, + _isOverAxis: function (t, e, i) { + return e <= t && t < e + i + }, + _isFloating: function (t) { + return ( + /left|right/.test(t.css('float')) || + /inline|table-cell/.test(t.css('display')) + ) + }, + _create: function () { + ;(this.containerCache = {}), + this._addClass('ui-sortable'), + this.refresh(), + (this.offset = this.element.offset()), + this._mouseInit(), + this._setHandleClassName(), + (this.ready = !0) + }, + _setOption: function (t, e) { + this._super(t, e), 'handle' === t && this._setHandleClassName() + }, + _setHandleClassName: function () { + var t = this + this._removeClass( + this.element.find('.ui-sortable-handle'), + 'ui-sortable-handle' + ), + x.each(this.items, function () { + t._addClass( + this.instance.options.handle + ? this.item.find(this.instance.options.handle) + : this.item, + 'ui-sortable-handle' + ) + }) + }, + _destroy: function () { + this._mouseDestroy() + for (var t = this.items.length - 1; 0 <= t; t--) + this.items[t].item.removeData(this.widgetName + '-item') + return this + }, + _mouseCapture: function (t, e) { + var i = null, + s = !1, + o = this + return ( + !this.reverting && + !this.options.disabled && + 'static' !== this.options.type && + (this._refreshItems(t), + x(t.target) + .parents() + .each(function () { + if (x.data(this, o.widgetName + '-item') === o) + return (i = x(this)), !1 + }), + !!(i = + x.data(t.target, o.widgetName + '-item') === o + ? x(t.target) + : i) && + !( + this.options.handle && + !e && + (x(this.options.handle, i) + .find('*') + .addBack() + .each(function () { + this === t.target && (s = !0) + }), + !s) + ) && + ((this.currentItem = i), + this._removeCurrentsFromItems(), + !0)) + ) + }, + _mouseStart: function (t, e, i) { + var s, + o, + n = this.options + if ( + ((this.currentContainer = this).refreshPositions(), + (this.appendTo = x( + 'parent' !== n.appendTo + ? n.appendTo + : this.currentItem.parent() + )), + (this.helper = this._createHelper(t)), + this._cacheHelperProportions(), + this._cacheMargins(), + (this.offset = this.currentItem.offset()), + (this.offset = { + top: this.offset.top - this.margins.top, + left: this.offset.left - this.margins.left, + }), + x.extend(this.offset, { + click: { + left: t.pageX - this.offset.left, + top: t.pageY - this.offset.top, + }, + relative: this._getRelativeOffset(), + }), + this.helper.css('position', 'absolute'), + (this.cssPosition = this.helper.css('position')), + n.cursorAt && this._adjustOffsetFromHelper(n.cursorAt), + (this.domPosition = { + prev: this.currentItem.prev()[0], + parent: this.currentItem.parent()[0], + }), + this.helper[0] !== this.currentItem[0] && + this.currentItem.hide(), + this._createPlaceholder(), + (this.scrollParent = this.placeholder.scrollParent()), + x.extend(this.offset, { parent: this._getParentOffset() }), + n.containment && this._setContainment(), + n.cursor && + 'auto' !== n.cursor && + ((o = this.document.find('body')), + (this.storedCursor = o.css('cursor')), + o.css('cursor', n.cursor), + (this.storedStylesheet = x( + '' + ).appendTo(o))), + n.zIndex && + (this.helper.css('zIndex') && + (this._storedZIndex = this.helper.css('zIndex')), + this.helper.css('zIndex', n.zIndex)), + n.opacity && + (this.helper.css('opacity') && + (this._storedOpacity = this.helper.css('opacity')), + this.helper.css('opacity', n.opacity)), + this.scrollParent[0] !== this.document[0] && + 'HTML' !== this.scrollParent[0].tagName && + (this.overflowOffset = this.scrollParent.offset()), + this._trigger('start', t, this._uiHash()), + this._preserveHelperProportions || + this._cacheHelperProportions(), + !i) + ) + for (s = this.containers.length - 1; 0 <= s; s--) + this.containers[s]._trigger( + 'activate', + t, + this._uiHash(this) + ) + return ( + x.ui.ddmanager && (x.ui.ddmanager.current = this), + x.ui.ddmanager && + !n.dropBehaviour && + x.ui.ddmanager.prepareOffsets(this, t), + (this.dragging = !0), + this._addClass(this.helper, 'ui-sortable-helper'), + this.helper.parent().is(this.appendTo) || + (this.helper.detach().appendTo(this.appendTo), + (this.offset.parent = this._getParentOffset())), + (this.position = this.originalPosition = + this._generatePosition(t)), + (this.originalPageX = t.pageX), + (this.originalPageY = t.pageY), + (this.lastPositionAbs = this.positionAbs = + this._convertPositionTo('absolute')), + this._mouseDrag(t), + !0 + ) + }, + _scroll: function (t) { + var e = this.options, + i = !1 + return ( + this.scrollParent[0] !== this.document[0] && + 'HTML' !== this.scrollParent[0].tagName + ? (this.overflowOffset.top + + this.scrollParent[0].offsetHeight - + t.pageY < + e.scrollSensitivity + ? (this.scrollParent[0].scrollTop = i = + this.scrollParent[0].scrollTop + + e.scrollSpeed) + : t.pageY - this.overflowOffset.top < + e.scrollSensitivity && + (this.scrollParent[0].scrollTop = i = + this.scrollParent[0].scrollTop - + e.scrollSpeed), + this.overflowOffset.left + + this.scrollParent[0].offsetWidth - + t.pageX < + e.scrollSensitivity + ? (this.scrollParent[0].scrollLeft = i = + this.scrollParent[0].scrollLeft + + e.scrollSpeed) + : t.pageX - this.overflowOffset.left < + e.scrollSensitivity && + (this.scrollParent[0].scrollLeft = i = + this.scrollParent[0].scrollLeft - + e.scrollSpeed)) + : (t.pageY - this.document.scrollTop() < + e.scrollSensitivity + ? (i = this.document.scrollTop( + this.document.scrollTop() - e.scrollSpeed + )) + : this.window.height() - + (t.pageY - this.document.scrollTop()) < + e.scrollSensitivity && + (i = this.document.scrollTop( + this.document.scrollTop() + e.scrollSpeed + )), + t.pageX - this.document.scrollLeft() < + e.scrollSensitivity + ? (i = this.document.scrollLeft( + this.document.scrollLeft() - e.scrollSpeed + )) + : this.window.width() - + (t.pageX - this.document.scrollLeft()) < + e.scrollSensitivity && + (i = this.document.scrollLeft( + this.document.scrollLeft() + e.scrollSpeed + ))), + i + ) + }, + _mouseDrag: function (t) { + var e, + i, + s, + o, + n = this.options + for ( + this.position = this._generatePosition(t), + this.positionAbs = this._convertPositionTo('absolute'), + (this.options.axis && 'y' === this.options.axis) || + (this.helper[0].style.left = + this.position.left + 'px'), + (this.options.axis && 'x' === this.options.axis) || + (this.helper[0].style.top = + this.position.top + 'px'), + n.scroll && + !1 !== this._scroll(t) && + (this._refreshItemPositions(!0), + x.ui.ddmanager && + !n.dropBehaviour && + x.ui.ddmanager.prepareOffsets(this, t)), + this.dragDirection = { + vertical: this._getDragVerticalDirection(), + horizontal: this._getDragHorizontalDirection(), + }, + e = this.items.length - 1; + 0 <= e; + e-- + ) + if ( + ((s = (i = this.items[e]).item[0]), + (o = this._intersectsWithPointer(i)) && + i.instance === this.currentContainer && + !( + s === this.currentItem[0] || + this.placeholder[ + 1 === o ? 'next' : 'prev' + ]()[0] === s || + x.contains(this.placeholder[0], s) || + ('semi-dynamic' === this.options.type && + x.contains(this.element[0], s)) + )) + ) { + if ( + ((this.direction = 1 === o ? 'down' : 'up'), + 'pointer' !== this.options.tolerance && + !this._intersectsWithSides(i)) + ) + break + this._rearrange(t, i), + this._trigger('change', t, this._uiHash()) + break + } + return ( + this._contactContainers(t), + x.ui.ddmanager && x.ui.ddmanager.drag(this, t), + this._trigger('sort', t, this._uiHash()), + (this.lastPositionAbs = this.positionAbs), + !1 + ) + }, + _mouseStop: function (t, e) { + var i, s, o, n + if (t) + return ( + x.ui.ddmanager && + !this.options.dropBehaviour && + x.ui.ddmanager.drop(this, t), + this.options.revert + ? ((s = (i = this).placeholder.offset()), + (n = {}), + ((o = this.options.axis) && 'x' !== o) || + (n.left = + s.left - + this.offset.parent.left - + this.margins.left + + (this.offsetParent[0] === + this.document[0].body + ? 0 + : this.offsetParent[0].scrollLeft)), + (o && 'y' !== o) || + (n.top = + s.top - + this.offset.parent.top - + this.margins.top + + (this.offsetParent[0] === + this.document[0].body + ? 0 + : this.offsetParent[0].scrollTop)), + (this.reverting = !0), + x(this.helper).animate( + n, + parseInt(this.options.revert, 10) || 500, + function () { + i._clear(t) + } + )) + : this._clear(t, e), + !1 + ) + }, + cancel: function () { + if (this.dragging) { + this._mouseUp(new x.Event('mouseup', { target: null })), + 'original' === this.options.helper + ? (this.currentItem.css(this._storedCSS), + this._removeClass( + this.currentItem, + 'ui-sortable-helper' + )) + : this.currentItem.show() + for (var t = this.containers.length - 1; 0 <= t; t--) + this.containers[t]._trigger( + 'deactivate', + null, + this._uiHash(this) + ), + this.containers[t].containerCache.over && + (this.containers[t]._trigger( + 'out', + null, + this._uiHash(this) + ), + (this.containers[t].containerCache.over = 0)) + } + return ( + this.placeholder && + (this.placeholder[0].parentNode && + this.placeholder[0].parentNode.removeChild( + this.placeholder[0] + ), + 'original' !== this.options.helper && + this.helper && + this.helper[0].parentNode && + this.helper.remove(), + x.extend(this, { + helper: null, + dragging: !1, + reverting: !1, + _noFinalSort: null, + }), + this.domPosition.prev + ? x(this.domPosition.prev).after(this.currentItem) + : x(this.domPosition.parent).prepend( + this.currentItem + )), + this + ) + }, + serialize: function (e) { + var t = this._getItemsAsjQuery(e && e.connected), + i = [] + return ( + (e = e || {}), + x(t).each(function () { + var t = ( + x(e.item || this).attr(e.attribute || 'id') || '' + ).match(e.expression || /(.+)[\-=_](.+)/) + t && + i.push( + (e.key || t[1] + '[]') + + '=' + + (e.key && e.expression ? t[1] : t[2]) + ) + }), + !i.length && e.key && i.push(e.key + '='), + i.join('&') + ) + }, + toArray: function (t) { + var e = this._getItemsAsjQuery(t && t.connected), + i = [] + return ( + (t = t || {}), + e.each(function () { + i.push( + x(t.item || this).attr(t.attribute || 'id') || '' + ) + }), + i + ) + }, + _intersectsWith: function (t) { + var e = this.positionAbs.left, + i = e + this.helperProportions.width, + s = this.positionAbs.top, + o = s + this.helperProportions.height, + n = t.left, + r = n + t.width, + a = t.top, + h = a + t.height, + l = this.offset.click.top, + c = this.offset.click.left, + l = 'x' === this.options.axis || (a < s + l && s + l < h), + c = 'y' === this.options.axis || (n < e + c && e + c < r) + return 'pointer' === this.options.tolerance || + this.options.forcePointerForContainers || + ('pointer' !== this.options.tolerance && + this.helperProportions[ + this.floating ? 'width' : 'height' + ] > t[this.floating ? 'width' : 'height']) + ? l && c + : n < e + this.helperProportions.width / 2 && + i - this.helperProportions.width / 2 < r && + a < s + this.helperProportions.height / 2 && + o - this.helperProportions.height / 2 < h + }, + _intersectsWithPointer: function (t) { + var e = + 'x' === this.options.axis || + this._isOverAxis( + this.positionAbs.top + this.offset.click.top, + t.top, + t.height + ), + t = + 'y' === this.options.axis || + this._isOverAxis( + this.positionAbs.left + this.offset.click.left, + t.left, + t.width + ) + return ( + !(!e || !t) && + ((e = this.dragDirection.vertical), + (t = this.dragDirection.horizontal), + this.floating + ? 'right' === t || 'down' === e + ? 2 + : 1 + : e && ('down' === e ? 2 : 1)) + ) + }, + _intersectsWithSides: function (t) { + var e = this._isOverAxis( + this.positionAbs.top + this.offset.click.top, + t.top + t.height / 2, + t.height + ), + i = this._isOverAxis( + this.positionAbs.left + this.offset.click.left, + t.left + t.width / 2, + t.width + ), + s = this.dragDirection.vertical, + t = this.dragDirection.horizontal + return this.floating && t + ? ('right' === t && i) || ('left' === t && !i) + : s && (('down' === s && e) || ('up' === s && !e)) + }, + _getDragVerticalDirection: function () { + var t = this.positionAbs.top - this.lastPositionAbs.top + return 0 != t && (0 < t ? 'down' : 'up') + }, + _getDragHorizontalDirection: function () { + var t = this.positionAbs.left - this.lastPositionAbs.left + return 0 != t && (0 < t ? 'right' : 'left') + }, + refresh: function (t) { + return ( + this._refreshItems(t), + this._setHandleClassName(), + this.refreshPositions(), + this + ) + }, + _connectWith: function () { + var t = this.options + return t.connectWith.constructor === String + ? [t.connectWith] + : t.connectWith + }, + _getItemsAsjQuery: function (t) { + var e, + i, + s, + o, + n = [], + r = [], + a = this._connectWith() + if (a && t) + for (e = a.length - 1; 0 <= e; e--) + for ( + i = (s = x(a[e], this.document[0])).length - 1; + 0 <= i; + i-- + ) + (o = x.data(s[i], this.widgetFullName)) && + o !== this && + !o.options.disabled && + r.push([ + 'function' == typeof o.options.items + ? o.options.items.call(o.element) + : x(o.options.items, o.element) + .not('.ui-sortable-helper') + .not('.ui-sortable-placeholder'), + o, + ]) + function h() { + n.push(this) + } + for ( + r.push([ + 'function' == typeof this.options.items + ? this.options.items.call(this.element, null, { + options: this.options, + item: this.currentItem, + }) + : x(this.options.items, this.element) + .not('.ui-sortable-helper') + .not('.ui-sortable-placeholder'), + this, + ]), + e = r.length - 1; + 0 <= e; + e-- + ) + r[e][0].each(h) + return x(n) + }, + _removeCurrentsFromItems: function () { + var i = this.currentItem.find( + ':data(' + this.widgetName + '-item)' + ) + this.items = x.grep(this.items, function (t) { + for (var e = 0; e < i.length; e++) + if (i[e] === t.item[0]) return !1 + return !0 + }) + }, + _refreshItems: function (t) { + ;(this.items = []), (this.containers = [this]) + var e, + i, + s, + o, + n, + r, + a, + h, + l = this.items, + c = [ + [ + 'function' == typeof this.options.items + ? this.options.items.call(this.element[0], t, { + item: this.currentItem, + }) + : x(this.options.items, this.element), + this, + ], + ], + p = this._connectWith() + if (p && this.ready) + for (e = p.length - 1; 0 <= e; e--) + for ( + i = (s = x(p[e], this.document[0])).length - 1; + 0 <= i; + i-- + ) + (o = x.data(s[i], this.widgetFullName)) && + o !== this && + !o.options.disabled && + (c.push([ + 'function' == typeof o.options.items + ? o.options.items.call( + o.element[0], + t, + { item: this.currentItem } + ) + : x(o.options.items, o.element), + o, + ]), + this.containers.push(o)) + for (e = c.length - 1; 0 <= e; e--) + for ( + n = c[e][1], h = (r = c[e][(i = 0)]).length; + i < h; + i++ + ) + (a = x(r[i])).data(this.widgetName + '-item', n), + l.push({ + item: a, + instance: n, + width: 0, + height: 0, + left: 0, + top: 0, + }) + }, + _refreshItemPositions: function (t) { + for (var e, i, s = this.items.length - 1; 0 <= s; s--) + (e = this.items[s]), + (this.currentContainer && + e.instance !== this.currentContainer && + e.item[0] !== this.currentItem[0]) || + ((i = this.options.toleranceElement + ? x(this.options.toleranceElement, e.item) + : e.item), + t || + ((e.width = i.outerWidth()), + (e.height = i.outerHeight())), + (i = i.offset()), + (e.left = i.left), + (e.top = i.top)) + }, + refreshPositions: function (t) { + var e, i + if ( + ((this.floating = + !!this.items.length && + ('x' === this.options.axis || + this._isFloating(this.items[0].item))), + this.offsetParent && + this.helper && + (this.offset.parent = this._getParentOffset()), + this._refreshItemPositions(t), + this.options.custom && + this.options.custom.refreshContainers) + ) + this.options.custom.refreshContainers.call(this) + else + for (e = this.containers.length - 1; 0 <= e; e--) + (i = this.containers[e].element.offset()), + (this.containers[e].containerCache.left = i.left), + (this.containers[e].containerCache.top = i.top), + (this.containers[e].containerCache.width = + this.containers[e].element.outerWidth()), + (this.containers[e].containerCache.height = + this.containers[e].element.outerHeight()) + return this + }, + _createPlaceholder: function (i) { + var s, + o, + n = (i = i || this).options + ;(n.placeholder && n.placeholder.constructor !== String) || + ((s = n.placeholder), + (o = i.currentItem[0].nodeName.toLowerCase()), + (n.placeholder = { + element: function () { + var t = x('<' + o + '>', i.document[0]) + return ( + i + ._addClass( + t, + 'ui-sortable-placeholder', + s || i.currentItem[0].className + ) + ._removeClass(t, 'ui-sortable-helper'), + 'tbody' === o + ? i._createTrPlaceholder( + i.currentItem.find('tr').eq(0), + x('', i.document[0]).appendTo(t) + ) + : 'tr' === o + ? i._createTrPlaceholder(i.currentItem, t) + : 'img' === o && + t.attr('src', i.currentItem.attr('src')), + s || t.css('visibility', 'hidden'), + t + ) + }, + update: function (t, e) { + ;(s && !n.forcePlaceholderSize) || + ((e.height() && + (!n.forcePlaceholderSize || + ('tbody' !== o && 'tr' !== o))) || + e.height( + i.currentItem.innerHeight() - + parseInt( + i.currentItem.css( + 'paddingTop' + ) || 0, + 10 + ) - + parseInt( + i.currentItem.css( + 'paddingBottom' + ) || 0, + 10 + ) + ), + e.width() || + e.width( + i.currentItem.innerWidth() - + parseInt( + i.currentItem.css( + 'paddingLeft' + ) || 0, + 10 + ) - + parseInt( + i.currentItem.css( + 'paddingRight' + ) || 0, + 10 + ) + )) + }, + })), + (i.placeholder = x( + n.placeholder.element.call(i.element, i.currentItem) + )), + i.currentItem.after(i.placeholder), + n.placeholder.update(i, i.placeholder) + }, + _createTrPlaceholder: function (t, e) { + var i = this + t.children().each(function () { + x(' ', i.document[0]) + .attr('colspan', x(this).attr('colspan') || 1) + .appendTo(e) + }) + }, + _contactContainers: function (t) { + for ( + var e, + i, + s, + o, + n, + r, + a, + h, + l, + c = null, + p = null, + u = this.containers.length - 1; + 0 <= u; + u-- + ) + x.contains( + this.currentItem[0], + this.containers[u].element[0] + ) || + (this._intersectsWith(this.containers[u].containerCache) + ? (c && + x.contains( + this.containers[u].element[0], + c.element[0] + )) || + ((c = this.containers[u]), (p = u)) + : this.containers[u].containerCache.over && + (this.containers[u]._trigger( + 'out', + t, + this._uiHash(this) + ), + (this.containers[u].containerCache.over = 0))) + if (c) + if (1 === this.containers.length) + this.containers[p].containerCache.over || + (this.containers[p]._trigger( + 'over', + t, + this._uiHash(this) + ), + (this.containers[p].containerCache.over = 1)) + else { + for ( + i = 1e4, + s = null, + o = (h = + c.floating || + this._isFloating(this.currentItem)) + ? 'left' + : 'top', + n = h ? 'width' : 'height', + l = h ? 'pageX' : 'pageY', + e = this.items.length - 1; + 0 <= e; + e-- + ) + x.contains( + this.containers[p].element[0], + this.items[e].item[0] + ) && + this.items[e].item[0] !== this.currentItem[0] && + ((r = this.items[e].item.offset()[o]), + (a = !1), + t[l] - r > this.items[e][n] / 2 && (a = !0), + Math.abs(t[l] - r) < i && + ((i = Math.abs(t[l] - r)), + (s = this.items[e]), + (this.direction = a ? 'up' : 'down'))) + ;(s || this.options.dropOnEmpty) && + (this.currentContainer !== this.containers[p] + ? (s + ? this._rearrange(t, s, null, !0) + : this._rearrange( + t, + null, + this.containers[p].element, + !0 + ), + this._trigger('change', t, this._uiHash()), + this.containers[p]._trigger( + 'change', + t, + this._uiHash(this) + ), + (this.currentContainer = this.containers[p]), + this.options.placeholder.update( + this.currentContainer, + this.placeholder + ), + (this.scrollParent = + this.placeholder.scrollParent()), + this.scrollParent[0] !== this.document[0] && + 'HTML' !== this.scrollParent[0].tagName && + (this.overflowOffset = + this.scrollParent.offset()), + this.containers[p]._trigger( + 'over', + t, + this._uiHash(this) + ), + (this.containers[p].containerCache.over = 1)) + : this.currentContainer.containerCache.over || + (this.containers[p]._trigger( + 'over', + t, + this._uiHash() + ), + (this.currentContainer.containerCache.over = 1))) + } + }, + _createHelper: function (t) { + var e = this.options, + t = + 'function' == typeof e.helper + ? x( + e.helper.apply(this.element[0], [ + t, + this.currentItem, + ]) + ) + : 'clone' === e.helper + ? this.currentItem.clone() + : this.currentItem + return ( + t.parents('body').length || + this.appendTo[0].appendChild(t[0]), + t[0] === this.currentItem[0] && + (this._storedCSS = { + width: this.currentItem[0].style.width, + height: this.currentItem[0].style.height, + position: this.currentItem.css('position'), + top: this.currentItem.css('top'), + left: this.currentItem.css('left'), + }), + (t[0].style.width && !e.forceHelperSize) || + t.width(this.currentItem.width()), + (t[0].style.height && !e.forceHelperSize) || + t.height(this.currentItem.height()), + t + ) + }, + _adjustOffsetFromHelper: function (t) { + 'string' == typeof t && (t = t.split(' ')), + 'left' in + (t = Array.isArray(t) + ? { left: +t[0], top: +t[1] || 0 } + : t) && + (this.offset.click.left = t.left + this.margins.left), + 'right' in t && + (this.offset.click.left = + this.helperProportions.width - + t.right + + this.margins.left), + 'top' in t && + (this.offset.click.top = t.top + this.margins.top), + 'bottom' in t && + (this.offset.click.top = + this.helperProportions.height - + t.bottom + + this.margins.top) + }, + _getParentOffset: function () { + this.offsetParent = this.helper.offsetParent() + var t = this.offsetParent.offset() + return ( + 'absolute' === this.cssPosition && + this.scrollParent[0] !== this.document[0] && + x.contains( + this.scrollParent[0], + this.offsetParent[0] + ) && + ((t.left += this.scrollParent.scrollLeft()), + (t.top += this.scrollParent.scrollTop())), + { + top: + (t = + this.offsetParent[0] === + this.document[0].body || + (this.offsetParent[0].tagName && + 'html' === + this.offsetParent[0].tagName.toLowerCase() && + x.ui.ie) + ? { top: 0, left: 0 } + : t).top + + (parseInt( + this.offsetParent.css('borderTopWidth'), + 10 + ) || 0), + left: + t.left + + (parseInt( + this.offsetParent.css('borderLeftWidth'), + 10 + ) || 0), + } + ) + }, + _getRelativeOffset: function () { + if ('relative' !== this.cssPosition) return { top: 0, left: 0 } + var t = this.currentItem.position() + return { + top: + t.top - + (parseInt(this.helper.css('top'), 10) || 0) + + this.scrollParent.scrollTop(), + left: + t.left - + (parseInt(this.helper.css('left'), 10) || 0) + + this.scrollParent.scrollLeft(), + } + }, + _cacheMargins: function () { + this.margins = { + left: parseInt(this.currentItem.css('marginLeft'), 10) || 0, + top: parseInt(this.currentItem.css('marginTop'), 10) || 0, + } + }, + _cacheHelperProportions: function () { + this.helperProportions = { + width: this.helper.outerWidth(), + height: this.helper.outerHeight(), + } + }, + _setContainment: function () { + var t, + e, + i = this.options + 'parent' === i.containment && + (i.containment = this.helper[0].parentNode), + ('document' !== i.containment && + 'window' !== i.containment) || + (this.containment = [ + 0 - + this.offset.relative.left - + this.offset.parent.left, + 0 - + this.offset.relative.top - + this.offset.parent.top, + 'document' === i.containment + ? this.document.width() + : this.window.width() - + this.helperProportions.width - + this.margins.left, + ('document' === i.containment + ? this.document.height() || + document.body.parentNode.scrollHeight + : this.window.height() || + this.document[0].body.parentNode + .scrollHeight) - + this.helperProportions.height - + this.margins.top, + ]), + /^(document|window|parent)$/.test(i.containment) || + ((t = x(i.containment)[0]), + (e = x(i.containment).offset()), + (i = 'hidden' !== x(t).css('overflow')), + (this.containment = [ + e.left + + (parseInt(x(t).css('borderLeftWidth'), 10) || + 0) + + (parseInt(x(t).css('paddingLeft'), 10) || 0) - + this.margins.left, + e.top + + (parseInt(x(t).css('borderTopWidth'), 10) || + 0) + + (parseInt(x(t).css('paddingTop'), 10) || 0) - + this.margins.top, + e.left + + (i + ? Math.max(t.scrollWidth, t.offsetWidth) + : t.offsetWidth) - + (parseInt(x(t).css('borderLeftWidth'), 10) || + 0) - + (parseInt(x(t).css('paddingRight'), 10) || 0) - + this.helperProportions.width - + this.margins.left, + e.top + + (i + ? Math.max(t.scrollHeight, t.offsetHeight) + : t.offsetHeight) - + (parseInt(x(t).css('borderTopWidth'), 10) || + 0) - + (parseInt(x(t).css('paddingBottom'), 10) || 0) - + this.helperProportions.height - + this.margins.top, + ])) + }, + _convertPositionTo: function (t, e) { + e = e || this.position + var i = 'absolute' === t ? 1 : -1, + s = + 'absolute' !== this.cssPosition || + (this.scrollParent[0] !== this.document[0] && + x.contains( + this.scrollParent[0], + this.offsetParent[0] + )) + ? this.scrollParent + : this.offsetParent, + t = /(html|body)/i.test(s[0].tagName) + return { + top: + e.top + + this.offset.relative.top * i + + this.offset.parent.top * i - + ('fixed' === this.cssPosition + ? -this.scrollParent.scrollTop() + : t + ? 0 + : s.scrollTop()) * + i, + left: + e.left + + this.offset.relative.left * i + + this.offset.parent.left * i - + ('fixed' === this.cssPosition + ? -this.scrollParent.scrollLeft() + : t + ? 0 + : s.scrollLeft()) * + i, + } + }, + _generatePosition: function (t) { + var e = this.options, + i = t.pageX, + s = t.pageY, + o = + 'absolute' !== this.cssPosition || + (this.scrollParent[0] !== this.document[0] && + x.contains( + this.scrollParent[0], + this.offsetParent[0] + )) + ? this.scrollParent + : this.offsetParent, + n = /(html|body)/i.test(o[0].tagName) + return ( + 'relative' !== this.cssPosition || + (this.scrollParent[0] !== this.document[0] && + this.scrollParent[0] !== this.offsetParent[0]) || + (this.offset.relative = this._getRelativeOffset()), + this.originalPosition && + (this.containment && + (t.pageX - this.offset.click.left < + this.containment[0] && + (i = + this.containment[0] + + this.offset.click.left), + t.pageY - this.offset.click.top < + this.containment[1] && + (s = + this.containment[1] + + this.offset.click.top), + t.pageX - this.offset.click.left > + this.containment[2] && + (i = + this.containment[2] + + this.offset.click.left), + t.pageY - this.offset.click.top > + this.containment[3] && + (s = + this.containment[3] + + this.offset.click.top)), + e.grid && + ((t = + this.originalPageY + + Math.round( + (s - this.originalPageY) / e.grid[1] + ) * + e.grid[1]), + (s = + !this.containment || + (t - this.offset.click.top >= + this.containment[1] && + t - this.offset.click.top <= + this.containment[3]) + ? t + : t - this.offset.click.top >= + this.containment[1] + ? t - e.grid[1] + : t + e.grid[1]), + (t = + this.originalPageX + + Math.round( + (i - this.originalPageX) / e.grid[0] + ) * + e.grid[0]), + (i = + !this.containment || + (t - this.offset.click.left >= + this.containment[0] && + t - this.offset.click.left <= + this.containment[2]) + ? t + : t - this.offset.click.left >= + this.containment[0] + ? t - e.grid[0] + : t + e.grid[0]))), + { + top: + s - + this.offset.click.top - + this.offset.relative.top - + this.offset.parent.top + + ('fixed' === this.cssPosition + ? -this.scrollParent.scrollTop() + : n + ? 0 + : o.scrollTop()), + left: + i - + this.offset.click.left - + this.offset.relative.left - + this.offset.parent.left + + ('fixed' === this.cssPosition + ? -this.scrollParent.scrollLeft() + : n + ? 0 + : o.scrollLeft()), + } + ) + }, + _rearrange: function (t, e, i, s) { + i + ? i[0].appendChild(this.placeholder[0]) + : e.item[0].parentNode.insertBefore( + this.placeholder[0], + 'down' === this.direction + ? e.item[0] + : e.item[0].nextSibling + ), + (this.counter = this.counter ? ++this.counter : 1) + var o = this.counter + this._delay(function () { + o === this.counter && this.refreshPositions(!s) + }) + }, + _clear: function (t, e) { + this.reverting = !1 + var i, + s = [] + if ( + (!this._noFinalSort && + this.currentItem.parent().length && + this.placeholder.before(this.currentItem), + (this._noFinalSort = null), + this.helper[0] === this.currentItem[0]) + ) { + for (i in this._storedCSS) + ('auto' !== this._storedCSS[i] && + 'static' !== this._storedCSS[i]) || + (this._storedCSS[i] = '') + this.currentItem.css(this._storedCSS), + this._removeClass( + this.currentItem, + 'ui-sortable-helper' + ) + } else this.currentItem.show() + function o(e, i, s) { + return function (t) { + s._trigger(e, t, i._uiHash(i)) + } + } + for ( + this.fromOutside && + !e && + s.push(function (t) { + this._trigger( + 'receive', + t, + this._uiHash(this.fromOutside) + ) + }), + (!this.fromOutside && + this.domPosition.prev === + this.currentItem + .prev() + .not('.ui-sortable-helper')[0] && + this.domPosition.parent === + this.currentItem.parent()[0]) || + e || + s.push(function (t) { + this._trigger('update', t, this._uiHash()) + }), + this !== this.currentContainer && + (e || + (s.push(function (t) { + this._trigger('remove', t, this._uiHash()) + }), + s.push( + function (e) { + return function (t) { + e._trigger( + 'receive', + t, + this._uiHash(this) + ) + } + }.call(this, this.currentContainer) + ), + s.push( + function (e) { + return function (t) { + e._trigger( + 'update', + t, + this._uiHash(this) + ) + } + }.call(this, this.currentContainer) + ))), + i = this.containers.length - 1; + 0 <= i; + i-- + ) + e || s.push(o('deactivate', this, this.containers[i])), + this.containers[i].containerCache.over && + (s.push(o('out', this, this.containers[i])), + (this.containers[i].containerCache.over = 0)) + if ( + (this.storedCursor && + (this.document + .find('body') + .css('cursor', this.storedCursor), + this.storedStylesheet.remove()), + this._storedOpacity && + this.helper.css('opacity', this._storedOpacity), + this._storedZIndex && + this.helper.css( + 'zIndex', + 'auto' === this._storedZIndex + ? '' + : this._storedZIndex + ), + (this.dragging = !1), + e || this._trigger('beforeStop', t, this._uiHash()), + this.placeholder[0].parentNode.removeChild( + this.placeholder[0] + ), + this.cancelHelperRemoval || + (this.helper[0] !== this.currentItem[0] && + this.helper.remove(), + (this.helper = null)), + !e) + ) { + for (i = 0; i < s.length; i++) s[i].call(this, t) + this._trigger('stop', t, this._uiHash()) + } + return (this.fromOutside = !1), !this.cancelHelperRemoval + }, + _trigger: function () { + !1 === x.Widget.prototype._trigger.apply(this, arguments) && + this.cancel() + }, + _uiHash: function (t) { + var e = t || this + return { + helper: e.helper, + placeholder: e.placeholder || x([]), + position: e.position, + originalPosition: e.originalPosition, + offset: e.positionAbs, + item: e.currentItem, + sender: t ? t.element : null, + } + }, + }), + x.widget('ui.accordion', { + version: '1.13.1', + options: { + active: 0, + animate: {}, + classes: { + 'ui-accordion-header': 'ui-corner-top', + 'ui-accordion-header-collapsed': 'ui-corner-all', + 'ui-accordion-content': 'ui-corner-bottom', + }, + collapsible: !1, + event: 'click', + header: function (t) { + return t + .find('> li > :first-child') + .add(t.find('> :not(li)').even()) + }, + heightStyle: 'auto', + icons: { + activeHeader: 'ui-icon-triangle-1-s', + header: 'ui-icon-triangle-1-e', + }, + activate: null, + beforeActivate: null, + }, + hideProps: { + borderTopWidth: 'hide', + borderBottomWidth: 'hide', + paddingTop: 'hide', + paddingBottom: 'hide', + height: 'hide', + }, + showProps: { + borderTopWidth: 'show', + borderBottomWidth: 'show', + paddingTop: 'show', + paddingBottom: 'show', + height: 'show', + }, + _create: function () { + var t = this.options + ;(this.prevShow = this.prevHide = x()), + this._addClass('ui-accordion', 'ui-widget ui-helper-reset'), + this.element.attr('role', 'tablist'), + t.collapsible || + (!1 !== t.active && null != t.active) || + (t.active = 0), + this._processPanels(), + t.active < 0 && (t.active += this.headers.length), + this._refresh() + }, + _getCreateEventData: function () { + return { + header: this.active, + panel: this.active.length ? this.active.next() : x(), + } + }, + _createIcons: function () { + var t, + e = this.options.icons + e && + ((t = x('')), + this._addClass( + t, + 'ui-accordion-header-icon', + 'ui-icon ' + e.header + ), + t.prependTo(this.headers), + (t = this.active.children('.ui-accordion-header-icon')), + this._removeClass(t, e.header) + ._addClass(t, null, e.activeHeader) + ._addClass(this.headers, 'ui-accordion-icons')) + }, + _destroyIcons: function () { + this._removeClass(this.headers, 'ui-accordion-icons'), + this.headers.children('.ui-accordion-header-icon').remove() + }, + _destroy: function () { + var t + this.element.removeAttr('role'), + this.headers + .removeAttr( + 'role aria-expanded aria-selected aria-controls tabIndex' + ) + .removeUniqueId(), + this._destroyIcons(), + (t = this.headers + .next() + .css('display', '') + .removeAttr('role aria-hidden aria-labelledby') + .removeUniqueId()), + 'content' !== this.options.heightStyle && + t.css('height', '') + }, + _setOption: function (t, e) { + 'active' !== t + ? ('event' === t && + (this.options.event && + this._off(this.headers, this.options.event), + this._setupEvents(e)), + this._super(t, e), + 'collapsible' !== t || + e || + !1 !== this.options.active || + this._activate(0), + 'icons' === t && + (this._destroyIcons(), e && this._createIcons())) + : this._activate(e) + }, + _setOptionDisabled: function (t) { + this._super(t), + this.element.attr('aria-disabled', t), + this._toggleClass(null, 'ui-state-disabled', !!t), + this._toggleClass( + this.headers.add(this.headers.next()), + null, + 'ui-state-disabled', + !!t + ) + }, + _keydown: function (t) { + if (!t.altKey && !t.ctrlKey) { + var e = x.ui.keyCode, + i = this.headers.length, + s = this.headers.index(t.target), + o = !1 + switch (t.keyCode) { + case e.RIGHT: + case e.DOWN: + o = this.headers[(s + 1) % i] + break + case e.LEFT: + case e.UP: + o = this.headers[(s - 1 + i) % i] + break + case e.SPACE: + case e.ENTER: + this._eventHandler(t) + break + case e.HOME: + o = this.headers[0] + break + case e.END: + o = this.headers[i - 1] + } + o && + (x(t.target).attr('tabIndex', -1), + x(o).attr('tabIndex', 0), + x(o).trigger('focus'), + t.preventDefault()) + } + }, + _panelKeyDown: function (t) { + t.keyCode === x.ui.keyCode.UP && + t.ctrlKey && + x(t.currentTarget).prev().trigger('focus') + }, + refresh: function () { + var t = this.options + this._processPanels(), + (!1 === t.active && !0 === t.collapsible) || + !this.headers.length + ? ((t.active = !1), (this.active = x())) + : !1 === t.active + ? this._activate(0) + : this.active.length && + !x.contains(this.element[0], this.active[0]) + ? this.headers.length === + this.headers.find('.ui-state-disabled').length + ? ((t.active = !1), (this.active = x())) + : this._activate(Math.max(0, t.active - 1)) + : (t.active = this.headers.index(this.active)), + this._destroyIcons(), + this._refresh() + }, + _processPanels: function () { + var t = this.headers, + e = this.panels + 'function' == typeof this.options.header + ? (this.headers = this.options.header(this.element)) + : (this.headers = this.element.find(this.options.header)), + this._addClass( + this.headers, + 'ui-accordion-header ui-accordion-header-collapsed', + 'ui-state-default' + ), + (this.panels = this.headers + .next() + .filter(':not(.ui-accordion-content-active)') + .hide()), + this._addClass( + this.panels, + 'ui-accordion-content', + 'ui-helper-reset ui-widget-content' + ), + e && + (this._off(t.not(this.headers)), + this._off(e.not(this.panels))) + }, + _refresh: function () { + var i, + t = this.options, + e = t.heightStyle, + s = this.element.parent() + ;(this.active = this._findActive(t.active)), + this._addClass( + this.active, + 'ui-accordion-header-active', + 'ui-state-active' + )._removeClass( + this.active, + 'ui-accordion-header-collapsed' + ), + this._addClass( + this.active.next(), + 'ui-accordion-content-active' + ), + this.active.next().show(), + this.headers + .attr('role', 'tab') + .each(function () { + var t = x(this), + e = t.uniqueId().attr('id'), + i = t.next(), + s = i.uniqueId().attr('id') + t.attr('aria-controls', s), + i.attr('aria-labelledby', e) + }) + .next() + .attr('role', 'tabpanel'), + this.headers + .not(this.active) + .attr({ + 'aria-selected': 'false', + 'aria-expanded': 'false', + tabIndex: -1, + }) + .next() + .attr({ 'aria-hidden': 'true' }) + .hide(), + this.active.length + ? this.active + .attr({ + 'aria-selected': 'true', + 'aria-expanded': 'true', + tabIndex: 0, + }) + .next() + .attr({ 'aria-hidden': 'false' }) + : this.headers.eq(0).attr('tabIndex', 0), + this._createIcons(), + this._setupEvents(t.event), + 'fill' === e + ? ((i = s.height()), + this.element.siblings(':visible').each(function () { + var t = x(this), + e = t.css('position') + 'absolute' !== e && + 'fixed' !== e && + (i -= t.outerHeight(!0)) + }), + this.headers.each(function () { + i -= x(this).outerHeight(!0) + }), + this.headers + .next() + .each(function () { + x(this).height( + Math.max( + 0, + i - + x(this).innerHeight() + + x(this).height() + ) + ) + }) + .css('overflow', 'auto')) + : 'auto' === e && + ((i = 0), + this.headers + .next() + .each(function () { + var t = x(this).is(':visible') + t || x(this).show(), + (i = Math.max( + i, + x(this).css('height', '').height() + )), + t || x(this).hide() + }) + .height(i)) + }, + _activate: function (t) { + t = this._findActive(t)[0] + t !== this.active[0] && + ((t = t || this.active[0]), + this._eventHandler({ + target: t, + currentTarget: t, + preventDefault: x.noop, + })) + }, + _findActive: function (t) { + return 'number' == typeof t ? this.headers.eq(t) : x() + }, + _setupEvents: function (t) { + var i = { keydown: '_keydown' } + t && + x.each(t.split(' '), function (t, e) { + i[e] = '_eventHandler' + }), + this._off(this.headers.add(this.headers.next())), + this._on(this.headers, i), + this._on(this.headers.next(), { keydown: '_panelKeyDown' }), + this._hoverable(this.headers), + this._focusable(this.headers) + }, + _eventHandler: function (t) { + var e = this.options, + i = this.active, + s = x(t.currentTarget), + o = s[0] === i[0], + n = o && e.collapsible, + r = n ? x() : s.next(), + a = i.next(), + r = { + oldHeader: i, + oldPanel: a, + newHeader: n ? x() : s, + newPanel: r, + } + t.preventDefault(), + (o && !e.collapsible) || + !1 === this._trigger('beforeActivate', t, r) || + ((e.active = !n && this.headers.index(s)), + (this.active = o ? x() : s), + this._toggle(r), + this._removeClass( + i, + 'ui-accordion-header-active', + 'ui-state-active' + ), + e.icons && + ((i = i.children('.ui-accordion-header-icon')), + this._removeClass( + i, + null, + e.icons.activeHeader + )._addClass(i, null, e.icons.header)), + o || + (this._removeClass( + s, + 'ui-accordion-header-collapsed' + )._addClass( + s, + 'ui-accordion-header-active', + 'ui-state-active' + ), + e.icons && + ((o = s.children('.ui-accordion-header-icon')), + this._removeClass( + o, + null, + e.icons.header + )._addClass(o, null, e.icons.activeHeader)), + this._addClass( + s.next(), + 'ui-accordion-content-active' + ))) + }, + _toggle: function (t) { + var e = t.newPanel, + i = this.prevShow.length ? this.prevShow : t.oldPanel + this.prevShow.add(this.prevHide).stop(!0, !0), + (this.prevShow = e), + (this.prevHide = i), + this.options.animate + ? this._animate(e, i, t) + : (i.hide(), e.show(), this._toggleComplete(t)), + i.attr({ 'aria-hidden': 'true' }), + i.prev().attr({ + 'aria-selected': 'false', + 'aria-expanded': 'false', + }), + e.length && i.length + ? i + .prev() + .attr({ tabIndex: -1, 'aria-expanded': 'false' }) + : e.length && + this.headers + .filter(function () { + return ( + 0 === + parseInt(x(this).attr('tabIndex'), 10) + ) + }) + .attr('tabIndex', -1), + e.attr('aria-hidden', 'false').prev().attr({ + 'aria-selected': 'true', + 'aria-expanded': 'true', + tabIndex: 0, + }) + }, + _animate: function (t, i, e) { + var s, + o, + n, + r = this, + a = 0, + h = t.css('box-sizing'), + l = t.length && (!i.length || t.index() < i.index()), + c = this.options.animate || {}, + p = (l && c.down) || c, + l = function () { + r._toggleComplete(e) + } + return ( + (o = + (o = 'string' == typeof p ? p : o) || + p.easing || + c.easing), + (n = + (n = 'number' == typeof p ? p : n) || + p.duration || + c.duration), + i.length + ? t.length + ? ((s = t.show().outerHeight()), + i.animate(this.hideProps, { + duration: n, + easing: o, + step: function (t, e) { + e.now = Math.round(t) + }, + }), + void t.hide().animate(this.showProps, { + duration: n, + easing: o, + complete: l, + step: function (t, e) { + ;(e.now = Math.round(t)), + 'height' !== e.prop + ? 'content-box' === h && + (a += e.now) + : 'content' !== + r.options.heightStyle && + ((e.now = Math.round( + s - i.outerHeight() - a + )), + (a = 0)) + }, + })) + : i.animate(this.hideProps, n, o, l) + : t.animate(this.showProps, n, o, l) + ) + }, + _toggleComplete: function (t) { + var e = t.oldPanel, + i = e.prev() + this._removeClass(e, 'ui-accordion-content-active'), + this._removeClass( + i, + 'ui-accordion-header-active' + )._addClass(i, 'ui-accordion-header-collapsed'), + e.length && + (e.parent()[0].className = e.parent()[0].className), + this._trigger('activate', null, t) + }, + }) + var g = /ui-corner-([a-z]){2,6}/g + x.widget('ui.controlgroup', { + version: '1.13.1', + defaultElement: '
', + options: { + direction: 'horizontal', + disabled: null, + onlyVisible: !0, + items: { + button: 'input[type=button], input[type=submit], input[type=reset], button, a', + controlgroupLabel: '.ui-controlgroup-label', + checkboxradio: "input[type='checkbox'], input[type='radio']", + selectmenu: 'select', + spinner: '.ui-spinner-input', + }, + }, + _create: function () { + this._enhance() + }, + _enhance: function () { + this.element.attr('role', 'toolbar'), this.refresh() + }, + _destroy: function () { + this._callChildMethod('destroy'), + this.childWidgets.removeData('ui-controlgroup-data'), + this.element.removeAttr('role'), + this.options.items.controlgroupLabel && + this.element + .find(this.options.items.controlgroupLabel) + .find('.ui-controlgroup-label-contents') + .contents() + .unwrap() + }, + _initWidgets: function () { + var n = this, + r = [] + x.each(this.options.items, function (s, t) { + var e, + o = {} + if (t) + return 'controlgroupLabel' === s + ? ((e = n.element.find(t)).each(function () { + var t = x(this) + t.children('.ui-controlgroup-label-contents') + .length || + t + .contents() + .wrapAll( + "" + ) + }), + n._addClass( + e, + null, + 'ui-widget ui-widget-content ui-state-default' + ), + void (r = r.concat(e.get()))) + : void ( + x.fn[s] && + ((o = n['_' + s + 'Options'] + ? n['_' + s + 'Options']('middle') + : { classes: {} }), + n.element.find(t).each(function () { + var t = x(this), + e = t[s]('instance'), + i = x.widget.extend({}, o) + ;('button' === s && + t.parent('.ui-spinner').length) || + ((e = e || t[s]()[s]('instance')) && + (i.classes = n._resolveClassesValues( + i.classes, + e + )), + t[s](i), + (i = t[s]('widget')), + x.data( + i[0], + 'ui-controlgroup-data', + e || t[s]('instance') + ), + r.push(i[0])) + })) + ) + }), + (this.childWidgets = x(x.uniqueSort(r))), + this._addClass(this.childWidgets, 'ui-controlgroup-item') + }, + _callChildMethod: function (e) { + this.childWidgets.each(function () { + var t = x(this).data('ui-controlgroup-data') + t && t[e] && t[e]() + }) + }, + _updateCornerClass: function (t, e) { + e = this._buildSimpleOptions(e, 'label').classes.label + this._removeClass( + t, + null, + 'ui-corner-top ui-corner-bottom ui-corner-left ui-corner-right ui-corner-all' + ), + this._addClass(t, null, e) + }, + _buildSimpleOptions: function (t, e) { + var i = 'vertical' === this.options.direction, + s = { classes: {} } + return ( + (s.classes[e] = { + middle: '', + first: 'ui-corner-' + (i ? 'top' : 'left'), + last: 'ui-corner-' + (i ? 'bottom' : 'right'), + only: 'ui-corner-all', + }[t]), + s + ) + }, + _spinnerOptions: function (t) { + t = this._buildSimpleOptions(t, 'ui-spinner') + return ( + (t.classes['ui-spinner-up'] = ''), + (t.classes['ui-spinner-down'] = ''), + t + ) + }, + _buttonOptions: function (t) { + return this._buildSimpleOptions(t, 'ui-button') + }, + _checkboxradioOptions: function (t) { + return this._buildSimpleOptions(t, 'ui-checkboxradio-label') + }, + _selectmenuOptions: function (t) { + var e = 'vertical' === this.options.direction + return { + width: e && 'auto', + classes: { + middle: { + 'ui-selectmenu-button-open': '', + 'ui-selectmenu-button-closed': '', + }, + first: { + 'ui-selectmenu-button-open': + 'ui-corner-' + (e ? 'top' : 'tl'), + 'ui-selectmenu-button-closed': + 'ui-corner-' + (e ? 'top' : 'left'), + }, + last: { + 'ui-selectmenu-button-open': e ? '' : 'ui-corner-tr', + 'ui-selectmenu-button-closed': + 'ui-corner-' + (e ? 'bottom' : 'right'), + }, + only: { + 'ui-selectmenu-button-open': 'ui-corner-top', + 'ui-selectmenu-button-closed': 'ui-corner-all', + }, + }[t], + } + }, + _resolveClassesValues: function (i, s) { + var o = {} + return ( + x.each(i, function (t) { + var e = s.options.classes[t] || '', + e = String.prototype.trim.call(e.replace(g, '')) + o[t] = (e + ' ' + i[t]).replace(/\s+/g, ' ') + }), + o + ) + }, + _setOption: function (t, e) { + 'direction' === t && + this._removeClass('ui-controlgroup-' + this.options.direction), + this._super(t, e), + 'disabled' !== t + ? this.refresh() + : this._callChildMethod(e ? 'disable' : 'enable') + }, + refresh: function () { + var o, + n = this + this._addClass( + 'ui-controlgroup ui-controlgroup-' + this.options.direction + ), + 'horizontal' === this.options.direction && + this._addClass(null, 'ui-helper-clearfix'), + this._initWidgets(), + (o = this.childWidgets), + (o = this.options.onlyVisible ? o.filter(':visible') : o) + .length && + (x.each(['first', 'last'], function (t, e) { + var i, + s = o[e]().data('ui-controlgroup-data') + s && n['_' + s.widgetName + 'Options'] + ? (((i = n['_' + s.widgetName + 'Options']( + 1 === o.length ? 'only' : e + )).classes = n._resolveClassesValues( + i.classes, + s + )), + s.element[s.widgetName](i)) + : n._updateCornerClass(o[e](), e) + }), + this._callChildMethod('refresh')) + }, + }) + x.widget('ui.checkboxradio', [ + x.ui.formResetMixin, + { + version: '1.13.1', + options: { + disabled: null, + label: null, + icon: !0, + classes: { + 'ui-checkboxradio-label': 'ui-corner-all', + 'ui-checkboxradio-icon': 'ui-corner-all', + }, + }, + _getCreateOptions: function () { + var t, + e = this, + i = this._super() || {} + return ( + this._readType(), + (t = this.element.labels()), + (this.label = x(t[t.length - 1])), + this.label.length || + x.error('No label found for checkboxradio widget'), + (this.originalLabel = ''), + this.label + .contents() + .not(this.element[0]) + .each(function () { + e.originalLabel += + 3 === this.nodeType + ? x(this).text() + : this.outerHTML + }), + this.originalLabel && (i.label = this.originalLabel), + null != (t = this.element[0].disabled) && (i.disabled = t), + i + ) + }, + _create: function () { + var t = this.element[0].checked + this._bindFormResetHandler(), + null == this.options.disabled && + (this.options.disabled = this.element[0].disabled), + this._setOption('disabled', this.options.disabled), + this._addClass( + 'ui-checkboxradio', + 'ui-helper-hidden-accessible' + ), + this._addClass( + this.label, + 'ui-checkboxradio-label', + 'ui-button ui-widget' + ), + 'radio' === this.type && + this._addClass( + this.label, + 'ui-checkboxradio-radio-label' + ), + this.options.label && + this.options.label !== this.originalLabel + ? this._updateLabel() + : this.originalLabel && + (this.options.label = this.originalLabel), + this._enhance(), + t && + this._addClass( + this.label, + 'ui-checkboxradio-checked', + 'ui-state-active' + ), + this._on({ + change: '_toggleClasses', + focus: function () { + this._addClass( + this.label, + null, + 'ui-state-focus ui-visual-focus' + ) + }, + blur: function () { + this._removeClass( + this.label, + null, + 'ui-state-focus ui-visual-focus' + ) + }, + }) + }, + _readType: function () { + var t = this.element[0].nodeName.toLowerCase() + ;(this.type = this.element[0].type), + ('input' === t && /radio|checkbox/.test(this.type)) || + x.error( + "Can't create checkboxradio on element.nodeName=" + + t + + ' and element.type=' + + this.type + ) + }, + _enhance: function () { + this._updateIcon(this.element[0].checked) + }, + widget: function () { + return this.label + }, + _getRadioGroup: function () { + var t = this.element[0].name, + e = "input[name='" + x.escapeSelector(t) + "']" + return t + ? (this.form.length + ? x(this.form[0].elements).filter(e) + : x(e).filter(function () { + return 0 === x(this)._form().length + }) + ).not(this.element) + : x([]) + }, + _toggleClasses: function () { + var t = this.element[0].checked + this._toggleClass( + this.label, + 'ui-checkboxradio-checked', + 'ui-state-active', + t + ), + this.options.icon && + 'checkbox' === this.type && + this._toggleClass( + this.icon, + null, + 'ui-icon-check ui-state-checked', + t + )._toggleClass(this.icon, null, 'ui-icon-blank', !t), + 'radio' === this.type && + this._getRadioGroup().each(function () { + var t = x(this).checkboxradio('instance') + t && + t._removeClass( + t.label, + 'ui-checkboxradio-checked', + 'ui-state-active' + ) + }) + }, + _destroy: function () { + this._unbindFormResetHandler(), + this.icon && (this.icon.remove(), this.iconSpace.remove()) + }, + _setOption: function (t, e) { + if ('label' !== t || e) { + if ((this._super(t, e), 'disabled' === t)) + return ( + this._toggleClass( + this.label, + null, + 'ui-state-disabled', + e + ), + void (this.element[0].disabled = e) + ) + this.refresh() + } + }, + _updateIcon: function (t) { + var e = 'ui-icon ui-icon-background ' + this.options.icon + ? (this.icon || + ((this.icon = x('')), + (this.iconSpace = x(' ')), + this._addClass( + this.iconSpace, + 'ui-checkboxradio-icon-space' + )), + 'checkbox' === this.type + ? ((e += t + ? 'ui-icon-check ui-state-checked' + : 'ui-icon-blank'), + this._removeClass( + this.icon, + null, + t ? 'ui-icon-blank' : 'ui-icon-check' + )) + : (e += 'ui-icon-blank'), + this._addClass(this.icon, 'ui-checkboxradio-icon', e), + t || + this._removeClass( + this.icon, + null, + 'ui-icon-check ui-state-checked' + ), + this.icon.prependTo(this.label).after(this.iconSpace)) + : void 0 !== this.icon && + (this.icon.remove(), + this.iconSpace.remove(), + delete this.icon) + }, + _updateLabel: function () { + var t = this.label.contents().not(this.element[0]) + this.icon && (t = t.not(this.icon[0])), + (t = this.iconSpace + ? t.not(this.iconSpace[0]) + : t).remove(), + this.label.append(this.options.label) + }, + refresh: function () { + var t = this.element[0].checked, + e = this.element[0].disabled + this._updateIcon(t), + this._toggleClass( + this.label, + 'ui-checkboxradio-checked', + 'ui-state-active', + t + ), + null !== this.options.label && this._updateLabel(), + e !== this.options.disabled && + this._setOptions({ disabled: e }) + }, + }, + ]) + var m + x.ui.checkboxradio + x.widget('ui.button', { + version: '1.13.1', + defaultElement: '" + ) + .button({ + label: x('').text(this.options.closeText).html(), + icon: 'ui-icon-closethick', + showLabel: !1, + }) + .appendTo(this.uiDialogTitlebar)), + this._addClass( + this.uiDialogTitlebarClose, + 'ui-dialog-titlebar-close' + ), + this._on(this.uiDialogTitlebarClose, { + click: function (t) { + t.preventDefault(), this.close(t) + }, + }), + (t = x('').uniqueId().prependTo(this.uiDialogTitlebar)), + this._addClass(t, 'ui-dialog-title'), + this._title(t), + this.uiDialogTitlebar.prependTo(this.uiDialog), + this.uiDialog.attr({ 'aria-labelledby': t.attr('id') }) + }, + _title: function (t) { + this.options.title ? t.text(this.options.title) : t.html(' ') + }, + _createButtonPane: function () { + ;(this.uiDialogButtonPane = x('
')), + this._addClass( + this.uiDialogButtonPane, + 'ui-dialog-buttonpane', + 'ui-widget-content ui-helper-clearfix' + ), + (this.uiButtonSet = x('
').appendTo( + this.uiDialogButtonPane + )), + this._addClass(this.uiButtonSet, 'ui-dialog-buttonset'), + this._createButtons() + }, + _createButtons: function () { + var s = this, + t = this.options.buttons + this.uiDialogButtonPane.remove(), + this.uiButtonSet.empty(), + x.isEmptyObject(t) || (Array.isArray(t) && !t.length) + ? this._removeClass(this.uiDialog, 'ui-dialog-buttons') + : (x.each(t, function (t, e) { + var i + ;(e = x.extend( + { type: 'button' }, + (e = + 'function' == typeof e + ? { click: e, text: t } + : e) + )), + (i = e.click), + (t = { + icon: e.icon, + iconPosition: e.iconPosition, + showLabel: e.showLabel, + icons: e.icons, + text: e.text, + }), + delete e.click, + delete e.icon, + delete e.iconPosition, + delete e.showLabel, + delete e.icons, + 'boolean' == typeof e.text && delete e.text, + x('', e) + .button(t) + .appendTo(s.uiButtonSet) + .on('click', function () { + i.apply(s.element[0], arguments) + }) + }), + this._addClass(this.uiDialog, 'ui-dialog-buttons'), + this.uiDialogButtonPane.appendTo(this.uiDialog)) + }, + _makeDraggable: function () { + var o = this, + n = this.options + function r(t) { + return { position: t.position, offset: t.offset } + } + this.uiDialog.draggable({ + cancel: '.ui-dialog-content, .ui-dialog-titlebar-close', + handle: '.ui-dialog-titlebar', + containment: 'document', + start: function (t, e) { + o._addClass(x(this), 'ui-dialog-dragging'), + o._blockFrames(), + o._trigger('dragStart', t, r(e)) + }, + drag: function (t, e) { + o._trigger('drag', t, r(e)) + }, + stop: function (t, e) { + var i = e.offset.left - o.document.scrollLeft(), + s = e.offset.top - o.document.scrollTop() + ;(n.position = { + my: 'left top', + at: + 'left' + + (0 <= i ? '+' : '') + + i + + ' top' + + (0 <= s ? '+' : '') + + s, + of: o.window, + }), + o._removeClass(x(this), 'ui-dialog-dragging'), + o._unblockFrames(), + o._trigger('dragStop', t, r(e)) + }, + }) + }, + _makeResizable: function () { + var o = this, + n = this.options, + t = n.resizable, + e = this.uiDialog.css('position'), + t = 'string' == typeof t ? t : 'n,e,s,w,se,sw,ne,nw' + function r(t) { + return { + originalPosition: t.originalPosition, + originalSize: t.originalSize, + position: t.position, + size: t.size, + } + } + this.uiDialog + .resizable({ + cancel: '.ui-dialog-content', + containment: 'document', + alsoResize: this.element, + maxWidth: n.maxWidth, + maxHeight: n.maxHeight, + minWidth: n.minWidth, + minHeight: this._minHeight(), + handles: t, + start: function (t, e) { + o._addClass(x(this), 'ui-dialog-resizing'), + o._blockFrames(), + o._trigger('resizeStart', t, r(e)) + }, + resize: function (t, e) { + o._trigger('resize', t, r(e)) + }, + stop: function (t, e) { + var i = o.uiDialog.offset(), + s = i.left - o.document.scrollLeft(), + i = i.top - o.document.scrollTop() + ;(n.height = o.uiDialog.height()), + (n.width = o.uiDialog.width()), + (n.position = { + my: 'left top', + at: + 'left' + + (0 <= s ? '+' : '') + + s + + ' top' + + (0 <= i ? '+' : '') + + i, + of: o.window, + }), + o._removeClass(x(this), 'ui-dialog-resizing'), + o._unblockFrames(), + o._trigger('resizeStop', t, r(e)) + }, + }) + .css('position', e) + }, + _trackFocus: function () { + this._on(this.widget(), { + focusin: function (t) { + this._makeFocusTarget(), + (this._focusedElement = x(t.target)) + }, + }) + }, + _makeFocusTarget: function () { + this._untrackInstance(), this._trackingInstances().unshift(this) + }, + _untrackInstance: function () { + var t = this._trackingInstances(), + e = x.inArray(this, t) + ;-1 !== e && t.splice(e, 1) + }, + _trackingInstances: function () { + var t = this.document.data('ui-dialog-instances') + return t || this.document.data('ui-dialog-instances', (t = [])), t + }, + _minHeight: function () { + var t = this.options + return 'auto' === t.height + ? t.minHeight + : Math.min(t.minHeight, t.height) + }, + _position: function () { + var t = this.uiDialog.is(':visible') + t || this.uiDialog.show(), + this.uiDialog.position(this.options.position), + t || this.uiDialog.hide() + }, + _setOptions: function (t) { + var i = this, + s = !1, + o = {} + x.each(t, function (t, e) { + i._setOption(t, e), + t in i.sizeRelatedOptions && (s = !0), + t in i.resizableRelatedOptions && (o[t] = e) + }), + s && (this._size(), this._position()), + this.uiDialog.is(':data(ui-resizable)') && + this.uiDialog.resizable('option', o) + }, + _setOption: function (t, e) { + var i, + s = this.uiDialog + 'disabled' !== t && + (this._super(t, e), + 'appendTo' === t && this.uiDialog.appendTo(this._appendTo()), + 'buttons' === t && this._createButtons(), + 'closeText' === t && + this.uiDialogTitlebarClose.button({ + label: x('') + .text('' + this.options.closeText) + .html(), + }), + 'draggable' === t && + ((i = s.is(':data(ui-draggable)')) && + !e && + s.draggable('destroy'), + !i && e && this._makeDraggable()), + 'position' === t && this._position(), + 'resizable' === t && + ((i = s.is(':data(ui-resizable)')) && + !e && + s.resizable('destroy'), + i && + 'string' == typeof e && + s.resizable('option', 'handles', e), + i || !1 === e || this._makeResizable()), + 'title' === t && + this._title(this.uiDialogTitlebar.find('.ui-dialog-title'))) + }, + _size: function () { + var t, + e, + i, + s = this.options + this.element.show().css({ + width: 'auto', + minHeight: 0, + maxHeight: 'none', + height: 0, + }), + s.minWidth > s.width && (s.width = s.minWidth), + (t = this.uiDialog + .css({ height: 'auto', width: s.width }) + .outerHeight()), + (e = Math.max(0, s.minHeight - t)), + (i = + 'number' == typeof s.maxHeight + ? Math.max(0, s.maxHeight - t) + : 'none'), + 'auto' === s.height + ? this.element.css({ + minHeight: e, + maxHeight: i, + height: 'auto', + }) + : this.element.height(Math.max(0, s.height - t)), + this.uiDialog.is(':data(ui-resizable)') && + this.uiDialog.resizable( + 'option', + 'minHeight', + this._minHeight() + ) + }, + _blockFrames: function () { + this.iframeBlocks = this.document.find('iframe').map(function () { + var t = x(this) + return x('
') + .css({ + position: 'absolute', + width: t.outerWidth(), + height: t.outerHeight(), + }) + .appendTo(t.parent()) + .offset(t.offset())[0] + }) + }, + _unblockFrames: function () { + this.iframeBlocks && + (this.iframeBlocks.remove(), delete this.iframeBlocks) + }, + _allowInteraction: function (t) { + return ( + !!x(t.target).closest('.ui-dialog').length || + !!x(t.target).closest('.ui-datepicker').length + ) + }, + _createOverlay: function () { + var i, s + this.options.modal && + ((i = x.fn.jquery.substring(0, 4)), + (s = !0), + this._delay(function () { + s = !1 + }), + this.document.data('ui-dialog-overlays') || + this.document.on( + 'focusin.ui-dialog', + function (t) { + var e + s || + (e = + this._trackingInstances()[0])._allowInteraction( + t + ) || + (t.preventDefault(), + e._focusTabbable(), + ('3.4.' !== i && '3.5.' !== i) || + e._delay(e._restoreTabbableFocus)) + }.bind(this) + ), + (this.overlay = x('
').appendTo(this._appendTo())), + this._addClass( + this.overlay, + null, + 'ui-widget-overlay ui-front' + ), + this._on(this.overlay, { mousedown: '_keepFocus' }), + this.document.data( + 'ui-dialog-overlays', + (this.document.data('ui-dialog-overlays') || 0) + 1 + )) + }, + _destroyOverlay: function () { + var t + this.options.modal && + this.overlay && + ((t = this.document.data('ui-dialog-overlays') - 1) + ? this.document.data('ui-dialog-overlays', t) + : (this.document.off('focusin.ui-dialog'), + this.document.removeData('ui-dialog-overlays')), + this.overlay.remove(), + (this.overlay = null)) + }, + }), + !1 !== x.uiBackCompat && + x.widget('ui.dialog', x.ui.dialog, { + options: { dialogClass: '' }, + _createWrapper: function () { + this._super(), + this.uiDialog.addClass(this.options.dialogClass) + }, + _setOption: function (t, e) { + 'dialogClass' === t && + this.uiDialog + .removeClass(this.options.dialogClass) + .addClass(e), + this._superApply(arguments) + }, + }) + x.ui.dialog + var v = x, + _ = {}, + b = _.toString, + y = /^([\-+])=\s*(\d+\.?\d*)/, + w = [ + { + re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/, + parse: function (t) { + return [t[1], t[2], t[3], t[4]] + }, + }, + { + re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/, + parse: function (t) { + return [2.55 * t[1], 2.55 * t[2], 2.55 * t[3], t[4]] + }, + }, + { + re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})?/, + parse: function (t) { + return [ + parseInt(t[1], 16), + parseInt(t[2], 16), + parseInt(t[3], 16), + t[4] ? (parseInt(t[4], 16) / 255).toFixed(2) : 1, + ] + }, + }, + { + re: /#([a-f0-9])([a-f0-9])([a-f0-9])([a-f0-9])?/, + parse: function (t) { + return [ + parseInt(t[1] + t[1], 16), + parseInt(t[2] + t[2], 16), + parseInt(t[3] + t[3], 16), + t[4] ? (parseInt(t[4] + t[4], 16) / 255).toFixed(2) : 1, + ] + }, + }, + { + re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/, + space: 'hsla', + parse: function (t) { + return [t[1], t[2] / 100, t[3] / 100, t[4]] + }, + }, + ], + I = (v.Color = function (t, e, i, s) { + return new v.Color.fn.parse(t, e, i, s) + }), + S = { + rgba: { + props: { + red: { idx: 0, type: 'byte' }, + green: { idx: 1, type: 'byte' }, + blue: { idx: 2, type: 'byte' }, + }, + }, + hsla: { + props: { + hue: { idx: 0, type: 'degrees' }, + saturation: { idx: 1, type: 'percent' }, + lightness: { idx: 2, type: 'percent' }, + }, + }, + }, + D = { + byte: { floor: !0, max: 255 }, + percent: { max: 1 }, + degrees: { mod: 360, floor: !0 }, + }, + W = (I.support = {}), + O = v('

')[0], + E = v.each + function L(t) { + return null == t + ? t + '' + : 'object' == typeof t + ? _[b.call(t)] || 'object' + : typeof t + } + function N(t, e, i) { + var s = D[e.type] || {} + return null == t + ? i || !e.def + ? null + : e.def + : ((t = s.floor ? ~~t : parseFloat(t)), + isNaN(t) + ? e.def + : s.mod + ? (t + s.mod) % s.mod + : Math.min(s.max, Math.max(0, t))) + } + function R(s) { + var o = I(), + n = (o._rgba = []) + return ( + (s = s.toLowerCase()), + E(w, function (t, e) { + var i = e.re.exec(s), + i = i && e.parse(i), + e = e.space || 'rgba' + if (i) + return ( + (i = o[e](i)), + (o[S[e].cache] = i[S[e].cache]), + (n = o._rgba = i._rgba), + !1 + ) + }), + n.length + ? ('0,0,0,0' === n.join() && v.extend(n, V.transparent), o) + : V[s] + ) + } + function M(t, e, i) { + return 6 * (i = (i + 1) % 1) < 1 + ? t + (e - t) * i * 6 + : 2 * i < 1 + ? e + : 3 * i < 2 + ? t + (e - t) * (2 / 3 - i) * 6 + : t + } + ;(O.style.cssText = 'background-color:rgba(1,1,1,.5)'), + (W.rgba = -1 < O.style.backgroundColor.indexOf('rgba')), + E(S, function (t, e) { + ;(e.cache = '_' + t), + (e.props.alpha = { idx: 3, type: 'percent', def: 1 }) + }), + v.each( + 'Boolean Number String Function Array Date RegExp Object Error Symbol'.split( + ' ' + ), + function (t, e) { + _['[object ' + e + ']'] = e.toLowerCase() + } + ), + ((I.fn = v.extend(I.prototype, { + parse: function (o, t, e, i) { + if (void 0 === o) + return (this._rgba = [null, null, null, null]), this + ;(o.jquery || o.nodeType) && ((o = v(o).css(t)), (t = void 0)) + var n = this, + s = L(o), + r = (this._rgba = []) + return ( + void 0 !== t && ((o = [o, t, e, i]), (s = 'array')), + 'string' === s + ? this.parse(R(o) || V._default) + : 'array' === s + ? (E(S.rgba.props, function (t, e) { + r[e.idx] = N(o[e.idx], e) + }), + this) + : 'object' === s + ? (E( + S, + o instanceof I + ? function (t, e) { + o[e.cache] && + (n[e.cache] = o[e.cache].slice()) + } + : function (t, i) { + var s = i.cache + E(i.props, function (t, e) { + if (!n[s] && i.to) { + if ( + 'alpha' === t || + null == o[t] + ) + return + n[s] = i.to(n._rgba) + } + n[s][e.idx] = N(o[t], e, !0) + }), + n[s] && + v.inArray( + null, + n[s].slice(0, 3) + ) < 0 && + (null == n[s][3] && + (n[s][3] = 1), + i.from && + (n._rgba = i.from(n[s]))) + } + ), + this) + : void 0 + ) + }, + is: function (t) { + var o = I(t), + n = !0, + r = this + return ( + E(S, function (t, e) { + var i, + s = o[e.cache] + return ( + s && + ((i = + r[e.cache] || + (e.to && e.to(r._rgba)) || + []), + E(e.props, function (t, e) { + if (null != s[e.idx]) + return (n = s[e.idx] === i[e.idx]) + })), + n + ) + }), + n + ) + }, + _space: function () { + var i = [], + s = this + return ( + E(S, function (t, e) { + s[e.cache] && i.push(t) + }), + i.pop() + ) + }, + transition: function (t, r) { + var e = (l = I(t))._space(), + i = S[e], + t = 0 === this.alpha() ? I('transparent') : this, + a = t[i.cache] || i.to(t._rgba), + h = a.slice(), + l = l[i.cache] + return ( + E(i.props, function (t, e) { + var i = e.idx, + s = a[i], + o = l[i], + n = D[e.type] || {} + null !== o && + (null === s + ? (h[i] = o) + : (n.mod && + (o - s > n.mod / 2 + ? (s += n.mod) + : s - o > n.mod / 2 && (s -= n.mod)), + (h[i] = N((o - s) * r + s, e)))) + }), + this[e](h) + ) + }, + blend: function (t) { + if (1 === this._rgba[3]) return this + var e = this._rgba.slice(), + i = e.pop(), + s = I(t)._rgba + return I( + v.map(e, function (t, e) { + return (1 - i) * s[e] + i * t + }) + ) + }, + toRgbaString: function () { + var t = 'rgba(', + e = v.map(this._rgba, function (t, e) { + return null != t ? t : 2 < e ? 1 : 0 + }) + return 1 === e[3] && (e.pop(), (t = 'rgb(')), t + e.join() + ')' + }, + toHslaString: function () { + var t = 'hsla(', + e = v.map(this.hsla(), function (t, e) { + return ( + null == t && (t = 2 < e ? 1 : 0), + (t = e && e < 3 ? Math.round(100 * t) + '%' : t) + ) + }) + return 1 === e[3] && (e.pop(), (t = 'hsl(')), t + e.join() + ')' + }, + toHexString: function (t) { + var e = this._rgba.slice(), + i = e.pop() + return ( + t && e.push(~~(255 * i)), + '#' + + v + .map(e, function (t) { + return 1 === (t = (t || 0).toString(16)).length + ? '0' + t + : t + }) + .join('') + ) + }, + toString: function () { + return 0 === this._rgba[3] ? 'transparent' : this.toRgbaString() + }, + })).parse.prototype = I.fn), + (S.hsla.to = function (t) { + if (null == t[0] || null == t[1] || null == t[2]) + return [null, null, null, t[3]] + var e = t[0] / 255, + i = t[1] / 255, + s = t[2] / 255, + o = t[3], + n = Math.max(e, i, s), + r = Math.min(e, i, s), + a = n - r, + h = n + r, + t = 0.5 * h, + i = + r === n + ? 0 + : e === n + ? (60 * (i - s)) / a + 360 + : i === n + ? (60 * (s - e)) / a + 120 + : (60 * (e - i)) / a + 240, + h = 0 == a ? 0 : t <= 0.5 ? a / h : a / (2 - h) + return [Math.round(i) % 360, h, t, null == o ? 1 : o] + }), + (S.hsla.from = function (t) { + if (null == t[0] || null == t[1] || null == t[2]) + return [null, null, null, t[3]] + var e = t[0] / 360, + i = t[1], + s = t[2], + t = t[3], + i = s <= 0.5 ? s * (1 + i) : s + i - s * i, + s = 2 * s - i + return [ + Math.round(255 * M(s, i, e + 1 / 3)), + Math.round(255 * M(s, i, e)), + Math.round(255 * M(s, i, e - 1 / 3)), + t, + ] + }), + E(S, function (h, t) { + var e = t.props, + n = t.cache, + r = t.to, + a = t.from + ;(I.fn[h] = function (t) { + if ((r && !this[n] && (this[n] = r(this._rgba)), void 0 === t)) + return this[n].slice() + var i = L(t), + s = 'array' === i || 'object' === i ? t : arguments, + o = this[n].slice() + return ( + E(e, function (t, e) { + t = s['object' === i ? t : e.idx] + null == t && (t = o[e.idx]), (o[e.idx] = N(t, e)) + }), + a ? (((t = I(a(o)))[n] = o), t) : I(o) + ) + }), + E(e, function (r, a) { + I.fn[r] || + (I.fn[r] = function (t) { + var e, + i = L(t), + s = + 'alpha' === r + ? this._hsla + ? 'hsla' + : 'rgba' + : h, + o = this[s](), + n = o[a.idx] + return 'undefined' === i + ? n + : ('function' === i && + (i = L((t = t.call(this, n)))), + null == t && a.empty + ? this + : ('string' === i && + (e = y.exec(t)) && + (t = + n + + parseFloat(e[2]) * + ('+' === e[1] ? 1 : -1)), + (o[a.idx] = t), + this[s](o))) + }) + }) + }), + (I.hook = function (t) { + t = t.split(' ') + E(t, function (t, n) { + ;(v.cssHooks[n] = { + set: function (t, e) { + var i, + s, + o = '' + if ( + 'transparent' !== e && + ('string' !== L(e) || (i = R(e))) + ) { + if ( + ((e = I(i || e)), !W.rgba && 1 !== e._rgba[3]) + ) { + for ( + s = + 'backgroundColor' === n + ? t.parentNode + : t; + ('' === o || 'transparent' === o) && + s && + s.style; + + ) + try { + ;(o = v.css(s, 'backgroundColor')), + (s = s.parentNode) + } catch (t) {} + e = e.blend( + o && 'transparent' !== o ? o : '_default' + ) + } + e = e.toRgbaString() + } + try { + t.style[n] = e + } catch (t) {} + }, + }), + (v.fx.step[n] = function (t) { + t.colorInit || + ((t.start = I(t.elem, n)), + (t.end = I(t.end)), + (t.colorInit = !0)), + v.cssHooks[n].set( + t.elem, + t.start.transition(t.end, t.pos) + ) + }) + }) + })( + 'backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor' + ), + (v.cssHooks.borderColor = { + expand: function (i) { + var s = {} + return ( + E(['Top', 'Right', 'Bottom', 'Left'], function (t, e) { + s['border' + e + 'Color'] = i + }), + s + ) + }, + }) + var A, + B, + F, + q, + j, + X, + Y, + U, + K, + $, + V = (v.Color.names = { + aqua: '#00ffff', + black: '#000000', + blue: '#0000ff', + fuchsia: '#ff00ff', + gray: '#808080', + green: '#008000', + lime: '#00ff00', + maroon: '#800000', + navy: '#000080', + olive: '#808000', + purple: '#800080', + red: '#ff0000', + silver: '#c0c0c0', + teal: '#008080', + white: '#ffffff', + yellow: '#ffff00', + transparent: [null, null, null, 0], + _default: '#ffffff', + }), + Q = 'ui-effects-', + G = 'ui-effects-style', + Z = 'ui-effects-animated' + function J(t) { + var e, + i, + s = t.ownerDocument.defaultView + ? t.ownerDocument.defaultView.getComputedStyle(t, null) + : t.currentStyle, + o = {} + if (s && s.length && s[0] && s[s[0]]) + for (i = s.length; i--; ) + 'string' == typeof s[(e = s[i])] && + (o[ + e.replace(/-([\da-z])/gi, function (t, e) { + return e.toUpperCase() + }) + ] = s[e]) + else for (e in s) 'string' == typeof s[e] && (o[e] = s[e]) + return o + } + function tt(t, e, i, s) { + return ( + (t = { effect: (t = x.isPlainObject(t) ? (e = t).effect : t) }), + 'function' == typeof (e = null == e ? {} : e) && + ((s = e), (i = null), (e = {})), + ('number' != typeof e && !x.fx.speeds[e]) || + ((s = i), (i = e), (e = {})), + 'function' == typeof i && ((s = i), (i = null)), + e && x.extend(t, e), + (i = i || e.duration), + (t.duration = x.fx.off + ? 0 + : 'number' == typeof i + ? i + : i in x.fx.speeds + ? x.fx.speeds[i] + : x.fx.speeds._default), + (t.complete = s || e.complete), + t + ) + } + function et(t) { + return ( + !t || + 'number' == typeof t || + x.fx.speeds[t] || + ('string' == typeof t && !x.effects.effect[t]) || + 'function' == typeof t || + ('object' == typeof t && !t.effect) + ) + } + function it(t, e) { + var i = e.outerWidth(), + e = e.outerHeight(), + t = + /^rect\((-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto)\)$/.exec( + t + ) || ['', 0, i, e, 0] + return { + top: parseFloat(t[1]) || 0, + right: 'auto' === t[2] ? i : parseFloat(t[2]), + bottom: 'auto' === t[3] ? e : parseFloat(t[3]), + left: parseFloat(t[4]) || 0, + } + } + ;(x.effects = { effect: {} }), + (q = ['add', 'remove', 'toggle']), + (j = { + border: 1, + borderBottom: 1, + borderColor: 1, + borderLeft: 1, + borderRight: 1, + borderTop: 1, + borderWidth: 1, + margin: 1, + padding: 1, + }), + x.each( + [ + 'borderLeftStyle', + 'borderRightStyle', + 'borderBottomStyle', + 'borderTopStyle', + ], + function (t, e) { + x.fx.step[e] = function (t) { + ;(('none' !== t.end && !t.setAttr) || + (1 === t.pos && !t.setAttr)) && + (v.style(t.elem, e, t.end), (t.setAttr = !0)) + } + } + ), + x.fn.addBack || + (x.fn.addBack = function (t) { + return this.add( + null == t ? this.prevObject : this.prevObject.filter(t) + ) + }), + (x.effects.animateClass = function (o, t, e, i) { + var n = x.speed(t, e, i) + return this.queue(function () { + var i = x(this), + t = i.attr('class') || '', + e = (e = n.children ? i.find('*').addBack() : i).map( + function () { + return { el: x(this), start: J(this) } + } + ), + s = function () { + x.each(q, function (t, e) { + o[e] && i[e + 'Class'](o[e]) + }) + } + s(), + (e = e.map(function () { + return ( + (this.end = J(this.el[0])), + (this.diff = (function (t, e) { + var i, + s, + o = {} + for (i in e) + (s = e[i]), + t[i] !== s && + (j[i] || + (!x.fx.step[i] && + isNaN(parseFloat(s))) || + (o[i] = s)) + return o + })(this.start, this.end)), + this + ) + })), + i.attr('class', t), + (e = e.map(function () { + var t = this, + e = x.Deferred(), + i = x.extend({}, n, { + queue: !1, + complete: function () { + e.resolve(t) + }, + }) + return this.el.animate(this.diff, i), e.promise() + })), + x.when.apply(x, e.get()).done(function () { + s(), + x.each(arguments, function () { + var e = this.el + x.each(this.diff, function (t) { + e.css(t, '') + }) + }), + n.complete.call(i[0]) + }) + }) + }), + x.fn.extend({ + addClass: + ((F = x.fn.addClass), + function (t, e, i, s) { + return e + ? x.effects.animateClass.call(this, { add: t }, e, i, s) + : F.apply(this, arguments) + }), + removeClass: + ((B = x.fn.removeClass), + function (t, e, i, s) { + return 1 < arguments.length + ? x.effects.animateClass.call( + this, + { remove: t }, + e, + i, + s + ) + : B.apply(this, arguments) + }), + toggleClass: + ((A = x.fn.toggleClass), + function (t, e, i, s, o) { + return 'boolean' == typeof e || void 0 === e + ? i + ? x.effects.animateClass.call( + this, + e ? { add: t } : { remove: t }, + i, + s, + o + ) + : A.apply(this, arguments) + : x.effects.animateClass.call( + this, + { toggle: t }, + e, + i, + s + ) + }), + switchClass: function (t, e, i, s, o) { + return x.effects.animateClass.call( + this, + { add: e, remove: t }, + i, + s, + o + ) + }, + }), + x.expr && + x.expr.pseudos && + x.expr.pseudos.animated && + (x.expr.pseudos.animated = + ((X = x.expr.pseudos.animated), + function (t) { + return !!x(t).data(Z) || X(t) + })), + !1 !== x.uiBackCompat && + x.extend(x.effects, { + save: function (t, e) { + for (var i = 0, s = e.length; i < s; i++) + null !== e[i] && t.data(Q + e[i], t[0].style[e[i]]) + }, + restore: function (t, e) { + for (var i, s = 0, o = e.length; s < o; s++) + null !== e[s] && + ((i = t.data(Q + e[s])), t.css(e[s], i)) + }, + setMode: function (t, e) { + return (e = + 'toggle' === e + ? t.is(':hidden') + ? 'show' + : 'hide' + : e) + }, + createWrapper: function (i) { + if (i.parent().is('.ui-effects-wrapper')) return i.parent() + var s = { + width: i.outerWidth(!0), + height: i.outerHeight(!0), + float: i.css('float'), + }, + t = x('

') + .addClass('ui-effects-wrapper') + .css({ + fontSize: '100%', + background: 'transparent', + border: 'none', + margin: 0, + padding: 0, + }), + e = { width: i.width(), height: i.height() }, + o = document.activeElement + try { + o.id + } catch (t) { + o = document.body + } + return ( + i.wrap(t), + (i[0] !== o && !x.contains(i[0], o)) || + x(o).trigger('focus'), + (t = i.parent()), + 'static' === i.css('position') + ? (t.css({ position: 'relative' }), + i.css({ position: 'relative' })) + : (x.extend(s, { + position: i.css('position'), + zIndex: i.css('z-index'), + }), + x.each( + ['top', 'left', 'bottom', 'right'], + function (t, e) { + ;(s[e] = i.css(e)), + isNaN(parseInt(s[e], 10)) && + (s[e] = 'auto') + } + ), + i.css({ + position: 'relative', + top: 0, + left: 0, + right: 'auto', + bottom: 'auto', + })), + i.css(e), + t.css(s).show() + ) + }, + removeWrapper: function (t) { + var e = document.activeElement + return ( + t.parent().is('.ui-effects-wrapper') && + (t.parent().replaceWith(t), + (t[0] !== e && !x.contains(t[0], e)) || + x(e).trigger('focus')), + t + ) + }, + }), + x.extend(x.effects, { + version: '1.13.1', + define: function (t, e, i) { + return ( + i || ((i = e), (e = 'effect')), + (x.effects.effect[t] = i), + (x.effects.effect[t].mode = e), + i + ) + }, + scaledDimensions: function (t, e, i) { + if (0 === e) + return { + height: 0, + width: 0, + outerHeight: 0, + outerWidth: 0, + } + var s = 'horizontal' !== i ? (e || 100) / 100 : 1, + e = 'vertical' !== i ? (e || 100) / 100 : 1 + return { + height: t.height() * e, + width: t.width() * s, + outerHeight: t.outerHeight() * e, + outerWidth: t.outerWidth() * s, + } + }, + clipToBox: function (t) { + return { + width: t.clip.right - t.clip.left, + height: t.clip.bottom - t.clip.top, + left: t.clip.left, + top: t.clip.top, + } + }, + unshift: function (t, e, i) { + var s = t.queue() + 1 < e && s.splice.apply(s, [1, 0].concat(s.splice(e, i))), + t.dequeue() + }, + saveStyle: function (t) { + t.data(G, t[0].style.cssText) + }, + restoreStyle: function (t) { + ;(t[0].style.cssText = t.data(G) || ''), t.removeData(G) + }, + mode: function (t, e) { + t = t.is(':hidden') + return ( + 'toggle' === e && (e = t ? 'show' : 'hide'), + (e = (t ? 'hide' === e : 'show' === e) ? 'none' : e) + ) + }, + getBaseline: function (t, e) { + var i, s + switch (t[0]) { + case 'top': + i = 0 + break + case 'middle': + i = 0.5 + break + case 'bottom': + i = 1 + break + default: + i = t[0] / e.height + } + switch (t[1]) { + case 'left': + s = 0 + break + case 'center': + s = 0.5 + break + case 'right': + s = 1 + break + default: + s = t[1] / e.width + } + return { x: s, y: i } + }, + createPlaceholder: function (t) { + var e, + i = t.css('position'), + s = t.position() + return ( + t + .css({ + marginTop: t.css('marginTop'), + marginBottom: t.css('marginBottom'), + marginLeft: t.css('marginLeft'), + marginRight: t.css('marginRight'), + }) + .outerWidth(t.outerWidth()) + .outerHeight(t.outerHeight()), + /^(static|relative)/.test(i) && + ((i = 'absolute'), + (e = x('<' + t[0].nodeName + '>') + .insertAfter(t) + .css({ + display: /^(inline|ruby)/.test(t.css('display')) + ? 'inline-block' + : 'block', + visibility: 'hidden', + marginTop: t.css('marginTop'), + marginBottom: t.css('marginBottom'), + marginLeft: t.css('marginLeft'), + marginRight: t.css('marginRight'), + float: t.css('float'), + }) + .outerWidth(t.outerWidth()) + .outerHeight(t.outerHeight()) + .addClass('ui-effects-placeholder')), + t.data(Q + 'placeholder', e)), + t.css({ position: i, left: s.left, top: s.top }), + e + ) + }, + removePlaceholder: function (t) { + var e = Q + 'placeholder', + i = t.data(e) + i && (i.remove(), t.removeData(e)) + }, + cleanUp: function (t) { + x.effects.restoreStyle(t), x.effects.removePlaceholder(t) + }, + setTransition: function (s, t, o, n) { + return ( + (n = n || {}), + x.each(t, function (t, e) { + var i = s.cssUnit(e) + 0 < i[0] && (n[e] = i[0] * o + i[1]) + }), + n + ) + }, + }), + x.fn.extend({ + effect: function () { + function t(t) { + var e = x(this), + i = x.effects.mode(e, a) || n + e.data(Z, !0), + h.push(i), + n && + ('show' === i || (i === n && 'hide' === i)) && + e.show(), + (n && 'none' === i) || x.effects.saveStyle(e), + 'function' == typeof t && t() + } + var s = tt.apply(this, arguments), + o = x.effects.effect[s.effect], + n = o.mode, + e = s.queue, + i = e || 'fx', + r = s.complete, + a = s.mode, + h = [] + return x.fx.off || !o + ? a + ? this[a](s.duration, r) + : this.each(function () { + r && r.call(this) + }) + : !1 === e + ? this.each(t).each(l) + : this.queue(i, t).queue(i, l) + function l(t) { + var e = x(this) + function i() { + 'function' == typeof r && r.call(e[0]), + 'function' == typeof t && t() + } + ;(s.mode = h.shift()), + !1 === x.uiBackCompat || n + ? 'none' === s.mode + ? (e[a](), i()) + : o.call(e[0], s, function () { + e.removeData(Z), + x.effects.cleanUp(e), + 'hide' === s.mode && e.hide(), + i() + }) + : (e.is(':hidden') ? 'hide' === a : 'show' === a) + ? (e[a](), i()) + : o.call(e[0], s, i) + } + }, + show: + ((K = x.fn.show), + function (t) { + if (et(t)) return K.apply(this, arguments) + t = tt.apply(this, arguments) + return (t.mode = 'show'), this.effect.call(this, t) + }), + hide: + ((U = x.fn.hide), + function (t) { + if (et(t)) return U.apply(this, arguments) + t = tt.apply(this, arguments) + return (t.mode = 'hide'), this.effect.call(this, t) + }), + toggle: + ((Y = x.fn.toggle), + function (t) { + if (et(t) || 'boolean' == typeof t) + return Y.apply(this, arguments) + t = tt.apply(this, arguments) + return (t.mode = 'toggle'), this.effect.call(this, t) + }), + cssUnit: function (t) { + var i = this.css(t), + s = [] + return ( + x.each(['em', 'px', '%', 'pt'], function (t, e) { + 0 < i.indexOf(e) && (s = [parseFloat(i), e]) + }), + s + ) + }, + cssClip: function (t) { + return t + ? this.css( + 'clip', + 'rect(' + + t.top + + 'px ' + + t.right + + 'px ' + + t.bottom + + 'px ' + + t.left + + 'px)' + ) + : it(this.css('clip'), this) + }, + transfer: function (t, e) { + var i = x(this), + s = x(t.to), + o = 'fixed' === s.css('position'), + n = x('body'), + r = o ? n.scrollTop() : 0, + a = o ? n.scrollLeft() : 0, + n = s.offset(), + n = { + top: n.top - r, + left: n.left - a, + height: s.innerHeight(), + width: s.innerWidth(), + }, + s = i.offset(), + h = x("
") + h.appendTo('body') + .addClass(t.className) + .css({ + top: s.top - r, + left: s.left - a, + height: i.innerHeight(), + width: i.innerWidth(), + position: o ? 'fixed' : 'absolute', + }) + .animate(n, t.duration, t.easing, function () { + h.remove(), 'function' == typeof e && e() + }) + }, + }), + (x.fx.step.clip = function (t) { + t.clipInit || + ((t.start = x(t.elem).cssClip()), + 'string' == typeof t.end && (t.end = it(t.end, t.elem)), + (t.clipInit = !0)), + x(t.elem).cssClip({ + top: t.pos * (t.end.top - t.start.top) + t.start.top, + right: + t.pos * (t.end.right - t.start.right) + t.start.right, + bottom: + t.pos * (t.end.bottom - t.start.bottom) + + t.start.bottom, + left: t.pos * (t.end.left - t.start.left) + t.start.left, + }) + }), + ($ = {}), + x.each(['Quad', 'Cubic', 'Quart', 'Quint', 'Expo'], function (e, t) { + $[t] = function (t) { + return Math.pow(t, e + 2) + } + }), + x.extend($, { + Sine: function (t) { + return 1 - Math.cos((t * Math.PI) / 2) + }, + Circ: function (t) { + return 1 - Math.sqrt(1 - t * t) + }, + Elastic: function (t) { + return 0 === t || 1 === t + ? t + : -Math.pow(2, 8 * (t - 1)) * + Math.sin(((80 * (t - 1) - 7.5) * Math.PI) / 15) + }, + Back: function (t) { + return t * t * (3 * t - 2) + }, + Bounce: function (t) { + for (var e, i = 4; t < ((e = Math.pow(2, --i)) - 1) / 11; ); + return ( + 1 / Math.pow(4, 3 - i) - + 7.5625 * Math.pow((3 * e - 2) / 22 - t, 2) + ) + }, + }), + x.each($, function (t, e) { + ;(x.easing['easeIn' + t] = e), + (x.easing['easeOut' + t] = function (t) { + return 1 - e(1 - t) + }), + (x.easing['easeInOut' + t] = function (t) { + return t < 0.5 ? e(2 * t) / 2 : 1 - e(-2 * t + 2) / 2 + }) + }) + ;(O = x.effects), + x.effects.define('blind', 'hide', function (t, e) { + var i = { + up: ['bottom', 'top'], + vertical: ['bottom', 'top'], + down: ['top', 'bottom'], + left: ['right', 'left'], + horizontal: ['right', 'left'], + right: ['left', 'right'], + }, + s = x(this), + o = t.direction || 'up', + n = s.cssClip(), + r = { clip: x.extend({}, n) }, + a = x.effects.createPlaceholder(s) + ;(r.clip[i[o][0]] = r.clip[i[o][1]]), + 'show' === t.mode && + (s.cssClip(r.clip), + a && a.css(x.effects.clipToBox(r)), + (r.clip = n)), + a && a.animate(x.effects.clipToBox(r), t.duration, t.easing), + s.animate(r, { + queue: !1, + duration: t.duration, + easing: t.easing, + complete: e, + }) + }), + x.effects.define('bounce', function (t, e) { + var i, + s, + o = x(this), + n = t.mode, + r = 'hide' === n, + a = 'show' === n, + h = t.direction || 'up', + l = t.distance, + c = t.times || 5, + n = 2 * c + (a || r ? 1 : 0), + p = t.duration / n, + u = t.easing, + d = 'up' === h || 'down' === h ? 'top' : 'left', + f = 'up' === h || 'left' === h, + g = 0, + t = o.queue().length + for ( + x.effects.createPlaceholder(o), + h = o.css(d), + l = l || o['top' == d ? 'outerHeight' : 'outerWidth']() / 3, + a && + (((s = { opacity: 1 })[d] = h), + o + .css('opacity', 0) + .css(d, f ? 2 * -l : 2 * l) + .animate(s, p, u)), + r && (l /= Math.pow(2, c - 1)), + (s = {})[d] = h; + g < c; + g++ + ) + ((i = {})[d] = (f ? '-=' : '+=') + l), + o.animate(i, p, u).animate(s, p, u), + (l = r ? 2 * l : l / 2) + r && + (((i = { opacity: 0 })[d] = (f ? '-=' : '+=') + l), + o.animate(i, p, u)), + o.queue(e), + x.effects.unshift(o, t, 1 + n) + }), + x.effects.define('clip', 'hide', function (t, e) { + var i = {}, + s = x(this), + o = t.direction || 'vertical', + n = 'both' === o, + r = n || 'horizontal' === o, + n = n || 'vertical' === o, + o = s.cssClip() + ;(i.clip = { + top: n ? (o.bottom - o.top) / 2 : o.top, + right: r ? (o.right - o.left) / 2 : o.right, + bottom: n ? (o.bottom - o.top) / 2 : o.bottom, + left: r ? (o.right - o.left) / 2 : o.left, + }), + x.effects.createPlaceholder(s), + 'show' === t.mode && (s.cssClip(i.clip), (i.clip = o)), + s.animate(i, { + queue: !1, + duration: t.duration, + easing: t.easing, + complete: e, + }) + }), + x.effects.define('drop', 'hide', function (t, e) { + var i = x(this), + s = 'show' === t.mode, + o = t.direction || 'left', + n = 'up' === o || 'down' === o ? 'top' : 'left', + r = 'up' === o || 'left' === o ? '-=' : '+=', + a = '+=' == r ? '-=' : '+=', + h = { opacity: 0 } + x.effects.createPlaceholder(i), + (o = + t.distance || + i['top' == n ? 'outerHeight' : 'outerWidth'](!0) / 2), + (h[n] = r + o), + s && (i.css(h), (h[n] = a + o), (h.opacity = 1)), + i.animate(h, { + queue: !1, + duration: t.duration, + easing: t.easing, + complete: e, + }) + }), + x.effects.define('explode', 'hide', function (t, e) { + var i, + s, + o, + n, + r, + a, + h = t.pieces ? Math.round(Math.sqrt(t.pieces)) : 3, + l = h, + c = x(this), + p = 'show' === t.mode, + u = c.show().css('visibility', 'hidden').offset(), + d = Math.ceil(c.outerWidth() / l), + f = Math.ceil(c.outerHeight() / h), + g = [] + function m() { + g.push(this), + g.length === h * l && + (c.css({ visibility: 'visible' }), x(g).remove(), e()) + } + for (i = 0; i < h; i++) + for (n = u.top + i * f, a = i - (h - 1) / 2, s = 0; s < l; s++) + (o = u.left + s * d), + (r = s - (l - 1) / 2), + c + .clone() + .appendTo('body') + .wrap('
') + .css({ + position: 'absolute', + visibility: 'visible', + left: -s * d, + top: -i * f, + }) + .parent() + .addClass('ui-effects-explode') + .css({ + position: 'absolute', + overflow: 'hidden', + width: d, + height: f, + left: o + (p ? r * d : 0), + top: n + (p ? a * f : 0), + opacity: p ? 0 : 1, + }) + .animate( + { + left: o + (p ? 0 : r * d), + top: n + (p ? 0 : a * f), + opacity: p ? 1 : 0, + }, + t.duration || 500, + t.easing, + m + ) + }), + x.effects.define('fade', 'toggle', function (t, e) { + var i = 'show' === t.mode + x(this) + .css('opacity', i ? 0 : 1) + .animate( + { opacity: i ? 1 : 0 }, + { + queue: !1, + duration: t.duration, + easing: t.easing, + complete: e, + } + ) + }), + x.effects.define('fold', 'hide', function (e, t) { + var i = x(this), + s = e.mode, + o = 'show' === s, + n = 'hide' === s, + r = e.size || 15, + a = /([0-9]+)%/.exec(r), + h = e.horizFirst ? ['right', 'bottom'] : ['bottom', 'right'], + l = e.duration / 2, + c = x.effects.createPlaceholder(i), + p = i.cssClip(), + u = { clip: x.extend({}, p) }, + d = { clip: x.extend({}, p) }, + f = [p[h[0]], p[h[1]]], + s = i.queue().length + a && (r = (parseInt(a[1], 10) / 100) * f[n ? 0 : 1]), + (u.clip[h[0]] = r), + (d.clip[h[0]] = r), + (d.clip[h[1]] = 0), + o && + (i.cssClip(d.clip), + c && c.css(x.effects.clipToBox(d)), + (d.clip = p)), + i + .queue(function (t) { + c && + c + .animate(x.effects.clipToBox(u), l, e.easing) + .animate(x.effects.clipToBox(d), l, e.easing), + t() + }) + .animate(u, l, e.easing) + .animate(d, l, e.easing) + .queue(t), + x.effects.unshift(i, s, 4) + }), + x.effects.define('highlight', 'show', function (t, e) { + var i = x(this), + s = { backgroundColor: i.css('backgroundColor') } + 'hide' === t.mode && (s.opacity = 0), + x.effects.saveStyle(i), + i + .css({ + backgroundImage: 'none', + backgroundColor: t.color || '#ffff99', + }) + .animate(s, { + queue: !1, + duration: t.duration, + easing: t.easing, + complete: e, + }) + }), + x.effects.define('size', function (s, e) { + var o, + i = x(this), + t = ['fontSize'], + n = [ + 'borderTopWidth', + 'borderBottomWidth', + 'paddingTop', + 'paddingBottom', + ], + r = [ + 'borderLeftWidth', + 'borderRightWidth', + 'paddingLeft', + 'paddingRight', + ], + a = s.mode, + h = 'effect' !== a, + l = s.scale || 'both', + c = s.origin || ['middle', 'center'], + p = i.css('position'), + u = i.position(), + d = x.effects.scaledDimensions(i), + f = s.from || d, + g = s.to || x.effects.scaledDimensions(i, 0) + x.effects.createPlaceholder(i), + 'show' === a && ((a = f), (f = g), (g = a)), + (o = { + from: { y: f.height / d.height, x: f.width / d.width }, + to: { y: g.height / d.height, x: g.width / d.width }, + }), + ('box' !== l && 'both' !== l) || + (o.from.y !== o.to.y && + ((f = x.effects.setTransition(i, n, o.from.y, f)), + (g = x.effects.setTransition(i, n, o.to.y, g))), + o.from.x !== o.to.x && + ((f = x.effects.setTransition(i, r, o.from.x, f)), + (g = x.effects.setTransition(i, r, o.to.x, g)))), + ('content' !== l && 'both' !== l) || + (o.from.y !== o.to.y && + ((f = x.effects.setTransition(i, t, o.from.y, f)), + (g = x.effects.setTransition(i, t, o.to.y, g)))), + c && + ((c = x.effects.getBaseline(c, d)), + (f.top = (d.outerHeight - f.outerHeight) * c.y + u.top), + (f.left = (d.outerWidth - f.outerWidth) * c.x + u.left), + (g.top = (d.outerHeight - g.outerHeight) * c.y + u.top), + (g.left = (d.outerWidth - g.outerWidth) * c.x + u.left)), + delete f.outerHeight, + delete f.outerWidth, + i.css(f), + ('content' !== l && 'both' !== l) || + ((n = n.concat(['marginTop', 'marginBottom']).concat(t)), + (r = r.concat(['marginLeft', 'marginRight'])), + i.find('*[width]').each(function () { + var t = x(this), + e = x.effects.scaledDimensions(t), + i = { + height: e.height * o.from.y, + width: e.width * o.from.x, + outerHeight: e.outerHeight * o.from.y, + outerWidth: e.outerWidth * o.from.x, + }, + e = { + height: e.height * o.to.y, + width: e.width * o.to.x, + outerHeight: e.height * o.to.y, + outerWidth: e.width * o.to.x, + } + o.from.y !== o.to.y && + ((i = x.effects.setTransition(t, n, o.from.y, i)), + (e = x.effects.setTransition(t, n, o.to.y, e))), + o.from.x !== o.to.x && + ((i = x.effects.setTransition( + t, + r, + o.from.x, + i + )), + (e = x.effects.setTransition(t, r, o.to.x, e))), + h && x.effects.saveStyle(t), + t.css(i), + t.animate(e, s.duration, s.easing, function () { + h && x.effects.restoreStyle(t) + }) + })), + i.animate(g, { + queue: !1, + duration: s.duration, + easing: s.easing, + complete: function () { + var t = i.offset() + 0 === g.opacity && i.css('opacity', f.opacity), + h || + (i + .css( + 'position', + 'static' === p ? 'relative' : p + ) + .offset(t), + x.effects.saveStyle(i)), + e() + }, + }) + }), + x.effects.define('scale', function (t, e) { + var i = x(this), + s = t.mode, + s = + parseInt(t.percent, 10) || + (0 === parseInt(t.percent, 10) || 'effect' !== s ? 0 : 100), + s = x.extend( + !0, + { + from: x.effects.scaledDimensions(i), + to: x.effects.scaledDimensions( + i, + s, + t.direction || 'both' + ), + origin: t.origin || ['middle', 'center'], + }, + t + ) + t.fade && ((s.from.opacity = 1), (s.to.opacity = 0)), + x.effects.effect.size.call(this, s, e) + }), + x.effects.define('puff', 'hide', function (t, e) { + t = x.extend(!0, {}, t, { + fade: !0, + percent: parseInt(t.percent, 10) || 150, + }) + x.effects.effect.scale.call(this, t, e) + }), + x.effects.define('pulsate', 'show', function (t, e) { + var i = x(this), + s = t.mode, + o = 'show' === s, + n = 2 * (t.times || 5) + (o || 'hide' === s ? 1 : 0), + r = t.duration / n, + a = 0, + h = 1, + s = i.queue().length + for ( + (!o && i.is(':visible')) || + (i.css('opacity', 0).show(), (a = 1)); + h < n; + h++ + ) + i.animate({ opacity: a }, r, t.easing), (a = 1 - a) + i.animate({ opacity: a }, r, t.easing), + i.queue(e), + x.effects.unshift(i, s, 1 + n) + }), + x.effects.define('shake', function (t, e) { + var i = 1, + s = x(this), + o = t.direction || 'left', + n = t.distance || 20, + r = t.times || 3, + a = 2 * r + 1, + h = Math.round(t.duration / a), + l = 'up' === o || 'down' === o ? 'top' : 'left', + c = 'up' === o || 'left' === o, + p = {}, + u = {}, + d = {}, + o = s.queue().length + for ( + x.effects.createPlaceholder(s), + p[l] = (c ? '-=' : '+=') + n, + u[l] = (c ? '+=' : '-=') + 2 * n, + d[l] = (c ? '-=' : '+=') + 2 * n, + s.animate(p, h, t.easing); + i < r; + i++ + ) + s.animate(u, h, t.easing).animate(d, h, t.easing) + s + .animate(u, h, t.easing) + .animate(p, h / 2, t.easing) + .queue(e), + x.effects.unshift(s, o, 1 + a) + }), + x.effects.define('slide', 'show', function (t, e) { + var i, + s, + o = x(this), + n = { + up: ['bottom', 'top'], + down: ['top', 'bottom'], + left: ['right', 'left'], + right: ['left', 'right'], + }, + r = t.mode, + a = t.direction || 'left', + h = 'up' === a || 'down' === a ? 'top' : 'left', + l = 'up' === a || 'left' === a, + c = + t.distance || + o['top' == h ? 'outerHeight' : 'outerWidth'](!0), + p = {} + x.effects.createPlaceholder(o), + (i = o.cssClip()), + (s = o.position()[h]), + (p[h] = (l ? -1 : 1) * c + s), + (p.clip = o.cssClip()), + (p.clip[n[a][1]] = p.clip[n[a][0]]), + 'show' === r && + (o.cssClip(p.clip), + o.css(h, p[h]), + (p.clip = i), + (p[h] = s)), + o.animate(p, { + queue: !1, + duration: t.duration, + easing: t.easing, + complete: e, + }) + }), + !1 !== x.uiBackCompat && + x.effects.define('transfer', function (t, e) { + x(this).transfer(t, e) + }) +}) diff --git a/v1/src/simulator/vendor/table2csv.js b/v1/src/simulator/vendor/table2csv.js new file mode 100644 index 00000000..ab734062 --- /dev/null +++ b/v1/src/simulator/vendor/table2csv.js @@ -0,0 +1,113 @@ +jQuery.fn.table2CSV = function (options) { + var options = jQuery.extend( + { + separator: ',', + header: [], + headerSelector: 'none', + columnSelector: 'td, th', + delivery: 'value', // popup, value, download + // filename: 'test.csv', // filename to download + transform_gt_lt: true, // make > and < to > and < + }, + options + ) + + var csvData = [] + var headerArr = [] + var el = this + + //header + var numCols = options.header.length + var tmpRow = [] // construct header avalible array + + if (numCols > 0) { + for (var i = 0; i < numCols; i++) { + tmpRow[tmpRow.length] = formatData(options.header[i]) + } + } else { + $(el) + .filter(':visible') + .find(options.headerSelector) + .each(function () { + if ($(this).css('display') != 'none') + tmpRow[tmpRow.length] = formatData($(this).html()) + }) + } + + row2CSV(tmpRow) + + // actual data + $(el) + .find('tr') + .each(function () { + var tmpRow = [] + $(this) + .filter(':visible') + .find(options.columnSelector) + .each(function () { + if ($(this).css('display') != 'none') + tmpRow[tmpRow.length] = formatData($(this).html()) + }) + row2CSV(tmpRow) + }) + if (options.delivery == 'popup') { + var mydata = csvData.join('\n') + if (options.transform_gt_lt) { + mydata = sinri_recover_gt_and_lt(mydata) + } + return popup(mydata) + } else if (options.delivery == 'download') { + var mydata = csvData.join('\n') + if (options.transform_gt_lt) { + mydata = sinri_recover_gt_and_lt(mydata) + } + var url = 'data:text/csv;charset=utf8,' + encodeURIComponent(mydata) + window.open(url) + return true + } else { + var mydata = csvData.join('\n') + if (options.transform_gt_lt) { + mydata = sinri_recover_gt_and_lt(mydata) + } + return mydata + } + + function sinri_recover_gt_and_lt(input) { + var regexp = new RegExp(/>/g) + var input = input.replace(regexp, '>') + var regexp = new RegExp(/</g) + var input = input.replace(regexp, '<') + return input + } + + function row2CSV(tmpRow) { + var tmp = tmpRow.join('') // to remove any blank rows + // alert(tmp); + if (tmpRow.length > 0 && tmp != '') { + var mystr = tmpRow.join(options.separator) + csvData[csvData.length] = mystr + } + } + function formatData(input) { + // double " according to rfc4180 + var regexp = new RegExp(/["]/g) + var output = input.replace(regexp, '""') + //HTML + var regexp = new RegExp(/\<[^\<]+\>/g) + var output = output.replace(regexp, '') + output = output.replace(/ /gi, ' ') //replace   + if (output == '') return '' + return '"' + output.trim() + '"' + } + function popup(data) { + var generator = window.open('', 'csv', 'height=400,width=600') + generator.document.write('CSV') + generator.document.write('') + generator.document.write('') + generator.document.write('') + generator.document.close() + return true + } +} diff --git a/v1/src/store/SimulatorStore/SimulatorStore.ts b/v1/src/store/SimulatorStore/SimulatorStore.ts new file mode 100644 index 00000000..69aa9cb3 --- /dev/null +++ b/v1/src/store/SimulatorStore/SimulatorStore.ts @@ -0,0 +1,13 @@ +import { extractStore } from '../extractStore' +import { defineStore } from 'pinia' +import { useActions } from './actions' +import { useGetters } from './getters' +import { useState } from './state' + +export const SimulatorStore = defineStore('simulatorStore', () => { + return { + ...extractStore(useState()), + ...extractStore(useGetters()), + ...extractStore(useActions()), + } +}) diff --git a/v1/src/store/SimulatorStore/actions.ts b/v1/src/store/SimulatorStore/actions.ts new file mode 100644 index 00000000..e127bc5f --- /dev/null +++ b/v1/src/store/SimulatorStore/actions.ts @@ -0,0 +1,16 @@ +import { defineStore } from 'pinia' +import { useState } from './state' + +export const useActions = defineStore('simulatorStore.actions', () => { + const state = useState() + + function showTitle(): void { + console.log(state.title) + } + + // Note you are free to define as many internal functions as you want. + // You only expose the functions that are returned. + return { + showTitle, + } +}) diff --git a/v1/src/store/SimulatorStore/getters.ts b/v1/src/store/SimulatorStore/getters.ts new file mode 100644 index 00000000..6d409942 --- /dev/null +++ b/v1/src/store/SimulatorStore/getters.ts @@ -0,0 +1,15 @@ +import { defineStore } from 'pinia' +import { computed } from 'vue' +import { useState } from './state' + +export const useGetters = defineStore('simulatorStore.getters', () => { + const state = useState() + + const getTitle = computed((): string => { + return `${state.title} !!!` + }) + + return { + getTitle, + } +}) diff --git a/v1/src/store/SimulatorStore/state.ts b/v1/src/store/SimulatorStore/state.ts new file mode 100644 index 00000000..1bad89ad --- /dev/null +++ b/v1/src/store/SimulatorStore/state.ts @@ -0,0 +1,66 @@ +import { defineStore } from 'pinia' + +// use camel case variable names +export interface State { + title: string + activeCircuit: + | Object + | { + id: number | string + name: string + } + circuit_list: Array + dialogBox: { + // create_circuit: boolean + // delete_circuit: boolean + combinationalanalysis_dialog: boolean + hex_bin_dec_converter_dialog: boolean + saveimage_dialog: boolean + theme_dialog: boolean + customshortcut_dialog: boolean + insertsubcircuit_dialog: boolean + exportverilog_dialog: boolean + save_project_dialog: boolean + open_project_dialog: boolean + export_project_dialog: boolean + import_project_dialog: boolean + } + // createCircuit: Object | { circuitName: string } + combinationalAnalysis: Object +} + +export const useState = defineStore({ + id: 'simulatorStore.state', + + state: (): State => { + return { + title: 'Welcome to CircuitVerse Simulator', + activeCircuit: {}, + circuit_list: [], + dialogBox: { + // create_circuit: false, + // delete_circuit: false, + combinationalanalysis_dialog: false, + hex_bin_dec_converter_dialog: false, + saveimage_dialog: false, + theme_dialog: false, + customshortcut_dialog: false, + insertsubcircuit_dialog: false, + exportverilog_dialog: false, + save_project_dialog: false, + open_project_dialog: false, + export_project_dialog: false, + import_project_dialog: false, + }, + // createCircuit: { + // circuitName: 'Untitled Circuit', + // }, + combinationalAnalysis: { + inputNameList: 'eg. In A, In B', + outputNameList: 'eg. Out X, Out Y', + booleanExpression: 'Example: (AB)', + decimalColumnBox: false, + }, + } + }, +}) diff --git a/v1/src/store/authStore.ts b/v1/src/store/authStore.ts new file mode 100644 index 00000000..12dd433c --- /dev/null +++ b/v1/src/store/authStore.ts @@ -0,0 +1,57 @@ +import { defineStore } from 'pinia' + +interface AuthStoreType { + isLoggedIn: boolean + userId: string | number + username: string + locale: string + isAdmin: boolean +} + +interface UserInfo { + isLoggedIn: boolean + id: string + attributes: { + name: string + locale: string + admin: boolean + } +} +export const useAuthStore = defineStore({ + id: 'authStore', + state: (): AuthStoreType => ({ + isLoggedIn: false, + userId: '', + username: '', + locale: 'en', + isAdmin: false, + }), + actions: { + setUserInfo(userInfo: UserInfo): void { + this.isLoggedIn = true + this.userId = userInfo.id ?? '' + this.username = userInfo.attributes.name ?? '' + this.locale = userInfo.attributes.locale ?? 'en' + this.isAdmin = userInfo.attributes.admin + }, + }, + getters: { + getIsLoggedIn(): boolean { + return this.isLoggedIn + }, + getUserId(): string | number { + return this.userId + }, + getUsername(): string { + return this.username + }, + getLocale(): string { + return this.locale + }, + getIsAdmin(): boolean { + return this.isAdmin + }, + }, +}) + +// TODO: extract store verify and check better ways to impliment diff --git a/v1/src/store/extractStore.ts b/v1/src/store/extractStore.ts new file mode 100644 index 00000000..f7b48129 --- /dev/null +++ b/v1/src/store/extractStore.ts @@ -0,0 +1,40 @@ +import type { + PiniaCustomStateProperties, + StoreActions, + StoreGeneric, + StoreGetters, + StoreState, +} from 'pinia' +import type { ToRefs } from 'vue' +import { isReactive, isRef, toRaw, toRef } from 'vue' + +type Extracted = ToRefs< + StoreState & + StoreGetters & + PiniaCustomStateProperties> +> & + StoreActions + +/** + * Creates an object of references with all the state, getters, actions + * and plugin-added state properties of the store. + * + * @param store - store to extract the refs from + */ +export function extractStore( + store: SS +): Extracted { + const rawStore = toRaw(store) + const refs: Record = {} + + for (const [key, value] of Object.entries(rawStore)) { + if (isRef(value) || isReactive(value)) { + refs[key] = toRef(store, key) + } else if (typeof value === 'function') { + refs[key] = value + } + } + + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + return refs as Extracted +} diff --git a/v1/src/store/projectStore.ts b/v1/src/store/projectStore.ts new file mode 100644 index 00000000..3a09d0c8 --- /dev/null +++ b/v1/src/store/projectStore.ts @@ -0,0 +1,37 @@ +import { defineStore } from 'pinia' + +interface projectStoreType { + project: { + // id: number //use later if needed + name: string + nameDefined: boolean + } +} + +export const useProjectStore = defineStore({ + id: 'projectStore', + state: (): projectStoreType => ({ + project: { + // id: 0, //use later if needed + name: 'Untitled', + nameDefined: false, + }, + }), + actions: { + setProjectName(projectName: string): void { + this.project.name = projectName + this.project.nameDefined = true + }, + setProjectNameDefined(defined: boolean = true): void { + this.project.nameDefined = defined + }, + }, + getters: { + getProjectName(): string { + return this.project.name + }, + getProjectNameDefined(): boolean { + return this.project.nameDefined + }, + }, +}) diff --git a/v1/src/store/promptStore.ts b/v1/src/store/promptStore.ts new file mode 100644 index 00000000..612c35b3 --- /dev/null +++ b/v1/src/store/promptStore.ts @@ -0,0 +1,113 @@ +import { HTMLContent } from '@tiptap/core' +import { defineStore } from 'pinia' + +interface promptStoreType { + resolvePromise: Function + // resolvePromise: (value?: string | undefined) => void + prompt: { + activate: boolean + messageText: string + isPersistent: boolean + buttonList: Array<{ + text: string + emitOption: string + }> + inputList: Array<{ + text: string + val: string + placeholder: string + id: string + class: string + style: string + type: string + }> + } + confirm: { + activate: boolean + messageText: string + isPersistent: boolean + buttonList: Array<{ + text: string + emitOption: string | boolean + }> + } + DeleteCircuit: { + activate: boolean + messageText: string + isPersistent: boolean + buttonList: Array<{ + text: string + emitOption: string + }> + circuitItem: object + } + UpdateProjectDetail: { + activate: boolean + projectId: number + projectName: string + projectTags: string + projectType: Readonly | string + projectDescription: HTMLContent + } +} + +export const usePromptStore = defineStore({ + id: 'promptStore', + state: (): promptStoreType => ({ + resolvePromise: (): any => {}, + prompt: { + activate: false, + messageText: '', + isPersistent: false, + buttonList: [], + inputList: [], + }, + confirm: { + activate: false, + messageText: '', + isPersistent: false, + buttonList: [], + }, + DeleteCircuit: { + activate: false, + messageText: '', + isPersistent: false, + buttonList: [], + circuitItem: {}, + }, + UpdateProjectDetail: { + activate: false, + projectId: 0, + projectName: '', + projectTags: '', + projectType: 'Public', + projectDescription: '', + }, + }), + actions: { + // resolvePromise(): any {}, + setProjectName(projectName: string): void { + this.UpdateProjectDetail.projectName = projectName + }, + setProjectId(projectId: number): void { + this.UpdateProjectDetail.projectId = projectId + }, + }, + getters: { + getProjectName(): string { + return this.UpdateProjectDetail.projectName + }, + getProjectId(): number { + return this.UpdateProjectDetail.projectId + }, + getProjectTags(): string { + return this.UpdateProjectDetail.projectTags + }, + getProjectType(): Readonly | string { + return this.UpdateProjectDetail.projectType + }, + getProjectDescription(): HTMLContent { + return this.UpdateProjectDetail.projectDescription + }, + }, +}) diff --git a/v1/src/styles/color_theme.scss b/v1/src/styles/color_theme.scss new file mode 100644 index 00000000..5bdc9fff --- /dev/null +++ b/v1/src/styles/color_theme.scss @@ -0,0 +1,551 @@ +:root { + --primary: #454545; + + --bg-navbar: #454545; + --text-navbar--alt: #000; + --qp-br-tl: #333; + --qp-br-rd: #535353; + + --br-circuit: #454545; + --br-circuit-cur: #fff; + --bg-tabs: #8b8b8b; + --bg-circuit: #bbb; + + --text-lite: #fff; + --text-dark: #000; + --text-panel: #fff; + --text-circuit: #000; + + --context-text: #fff; + --context-text-hov: #000; + + --cus-radio_label: #656565; + + --br-secondary: #7d7d7d; + + --br-primary: #fff; + --bg-primary-moz: hsla(0, 0%, 27%, 0.902); + --bg-primary-chr: hsla(0, 0%, 27%, 0.702); + --bg-icons: #7d7d7d; + --bg-text: #cacaca; + --bg-secondary: #bbb; + --canvas-stroke:#eee; + --canvas-fill: #fff; + --bg-toggle-btn-primary: #42b983; + --primary-btn-hov: #3ca877; + --btn-danger: #dc5656; + --btn-danger-darken: #b03662; + --disable: #6c8b93; + --qp-box-shadow-1: #3b3b3b; + --qp-box-shadow-2: #4f4f4f; + --cus-btn-hov--bg: #ddd; + --cus-btn-hov-text: #000; + --node: green; + --stroke: black; + --fill: white; + --hover-and-sel: rgba(255, 255, 32, 0.8); + --wire-draw: #000; + --wire-cnt: green; + --wire-pow: Lime; + --wire-sel: blue; + --wire-lose: red; + --mini-map: green; + --mini-map-stroke: darkgreen; + --input-text: green; + --secondary-stroke: red; + --text: black; + --wire-norm: black; + --node-norm: green; + --splitter: black; + --output-rect: blue; +} + +::-moz-selection { + color: var(--text-lite); + background: var(--bg-icons); +} + +::selection { + color: var(--text-lite); + background: var(--bg-icons); +} + +.navbar-menu > li > a { + color: #fff; +} + +.projectName { + color: #fff; +} + +.header { + background: var(--bg-navbar); +} + +.dropdown > ul { + background-color: var(--bg-primary-moz); + border: 0.5px solid var(--bg-tabs); +} + +@supports (backdrop-filter: blur()) { + .dropdown > ul { + background-color: var(--bg-primary-chr); + } +} + +.dropdown > ul::before { + border-top: 1px solid var(--bg-tabs); + border-right: 1px solid var(--bg-tabs); +} + +.dropdown > ul::after { + border-top: 1.5px solid var(--primary); +} + +.dropdown-menu > li > a { + color: #fff !important; +} + +.signIn-btn { + color: #fff !important; +} + +.dropdown-menu > li > a:hover { + color: var(--text-navbar--alt) !important; + background: var(--bg-text); +} + +#contextMenu { + border: 0.5px solid var(--bg-tabs); + background-color: var(--bg-primary-moz); + color: var(--context-text); +} + +#contextMenu ul li:hover { + color: var(--context-text-hov); + background: var(--bg-text); +} + +@supports (backdrop-filter: blur()) { + #contextMenu { + background-color: var(--bg-primary-chr); + } +} + +.draggable-panel { + background: var(--primary); + border: 2px solid var(--br-primary); + box-shadow: 0px 0px 10px #4545457f; +} + +.panel-header { + color: var(--text-panel); + background: var(--primary); +} + +.panel-body { + color: var(--text-panel); + border-top: 1px solid var(--br-secondary); +} + +.panel-header::before { + border-top: 2px solid var(--text-panel); +} + +.search-input { + color: var(--text-panel); + border: 1px solid var(--br-secondary); +} + +.timing-diagram-toolbar input { + color: var(--text-panel); + border: 1px solid var(--br-secondary); +} + +.search-results, +.search-close { + color: var(--text-panel); +} + +#exitViewBtn { + border: 1px solid var(--br-primary); +} +.ce-hidden, +.prop-hidden, +#exitViewBtn { + color: var(--text-panel); + background: var(--primary); +} + +.ui-accordion-header { + color: var(--text-panel) !important; +} + +.panelHeader:hover { + background-color: var(--bg-icons); +} + +.panelHeader:after, +.panelHeader:before { + background-color: var(--br-primary); + border: 1px solid var(--br-primary); +} + +.ui-accordion .ui-accordion-content { + background-color: white; +} + +.icon { + background-color: white; +} + +.custom-tooltip-styling { + background-color: var(--bg-icons) !important; + color: var(--text-panel) !important; + border: 1px solid var(--br-primary); +} + +.icon:hover { + background-color: var(--bg-icons); +} + +.search-results::-webkit-scrollbar-thumb { + background-color: #585858; +} + +.search-results::-webkit-scrollbar-thumb:hover { + background-color: var(--primary); +} + +.timing-diagram-toolbar { + background-color: var(--bg-tabs); +} + +#tabsBar { + background-color: var(--bg-tabs); + border-top: 1px solid var(--br-primary); + border-bottom: 1px solid var(--br-primary); +} + +#tabsBar .circuits { + border: 1px solid var(--br-circuit); +} + +#tabsBar .circuits { + color: var(--text-circuit); + background-color: var(--bg-tabs); +} + +#tabsBar .current { + color: var(--text-circuit); + background-color: var(--bg-circuit); + border: 1px solid var(--br-circuit-cur); +} + +#tabsBar .current > span { +} + +#tabsBar button { + color: var(--text-panel); + background-color: var(--primary); + border: 1px solid var(--br-circuit-cur); +} + +#tabsBar button:hover { + color: var(--text-panel); + + border: 1px solid var(--br-circuit-cur); +} + +.moduleProperty input, +.moduleProperty textarea { + color: var(--text-panel); +} + +.moduleProperty { + background: var(--primary); + border: 2px solid var(--br-primary); + box-shadow: 0px 0px 10px #4545457f; + color: var(--text-panel); +} + +#moduleProperty-title { + color: var(--text-panel); +} + +.moduleProperty input, +.moduleProperty select, +.moduleProperty textarea { + border: 1px solid var(--br-secondary) !important; + color: var(--text-panel); +} + +.moduleProperty input:focus, +.moduleProperty select:focus, +.moduleProperty textarea:focus { + color: var(--text-panel); + border-color: var(--br-primary) !important; +} + +.input-group div button { + color: var(--text-lite); +} + +.input-group-prepend button:hover { + background: rgba(202, 202, 202, 0.5); +} + +.input-group-append button:hover { + background: rgba(202, 202, 202, 0.5); +} + +.slider { + background-color: #ccc; + box-shadow: inset 0px 0px 5px rgba(69, 69, 69, 0.255); +} + +.slider:before { + background-color: white; + box-shadow: 0px 0px 7px rgba(69, 69, 69, 0.8); +} + +input:checked + .slider { + background-color: var(--bg-toggle-btn-primary); +} + +.custom-btn--primary { + background-color: var(--bg-toggle-btn-primary); + color: var(--text-lite); +} +.custom-btn--primary:hover { + background-color: var(--primary-btn-hov); + color: var(--text-lite); +} +.custom-btn--secondary { + border: 1px solid white; + color: var(--text-lite); +} +.custom-btn--secondary:hover { + background-color: #ddd; + color: var(--cus-btn-hov-text); +} + +.custom-btn--secondary:active, +.custom-btn--secondary:focus { + border: 1px solid white; +} + +.custom-btn--tertiary { + background-color: var(--btn-danger); + color: var(--text-lite); +} + +.custom-btn--tertiary:hover { + background-color: var(--btn-danger-darken); + color: var(--text-lite); +} + +#HelpButton { + border: 2px solid var(--br-primary); + color: var(--text-panel); +} + +select { + background: var(--bg-secondary); + background-color: var(--primary); + color: var(--text-lite); +} + +#layoutDialog { + border: 2px solid var(--br-primary); + box-shadow: 0px 0px 10px #4545457f; + background-color: var(--primary); + color: var(--text-panel); +} + +#layoutDialog > div span:before { + color: var(--text-panel); +} + +.panel-heading { + color: var(--text-panel); +} + +.ui-dialog { + border: 0.5px solid var(--br-primary) !important; + background: var(--bg-primary-moz) !important; +} + +@supports (backdrop-filter: blur()) { + .ui-dialog { + background-color: var(--bg-primary-chr) !important; + } +} + +.ui-widget-header { + color: var(--text-lite) !important; + border-bottom: 0.5px solid var(--br-primary); +} + +.option { + background-color: white; + color: var(--cus-radio_label); +} + +.custom-radio span { + border: 3px solid var(--cus-radio_label); +} + +.custom-radio span:after { + background: var(--cus-radio_label); +} + +#saveImageDialog { + border: 1px solid var(--br-secondary); +} +.download-dialog-section-2 { + color: var(--text-lite); +} + +.download-dialog-section-2 .active-btn { + background: var(--bg-toggle-btn-primary); + color: var(--text-lite); +} + +.download-dialog-section-2 .inactive-btn { + color: var(--text-lite); +} + +.download-dialog-section-3 { + border: 1px solid var(--br-secondary); +} + +.download-dialog-section-3 > span { + color: var(--text-lite); +} + +.ui-dialog-titlebar-close::before:hover { + background-color: var(--primary); +} + +.ui-dialog .ui-dialog-buttonpane button:hover { + color: var(--cus-btn-hov-text) !important; + background: var(--cus-btn-hov--bg); + border: 1px solid transparent; +} + +.render-btn { + color: var(--text-lite); + border: 1px solid white; +} + +.render-btn:active { + border: 1px solid var(--br-primary); +} + +.render-btn:hover { + background: #3ba374; + color: var(--text-lite); + border: 1px solid transparent; +} + +#combinationalAnalysis { + border: 1px solid var(--br-secondary); + color: var(--text-lite); +} + +#combinationalAnalysis p input { + border-bottom: 1px solid white !important; + color: var(--text-lite); +} + +.content-table tr th { + background-color: var(--primary); + color: var(--text-lite); +} + +.content-table th, +.content-table td { + background-color: #f3f3f3; +} + +#openProjectDialog { + color: var(--text-lite); +} + +#openProjectDialog > label { + border: 1px solid var(--br-primary); + color: var(--text-lite); +} + +#openProjectDialog > label > span { + border: 3px solid white; +} + +#openProjectDialog > label > span:after { + background: var(--text-lite); +} + +#insertSubcircuitDialog { + color: var(--text-lite); +} + +.disable::after { + background: var(--disable); +} + +.radio-green { + background: #42b983; +} + +.btn-group-toggle { + border: 1px solid var(--br-secondary) !important; +} + +.set { + border: 2px solid cyan !important; +} + +.theme { + color: var(--text-panel) !important; + background: var(--bg-icons) !important; + border-radius: 1.5px; +} + +.input-group-prepend button:hover { + background: var(--bg-secondary) !important; + color: var(--text-lite) !important; +} + +.input-group-append button:hover { + background: var(--bg-secondary) !important; + color: var(--text-lite) !important; +} + +.input-group-prepend button { + color: var(--text-panel) !important; +} + +.input-group-append button { + color: var(--text-panel) !important; +} + +#Rectangle_1072 { + stroke: var(--text-panel); +} + +#Path_36 { + fill: var(--text-panel); +} + +.layout--btn-group { + display: block; + margin-right: 25px; + margin-top: -10px; +} + +#clockProperty { + background: var(--primary); + border: 1px solid var(--br-primary); + color: var(--text-panel); +} diff --git a/v1/src/styles/css/0-helpers/_color-config.scss b/v1/src/styles/css/0-helpers/_color-config.scss new file mode 100644 index 00000000..dfe909f0 --- /dev/null +++ b/v1/src/styles/css/0-helpers/_color-config.scss @@ -0,0 +1,22 @@ +// +// Color configuration +// + +$primary: $charcoal; //base color theme +$input: $silver; //input border (mainly bottom) +$text-primary: $white; //global font color +$text-secondary: $silver; //placeholder text +$sim-primary: $white; //simulator bg +$sim-secondary: $very-light-grey; //simulator gridlines +$dialog-primary: $primary; //dialog boxes bg color +$dialog-secondary: $grey; //dialog boxes box border color +$button-primary: $medium-sea-green; //btn primary +$button-secondary: $roman; //delete button +$error-primary: $carousel-pink; //error text color +$box-shadow: $grey-shadow; //box shadow +$border-primary: $white; //border color +$border-secondary: $suva-grey; //dropdown border color +$border-tertiary: $silver; //input box border +$bg-primary--nav: $suva-grey; //nav-bar bg color +$bg-secondary--nav: $primary; //active nav-bar menu bg color +$bg-tertiary--nav: $gainsboro; //inactive nav-bar menu bg color diff --git a/v1/src/styles/css/0-helpers/_functions.scss b/v1/src/styles/css/0-helpers/_functions.scss new file mode 100644 index 00000000..e69de29b diff --git a/v1/src/styles/css/0-helpers/_mixins.scss b/v1/src/styles/css/0-helpers/_mixins.scss new file mode 100644 index 00000000..e69de29b diff --git a/v1/src/styles/css/0-helpers/_variables.scss b/v1/src/styles/css/0-helpers/_variables.scss new file mode 100644 index 00000000..93711734 --- /dev/null +++ b/v1/src/styles/css/0-helpers/_variables.scss @@ -0,0 +1,16 @@ +// +// Color Variables +// + +$charcoal: #454545; +$silver: #bbbbbb; +$gainsboro: #dddddd; +$white: #ffffff; +$grey: #7d7d7d; +$very-light-grey: #cacaca; +$suva-grey: #8b8b8b; +$medium-sea-green: #42b983; +$roman: #dc5656; +$grey-shadow: #4545457f; +$fire-brick: #ac3522; +$carousel-pink: #f8d7da; diff --git a/v1/src/styles/css/2-basics/_buttons.scss b/v1/src/styles/css/2-basics/_buttons.scss new file mode 100644 index 00000000..e69de29b diff --git a/v1/src/styles/css/2-basics/_close.scss b/v1/src/styles/css/2-basics/_close.scss new file mode 100644 index 00000000..e69de29b diff --git a/v1/src/styles/css/2-basics/_global.scss b/v1/src/styles/css/2-basics/_global.scss new file mode 100644 index 00000000..e69de29b diff --git a/v1/src/styles/css/2-basics/_links.scss b/v1/src/styles/css/2-basics/_links.scss new file mode 100644 index 00000000..e69de29b diff --git a/v1/src/styles/css/2-basics/_toggle.scss b/v1/src/styles/css/2-basics/_toggle.scss new file mode 100644 index 00000000..e69de29b diff --git a/v1/src/styles/css/2-basics/_typography.scss b/v1/src/styles/css/2-basics/_typography.scss new file mode 100644 index 00000000..e69de29b diff --git a/v1/src/styles/css/2-basics/base.scss b/v1/src/styles/css/2-basics/base.scss new file mode 100644 index 00000000..f7f722b9 --- /dev/null +++ b/v1/src/styles/css/2-basics/base.scss @@ -0,0 +1,11 @@ +/* base/default rule set here + no class or ID selectors +*/ + +html { + box-sizing: border-box; +} + +* *:before *:after { + box-sizing: inherit; +} diff --git a/v1/src/styles/css/2-basics/reset.scss b/v1/src/styles/css/2-basics/reset.scss new file mode 100644 index 00000000..ba934af9 --- /dev/null +++ b/v1/src/styles/css/2-basics/reset.scss @@ -0,0 +1,64 @@ +/* http://meyerweb.com/eric/tools/css/reset/ + v2.0 | 20110126 + License: none (public domain) +*/ + +/* File modified - uncomment below */ +/* +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +*/ +/* HTML5 display-role reset for older browsers */ +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +menu, +nav, +section { + display: block; +} +body { + line-height: 1; +} +ol, +ul { + list-style: none; +} +blockquote, +q { + quotes: none; +} +blockquote:before, +blockquote:after, +q:before, +q:after { + content: ''; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} diff --git a/v1/src/styles/css/3-sub-components/_navigation.scss b/v1/src/styles/css/3-sub-components/_navigation.scss new file mode 100644 index 00000000..e69de29b diff --git a/v1/src/styles/css/3-sub-components/_searchbar.scss b/v1/src/styles/css/3-sub-components/_searchbar.scss new file mode 100644 index 00000000..e69de29b diff --git a/v1/src/styles/css/3-sub-components/_shortcut-menu.scss b/v1/src/styles/css/3-sub-components/_shortcut-menu.scss new file mode 100644 index 00000000..e69de29b diff --git a/v1/src/styles/css/3-sub-components/_zoom-slider.scss b/v1/src/styles/css/3-sub-components/_zoom-slider.scss new file mode 100644 index 00000000..e69de29b diff --git a/v1/src/styles/css/4-components/_bool-logic-dialog.scss b/v1/src/styles/css/4-components/_bool-logic-dialog.scss new file mode 100644 index 00000000..e69de29b diff --git a/v1/src/styles/css/4-components/_canvas.scss b/v1/src/styles/css/4-components/_canvas.scss new file mode 100644 index 00000000..e69de29b diff --git a/v1/src/styles/css/4-components/_circuitelements.scss b/v1/src/styles/css/4-components/_circuitelements.scss new file mode 100644 index 00000000..e69de29b diff --git a/v1/src/styles/css/4-components/_context-menu.scss b/v1/src/styles/css/4-components/_context-menu.scss new file mode 100644 index 00000000..e69de29b diff --git a/v1/src/styles/css/4-components/_dropdown.scss b/v1/src/styles/css/4-components/_dropdown.scss new file mode 100644 index 00000000..e69de29b diff --git a/v1/src/styles/css/4-components/_header.scss b/v1/src/styles/css/4-components/_header.scss new file mode 100644 index 00000000..e69de29b diff --git a/v1/src/styles/css/4-components/_layout-dialog.scss b/v1/src/styles/css/4-components/_layout-dialog.scss new file mode 100644 index 00000000..e69de29b diff --git a/v1/src/styles/css/4-components/_navbar.scss b/v1/src/styles/css/4-components/_navbar.scss new file mode 100644 index 00000000..e69de29b diff --git a/v1/src/styles/css/4-components/_new-circuit-dialog.scss b/v1/src/styles/css/4-components/_new-circuit-dialog.scss new file mode 100644 index 00000000..e69de29b diff --git a/v1/src/styles/css/4-components/_properties.scss b/v1/src/styles/css/4-components/_properties.scss new file mode 100644 index 00000000..e69de29b diff --git a/v1/src/styles/css/4-components/_save-img-dialog.scss b/v1/src/styles/css/4-components/_save-img-dialog.scss new file mode 100644 index 00000000..e69de29b diff --git a/v1/src/styles/css/4-components/_shortcut-dialog.scss b/v1/src/styles/css/4-components/_shortcut-dialog.scss new file mode 100644 index 00000000..e69de29b diff --git a/v1/src/styles/css/4-components/_tabs-bar.scss b/v1/src/styles/css/4-components/_tabs-bar.scss new file mode 100644 index 00000000..e69de29b diff --git a/v1/src/styles/css/5-layout/simulator.scss b/v1/src/styles/css/5-layout/simulator.scss new file mode 100644 index 00000000..89b84d4d --- /dev/null +++ b/v1/src/styles/css/5-layout/simulator.scss @@ -0,0 +1,321 @@ +/* old ui ruleset starts here */ +.deleteOfflineProject { + float: right; + cursor: pointer; + padding: 2px; +} + +.pointerCursor { + cursor: pointer; +} + +.defaultCursor { + cursor: default; +} + +#container { + display: table; + width: 100%; + height: 100%; +} + +#container > div { + display: table-row; + height: 0; +} + +#container > div.fill { + height: auto; +} + +/* END OF MODULES */ + +#restrictedDiv { + position: absolute; + top: 10px; + margin-left: 10px; + width: 560px; + z-index: 100; +} + +#restrictedElementsDiv { + position: absolute; + top: 90px; + right: 10px; + z-index: 100; + width: 200px; +} + +#MessageDiv { + position: absolute; + left: 30px; + bottom: 30px; + z-index: 110; +} + +.errorMessage { + height: auto; + width: 100%; + padding: 2px; + margin: 2px; + border: 1px solid red; + border-radius: 3px; + background-color: #fee; + font-size: 15px; +} + +.normalMessage { + height: auto; + width: 100%; + padding: 2px; + margin: 2px; + border: 1px solid green; + border-radius: 3px; + background-color: #99ff33; + font-size: 15px; +} + +#canvasArea { + display: block; + position: relative; + width: 100%; + background-color: red; +} + +.simulation { + position: relative; + width: auto; + height: 100%; + overflow: hidden; + background-color: 'white'; +} + +.left { + float: left; +} + +.right { + float: right; +} + +.objectPropertyAttributeChecked.btn { + width: 100%; + margin-bottom: 5px; +} + +/* For loading screen - pace.js */ + +.pace { + -webkit-pointer-events: none; + pointer-events: none; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + position: fixed; + width: 100vw; + height: 100vh; + background-color: #fff; + z-index: 100000; +} + +.pace-inactive { + display: none; +} + +#Help { + visibility: hidden; + /* Hidden by default. Visible on click */ + min-width: 250px; + /* Set a default minimum width */ + margin-left: -125px; + /* Divide value of min-width by 2 */ + background-color: #333; + /* Black background color */ + color: #fff; + /* White text color */ + text-align: center; + /* Centered text */ + border-radius: 2px; + /* Rounded borders */ + padding: 16px; + /* Padding */ + position: fixed; + /* Sit on top of the screen */ + z-index: 1; + /* Add a z-index if needed */ + right: 50px; + /* Center the snackbar */ + bottom: 50px; + /* 30px from the bottom */ + opacity: 0; +} + +#Help.show { + visibility: visible; + /* Show the snackbar */ + opacity: 1; + -webkit-transition-delay: 0.5s; + /* Safari */ + transition-delay: 0.5s; + -webkit-transition-duration: 0.3s; + /* Safari */ + transition-duration: 0.3s; +} + +@-webkit-keyframes fadein { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes fadein { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@-webkit-keyframes fadeout { + from { + opacity: 1; + } + to { + opacity: 0; + } +} + +@keyframes fadeout { + from { + bottom: 0px; + opacity: 1; + } + to { + bottom: 0; + opacity: 0; + } +} + +.pace .pace-progress { + background: #29d; + position: fixed; + z-index: 2000; + top: 0; + right: 100%; + width: 50%; + height: 5px; +} + +/* LOADING ICON CSS STARTS*/ + +.sk-folding-cube { + margin: 20px auto; + width: 40px; + height: 40px; + position: relative; + -webkit-transform: rotateZ(45deg); + transform: rotateZ(45deg); +} + +.sk-folding-cube .sk-cube { + float: left; + width: 50%; + height: 50%; + position: relative; + -webkit-transform: scale(1.1); + -ms-transform: scale(1.1); + transform: scale(1.1); +} + +.sk-folding-cube .sk-cube:before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: #09f; + -webkit-animation: sk-foldCubeAngle 2.4s infinite linear both; + animation: sk-foldCubeAngle 2.4s infinite linear both; + -webkit-transform-origin: 100% 100%; + -ms-transform-origin: 100% 100%; + transform-origin: 100% 100%; +} + +.sk-folding-cube .sk-cube2 { + -webkit-transform: scale(1.1) rotateZ(90deg); + transform: scale(1.1) rotateZ(90deg); +} + +.sk-folding-cube .sk-cube3 { + -webkit-transform: scale(1.1) rotateZ(180deg); + transform: scale(1.1) rotateZ(180deg); +} + +.sk-folding-cube .sk-cube4 { + -webkit-transform: scale(1.1) rotateZ(270deg); + transform: scale(1.1) rotateZ(270deg); +} + +.sk-folding-cube .sk-cube2:before { + -webkit-animation-delay: 0.3s; + animation-delay: 0.3s; +} + +.sk-folding-cube .sk-cube3:before { + -webkit-animation-delay: 0.6s; + animation-delay: 0.6s; +} + +.sk-folding-cube .sk-cube4:before { + -webkit-animation-delay: 0.9s; + animation-delay: 0.9s; +} + +@-webkit-keyframes sk-foldCubeAngle { + 0%, + 10% { + -webkit-transform: perspective(140px) rotateX(-180deg); + transform: perspective(140px) rotateX(-180deg); + opacity: 0; + } + 25%, + 75% { + -webkit-transform: perspective(140px) rotateX(0deg); + transform: perspective(140px) rotateX(0deg); + opacity: 1; + } + 90%, + 100% { + -webkit-transform: perspective(140px) rotateY(180deg); + transform: perspective(140px) rotateY(180deg); + opacity: 0; + } +} + +@keyframes sk-foldCubeAngle { + 0%, + 10% { + -webkit-transform: perspective(140px) rotateX(-180deg); + transform: perspective(140px) rotateX(-180deg); + opacity: 0; + } + 25%, + 75% { + -webkit-transform: perspective(140px) rotateX(0deg); + transform: perspective(140px) rotateX(0deg); + opacity: 1; + } + 90%, + 100% { + -webkit-transform: perspective(140px) rotateY(180deg); + transform: perspective(140px) rotateY(180deg); + opacity: 0; + } +} + +/* LOADING ICON CSS ENDS*/ diff --git a/v1/src/styles/css/UX.css b/v1/src/styles/css/UX.css new file mode 100644 index 00000000..e5011d8a --- /dev/null +++ b/v1/src/styles/css/UX.css @@ -0,0 +1,969 @@ +.deleteOfflineProject { + float: right; + cursor: pointer; + padding: 2px; +} + +#contextMenu { + width: 150px; + visibility: hidden; + box-shadow: 0px 2px 7px rgba(0, 0, 0, 0.2); + border: 1px solid rgba(0, 0, 0, 0.2); + position: fixed; + z-index: 100; + background: #fff; + opacity: 0; + top: 100; + left: 100; + cursor: pointer; + color: #000; + padding-bottom: 4px; + padding-top: 4px; + transition: opacity 0.2s ease-in-out; + user-select: none; +} + +#contextMenu ul { + margin: 0; + padding: 0; + font: 16px sans-serif; +} + +#contextMenu ul li { + list-style: none; + padding: 8px; + padding-left: 20px; +} + +#contextMenu ul li a { + text-decoration: none; + color: #000 !important; +} + +#contextMenu ul li:hover { + background: rgba(0, 0, 0, 0.1); +} + +button:focus { + outline: 0; +} + +.side { + height: 100%; + background-color: #333; + padding: 3px; + color: #fff; + border-side: 1px solid #0099ff; + border-bottom: 40px solid #0099ff; + padding: 0.5em; +} + +.option { + display: block; + background-color: black; + border: 1px solid #005cb3; + color: #0099ff; + padding: 5px; + width: 200px; + margin: 3px; + word-wrap: break-word; + overflow-x: hidden; +} + +.pannel-heading { + background-color: #f5f5f5; +} + +#layoutDialog { + position: absolute; + right: 100px; + top: 100px; + z-index: 101; + width: 200px; + height: 230px; + border: 1px solid grey; + border-radius: 2px; + background-color: white; + overflow-x: hidden; +} + +.projectName { + /*margin:3px;*/ + color: #0099ff; + margin: 0 auto; + text-align: center; + font-size: 1.4em; + position: static; + left: 50%; + display: block; + width: 500px; + text-align: center; + margin-left: -250px; +} + +.inline { + width: auto; + padding-right: 20px; + display: inline-block; +} + +.option:hover { + border-color: #0099ff; +} + +input[type='radio']:checked ~ label { + color: #0dff92; +} + +.option input[type='radio'] { + margin-right: 5px; + /*position: absolute;*/ + visibility: hidden; +} + +.option input[type='radio']:checked { + /*position: absolute;*/ + visibility: visible; +} + +.zoomButton:focus { + outline: 0; +} + +.zoomButton { + padding: 5px; + opacity: 0.3; +} + +.zoomButton:hover { + /*height:20px; + width:20px;*/ + opacity: 0.8; + transition: opacity 0.2s; +} + +.ui-accordion-header-icon.ui-icon { + /*background-image: url("./ui-icons_white_256x240.png");*/ +} + +.noSelect { + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + -o-user-select: none; +} + +.pointerCursor { + cursor: pointer; +} + +.defaultCursor { + cursor: default; +} + +#container { + display: table; + width: 100%; + height: 100%; +} + +#container > div { + display: table-row; + height: 0; +} + +#container > div.fill { + height: auto; +} + +#modules-header { + margin-bottom: 0.5em; + font-size: 1.3em; + text-transform: uppercase; + font-family: Arial, Helvetica, sans-serif; + color: #0099ff; + text-align: center; + padding-top: 0.3em; +} + +.panel { + padding: 0em; + background-color: #333; + margin: 0; + border-radius: 0; + margin-bottom: 0em; + border: 1px solid #0099ff; +} + +.ui-accordion-header { + background-color: #333; + color: #fff; + border: 1px solid #0099ff; + border-radius: 0; + margin: 0em; + padding: 0em; + outline: none; +} + +.ui-accordion-header.ui-accordion-header-active.ui-state-active { + background-color: #0099ff; + outline: none; + margin-bottom: 0; +} + +.ui-accordion-header.ui-state-hover { + background-color: #0066cc; + outline: none; + /*margin-bottom: 0;*/ +} + +/* MODULES */ + +.moduleProperty { + display: none; + background-color: #333; + color: #fff; + /*padding-bottom: 2em;*/ + margin-top: 1em; +} + +#moduleProperty-inner { + border: 1px solid #0099ff; + padding: 1em; + /*margin-bottom: 1em;*/ +} + +#moduleProperty-toolTip { + padding: 10px; + /*font-size: 1.1em;*/ + color: #0099ff; +} + +#moduleProperty-title { + text-transform: uppercase; + font-size: 1.3em; + color: #0099ff; + margin-bottom: 0.55em; + text-align: center; +} + +#moduleProperty-header { + font-size: 1.1em; + text-transform: uppercase; + margin-bottom: 0.5em; +} + +#moduleProperty-inner > p { + margin: 0; + margin-top: 0.2em; +} + +input, +select { + padding: 0.25rem; +} + +.moduleProperty input, +.moduleProperty select { + background-color: #333; + border: none; + border-bottom: 2px solid #ccc; +} + +.moduleProperty input:active, +.moduleProperty input:focus, +.moduleProperty select:active, +.moduleProperty select:focus { + border-bottom: 2px solid #0099ff; +} + +.navbar.navbar-default { + margin: 0px; + border-radius: 0px; + border: 0; + padding: 0px; + min-height: 0px; + border-bottom: 1px solid #0099ff; +} + +.navbar-brand { + padding: 7px 15px; + height: auto; +} + +/* END OF MODULES */ + +#tabsBar { + width: 100%; + /*height: 3em;*/ + margin-left: 20px; + background-color: #000; +} + +#tabsBar div { + display: inline-block; + /*padding-left: 0.5em;*/ + margin: 0.1em; + color: #fff; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + position: relative; +} + +#tabsBar .circuits { + color: #fff; + text-align: center; + background-color: #00284d; + padding-left: 0.5em; + padding-right: 1.5em; + border: 1px solid #005cb3; + border-radius: 0.1em; +} + +#tabsBar .circuits:hover { + background-color: #00ace6; + /*border: 1px solid #0099ff;*/ + transition-duration: 100ms; +} + +#tabsBar .current { + /*background-color: #0086b3;*/ + background-color: #004280; + border: 1px solid #0099ff; +} + +.tabsCloseButton:hover { + color: #fff; + font-family: 'Gill Sans', sans-serif; + margin-left: 1em; + opacity: 0.5; +} +.tabsCloseButton { + color: #111; + font-family: 'Gill Sans', sans-serif; + margin-left: 1em; + opacity: 1; + position: absolute; + top: 5px; + right: 3; +} + +th, +td { + padding-left: 15px; + padding-right: 15px; + text-align: left; + border: 1px solid #0099ff; + color: white; +} + +#booleanTable { + width: 200px; +} + +table { + border-collapse: collapse; + -webkit-user-select: none; + /* Chrome/Safari */ + -moz-user-select: none; + /* Firefox */ + -ms-user-select: none; + /* IE10+ */ + /* Rules below not implemented in browsers yet */ + -o-user-select: none; + user-select: none; +} + +body, +html { + width: 100%; + height: 100%; + margin: 0; + padding: 0; + overflow: hidden; +} + +#restrictedDiv { + position: absolute; + top: 10px; + margin-left: 10px; + width: 560px; + z-index: 100; +} + +#restrictedElementsDiv { + position: absolute; + top: 90px; + right: 10px; + z-index: 100; + width: 200px; +} + +#MessageDiv { + position: absolute; + margin-left: 30px; + bottom: 100px; + /*height:auto;*/ + /*width:60%;*/ + /*padding: 2px;*/ + /*border: 3px solid red;*/ + /*border-radius: 6px;*/ + /*background-color: #fcc;*/ + z-index: 10; +} + +.errorMessage { + /*position: absolute;*/ + /*margin-left: 30%;*/ + /*bottom: 1px;*/ + height: auto; + width: 100%; + /*margin-bottom: 10px;*/ + padding: 2px; + margin: 2px; + border: 1px solid red; + border-radius: 3px; + background-color: #fee; + font-size: 15px; + /*z-index: 10;*/ +} + +.normalMessage { + /*position: absolute;*/ + /*margin-left: 30%;*/ + /*bottom: 150px;*/ + height: auto; + width: 100%; + /*margin-bottom: 10px;*/ + padding: 2px; + margin: 2px; + border: 1px solid green; + border-radius: 3px; + background-color: #99ff33; + font-size: 15px; + /*z-index: 10;*/ +} + +#canvasArea { + display: block; + position: relative; + /*height: 100%;*/ + width: 100%; + background-color: red; +} + +.simulation { + position: relative; + width: auto; + height: 100%; + overflow: hidden; + background-color: 'white'; +} + +.switch { + position: relative; + display: inline-block; + width: 43px; + height: 17px; + margin-bottom: 0px; +} + +.switch input { + display: none; +} + +.slider { + position: absolute; + cursor: pointer; + top: 2; + left: 0; + right: 0; + bottom: -2; + background-color: #ccc; + -webkit-transition: 0.4s; + transition: 0.4s; +} + +.slider:before { + position: absolute; + content: ''; + height: 17px; + width: 17px; + left: 0px; + bottom: 0px; + background-color: white; + -webkit-transition: 0.4s; + transition: 0.4s; +} + +input:checked + .slider { + background-color: #2196f3; +} + +input:focus + .slider { + box-shadow: 0 0 1px #2196f3; +} + +input:checked + .slider:before { + -webkit-transform: translateX(26px); + -ms-transform: translateX(26px); + transform: translateX(26px); +} + +/* Rounded sliders */ + +.slider.round { + border-radius: 34px; +} + +.slider.round:before { + border-radius: 50%; +} + +/* Slider for white background */ +.slider2 { + position: absolute; + cursor: pointer; + top: 2; + left: 0; + right: 0; + bottom: -2; + /* border: 1px solid black; */ + background-color: #ccc; + -webkit-transition: 0.4s; + transition: 0.4s; +} + +.slider2:before { + position: absolute; + content: ''; + height: 17px; + width: 17px; + left: 0px; + bottom: 0px; + background-color: #aaa; + -webkit-transition: 0.4s; + transition: 0.4s; +} + +input:checked + .slider2 { + background-color: #2196f3; +} + +input:focus + .slider2 { + box-shadow: 0 0 1px #2196f3; +} + +input:checked + .slider2:before { + -webkit-transform: translateX(26px); + -ms-transform: translateX(26px); + transform: translateX(26px); +} + +/* Rounded sliders */ + +.slider2.round { + border-radius: 34px; +} + +.slider2.round:before { + border-radius: 50%; +} + +#miniMap { + position: fixed; + z-index: 2; + bottom: 20px; + right: 40px; + /*height:150px; + width: 25%;*/ + overflow-y: scroll; + background-color: black; + /*border:1px solid #aaa;*/ + opacity: 0.97; + box-shadow: 0px 0px 15px #888888; + overflow: hidden; + /*transition: opacity .25s ease-in-out;*/ +} + +#plot { + position: fixed; + z-index: 1; + bottom: 0; + right: 0; + /*display: block;*/ + /*height: 0px;*/ + /*width: 100%;*/ + overflow-y: scroll; + background-color: #eee; + /*background-blend-mode: color;*/ +} + +.left { + float: left; +} + +.right { + float: right; +} + +.icon { + position: relative; + height: 70px; + width: 70px; + /*margin: 1px;*/ + margin-bottom: 5px; + margin-left: 3px; + display: inline-block; + background-color: white; + border-radius: 4px; + /*border-color: #0099ff;*/ + border: 2px solid #0099ff; + text-align: center; + font-size: 8px; + padding: 5px; +} + +img { + display: none; +} + +div.icon img { + -webkit-user-drag: none; + -khtml-user-drag: none; + -moz-user-drag: none; + -o-user-drag: none; + user-drag: none; + /*margin: auto;*/ + width: 100%; + /*height:100%;*/ + display: inline-block; +} + +.img__description { + position: absolute; + /*top: 0;*/ + bottom: -16; + text-align: center; + left: 0; + right: 0; + background-color: #0099ff; + color: white; + font-size: 8px; + /*background: rgba(29, 106, 154, 0.72); + color: #fff;*/ + visibility: hidden; + border-bottom-left-radius: 2px; + border-bottom-right-radius: 2px; + opacity: 0; + /* transition effect. not necessary */ + transition: opacity 0.2s, visibility 0.2s; +} + +.icon:hover .img__description { + visibility: visible; + opacity: 1; +} + +.icon:hover { + /*background-color: #cce5ff;*/ + /*border-color: blue;*/ + margin-bottom: 1px; + height: 74px; + background-color: #f5f5f5; + transition: height 0.2s margin 0.2s; +} + +.objectPropertyAttributeChecked.btn { + width: 100%; + margin-bottom: 5px; +} + +/* For loading screen - pace.js */ + +.pace { + -webkit-pointer-events: none; + pointer-events: none; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + width: 100%; + height: 100%; + background-color: #fff; +} + +.pace-inactive { + display: none; +} + +#Help { + visibility: hidden; + /* Hidden by default. Visible on click */ + min-width: 250px; + /* Set a default minimum width */ + margin-left: -125px; + /* Divide value of min-width by 2 */ + background-color: #333; + /* Black background color */ + color: #fff; + /* White text color */ + text-align: center; + /* Centered text */ + border-radius: 2px; + /* Rounded borders */ + padding: 16px; + /* Padding */ + position: fixed; + /* Sit on top of the screen */ + z-index: 1; + /* Add a z-index if needed */ + right: 50px; + /* Center the snackbar */ + bottom: 50px; + /* 30px from the bottom */ + opacity: 0; +} + +#Help.show { + visibility: visible; + /* Show the snackbar */ + opacity: 1; + -webkit-transition-delay: 0.5s; + /* Safari */ + transition-delay: 0.5s; + -webkit-transition-duration: 0.3s; + /* Safari */ + transition-duration: 0.3s; +} + +/* Animations to fade the snackbar in and out */ + +@-webkit-keyframes fadein { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes fadein { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@-webkit-keyframes fadeout { + from { + opacity: 1; + } + to { + opacity: 0; + } +} + +@keyframes fadeout { + from { + bottom: 0px; + opacity: 1; + } + to { + bottom: 0; + opacity: 0; + } +} + +.pace .pace-progress { + background: #29d; + position: fixed; + z-index: 2000; + top: 0; + right: 100%; + width: 50%; + height: 5px; +} + +/* dropdown-menu styles */ + +.dropdown-menu { + background-color: black; + border: 1px solid #09f; + border-top: none; +} + +.navbar-nav > li > a { + padding: 7px 15px; +} + +.dropdown-menu > li > a { + color: #939393 !important; + padding: 3px 14px; +} + +.dropdown-menu > li > a:hover { + color: #4db8ff !important; + background-color: black; +} + +.ui-dialog { + background: #222; +} + +.ui-dialog p { + color: #9d9d9d; +} + +.ui-widget-header { + border: 1px solid #0099ff; + color: #0099ff; +} + +.ui-dialog-buttonpane { + background-color: black; +} + +.ui-dialog-titlebar { + background-color: black; +} + +.ui-dialog-titlebar-close { + background-image: url('../img/cross.png'); + position: absolute; + right: 0.3em; + top: 50%; + width: 21px; + margin: -10px 0 0 0; + padding: 1px; + height: 20px; +} + +.ui-icon-close { + background-position: -80px -128px; +} + +.ui-dialog .ui-dialog-buttonpane button { + background-color: #004280; + border: 1px solid #09f; + color: white; +} + +.navbar .nav.pull-right { + float: right; + margin-right: 10px; + /*set margin this way in your custom stylesheet*/ +} + +/* LOADING ICON CSS STARTS*/ + +.sk-folding-cube { + margin: 20px auto; + width: 40px; + height: 40px; + position: relative; + -webkit-transform: rotateZ(45deg); + transform: rotateZ(45deg); +} + +.sk-folding-cube .sk-cube { + float: left; + width: 50%; + height: 50%; + position: relative; + -webkit-transform: scale(1.1); + -ms-transform: scale(1.1); + transform: scale(1.1); +} + +.sk-folding-cube .sk-cube:before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: #09f; + -webkit-animation: sk-foldCubeAngle 2.4s infinite linear both; + animation: sk-foldCubeAngle 2.4s infinite linear both; + -webkit-transform-origin: 100% 100%; + -ms-transform-origin: 100% 100%; + transform-origin: 100% 100%; +} + +.sk-folding-cube .sk-cube2 { + -webkit-transform: scale(1.1) rotateZ(90deg); + transform: scale(1.1) rotateZ(90deg); +} + +.sk-folding-cube .sk-cube3 { + -webkit-transform: scale(1.1) rotateZ(180deg); + transform: scale(1.1) rotateZ(180deg); +} + +.sk-folding-cube .sk-cube4 { + -webkit-transform: scale(1.1) rotateZ(270deg); + transform: scale(1.1) rotateZ(270deg); +} + +.sk-folding-cube .sk-cube2:before { + -webkit-animation-delay: 0.3s; + animation-delay: 0.3s; +} + +.sk-folding-cube .sk-cube3:before { + -webkit-animation-delay: 0.6s; + animation-delay: 0.6s; +} + +.sk-folding-cube .sk-cube4:before { + -webkit-animation-delay: 0.9s; + animation-delay: 0.9s; +} + +@-webkit-keyframes sk-foldCubeAngle { + 0%, + 10% { + -webkit-transform: perspective(140px) rotateX(-180deg); + transform: perspective(140px) rotateX(-180deg); + opacity: 0; + } + 25%, + 75% { + -webkit-transform: perspective(140px) rotateX(0deg); + transform: perspective(140px) rotateX(0deg); + opacity: 1; + } + 90%, + 100% { + -webkit-transform: perspective(140px) rotateY(180deg); + transform: perspective(140px) rotateY(180deg); + opacity: 0; + } +} + +@keyframes sk-foldCubeAngle { + 0%, + 10% { + -webkit-transform: perspective(140px) rotateX(-180deg); + transform: perspective(140px) rotateX(-180deg); + opacity: 0; + } + 25%, + 75% { + -webkit-transform: perspective(140px) rotateX(0deg); + transform: perspective(140px) rotateX(0deg); + opacity: 1; + } + 90%, + 100% { + -webkit-transform: perspective(140px) rotateY(180deg); + transform: perspective(140px) rotateY(180deg); + opacity: 0; + } +} + +/* LOADING ICON CSS ENDS*/ diff --git a/v1/src/styles/css/assets/layout-panel/down.svg b/v1/src/styles/css/assets/layout-panel/down.svg new file mode 100644 index 00000000..d8e8d105 --- /dev/null +++ b/v1/src/styles/css/assets/layout-panel/down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/styles/css/assets/layout-panel/left.svg b/v1/src/styles/css/assets/layout-panel/left.svg new file mode 100644 index 00000000..f73a40ff --- /dev/null +++ b/v1/src/styles/css/assets/layout-panel/left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/styles/css/assets/layout-panel/minus.svg b/v1/src/styles/css/assets/layout-panel/minus.svg new file mode 100644 index 00000000..b26d17f4 --- /dev/null +++ b/v1/src/styles/css/assets/layout-panel/minus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/styles/css/assets/layout-panel/plus.svg b/v1/src/styles/css/assets/layout-panel/plus.svg new file mode 100644 index 00000000..364e696d --- /dev/null +++ b/v1/src/styles/css/assets/layout-panel/plus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/styles/css/assets/layout-panel/right.svg b/v1/src/styles/css/assets/layout-panel/right.svg new file mode 100644 index 00000000..bef89def --- /dev/null +++ b/v1/src/styles/css/assets/layout-panel/right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/styles/css/assets/layout-panel/up.svg b/v1/src/styles/css/assets/layout-panel/up.svg new file mode 100644 index 00000000..7c014709 --- /dev/null +++ b/v1/src/styles/css/assets/layout-panel/up.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/styles/css/assets/logo.svg b/v1/src/styles/css/assets/logo.svg new file mode 100644 index 00000000..ffae4558 --- /dev/null +++ b/v1/src/styles/css/assets/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/styles/css/assets/shorcuts/delete.svg b/v1/src/styles/css/assets/shorcuts/delete.svg new file mode 100644 index 00000000..3611bbac --- /dev/null +++ b/v1/src/styles/css/assets/shorcuts/delete.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/styles/css/assets/shorcuts/download.svg b/v1/src/styles/css/assets/shorcuts/download.svg new file mode 100644 index 00000000..9dc67f1e --- /dev/null +++ b/v1/src/styles/css/assets/shorcuts/download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/styles/css/assets/shorcuts/dragDots.svg b/v1/src/styles/css/assets/shorcuts/dragDots.svg new file mode 100644 index 00000000..dacde439 --- /dev/null +++ b/v1/src/styles/css/assets/shorcuts/dragDots.svg @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/v1/src/styles/css/assets/shorcuts/fit.svg b/v1/src/styles/css/assets/shorcuts/fit.svg new file mode 100644 index 00000000..4cc765e2 --- /dev/null +++ b/v1/src/styles/css/assets/shorcuts/fit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/styles/css/assets/shorcuts/new.svg b/v1/src/styles/css/assets/shorcuts/new.svg new file mode 100644 index 00000000..bc1bb3bd --- /dev/null +++ b/v1/src/styles/css/assets/shorcuts/new.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/styles/css/assets/shorcuts/redo.svg b/v1/src/styles/css/assets/shorcuts/redo.svg new file mode 100644 index 00000000..30e00a4c --- /dev/null +++ b/v1/src/styles/css/assets/shorcuts/redo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/styles/css/assets/shorcuts/save-online.svg b/v1/src/styles/css/assets/shorcuts/save-online.svg new file mode 100644 index 00000000..bb414b9a --- /dev/null +++ b/v1/src/styles/css/assets/shorcuts/save-online.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/styles/css/assets/shorcuts/save.svg b/v1/src/styles/css/assets/shorcuts/save.svg new file mode 100644 index 00000000..9e3c8da5 --- /dev/null +++ b/v1/src/styles/css/assets/shorcuts/save.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/styles/css/assets/shorcuts/undo.svg b/v1/src/styles/css/assets/shorcuts/undo.svg new file mode 100644 index 00000000..c82d8b64 --- /dev/null +++ b/v1/src/styles/css/assets/shorcuts/undo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/styles/css/assets/small-components/chevron-down.svg b/v1/src/styles/css/assets/small-components/chevron-down.svg new file mode 100644 index 00000000..e4c8404d --- /dev/null +++ b/v1/src/styles/css/assets/small-components/chevron-down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/styles/css/assets/small-components/close.svg b/v1/src/styles/css/assets/small-components/close.svg new file mode 100644 index 00000000..8dc79fa4 --- /dev/null +++ b/v1/src/styles/css/assets/small-components/close.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v1/src/styles/css/custom_mailer.css b/v1/src/styles/css/custom_mailer.css new file mode 100644 index 00000000..3284a610 --- /dev/null +++ b/v1/src/styles/css/custom_mailer.css @@ -0,0 +1,6 @@ +.navbar, +.navbar-search-active, +.footer-empty-div, +.footer-container-fluid { + display: none; +} diff --git a/v1/src/styles/css/embed.css b/v1/src/styles/css/embed.css new file mode 100644 index 00000000..ed09eb73 --- /dev/null +++ b/v1/src/styles/css/embed.css @@ -0,0 +1,261 @@ +.switch { + position: relative; + display: inline-block; + width: 43px; + height: 17px; + margin-bottom: 0px; +} + +.switch input { + display: none; +} + +.slider { + position: absolute; + cursor: pointer; + top: 2; + left: 0; + right: 0; + bottom: -2; + background-color: #ccc; + -webkit-transition: 0.4s; + transition: 0.4s; +} + +.slider:before { + position: absolute; + content: ''; + height: 17px; + width: 17px; + left: 0px; + bottom: 0px; + background-color: white; + -webkit-transition: 0.4s; + transition: 0.4s; +} + +#clockProperty { + padding: 10px; +} + +#clockPropertyHeader { + border-radius: 3px; +} +#clockProperty { + padding: 10px; + border-radius: 6px; + opacity: 0.1; + transition: 0.4s; + height: 109px; + display: flex; + flex-direction: column; + justify-content: space-between; +} +#clockProperty:hover { + opacity: 1; + -webkit-transition: 0.4s; + transition: 0.4s; +} +input:checked + .slider { + /* background-color: #2196F3; */ +} + +input:focus + .slider { + box-shadow: 0 0 1px #2196f3; +} + +input:checked + .slider:before { + -webkit-transform: translateX(26px); + -ms-transform: translateX(26px); + transform: translateX(26px); +} + +#tabsBar .circuits { + color: #000; + text-align: center; + padding-left: 0.5em; + padding-right: 0.5em; + border: 1px solid #111; + display: inline-block; +} +button:focus { + outline: 0; +} +#tabsBar .circuits:hover { + background-color: lightgray; + /*border: 1px solid #0099ff;*/ + transition-duration: 100ms; +} +#tabsBar .current { + transition-duration: 100ms; +} + +.noSelect { + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + -o-user-select: none; +} +.pointerCursor { + cursor: pointer; +} +.defaultCursor { + cursor: default; +} +.simulation { + position: relative; + width: auto; + height: 100%; + overflow: hidden; + background-color: 'white'; +} + +#elementName { + position: absolute; + left: 6px; + bottom: 6px; + background-color: white; + z-index: 101; + color: black; + padding: 1px; + border: 0.5px solid black; + display: none; +} + +/* LOADING ICON CSS STARTS*/ +.sk-folding-cube { + margin: 20px auto; + width: 40px; + height: 40px; + position: relative; + -webkit-transform: rotateZ(45deg); + transform: rotateZ(45deg); +} + +.sk-folding-cube .sk-cube { + float: left; + width: 50%; + height: 50%; + position: relative; + -webkit-transform: scale(1.1); + -ms-transform: scale(1.1); + transform: scale(1.1); +} +.sk-folding-cube .sk-cube:before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: #09f; + -webkit-animation: sk-foldCubeAngle 2.4s infinite linear both; + animation: sk-foldCubeAngle 2.4s infinite linear both; + -webkit-transform-origin: 100% 100%; + -ms-transform-origin: 100% 100%; + transform-origin: 100% 100%; +} +.sk-folding-cube .sk-cube2 { + -webkit-transform: scale(1.1) rotateZ(90deg); + transform: scale(1.1) rotateZ(90deg); +} +.sk-folding-cube .sk-cube3 { + -webkit-transform: scale(1.1) rotateZ(180deg); + transform: scale(1.1) rotateZ(180deg); +} +.sk-folding-cube .sk-cube4 { + -webkit-transform: scale(1.1) rotateZ(270deg); + transform: scale(1.1) rotateZ(270deg); +} +.sk-folding-cube .sk-cube2:before { + -webkit-animation-delay: 0.3s; + animation-delay: 0.3s; +} +.sk-folding-cube .sk-cube3:before { + -webkit-animation-delay: 0.6s; + animation-delay: 0.6s; +} +.sk-folding-cube .sk-cube4:before { + -webkit-animation-delay: 0.9s; + animation-delay: 0.9s; +} +@-webkit-keyframes sk-foldCubeAngle { + 0%, + 10% { + -webkit-transform: perspective(140px) rotateX(-180deg); + transform: perspective(140px) rotateX(-180deg); + opacity: 0; + } + 25%, + 75% { + -webkit-transform: perspective(140px) rotateX(0deg); + transform: perspective(140px) rotateX(0deg); + opacity: 1; + } + 90%, + 100% { + -webkit-transform: perspective(140px) rotateY(180deg); + transform: perspective(140px) rotateY(180deg); + opacity: 0; + } +} + +@keyframes sk-foldCubeAngle { + 0%, + 10% { + -webkit-transform: perspective(140px) rotateX(-180deg); + transform: perspective(140px) rotateX(-180deg); + opacity: 0; + } + 25%, + 75% { + -webkit-transform: perspective(140px) rotateX(0deg); + transform: perspective(140px) rotateX(0deg); + opacity: 1; + } + 90%, + 100% { + -webkit-transform: perspective(140px) rotateY(180deg); + transform: perspective(140px) rotateY(180deg); + opacity: 0; + } +} +/* LOADING ICON CSS ENDS*/ + +#restrictedElementsDiv { + position: absolute; + bottom: 10; + right: 2; + z-index: 100; + width: 215px; + font-size: 14px; + background-color: gainsboro; + border: 1px solid #0d3349; + opacity: 0.1; + padding: 3px 5px; +} + +#restrictedElementsDiv:hover { + opacity: 1; + transition: 0.4s; +} + +.zoom-wrapper { + position: absolute; + bottom: 1px; + right: 0px; + font-size: 10px; + z-index: 100; +} + +.zoom-wrapper button { + opacity: 0.3; +} +.zoom-wrapper button:hover { + opacity: 1; +} + +.embed-fullscreen-btn { + border-radius: 20px; +} diff --git a/v1/src/styles/css/error.css b/v1/src/styles/css/error.css new file mode 100644 index 00000000..cf03badb --- /dev/null +++ b/v1/src/styles/css/error.css @@ -0,0 +1,24 @@ +.error-code { + color: #42b983; + font-family: 'CircuitBoredNF'; + font-size: 142px; +} + +.help-text-main { + color: #1c1c1c; + font-family: 'Segoe UI'; + font-size: 22px; + font-weight: 400; + margin: 0; +} + +.return { + color: #42b983; + display: inline-block; + font-family: 'Segoe UI'; + font-weight: 700; +} + +.return:hover { + color: #42b983; +} diff --git a/v1/src/styles/css/main.stylesheet.css b/v1/src/styles/css/main.stylesheet.css new file mode 100644 index 00000000..b487179b --- /dev/null +++ b/v1/src/styles/css/main.stylesheet.css @@ -0,0 +1,1956 @@ +/* +************ +* This stylesheet is to be made modular later +************ +*/ + +@import './5-layout/simulator.scss'; +@import './2-basics/base.scss'; +@import './2-basics/reset.scss'; +@import './shortcut.panel.css'; +@import './embed.css'; +@import './plugin-stylesheets/checkBo.min.css'; +/** new UI ruleset starts here */ +/*! Adding color variables to root, required later for hokey binding */ + +/* typography */ +@font-face { + font-family: Raleway; + src: url('https://fonts.gstatic.com/s/raleway/v18/1Ptxg8zYS_SKggPN4iEgvnHyvveLxVvaorCIPrcVIT9d0c8.woff'); +} + +/* typography */ +@font-face { + font-family: 'Nunito', sans-serif; + src: url('https://fonts.googleapis.com/css2?family=Nunito:ital,wght@0,200;1,200&display=swap'); +} + +/** Global Styles starts here */ + +body { + line-height: 1 !important; +} + +body, +html { + font-family: 'Nunito', sans-serif; + font-weight: 200; + width: 100%; + height: 100%; + margin: 0; + padding: 0; + overflow: hidden; + position: fixed; +} + +button:focus { + outline: 0; +} + +a { + color: var(--text-primary); +} + +a:hover { + color: white; + text-decoration: none; +} + +select > option { + color: black; + padding: 2px 4px; + margin-right: 5px; +} + +select:focus, +select > option:focus { + border: none; + outline: none; +} + +input[type='number']:focus { + background-color: transparent; + outline: none; + border: none; + color: white; +} + +table { + border-collapse: collapse; + -webkit-user-select: none; + /* Chrome/Safari */ + -moz-user-select: none; + /* Firefox */ + -ms-user-select: none; + /* IE10+ */ + /* Rules below not implemented in browsers yet */ + -o-user-select: none; + user-select: none; +} + +button { + background: none; + color: inherit; + border: none; + padding: 0; + font: inherit; + cursor: pointer; + outline: inherit; + box-shadow: none; +} + +button:not('.quick-btn button'):active { + background: transparent !important; +} + +button:active, +button:focus { + box-shadow: none !important; + border: none; + outline: none; + /* border-color: white !important; */ +} + +button:focus { + box-shadow: none; +} + +input[type='text']:focus { + background: transparent; + /* color: white; */ +} + +/*! Global styles ends here */ + +.noSelect { + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + -o-user-select: none; +} + +.navbar-menu { + position: relative; + transition: all 0.2s ease-in-out; +} + +.navbar-menu > li > a { + border: 1px solid transparent; + border-radius: 1px; + padding: 2px 8px; + transition: all 0.2s ease-in-out; + margin-right: 10px; +} + +.navbar-menu > li > a span, +.acc-caret { + content: ''; + background: url(./assets/small-components/chevron-down.svg) center/cover + no-repeat; + display: inline-block; + height: 5px; + width: 5px; + position: absolute; + right: 0; + top: 50%; + transform: translateY(-66%); + padding: 4px; + margin: 0 5px; + transition: all 0.2s ease-in-out; +} + +.acc-caret { + right: -17px; +} +.navbar-menu > li > a:hover { + border-bottom: 1px solid white; + text-decoration: none; +} + +.navbar-menu > li > a:hover span, +.acc-drop:hover .acc-caret { + background: none; +} + +.projectName { + position: relative; + left: 0.5rem; + font-size: 1em; + text-align: center; + display: inline-block; + width: 35vw; + overflow: hidden; + text-overflow: ellipsis; +} + +@media (max-width: 991px) { + .projectName { + visibility: hidden; + } +} + +.account-btn { + position: absolute; + right: 13px; + padding: 4px 10px; + border: 1px solid transparent; + border-radius: 1px; + transition: all 0.2s ease-in-out; +} + +.account-btn:hover { + border-bottom: 1px solid white; + text-decoration: none; +} + +.user-field { + display: inline-block; + max-width: 11rem; + white-space: nowrap; + text-overflow: ellipsis; + text-align: right; +} + +@media (max-width: 991px) { + .user-field { + visibility: hidden; + } +} + +.signIn-btn { + color: var(--text-primary); +} + +.cur-user, +.signIn-btn { + color: #fff; +} + +.signIn-btn:hover { + color: var(--text-primary); + text-decoration: none; +} + +.logo { + background: url(./assets/logo.svg) center/cover; + height: 30px; + width: 105px; + display: inline-block; + margin-right: 36px; +} + +/* dropdown-menu styles */ + +.dropdown > ul { + border-radius: 5px; + text-align: center; + position: absolute; + left: 50%; + transform: translate(-50%, 13px); +} + +.draggable-panel-css { + border-radius: 5px; + z-index: 70; + transition: background 0.5s ease-out; + position: fixed; +} + +@supports (backdrop-filter: blur()) { + .dropdown > ul { + backdrop-filter: blur(5px); + } +} + +.mw-override { + min-width: 110px; +} + +.dropdown > ul::before { + background-color: transparent; + content: ''; + width: 10px; + display: inline-block; + height: 10px; + position: absolute; + transform: translate(-50%, -13px) rotate(-45deg); +} + +.dropdown > ul::after { + content: ''; + width: 11.5px; + display: inline-block; + height: 10px; + position: absolute; + transform: translate(-50%, -15.5px); + top: 14.5px; +} + +.dropdown-menu > li > a { + padding: 7px 0; + width: 90%; + margin: auto; + transition: all 0.2s ease-in-out; + text-align: left; + padding-left: 10px; +} + +.dropdown-menu > li > a:hover { + border-radius: 7px; + opacity: 1; +} + +@media (max-width: 991px) { + .navbar-nav .dropdown-menu { + position: absolute !important; + float: none; + } +} + +#contextMenu { + width: 150px; + visibility: hidden; + position: fixed; + z-index: 1000; + opacity: 0; + top: 100; + left: 100; + cursor: pointer; + padding-bottom: 7px; + padding-top: 7px; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + border-radius: 5px; +} + +#contextMenu ul { + margin: 0; + padding: 0; +} + +#contextMenu ul li { + list-style: none; + padding: 8px; + padding-left: 20px; + width: 90%; + margin: auto; +} + +#contextMenu ul li:hover { + border-radius: 7px; + opacity: 1; +} + +@supports (backdrop-filter: blur()) { + #contextMenu { + backdrop-filter: blur(5px); + } + #contextMenu ul li:hover { + backdrop-filter: blur(50px); + -webkit-backdrop-filter: blur(50px); + } +} + +/** ce-panel styling starts */ + +.ce-panel { + font: inherit; + width: 240px; + top: 90px; + left: 10px; +} + +.accordion > :last-child { + margin-bottom: 15px; +} + +.draggable-panel-css .panel-header { + border-radius: 5px; + border-top-right-radius: 5px; + padding-top: 15px; + padding: 10px; + padding-top: 15px; + padding-left: 17px; + font-weight: bold; + font-size: 16px; + text-transform: uppercase; + text-align: left; + cursor: move; +} + +.draggable-panel-css .panel-header::before { + content: ''; + width: 34px; + border-radius: 2px; + position: absolute; + left: 50%; + transform: translateX(-50%); + top: 6px; +} + +.draggable-panel-css .panel-body { + padding-top: 10px; +} + +.draggable-panel-css .panel { + padding: 0em; + margin: 0; + border-radius: 0; + margin-bottom: 0em; + display: grid; + grid-template-columns: 1fr 1fr 1fr; + max-height: 185px; + border-radius: 2px; + overflow: auto; +} + +.ui-accordion-header { + background-color: transparent; + margin: 0em; + padding: 0em; + outline: none; +} + +.ui-accordion-header.ui-accordion-header-active.ui-state-active { + outline: none; +} + +.ui-accordion-header.ui-state-hover { + outline: none; +} + +.ui-accordion-header-icon.ui-icon { + display: none; +} + +.accordion { + width: 90%; + margin: auto; + position: relative; +} + +/* expansion panel styles - start */ +.v-expansion-panel { + background-color: transparent; + color: var(--text-panel); +} +.v-expansion-panel--active > .v-expansion-panel-title { + min-height: 48px; +} +.v-expansion-panel-title__overlay { + border-radius: 3px; + outline: none; + background-color: #ededed; + font-weight: 400; +} + +.v-expansion-panel__shadow { + box-shadow: none; +} + +.v-expansion-panel-title { + cursor: pointer; + position: relative; + margin: 2px 0 0; + padding: 0 0.5em 0 0.7em; + font-size: 100%; + font-weight: 400; + line-height: 1.3; +} + +.v-expansion-panel-text { + background-color: var(--text-panel); + color: black; +} +.v-expansion-panel-text__wrapper { + padding: 0; +} +/* expansion panel styles - ends */ + +.panelHeader { + border: none; + border-radius: 0; + transition: all 0.2s ease-in-out; +} + +.panelHeader:hover { + border-radius: 3px; +} + +.panelHeader:after, +.panelHeader:before { + content: ''; + height: 8px; + display: inline-block; + right: 15px; + position: absolute; + border-radius: 5px; + top: 50%; + transform: translateY(-50%) rotate(132deg); + transition: 0.2s ease-out; + background-color: white; +} + +.panelHeader:after { + transform: translate(260%, -50%) rotate(226deg); +} + +.ui-accordion-header-active:before { + transition: 0.2s ease-out; + transform-origin: left; + transform: translate(29%, -40%) rotate(50deg); + top: 46%; +} + +.ui-accordion-header-active:after { + transform-origin: bottom; + transform: translate(420%, -50%) rotate(310deg); + transition: 0.2s ease-out; + top: 46%; +} + +.ui-accordion-header-active:hover { + background-color: transparent; +} + +.ui-accordion .ui-accordion-content { + border: none; + padding: 0; +} + +.icon { + position: relative; + width: 50px; + margin: 5px; + display: inline-block; + text-align: center; + font-size: 8px; +} + +img { + display: none; +} + +div.icon img { + -webkit-user-drag: none; + -khtml-user-drag: none; + -moz-user-drag: none; + -o-user-drag: none; + width: 100%; + display: inline-block; +} + +.custom-tooltip-styling { + box-shadow: none; + border-radius: 3px; + font: inherit; + font-size: 14px; + font-weight: 100; +} + +.icon:hover { + border-radius: 3px; +} + +/*! ce-panel styling ends */ + +/** custom scroll styling starts here */ + +.search-results { + scrollbar-width: thin; /* for firefox */ +} + +.search-results::-webkit-scrollbar { + margin-right: 3px; + width: 6px; +} + +/*! custom scroll styling starts ends here */ + +/*! ce-panel styling ends */ + +/** tab bar styling starts */ + +#tabsBar { + width: 100%; + /* height: 23.5px; */ + display: block; + align-items: center; + z-index: 99; + /* position: absolute; + top: 47px; */ +} + +.embed-tabs { + background-color: transparent !important; +} + +#tabsBar .placeholder { + justify-content: space-between; + padding: 1px; + display: inline-block; + margin: 2px; + text-align: center; + /* min-width: 110px; */ + font-size: 14px; + transition: all 0.2s ease-in-out; +} + +.placeholder::before { + display: inline-block; + padding: 2px 5px; + content: '|'; +} + +#tabsBar .circuits { + justify-content: space-between; + border-radius: 3px; + padding: 1px; + display: inline-flex; + align-items: center; + margin: 2px; + text-align: center; + /* min-width: 110px; */ + font-size: 14px; + transition: all 0.2s ease-in-out; +} + +#tabsBar .circuits > span { + display: inline-block; + padding: 2px 5px; +} + +.circuitName { + cursor: pointer; +} + +#tabsBar button { + order: 99; /* could have better solution */ + width: 20px; + align-items: center; + display: inline; + font-size: 20px; + text-align: center; + padding-bottom: 5px; + text-decoration: none; + outline: none; + border-radius: 1px; + transition: all 0.1s ease-in-out; + border-radius: 4px; + margin-left: 1px; +} + +#tabsBar button:focus { + outline: none !important; + box-shadow: none !important; +} +#tabsBar button:active { + outline: none !important; + box-shadow: none !important; +} + +/*! tab bar styling ends */ +/** Module property styling starts here */ + +.moduleProperty { + font: inherit; + width: 250px; + top: 90px; + right: 10px; +} + +.layoutElementPanel { + width: 220px; + font: inherit; + display: none; + top: 90px; + left: 10px; +} + +.timing-diagram-panel { + border-radius: 5px; + z-index: 70; + transition: background 0.5s ease-out; + position: fixed; + cursor: pointer; + left: 300px; + top: 90px; +} + +.timing-diagram-panel .panel-header { + border-radius: 5px; + border-top-right-radius: 5px; + padding: 3px; + font-weight: bold; + font-size: 16px; + text-transform: uppercase; + text-align: left; + cursor: move; +} + +/* Testbench UI Styling begin */ + +.testbench-manual-panel { + border-radius: 5px; + z-index: 100; + transition: background 0.5s ease-out; + position: fixed; + cursor: pointer; + left: 10px; + top: 470px; +} + +.testbench-manual-panel .panel-header { + border-radius: 5px; + border-top-right-radius: 5px; + padding: 3px; + font-weight: bold; + font-size: 16px; + text-transform: uppercase; + text-align: left; + cursor: move; +} + +.tb-case-arrow { + border: solid var(--text-panel); + border-width: 0 3px 3px 0; + display: inline-block; + padding: 3px; +} + +.tb-case-arrow-right { + transform: rotate(-45deg); + -webkit-transform: rotate(-45deg); +} + +.tb-case-arrow-left { + transform: rotate(135deg); + -webkit-transform: rotate(135deg); +} + +.testbench-manual-panel .panel-body { + width: 700px; +} + +.testbench-manual-panel b { + font-weight: bold; +} + +.tb-manual-test-data { + /*text-align: center;*/ + margin-top: 10px; + border-bottom: 1px solid var(--br-secondary); + padding-left: 8px; + padding-right: 8px; +} + +.tb-manual-test-data .tb-data { + margin-right: 10px; +} + +.tb-data span { + vertical-align: middle; + display: inline-block; + max-width: 200px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +.tb-data#data-title { + float: left; +} + +.tb-data#data-type { + float: right; +} + +.tb-manual-table { + position: relative; + display: inline-block; + margin-top: 10px; + color: var(--text-panel); + max-width: 650px; + overflow-x: auto; + white-space: nowrap; +} + +.tb-manual-table td, +.tb-manual-table th { + padding-left: 15px; + padding-right: 15px; + padding-top: 12px; + padding-bottom: 12px; + text-align: center; + min-width: 80px; +} + +.tb-manual-table th { + background: var(--table-head-dark); + height: 50px; +} + +.testbench-manual-panel-buttons { + position: relative; + display: table-cell; + flex-wrap: wrap; + right: 0px; + text-align: left; + width: 200px; +} + +.testbench-runall-label { + display: none; +} + +.tb-dialog-button { + display: inline; + margin: 8px; + border-radius: 5px !important; + padding-left: 8px !important; + padding-right: 8px !important; + padding-top: 4px !important; + padding-bottom: 4px !important; +} + +.tb-manual-test-buttons { + display: flex; + margin-top: 20px; + margin-left: 30px; + margin-right: 30px; + height: 25px; + overflow: auto; +} + +.tb-manual-test-buttons .tb-case-button-left { + border-bottom-left-radius: 5px; + border-top-left-radius: 5px; + width: 24px; +} + +.tb-manual-test-buttons .tb-case-button-right { + border-bottom-right-radius: 5px; + border-top-right-radius: 5px; + width: 24px; +} + +.tb-manual-test-buttons .tb-test-label { + position: relative; + top: 0px; + line-height: 25px; + height: 25px; + margin: 0px; + padding-left: 2px; + padding-right: 2px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + background: #c4c4c4; + color: black; +} + +.tb-manual-test-buttons .tb-test-label.group-label { + text-align: center; + width: 100px; +} + +.tb-manual-test-buttons .tb-test-label.case-label { + text-align: center; + width: 40px; +} + +.tb-group-buttons { + float: left; +} + +.tb-case-buttons { + float: right; +} + +.tb-test-null { + width: 350px !important; +} + +.validation-ui-table td, +.validation-ui-table th { + padding-left: 15px; + padding-right: 15px; + padding-top: 12px; + padding-bottom: 12px; + text-align: center; + min-width: 80px; + color: white; +} + +/* Testbench UI styling end */ + +#plotArea { + padding: 3px; + width: 100%; +} + +#verilogEditorPanel { + width: 220px; + font: inherit; + display: none; + top: 90px; + right: 300px; +} + +#moduleProperty-toolTip { + padding: 10px; +} + +#moduleProperty-inner { + width: 85%; + margin: auto; +} + +#moduleProperty-header { + font-size: 1.1em; + text-transform: uppercase; + margin-bottom: 20px; + text-align: left; +} + +#moduleProperty-inner > p span { + display: inline-block; + font-weight: bold; +} + +#moduleProperty-inner > div span { + display: inline-block; + font-weight: bold; +} + +#moduleProperty-inner > p button { + border-radius: 2px; + margin: 3px; +} + +#moduleProperty-inner > div button { + border-radius: 2px; + margin: 3px; +} + +#moduleProperty-inner:last-child { + margin-bottom: 15px; +} + +.moduleProperty select { + background-color: transparent; + border: none; + margin-left: 2px; + outline: none; +} + +.moduleProperty input, +.moduleProperty textarea { + background-color: transparent; + margin-top: 7px; + outline: none; + padding: 5px 5px; + width: 100%; +} + +.moduleProperty textarea { + text-align: left; +} + +.moduleProperty select, +.moduleProperty input, +.moduleProperty textarea { + border-radius: 7px !important; +} + +.moduleProperty input:focus, +.moduleProperty select:focus, +.moduleProperty textarea { + outline-width: 0; + outline: none; + box-shadow: none; +} + +.input-group-prepend button { + margin-right: 5px; +} +.input-group-append button { + margin-left: 5px; +} + +.input-group-prepend button:hover { + border-radius: 3px !important; +} +.input-group-append button:hover { + border-radius: 3px !important; +} + +/* toogle */ + +.switch { + position: relative; + width: 43px; + height: 17px; + margin-bottom: 0px; + float: right; +} + +.switch input { + display: none; +} + +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + -webkit-transition: 0.2s all ease; + transition: 0.2s all ease; + border-radius: 25px; + width: 35px; +} + +.slider:before { + position: absolute; + content: ''; + height: 20px; + width: 20px; + left: -3px; + top: 50%; + transform: translateY(-51%); + -webkit-transition: 0.2s all ease-in-out; + transition: 0.2s all ease-in-out; + border-radius: 50%; +} + +input:checked + .slider:before { + transform: translate(21px, -51%); +} + +/** custom button styling */ + +.custom-btn--primary { + border-radius: 1px; +} + +.custom-btn--secondary { + background-color: transparent; + border-radius: 1px; + width: 90%; + display: inline-block; + line-height: inherit; +} + +.custom-btn--secondary:hover { + /* color: white; */ + transition: all 0.3s ease; +} + +.custom-btn--tertiary { + border-radius: 1px; +} +/* Used to force auto width on secondary button */ +.custom-btn--basic { + border-radius: 1px; + border: solid 1px; + background-color: transparent; + display: inline-block; + background: var(--table-head-dark); +} + +.custom-btn--basic:focus { + border: solid 1px; +} + +#HelpButton { + background-color: transparent; + border: 2px solid white; + width: 90%; + margin-bottom: 15px; + margin-top: 15px; + font-weight: bold; +} + +.btn-parent { + width: 100%; + display: flex; + justify-content: center; + margin: 0; +} + +/* custom spin button */ + +/*! Module property styling starts here */ + +/** selects styling starts here */ + +.moduleProperty select { + text-align: center; + width: 81px; + height: 20px; + float: right; + font-size: 17px; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +/*! selects styling end here */ + +/** layout dialog styling starts here */ + +.layout-body { + text-align: center; + padding: 10px; + padding-bottom: 17px; + font-weight: bold; +} + +#layoutDialog { + /* display: none; */ + right: 10px; + top: 90px; + width: 220px; +} + +.layout-title span { + display: block; + font-weight: bold; + margin: 8px; +} + +.layout-title--enable { + display: flex; + justify-content: space-between; + margin: 15px 0; + padding: 0 8px; +} + +.Layout-btn { + width: 48%; + height: 30px; + line-height: inherit; +} + +.zoomButton-up { + /* background: url(./assets/layout-panel/up.svg) center/cover no-repeat; */ + display: inline-block; + height: 35px; + width: 35px; +} +.zoomButton-down { + /* background: url(./assets/layout-panel/down.svg) center/cover no-repeat; */ + display: inline-block; + height: 35px; + width: 35px; +} +.zoomButton-left { + /* background: url(./assets/layout-panel/left.svg) center/cover no-repeat; */ + display: inline-block; + height: 35px; + width: 35px; +} +.zoomButton-right { + /* background: url(./assets/layout-panel/right.svg) center/cover no-repeat; */ + display: inline-block; + height: 35px; + width: 35px; +} + +/*! layout dialog styling ends here */ + +/** download dialog styling starts here */ + +.ui-dialog { + /*this also affects all dialog created using jquery UI, needs to be more universe */ + font: inherit; + border-radius: 5px; + width: 600px; + height: 320px; + padding: 10px 17px; + padding-bottom: 0; + /* border: none !important; */ +} + +@supports (backdrop-filter: blur()) { + .ui-dialog { + backdrop-filter: blur(5px); + } +} + +.ui-widget-header { + background: transparent; + border: none; + border-radius: 0; +} + +.option { + display: inline-flex; + border-radius: 7px; + align-items: center; + justify-content: space-around; + padding: 0 7px; + position: relative; + cursor: pointer; + margin: 2px 3px; +} + +.option input[type='radio'] { + visibility: hidden; +} + +.download-dialog-section-2 .btn { + color: var(--text-lite); +} +.download-dialog-section-2 .btn:hover { + color: var(--text-lite); +} + +.download-dialog-section-2 .option { + background: transparent; +} + +.custom-radio span { + height: 20px; + width: 20px; + border-radius: 50%; + display: block; + position: absolute; + left: 2px; + top: 50%; + transform: translateY(-50%); +} + +.custom-radio span:after { + content: ''; + height: 8px; + width: 8px; + border-radius: 50%; + display: block; + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%) scale(0); + transition: 300ms ease-in-out 0s; +} + +.custom-radio input[type='radio']:checked ~ span:after { + transform: translate(-50%, -50%) scale(1); +} + +#saveImageDialog { + border-radius: 2px; + padding: 13px; + margin: 0; + margin-top: 15px; + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; + min-height: 188px !important; +} + +.download-dialog-section-2 .option { + padding: 0; +} + +.download-dialog-section-1 > label { + height: 30px; + width: 85px; +} + +.download-dialog-section-2 { + background: transparent; + width: 100%; + display: inline-flex; + justify-content: space-around; +} + +.btn-group-toggle { + background-color: transparent; + overflow: hidden; +} +.download-dialog-section-2 .active-btn { + box-shadow: none; +} + +.download-dialog-section-2 .btn input[type='radio']:disabled { + background: red !important; + color: red !important; +} + +.download-dialog-section-2_2 { + display: flex; + align-items: center; + justify-content: center; +} + +.download-dialog-section-3 { + border-radius: 2px; + display: flex; + justify-content: space-between; + align-items: center; + padding: 6px 10px; + width: 320px; + position: inherit; +} + +.download-dialog-section-3 > label { + width: 60px; + height: 25px; + margin-bottom: 0; +} + +.ui-dialog-buttonpane { + background: transparent; +} + +.ui-dialog .ui-dialog-titlebar { + padding: 0; + padding-bottom: 8px; + font: inherit; + line-height: inherit; + font-weight: bold; +} + +.ui-dialog-titlebar-close { + border: none; + color: white; + position: absolute; + top: 15px; + right: 15px; + visibility: hidden; +} + +.ui-dialog-titlebar-close::after { + content: ''; + display: block; + position: absolute; + background: url(./assets/small-components/close.svg) center/cover no-repeat; + height: 15px; + width: 15px; + visibility: visible; + right: 0; + top: 0; +} + +.ui-dialog-titlebar-close::hover { + border: none; +} + +.ui-dialog .ui-dialog-buttonpane { + border: none; + padding: 0; + margin: 0; + display: flex; + justify-content: center; + align-items: center; + margin: 12px; +} + +.ui-dialog .ui-dialog-buttonpane button { + background: transparent; + color: white; + line-height: inherit; + border-radius: 1px; + font: inherit; +} + +.ui-dialog .ui-dialog-buttonpane button:hover { + transition: all 0.3s ease; + border: 1px solid transparent; +} + +.render-btn { + height: 35px; + border-radius: 1px; +} +.navbar { + transition: background 0.5s ease-out; +} + +.navbar .nav.pull-right { + float: right; + margin-right: 10px; + min-width: 85px; +} + +@media (max-width: 991px) { + .navbar .nav.pull-right { + display: none; + } +} + +@media (max-width: 991px) { + .nav-dropdown { + text-align: center; + padding-top: 20px; + } +} + +/*! download dialog styling end here */ + +/** combinationalAnalysis dialog styling starts here */ + +.ui-dialog[aria-describedby='combinationalAnalysis'] { + width: 460px; + min-height: 210px; + border: none; +} + +#combinationalAnalysis { + margin-top: 10px; +} + +#combinationalAnalysis p input { + border: 1px solid white; + background: transparent; + font: inherit; + text-align: center; +} + +.ui-dialog input::placeholder { + /* Chrome, Firefox, Opera, Safari 10.1+ */ + color: white; + opacity: 0.7; /* Firefox */ +} + +#combinationalAnalysis table { + width: 460px; +} + +#booleanTable { + width: 200px; +} + +.content-table { + border-collapse: collapse; + font-size: 0.9em; + min-width: 400px; +} + +.content-table tr th { + font-weight: bold; +} + +.content-table th, +.content-table td { + padding: 5px 15px; + margin: 0 3px; + width: 20%; + border-radius: 2px; +} + +.content-table tbody tr { + text-align: center; + display: flex; + margin-bottom: 4px; +} + +.content-table tbody { + display: table-row-group; + overflow: auto !important; + margin-left: 52px; +} + +.output { + cursor: pointer; +} + +/*! combinationalAnalysis dialog styling end here */ + +#setTestbenchData input { + border: 1px solid white; + background: transparent; + text-align: center; + font: inherit; + color: white; +} + +#setTestbenchData p { + font: inherit; + color: white; +} + +/** openProjectDialog styling starts here */ + +#openProjectDialog { + display: grid; + /* grid-template-columns: 1fr 1fr 1fr; */ + /* grid-gap: 0 10px; */ + align-items: center; +} + +#openProjectDialog > label { + margin: 4px; + padding: 10px; + background: transparent; + border-radius: 1px; + width: 100%; +} + +/*! openProjectDialog styling ends here */ + +#insertSubcircuitDialog { + display: block; + padding-bottom: 0; + overflow: visible; +} + +#insertSubcircuitDialog > p { + margin-bottom: 0; +} + +#insertSubcircuitDialog > label { + height: 30px; + border-radius: 3px; + margin: 0 5px; + margin-bottom: 4px; + justify-content: center; + padding-left: 10px; +} + +#miniMap { + position: fixed; + z-index: 3; + bottom: 20px; + right: 40px; + overflow-y: scroll; + opacity: 0.5; + overflow: hidden; + border: none; +} + +.disable::after { + content: ''; + position: absolute; + height: 100%; + width: 100%; + cursor: not-allowed; + left: 0; +} + +.ui-dialog .ui-dialog-buttonpane button { + margin-left: 0.4em; +} + +.ui-dialog-titlebar-close:hover { + border: none; +} + +.radio-green { + background: #42b983; +} + +.search-input { + margin: 0 10px; + padding: 3px 10px; + width: 90%; + border-radius: 13px; + margin-bottom: 10px; + background: transparent !important; +} + +.search-input:focus { + outline: none !important; +} + +.search-close { + position: absolute; + right: 19px; + top: 6px; + cursor: pointer; +} + +.search-results { + padding: 15px; + transition: all 0.5s ease; + max-height: 340px; + overflow-y: scroll; + padding-right: 0; +} + +.search-results div { + border-radius: 3px; +} + +.draggable-panel-css .minimize { + position: absolute; + right: 15px; + cursor: pointer; +} + +.panel-button-icon { + cursor: pointer; +} + +.panel-button { + cursor: pointer; + padding: 2px; +} + +.draggable-panel-css .maximize { + position: absolute; + right: 15px; + cursor: pointer; + display: none; +} + +.ce-hidden, +.prop-hidden { + font-weight: bold; + padding: 10px; + font-size: 16px; + text-transform: uppercase; + border-radius: 5px; +} + +.largeButton.btn { + width: 100%; + margin-bottom: 5px; + margin-left: 0 !important; +} + +.objectPropertyAttributeChecked { + margin-left: 0 !important; +} + +#exitViewBtn { + position: fixed; + z-index: 1000000000; + right: 2%; + top: 3%; + box-shadow: 0px 0px 10Xpx #4545457f; + padding: 10px 15px; + border-radius: 5px; +} + +.ce-hidden, +.prop-hidden { + cursor: move; +} + +#canvasArea, +#backgroundArea, +#simulationArea, +canvas { + /* cursor: wait !important; */ +} + +/** Color them dialog styles starts here*/ + +.ui-dialog[aria-describedby='colorThemesDialog'] { + min-width: 760px; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} + +.colorThemesDialog { + height: 390px !important; + display: grid; + grid-template-columns: 1fr 1fr 1fr; + overflow-y: auto; + margin-top: 10px; + border: 1px solid white !important; +} + +.colorThemesDialog input { + margin: 15px; +} + +.colorThemesDialog label { + margin-bottom: 0; +} + +.theme { + color: white; + width: 202.5px; + line-height: 30px; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + margin: 15px; + border-radius: 1.5px; + transition: all 0.1s ease-out; + position: relative; + overflow-x: hidden; + height: 154px; +} + +.themeNameBox { + display: block; + width: 100%; + cursor: pointer; +} + +.themeSel { + background: transparent; + display: block; + width: 100%; + height: 100%; + position: absolute; +} + +/*! Color them dialog styles ends here*/ + +/*! Custom Color theme dialog styles starts here*/ + +.ui-dialog[aria-describedby='CustomColorThemesDialog'] { + min-width: 760px; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} + +#CustomColorThemesDialog { + height: 400px !important; + background: none; + overflow: auto; +} + +#CustomColorThemesDialog label { + color: var(--text-panel); + width: 60%; + height: 30px; +} + +#CustomColorThemesDialog input { + cursor: pointer; + width: 30%; + height: 30px; +} + +/*! Custom Color theme dialog styles ends here*/ + +.code-window .CodeMirror { + height: calc(100vh - 78px); + overflow: scroll; +} + +.code-window-embed .CodeMirror { + height: 100%; + overflow: scroll; +} + +.code-window-embed { + position: absolute; + top: 28px; + height: 100%; + width: 100%; + overflow: scroll; + z-index: 3; + display: none; +} + +.code-window { + display: none; +} + +#verilogOutput { + font-size: 12px; +} + +.embed-fullscreen-btn { + border-radius: 3px; + width: auto; +} + +#plot { + width: 800px; +} + +.timing-diagram-toolbar { + padding-left: 4px; + padding: 2px; + cursor: default; +} + +.timing-diagram-toolbar input { + width: 80px; + background: transparent !important; +} + +#timing-diagram-log { + font-size: 12.5px; + padding: 3px; + margin-left: 5px; + /* margin-bottom: 5px; */ + border-radius: 3px; +} + +/* CustomColorInput Styles Starts Here */ +.customColorInput { + cursor: pointer; + width: 30%; + height: 30px; + overflow: visible; + position: relative; + top: 8px; + appearance: auto; + background-color: buttonface; + color: buttontext; + border-width: 1px; + border-style: solid; + border-color: buttonborder; + border-image: initial; + padding: 1px 2px; +} +.customColorLabel { + width: 60%; + height: 30px; +} + +/* Vue Dialog Box Styles STARTS */ + +.inputField { + width: 100%; + padding: 10px 10px; + margin: 8px 0; + box-sizing: border-box; + border-radius: 5px; + border: 1px solid #c5c5c5; + color: white; + outline: none; +} + +.cAinput { + width: 30%; + padding: 0 5px; + margin: 8px 0; + box-sizing: border-box; + border-radius: 5px; + border: 1px solid #c5c5c5; + color: white; + outline: none; +} + +.combinationalAnalysisInput:first-child { + padding-top: 20px; +} + +.combinationalAnalysisInput { + display: flex; + flex-direction: row; + justify-content: space-evenly; + align-items: baseline; +} + +.inputField:focus { + border: 2px solid #c5c5c5; +} + +.v-card-actions { + width: fit-content; + display: flex; + flex-direction: row; + justify-content: center; + margin: auto; +} + +.v-input__details { + margin-bottom: 0.5rem; +} + +/* +.ProseMirror ul { + list-style-type: disc; +} + +.ProseMirror ol { + list-style-type: decimal; +} */ + +.ProseMirror { + height: 12rem; + overflow-y: auto; + padding-left: 0.5em; + padding-right: 0.5em; + outline: none; +} + +.fullscreen .ProseMirror { + height: 75vh; +} + +/* .ProseMirror ul, +.ProseMirror ol, +.ProseMirror li { + margin: 0; + padding: 0; + list-style: inherit; +} */ + +.messageBoxContent { + height: auto; + width: 760px; + justify-content: center; + margin: auto; + backdrop-filter: blur(5px); + border-radius: 5px; + border: 0.5px solid var(--br-primary) !important; + background: var(--bg-primary-moz) !important; + background-color: var(--bg-primary-chr) !important; + color: white; +} + +/* media query for .messageBoxContent */ +@media screen and (max-width: 991px) { + .messageBoxContent { + width: 100%; + } +} + +.tabsbarInput { + align-items: center; +} + +.messageBtn { + width: fit-content; + min-width: 50px; + border: 1px solid #c5c5c5; + padding: 5px 5px; +} + +.messageBtn:hover { + background: #c5c5c5; + color: black; +} + +.dialogHeader { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + margin-bottom: 10px; +} +.dialogClose { + position: absolute; + top: 5px; + right: 5px; + color: white; + background: none; + font-size: x-small; + box-shadow: none; +} +.dialogClose:hover { + font-weight: bold; + background: white; + opacity: 0.5; + color: black; +} + +.dialogHeader { + font-weight: bold; + margin-bottom: 25px; +} + +.downloadCheckbox { + width: 5px; + height: 5px; +} +/* STYLE ENDS */ diff --git a/v1/src/styles/css/plugin-stylesheets/checkBo.min.css b/v1/src/styles/css/plugin-stylesheets/checkBo.min.css new file mode 100644 index 00000000..199f3c9b --- /dev/null +++ b/v1/src/styles/css/plugin-stylesheets/checkBo.min.css @@ -0,0 +1,386 @@ +/* + * checkBo lightweight jQuery plugin v0.1.4 by @ElmahdiMahmoud + * Licensed under the MIT license - https://github.com/elmahdim/checkbo/blob/master/LICENSE + * + * Custom checkbox and radio + * Author URL: elmahdim.com + */ + +.cb-checkbox .cb-inner, +.cb-checkbox i { + width: 18px; + height: 18px; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; +} +.cb-checkbox.cb-sm .cb-inner, +.cb-checkbox.cb-sm i { + width: 14px; + height: 14px; +} +.cb-checkbox.cb-md .cb-inner, +.cb-checkbox.cb-md i { + width: 24px; + height: 24px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; +} +.cb-checkbox.cb-lg .cb-inner, +.cb-checkbox.cb-lg i { + width: 30px; + height: 30px; + -moz-border-radius: 6px; + -webkit-border-radius: 6px; + border-radius: 6px; +} +.cb-radio .cb-inner { + width: 18px; + height: 18px; +} +.cb-radio.cb-sm .cb-inner { + width: 14px; + height: 14px; +} +.cb-radio.cb-md .cb-inner { + width: 24px; + height: 24px; +} +.cb-radio.cb-lg .cb-inner { + width: 30px; + height: 30px; +} +.cb-checkbox, +.cb-radio { + padding: 3px 0; + color: inherit; + cursor: pointer; + overflow: hidden; + font-size: inherit; + font-weight: 400; + display: inline-block; + line-height: 18px; +} +.cb-checkbox input[type='checkbox'], +.cb-radio input[type='radio'], +.cb-switcher input[type='checkbox'], +.cb-switcher input[type='radio'] { + /* display: none; */ +} +.cb-checkbox.disabled, +.cb-checkbox.disabled *, +.cb-radio.disabled, +.cb-radio.disabled *, +.cb-switcher.disabled, +.cb-switcher.disabled * { + cursor: default; +} +.cb-checkbox.disabled, +.cb-checkbox.disabled .cb-inner { + color: #ddd; +} +.cb-checkbox.disabled:hover .cb-inner { + border-color: #ddd; +} +.cb-checkbox.disabled.checked .cb-inner { + background-color: #ddd; + border-color: #ddd; +} +.cb-radio.disabled { + color: #ddd; +} +.cb-radio.disabled .cb-inner { + border-color: #ddd; +} +.cb-radio.disabled i { + background-color: transparent; +} +.cb-radio.disabled.checked .cb-inner { + border-color: #ddd; +} +.cb-radio.disabled.checked .cb-inner i { + background-color: #ddd; +} +.cb-radio.disabled:hover .cb-inner { + border-color: #ddd; +} +.cb-checkbox .cb-inner { + float: left; + overflow: hidden; + margin: 0 5px 0 0; + position: relative; + background: transparent; + display: inline-block; + border: 1px solid #d6d6d6; + /* -moz-transition: all 0.5s ease; + -o-transition: all 0.5s ease; + -webkit-transition: all 0.5s ease; + transition: all 0.5s ease; */ +} +.cb-checkbox i { + top: 1px; + left: 2px; + display: block; + position: absolute; +} +.cb-checkbox i:after, +.cb-checkbox i:before { + height: 0; + width: 2px; + content: ''; + display: block; + position: absolute; + background-color: #fff; + /* -moz-transition: all 0.2s ease; + -o-transition: all 0.2s ease; + -webkit-transition: all 0.2s ease; + transition: all 0.2s ease; */ +} +.cb-checkbox i:before { + top: 0; + left: 0; + -moz-transform: rotate(-45deg); + -ms-transform: rotate(-45deg); + -webkit-transform: rotate(-45deg); + transform: rotate(-45deg); +} +.cb-checkbox i:after { + left: 7px; + bottom: 5px; + /* -moz-transition-delay: 0.3s; + -o-transition-delay: 0.3s; + -webkit-transition-delay: 0.3s; + transition-delay: 0.3s; */ + -moz-transform: rotate(30deg); + -ms-transform: rotate(30deg); + -webkit-transform: rotate(30deg); + transform: rotate(30deg); +} +.cb-radio .cb-inner { + float: left; + overflow: hidden; + margin: 0 5px 0 0; + position: relative; + display: inline-block; + border: 1px solid #d7d7d7; + background-color: transparent; + -moz-border-radius: 100%; + -webkit-border-radius: 100%; + border-radius: 100%; + -moz-transition: all 0.1s ease; + -o-transition: all 0.1s ease; + -webkit-transition: all 0.1s ease; + transition: all 0.1s ease; +} +.cb-radio i { + top: 50%; + left: 50%; + width: 6px; + height: 6px; + margin-top: -3px; + margin-left: -3px; + position: absolute; + background-color: transparent; + -moz-border-radius: 100%; + -webkit-border-radius: 100%; + border-radius: 100%; + -moz-transform: scale(0.05, 5); + -ms-transform: scale(0.05, 5); + -webkit-transform: scale(0.05, 5); + transform: scale(0.05, 5); + -moz-transition: all 0.2s ease; + -o-transition: all 0.2s ease; + -webkit-transition: all 0.2s ease; + transition: all 0.2s ease; +} +.cb-checkbox.cb-sm, +.cb-radio.cb-sm { + line-height: 14px; +} +.cb-checkbox.cb-md, +.cb-radio.cb-md { + line-height: 24px; +} +.cb-checkbox.cb-lg, +.cb-radio.cb-lg { + line-height: 30px; +} +.cb-checkbox.cb-sm i:before { + top: 4px; + left: 1px; +} +.cb-checkbox.cb-sm i:after { + left: 5px; +} +.cb-checkbox.cb-md i:before { + top: 10px; + left: 5px; +} +.cb-checkbox.cb-md i:after { + bottom: 6px; + left: 11px; +} +.cb-checkbox.checked .cb-inner { + border-color: transparent; + background-color: transparent; +} +.cb-checkbox.checked.cb-sm i:before { + top: 4px; + left: 1px; +} +.cb-checkbox.checked.cb-sm i:after { + height: 9px; +} +.cb-checkbox.checked.cb-md i:before { + top: 10px; + left: 4px; + height: 8px; +} +.cb-checkbox.checked.cb-md i:after { + bottom: 6px; + left: 11px; + height: 16px; +} +.cb-checkbox.checked.cb-lg i:before { + top: 11px; + left: 6px; + height: 12px; +} +.cb-checkbox.checked.cb-lg i:after { + left: 14px; + bottom: 7px; + height: 20px; +} +.cb-checkbox.checked i:before { + top: 6px; + left: 2px; + height: 6px; +} +.cb-checkbox.checked i:after { + height: 12px; +} +.cb-radio.checked .cb-inner { + background: #fff; + box-shadow: 0 0 3px #efefef; +} +.cb-radio.checked i { + -moz-transform: scale(1.1, 1.1); + -ms-transform: scale(1.1, 1.1); + -webkit-transform: scale(1.1, 1.1); + transform: scale(1.1, 1.1); + background-color: transparent; +} +.cb-checkbox:hover .cb-inner, +.cb-radio:hover .cb-inner { + border-color: white; +} +.cb-switcher { + display: inline-block; + border: 1px solid #eee; + background-color: #fff; + width: 95px; + height: 35px; + position: relative; + -moz-border-radius: 20px; + -webkit-border-radius: 20px; + border-radius: 20px; + -moz-transition: background 0.4s ease; + -o-transition: background 0.4s ease; + -webkit-transition: background 0.4s ease; + transition: background 0.4s ease; +} +.cb-switcher, +.cb-switcher * { + cursor: pointer; +} +.cb-switcher ::-moz-selection { + background-color: transparent; +} +.cb-switcher ::selection { + background-color: transparent; +} +.cb-switcher .cb-state { + z-index: 1; + text-align: center; + font-size: 12px; +} +.cb-switcher .cb-state, +.cb-switcher:before { + width: 34px; + height: 34px; + line-height: 34px; + position: absolute; + left: 0; + top: -1px; + -moz-border-radius: 100%; + -webkit-border-radius: 100%; + border-radius: 100%; + -moz-transition: all 0.4s ease; + -o-transition: all 0.4s ease; + -webkit-transition: all 0.4s ease; + transition: all 0.4s ease; +} +.cb-switcher:before { + content: ''; + background-color: #eee; + -moz-box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1); + -webkit-box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1); + box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1); +} +.cb-switcher.checked { + background-color: transparent; +} +.cb-switcher.checked .cb-state, +.cb-switcher.checked:before { + left: 60px; + color: transparent; +} +.cb-switcher.checked:before { + background-color: #fff; + -moz-box-shadow: -1px 1px 1px rgba(0, 0, 0, 0.1); + -webkit-box-shadow: -1px 1px 1px rgba(0, 0, 0, 0.1); + box-shadow: -1px 1px 1px rgba(0, 0, 0, 0.1); +} +.cb-switcher.checked .inner-switcher:before { + border-top-color: transparent; +} +.cb-switcher.checked .inner-switcher:after { + border-bottom-color: transparent; +} +.cb-switcher .inner-switcher:after, +.cb-switcher .inner-switcher:before { + content: ''; + position: absolute; + left: 50%; + width: 0; + height: 0; + z-index: 2; + margin-left: -20px; + border-left: 20px solid transparent; + border-right: 20px solid transparent; + -moz-transition: border 0.4s ease; + -o-transition: border 0.4s ease; + -webkit-transition: border 0.4s ease; + transition: border 0.4s ease; +} +.cb-switcher .inner-switcher:before { + border-top: 17px solid #fff; + top: 0; +} +.cb-switcher .inner-switcher:after { + border-bottom: 17px solid #fff; + bottom: 0; +} +.cb-state { + color: #ccc; + display: inline-block; +} +.cb-switcher-group .cb-state { + position: relative; + top: 7px; +} +.is-hidden { + display: none !important; + visibility: hidden !important; +} diff --git a/v1/src/styles/css/restrictedElements.css b/v1/src/styles/css/restrictedElements.css new file mode 100644 index 00000000..68c21493 --- /dev/null +++ b/v1/src/styles/css/restrictedElements.css @@ -0,0 +1,20 @@ +.display--none { + display: none; +} + +.circuit-element-category { + border-bottom: 1px solid #026e57; + font-weight: 500; + margin: 20px 0 5px; + padding-bottom: 5px; +} + +.restricted-elements-list { + margin: 10px 0 25px; +} + +.form-check-label { + font-size: 16px; + margin-bottom: 6px; + margin-top: 6px; +} diff --git a/v1/src/styles/css/shortcut.panel.css b/v1/src/styles/css/shortcut.panel.css new file mode 100644 index 00000000..a2be4f72 --- /dev/null +++ b/v1/src/styles/css/shortcut.panel.css @@ -0,0 +1,146 @@ +.ui-dialog[aria-describedby='customShortcutDialog'] { + min-width: 680px; +} + +#edit { + display: none; + position: absolute; + width: 490px; + height: 150px; + background: #2d302e; + border-radius: 5px; + z-index: 10000; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + animation: none; +} + +#edit > span { + margin-top: 10px; + position: absolute; + text-align: center; + width: 100%; +} + +#pressedKeys { + text-align: center; + position: absolute; + top: 50%; + width: 100%; +} + +#warning { + position: absolute; + bottom: 5px; + width: 100%; + text-align: center; + font-size: 14px; + color: #dc5656; +} + +#customShortcutDialog { + align-items: center; + color: white; + flex-direction: column; + max-height: 430px !important; + overflow: hidden !important; +} + +#closeDialog { + font-size: 25px; + color: white; + transform: rotate(45deg); + position: absolute; + top: 0%; + right: 2%; + cursor: pointer; + user-select: none; + -moz-user-select: none; +} + +#dialogTitle { + position: absolute; + top: 2%; + left: 3%; + user-select: none; + -moz-user-select: none; +} + +#heading { + display: flex; + justify-content: space-between; + align-items: center; + font-weight: bold; + width: 100%; + height: 35px; + user-select: none; + padding-bottom: 10px; +} + +#heading > span { + padding: 0 20px; +} + +#preference { + max-width: 600px; + height: 350px; + overflow-y: auto; +} + +#preference div { + display: inline-flex; + justify-content: space-between; + align-items: center; + height: 35px; + cursor: pointer; + user-select: none; + -moz-user-select: none; + width: 100%; + padding-left: 5px; + padding-right: 7px; +} + +/* #preference div:not('#edit-icon') > span { + padding: 0 30px; +} */ + +#preference > div:hover { + background-color: #7474743f; +} + +#preference > div:hover span { + visibility: visible; +} + +#edit-icon { + background: url(../../assets/img/edit_icon.png) no-repeat; + background-size: 15px 15px; + display: inline-block; + visibility: hidden; + width: 15px; + height: 15px; +} + +@keyframes shake { + 10%, + 90% { + transform: translate(-50.5%, -50%); + } + + 20%, + 80% { + transform: translate(-49.5%, -50%); + } + + 30%, + 50%, + 70% { + transform: translate(-50.5%, -50%); + } + + 40%, + 60% { + transform: translate(-49.5%, -50%); + } +} diff --git a/v1/src/styles/css/testCreator.css b/v1/src/styles/css/testCreator.css new file mode 100644 index 00000000..47877b3c --- /dev/null +++ b/v1/src/styles/css/testCreator.css @@ -0,0 +1,141 @@ +.tb-test-title { + text-align: center; + margin-top: 50px; +} + +.lower-button { + height: 40px; + width: auto; + min-width: 40px; + background-color: #ffffff; + border: 2px solid black; + color: black; + /*padding: 20px;*/ + text-align: center; + text-decoration: none; + display: inline-block; + /*font-size: 16px;*/ + margin: 4px 2px; + border-radius: 4px; +} + +.table-button { + height: 20px; + width: 20px; + background-color: #ffffff; + border: 2px solid black; + color: black; + text-decoration: none; + display: inline-block; + margin: 4px 4px; + padding: 0px; + border-radius: 5px; +} + +.plus-button { + font-size: 25px; +} + +.tb-minus { + color: red; +} + +.save-buton { + background-color: #42b983; + color: white; + border: 1px solid gray; + min-width: 70px; +} + +.latest-button { + float: left; +} + +.buttons-alignment { + display: flex; + flex-direction: row; + align-items: flex-start; +} + +.tablink { + background-color: #555; + color: white; + float: left; + border: 1px solid white; + border-radius: 5px; + outline: none; + cursor: pointer; + padding: 14px 16px; + font-size: 17px; + width: 50%; +} + +/* Change background color of buttons on hover */ +.tablink:hover.tablink-no-override { + background-color: #a5dfc5; +} + +.tablink-hover-override { +} + +.tablink.tab-selected { + background-color: #42b983; + color: #fff; + outline: none; +} + +.data-group { + margin-top: 2%; +} + +.tb-table { + table-layout: fixed; + width: 100%; + height: 20px; + border-spacing: 5px; +} + +.tb-table th, +td { + border: 2px solid black; + border-collapse: collapse; + padding: 15px; + text-align: center; + transition: transform 0.2s; +} + +.tb-table th { + text-align: center; +} + +.tb-table tr th:first-child, +tr td:first-child { + width: 250px; +} + +.label-table { + margin-top: 100px; +} + +.test-title { + display: flex; + width: 100%; + font-size: 25px; + margin-top: 20px; + margin-bottom: 10px; +} + +.test-title #test-title-label { + width: 80%; + border: 1px solid; + border-radius: 5px; +} + +.tb-handle { + padding: 0px !important; + border: 0px !important; +} + +body { + overflow: scroll; +} diff --git a/v1/src/styles/css/typeahead.min.css b/v1/src/styles/css/typeahead.min.css new file mode 100644 index 00000000..01891f6d --- /dev/null +++ b/v1/src/styles/css/typeahead.min.css @@ -0,0 +1,186 @@ +.has-warning .twitter-typeahead .tt-hint, +.has-warning .twitter-typeahead .tt-input { + border-color: #8a6d3b; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.has-warning .twitter-typeahead .tt-hint:focus, +.has-warning .twitter-typeahead .tt-input:focus { + border-color: #66512c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b; +} +.has-error .twitter-typeahead .tt-hint, +.has-error .twitter-typeahead .tt-input { + border-color: #a94442; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.has-error .twitter-typeahead .tt-hint:focus, +.has-error .twitter-typeahead .tt-input:focus { + border-color: #843534; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; +} +.has-success .twitter-typeahead .tt-hint, +.has-success .twitter-typeahead .tt-input { + border-color: #3c763d; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.has-success .twitter-typeahead .tt-hint:focus, +.has-success .twitter-typeahead .tt-input:focus { + border-color: #2b542c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168; +} +.input-group .twitter-typeahead:first-child .tt-hint, +.input-group .twitter-typeahead:first-child .tt-input { + border-bottom-left-radius: 4px; + border-top-left-radius: 4px; + width: 100%; +} +.input-group .twitter-typeahead:last-child .tt-hint, +.input-group .twitter-typeahead:last-child .tt-input { + border-bottom-right-radius: 4px; + border-top-right-radius: 4px; + width: 100%; +} +.input-group.input-group-sm .twitter-typeahead .tt-hint, +.input-group.input-group-sm .twitter-typeahead .tt-input { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-group.input-group-sm .twitter-typeahead .tt-hint, +select.input-group.input-group-sm .twitter-typeahead .tt-input { + height: 30px; + line-height: 30px; +} +select[multiple].input-group.input-group-sm .twitter-typeahead .tt-hint, +select[multiple].input-group.input-group-sm .twitter-typeahead .tt-input, +textarea.input-group.input-group-sm .twitter-typeahead .tt-hint, +textarea.input-group.input-group-sm .twitter-typeahead .tt-input { + height: auto; +} +.input-group.input-group-sm + .twitter-typeahead:not(:first-child):not(:last-child) + .tt-hint, +.input-group.input-group-sm + .twitter-typeahead:not(:first-child):not(:last-child) + .tt-input { + border-radius: 0; +} +.input-group.input-group-sm .twitter-typeahead:first-child .tt-hint, +.input-group.input-group-sm .twitter-typeahead:first-child .tt-input { + border-radius: 3px 0 0 3px; +} +.input-group.input-group-sm .twitter-typeahead:last-child .tt-hint, +.input-group.input-group-sm .twitter-typeahead:last-child .tt-input { + border-radius: 0 3px 3px 0; +} +.input-group.input-group-lg .twitter-typeahead .tt-hint, +.input-group.input-group-lg .twitter-typeahead .tt-input { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} +select.input-group.input-group-lg .twitter-typeahead .tt-hint, +select.input-group.input-group-lg .twitter-typeahead .tt-input { + height: 46px; + line-height: 46px; +} +select[multiple].input-group.input-group-lg .twitter-typeahead .tt-hint, +select[multiple].input-group.input-group-lg .twitter-typeahead .tt-input, +textarea.input-group.input-group-lg .twitter-typeahead .tt-hint, +textarea.input-group.input-group-lg .twitter-typeahead .tt-input { + height: auto; +} +.input-group.input-group-lg + .twitter-typeahead:not(:first-child):not(:last-child) + .tt-hint, +.input-group.input-group-lg + .twitter-typeahead:not(:first-child):not(:last-child) + .tt-input { + border-radius: 0; +} +.input-group.input-group-lg .twitter-typeahead:first-child .tt-hint, +.input-group.input-group-lg .twitter-typeahead:first-child .tt-input { + border-radius: 6px 0 0 6px; +} +.input-group.input-group-lg .twitter-typeahead:last-child .tt-hint, +.input-group.input-group-lg .twitter-typeahead:last-child .tt-input { + border-radius: 0 6px 6px 0; +} +.twitter-typeahead { + width: 100%; + float: left; +} +.input-group .twitter-typeahead { + display: table-cell !important; +} +.twitter-typeahead .tt-hint { + color: #999; +} +.twitter-typeahead .tt-input { + z-index: 2; +} +.twitter-typeahead .tt-input[disabled], +.twitter-typeahead .tt-input[readonly], +fieldset[disabled] .twitter-typeahead .tt-input { + cursor: not-allowed; + background-color: #eee !important; +} +.tt-dropdown-menu, +.tt-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + min-width: 160px; + width: 100%; + padding: 5px 0; + margin: 2px 0 0; + list-style: none; + font-size: 14px; + background-color: #fff; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 4px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + background-clip: padding-box; +} +.tt-dropdown-menu .tt-suggestion, +.tt-menu .tt-suggestion { + display: block; + padding: 3px 20px; + clear: both; + font-weight: 400; + line-height: 1.42857143; + color: #333; +} +.tt-dropdown-menu .tt-suggestion.tt-cursor, +.tt-dropdown-menu .tt-suggestion:hover, +.tt-menu .tt-suggestion.tt-cursor, +.tt-menu .tt-suggestion:hover { + cursor: pointer; + text-decoration: none; + outline: 0; + background-color: #f5f5f5; + color: #262626; +} +.tt-dropdown-menu .tt-suggestion.tt-cursor a, +.tt-dropdown-menu .tt-suggestion:hover a, +.tt-menu .tt-suggestion.tt-cursor a, +.tt-menu .tt-suggestion:hover a { + color: #262626; +} +.tt-dropdown-menu .tt-suggestion p, +.tt-menu .tt-suggestion p { + margin: 0; +} diff --git a/v1/src/styles/simulator.scss b/v1/src/styles/simulator.scss new file mode 100644 index 00000000..f6460422 --- /dev/null +++ b/v1/src/styles/simulator.scss @@ -0,0 +1,39 @@ +$fa-font-path: '../../../node_modules/@fortawesome/fontawesome-free/webfonts'; +@import '../../../node_modules/@fortawesome/fontawesome-free/scss/fontawesome.scss'; +@import '../../../node_modules/@fortawesome/fontawesome-free/scss/solid.scss'; + +.subcircuitdialog { + display: none; + overflow-x: hidden; + overflow-y: auto; +} + +.side { + width: 330px; + max-width: 500px; + min-width: 200px; + overflow-x: hidden; + overflow-y: scroll; +} + +.report-sidebar a { + color: #fff; + font-size: 13px; + padding: 10px; + position: fixed; + right: -119px; + text-decoration: none; + bottom: 30px; + transition: 0.3s; + width: 160px; + z-index: 999; +} + +.report-sidebar span { + font-size: 1.2em; + padding-right: 20px; +} + +.report-sidebar a:hover { + right: 0; +} diff --git a/v1/src/styles/tutorials.scss b/v1/src/styles/tutorials.scss new file mode 100644 index 00000000..59c4db7b --- /dev/null +++ b/v1/src/styles/tutorials.scss @@ -0,0 +1,52 @@ +@import '/node_modules/driver.js/dist/driver.min.css'; + +#driver-highlighted-element-stage { + background-color: transparent !important; +} + +.driver-disabled { + border: 1.5px solid #ddd !important; +} + +.driver-btn-group button { + font-family: 'Nunito', sans-serif !important; + font-size: 14px !important; + border: 1.5px solid #42b983 !important; + text-shadow: none !important; + border-radius: 0px !important; +} + +.driver-btn-group button:hover { + font-family: 'Nunito', sans-serif !important; + background: #42b983 !important; + color: white !important; +} + +.driver-close-btn { + font-family: 'Nunito', sans-serif !important; + font-size: 14px !important; + border: none !important; + border-radius: 0px !important; + text-shadow: none !important; + background: #dc5656 !important; + color: white !important; + margin: auto 3px !important; +} + +.driver-popover-description, +.driver-popover-title { + font-family: 'Nunito', sans-serif !important; +} + +.bug-guide .right { + top: 114px !important; +} + +.tourHelpStep.driver-popover-title { + display: none !important; +} + +.driver-next-btn { + color: white !important; + background-color: #42b983 !important; +}