From e721cad66251c84dd60c03969535a227ca6ff735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Fri, 20 Jan 2017 17:49:05 +0100 Subject: [PATCH 001/124] Apply hierarchy to the Verilog compiler --- app/scripts/services/compiler.service.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/scripts/services/compiler.service.js b/app/scripts/services/compiler.service.js index a6872a477..de706c7f1 100644 --- a/app/scripts/services/compiler.service.js +++ b/app/scripts/services/compiler.service.js @@ -240,12 +240,12 @@ angular.module('icestudio') // Header - var instance = ''; + var instance = name + '_'; if (block.type === 'basic.code') { - instance = digestId(block.id); + instance += digestId(block.id); } else { - instance = digestId(block.type); + instance += digestId(block.type); } //-- Parameters @@ -347,7 +347,7 @@ angular.module('icestudio') // Dependencies modules for (var d in dependencies) { - code += verilogCompiler(digestId(d), dependencies[d]); + code += verilogCompiler(name + '_' + digestId(d), dependencies[d]); } // Code modules @@ -357,7 +357,7 @@ angular.module('icestudio') if (block) { if (block.type === 'basic.code') { data = { - name: digestId(block.id), + name: name + '_' + digestId(block.id), params: block.data.params, ports: block.data.ports, content: block.data.code From 0939b358d8f6e460ee3465dececa7db50443939f Mon Sep 17 00:00:00 2001 From: Lorea-Aldabaldetreku Date: Sat, 21 Jan 2017 18:41:32 +0100 Subject: [PATCH 002/124] =?UTF-8?q?actualizar=20traducci=C3=B3n=20del=20eu?= =?UTF-8?q?skara?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/resources/locale/eu_ES/eu_ES.po | 50 ++++++++++++++--------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/app/resources/locale/eu_ES/eu_ES.po b/app/resources/locale/eu_ES/eu_ES.po index 03b6e220d..530cea3f7 100644 --- a/app/resources/locale/eu_ES/eu_ES.po +++ b/app/resources/locale/eu_ES/eu_ES.po @@ -9,7 +9,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 1.8.7.1\n" +"X-Generator: Poedit 1.8.11\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #. 1. Basic @@ -63,7 +63,7 @@ msgstr "Icestudio-ri buruz..." #: app/views/menu.html:275 msgid "Add" -msgstr "" +msgstr "Gehitu" #: app/scripts/controllers/menu.js:431 msgid "Add a block to start" @@ -75,11 +75,11 @@ msgstr "Blokea sortu" #: app/scripts/services/tools.service.js:590 msgid "All collections removed" -msgstr "" +msgstr "Bildumak ezabatu dira" #: app/scripts/controllers/menu.js:455 msgid "All stored collections will be lost. Do you want to continue?" -msgstr "" +msgstr "Gordetako bildumak galduko dira. Jarraitu nahi al duzu?" #: app/scripts/services/utils.service.js:681 msgid "Author" @@ -138,19 +138,19 @@ msgstr "Kodea" #: app/scripts/services/tools.service.js:577 msgid "Collection file {{name}} added" -msgstr "" +msgstr "{{name}} bildumen fitxategia gehitu da" #: app/scripts/services/tools.service.js:584 msgid "Collection {{name}} removed" -msgstr "" +msgstr "{{name}} bilduma ezabatu da" #: app/scripts/controllers/menu.js:374 msgid "Collection {{name}} selected" -msgstr "" +msgstr "{{name}} bilduma hautatu da" #: app/views/menu.html:185 app/views/menu.html:271 msgid "Collections" -msgstr "" +msgstr "Bildumak" #: app/views/menu.html:317 msgid "Community forum" @@ -162,11 +162,11 @@ msgstr "Konstantea" #: app/views/menu.html:101 msgid "Copy" -msgstr "" +msgstr "Kopiatu" #: app/views/menu.html:98 msgid "Cut" -msgstr "" +msgstr "Ebaki" #: app/views/menu.html:181 msgid "Datasheet" @@ -174,7 +174,7 @@ msgstr "Datu-fitxa" #: app/views/menu.html:189 msgid "Default" -msgstr "" +msgstr "Lehenetsia" #: app/scripts/services/tools.service.js:315 msgid "" @@ -194,11 +194,11 @@ msgstr "Desgaitu" #: app/scripts/controllers/menu.js:218 msgid "Do you want to close the application?" -msgstr "" +msgstr "Aplikazioa itxi nahi al duzu?" #: app/scripts/controllers/menu.js:445 msgid "Do you want to remove the {{name}} collection?" -msgstr "" +msgstr "{{name}} bilduma ezabatu nahi al duzu?" #: app/views/menu.html:310 msgid "Documentation" @@ -303,7 +303,7 @@ msgstr "FPGAren E/S portuak definitu gabe daude" #: app/scripts/services/blocks.service.js:58 #: app/scripts/services/blocks.service.js:640 msgid "FPGA pin" -msgstr "" +msgstr "FPGA pina" #: app/views/menu.html:26 msgid "File" @@ -326,7 +326,7 @@ msgstr "{{file}} fitxategia inportatu da" #: app/views/menu.html:114 msgid "Fit content" -msgstr "" +msgstr "Edukia doitu" #: app/views/menu.html:160 msgid "French" @@ -358,7 +358,7 @@ msgstr "{{name}} izeneko irudia gorde da" #: app/views/menu.html:338 msgid "Information" -msgstr "" +msgstr "Informazioa" #: app/views/menu.html:334 msgid "Input" @@ -442,7 +442,7 @@ msgstr "{{name}} proiektua sortu da" #: app/scripts/controllers/menu.js:463 msgid "No collections stored" -msgstr "" +msgstr "Ez dago gordeta bildumarik" #: app/scripts/controllers/main.js:15 msgid "OK" @@ -451,7 +451,7 @@ msgstr "Ok" #: app/scripts/services/project.service.js:101 #: app/scripts/services/project.service.js:308 msgid "Old project format {{version}}" -msgstr "" +msgstr "{{version}} formatuko proiektu zaharra" #: app/views/menu.html:32 msgid "Open" @@ -475,7 +475,7 @@ msgstr "PCF fitxategia esportatu da" #: app/views/menu.html:104 msgid "Paste" -msgstr "" +msgstr "Itsatsi" #: app/views/menu.html:118 msgid "Preferences" @@ -507,7 +507,7 @@ msgstr "Irakurtzeko soilik" #: app/views/menu.html:94 msgid "Redo" -msgstr "" +msgstr "Berregin" #: app/scripts/services/tools.service.js:264 msgid "Remote host {{name}} not connected" @@ -523,7 +523,7 @@ msgstr "Ezabatu" #: app/views/menu.html:291 msgid "Remove all" -msgstr "" +msgstr "Dena ezabatu" #: app/scripts/services/utils.service.js:703 msgid "Reset SVG" @@ -551,7 +551,7 @@ msgstr "Gorde honela" #: app/views/menu.html:107 msgid "Select all" -msgstr "" +msgstr "Dena hautatu" #: app/views/menu.html:313 msgid "Source code" @@ -633,7 +633,7 @@ msgstr "Tresnak" #: app/views/menu.html:91 msgid "Undo" -msgstr "" +msgstr "Desegin" #: app/scripts/services/tools.service.js:230 msgid "Unknown board" @@ -641,7 +641,7 @@ msgstr "Plaka ezezaguna" #: app/scripts/app.js:42 msgid "Untitled" -msgstr "" +msgstr "Izenik gabe" #: app/views/menu.html:235 msgid "Update" @@ -705,7 +705,7 @@ msgstr "Urrutiko ostalariaren {{name}} izena okerra da" #: app/scripts/controllers/menu.js:219 msgid "Your changes will be lost if you don’t save them" -msgstr "" +msgstr "Aldaketak galduko dituzu gordetzen ez badituzu" #. And #: app/resources/blocks/labels.js:34 From 154bc04cf31e5957fd16e1d2a3f4339bc65409b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Mon, 23 Jan 2017 12:27:36 +0100 Subject: [PATCH 003/124] Do not store "board" events if zeroProject --- app/scripts/plugins/dia/joint.dia.command.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/scripts/plugins/dia/joint.dia.command.js b/app/scripts/plugins/dia/joint.dia.command.js index 224d1e2e8..bd9d7dd92 100755 --- a/app/scripts/plugins/dia/joint.dia.command.js +++ b/app/scripts/plugins/dia/joint.dia.command.js @@ -21,6 +21,7 @@ joint.dia.CommandManager = Backbone.Model.extend({ this.paper = options.paper; this.graph = options.graph; + this.zeroProject = true; this.reset(); this.listen(); @@ -47,6 +48,10 @@ joint.dia.CommandManager = Backbone.Model.extend({ addCommand: function(cmdName, cell, graph, options) { + if (this.zeroProject && cmdName === 'board') { + return; + } + if (cmdName === 'change:labels' || cmdName === 'change:z') { return; @@ -60,6 +65,10 @@ joint.dia.CommandManager = Backbone.Model.extend({ return; } + if (this.zeroProject) { + this.zeroProject = false; + } + var push = _.bind(function(cmd) { this.redoStack = []; From efae538a038b4b6a5cfa525d228c882e0cb1e9ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Mon, 23 Jan 2017 12:55:30 +0100 Subject: [PATCH 004/124] Store current board. Closes #130 --- app/scripts/controllers/menu.js | 9 ++++++-- app/scripts/services/boards.service.js | 27 +++++++++++++++++------ app/scripts/services/graph.service.js | 3 ++- app/scripts/services/profile.service.js | 3 ++- app/scripts/services/project.service.js | 3 ++- app/scripts/services/resources.service.js | 2 +- 6 files changed, 34 insertions(+), 13 deletions(-) diff --git a/app/scripts/controllers/menu.js b/app/scripts/controllers/menu.js index 6aaba2cea..fe58aec43 100644 --- a/app/scripts/controllers/menu.js +++ b/app/scripts/controllers/menu.js @@ -33,7 +33,8 @@ angular.module('icestudio') var zeroProject = true; // New project without changes - // Initialize selected collection + // Initialize + updateSelectedBoard(); updateSelectedCollection(); // Window events @@ -402,11 +403,15 @@ angular.module('icestudio') } } function _boardSelected() { - graph.selectBoard(board.name); + profile.data.board = graph.selectBoard(board.name); alertify.success(gettextCatalog.getString('Board {{name}} selected', { name: utils.bold(board.info.label) })); } }; + function updateSelectedBoard() { + profile.data.board = boards.selectBoard(profile.data.board); + } + //-- Tools diff --git a/app/scripts/services/boards.service.js b/app/scripts/services/boards.service.js index 778e419d1..4fb0c7e2d 100644 --- a/app/scripts/services/boards.service.js +++ b/app/scripts/services/boards.service.js @@ -4,6 +4,8 @@ angular.module('icestudio') .service('boards', function(utils, nodeFs, nodePath) { + const DEFAULT = 'icezum'; + this.pinoutHTML = ''; this.selectedBoard = null; this.currentBoards = loadBoards(nodePath.join('resources', 'boards')); @@ -45,19 +47,30 @@ angular.module('icestudio') }); this.selectBoard = function(name) { - for (var i in this.currentBoards) { - if (name === this.currentBoards[i].name) { - this.selectedBoard = this.currentBoards[i]; - this.pinoutHTML = generateHTMLOptions(this.selectedBoard.pinout); + name = name || DEFAULT; + var i; + var selectedBoard = null; + for (i in this.currentBoards) { + if (this.currentBoards[i].name === name) { + selectedBoard = this.currentBoards[i]; break; } } + if (selectedBoard === null) { + // Board not found: select default board + for (i in this.currentBoards) { + if (this.currentBoards[i].name === DEFAULT) { + selectedBoard = this.currentBoards[i]; + break; + } + } + } + this.selectedBoard = selectedBoard; + this.pinoutHTML = generateHTMLOptions(this.selectedBoard.pinout); utils.rootScopeSafeApply(); + return this.selectedBoard.name; }; - // Set default board - this.selectBoard('icezum'); - this.getPinoutHTML = function() { return this.pinoutHTML; }; diff --git a/app/scripts/services/graph.service.js b/app/scripts/services/graph.service.js index 415747842..490128edf 100644 --- a/app/scripts/services/graph.service.js +++ b/app/scripts/services/graph.service.js @@ -528,7 +528,7 @@ angular.module('icestudio') next: boardName }; graph.trigger('board', { data: data }); - boards.selectBoard(boardName); + boardName = boards.selectBoard(boardName); var cells = graph.getCells(); // Reset choices in all I/O blocks for (var i in cells) { @@ -543,6 +543,7 @@ angular.module('icestudio') } } graph.stopBatch('change'); + return boardName; }; this.resetCommandStack = function() { diff --git a/app/scripts/services/profile.service.js b/app/scripts/services/profile.service.js index bd04bf74a..fb3b6156f 100644 --- a/app/scripts/services/profile.service.js +++ b/app/scripts/services/profile.service.js @@ -7,7 +7,8 @@ angular.module('icestudio') this.data = { 'language': '', 'remoterHostname': '', - 'collection': '' + 'collection': '', + 'board': '' }; this.load = function(callback) { diff --git a/app/scripts/services/project.service.js b/app/scripts/services/project.service.js index fe234a8dc..2fad3f39b 100644 --- a/app/scripts/services/project.service.js +++ b/app/scripts/services/project.service.js @@ -5,6 +5,7 @@ angular.module('icestudio') graph, boards, compiler, + profile, utils, gettextCatalog, nodeFs, @@ -109,7 +110,7 @@ angular.module('icestudio') }); if (ret) { - boards.selectBoard(project.design.board); + profile.data.board = boards.selectBoard(project.design.board); this.updateTitle(name); } else { diff --git a/app/scripts/services/resources.service.js b/app/scripts/services/resources.service.js index 0b5d98ec1..d58e1750a 100644 --- a/app/scripts/services/resources.service.js +++ b/app/scripts/services/resources.service.js @@ -31,7 +31,7 @@ angular.module('icestudio') name = name || DEFAULT; var selectedCollection = null; for (var i in this.currentCollections) { - if (name === this.currentCollections[i].name) { + if (this.currentCollections[i].name === name) { selectedCollection = this.currentCollections[i]; break; } From 2339f6b6520091c9e4a5f07580823545cf1b745e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Mon, 23 Jan 2017 13:18:29 +0100 Subject: [PATCH 005/124] Adjust SVG block image --- app/scripts/plugins/shapes/joint.shapes.js | 23 +++++++++++++++------- app/styles/design.css | 1 - 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/app/scripts/plugins/shapes/joint.shapes.js b/app/scripts/plugins/shapes/joint.shapes.js index 2fb7a5683..4aeb1ee36 100644 --- a/app/scripts/plugins/shapes/joint.shapes.js +++ b/app/scripts/plugins/shapes/joint.shapes.js @@ -349,19 +349,28 @@ joint.shapes.ice.GenericView = joint.shapes.ice.ModelView.extend({ joint.shapes.ice.ModelView.prototype.initialize.apply(this, arguments); var image = this.model.get('image'); - var name = this.model.get('label'); + var label = this.model.get('label'); this.tooltip = this.model.get('tooltip'); this.tooltiptext = this.$box.find('.tooltiptext'); + var imageSelector = this.$box.find('img'); + var labelSelector = this.$box.find('label'); + if (image) { - this.$box.find('img').attr('src', 'data:image/svg+xml,' + image); - this.$box.find('img').removeClass('hidden'); - this.$box.find('label').addClass('hidden'); + imageSelector.attr('src', 'data:image/svg+xml,' + image); + if (imageSelector[0].width > imageSelector[0].height) { + imageSelector.css('width', '80%'); + } + else { + imageSelector.css('height', '80%'); + } + imageSelector.removeClass('hidden'); + labelSelector.addClass('hidden'); } else { - this.$box.find('label').html(name); - this.$box.find('img').addClass('hidden'); - this.$box.find('label').removeClass('hidden'); + labelSelector.html(label); + labelSelector.removeClass('hidden'); + imageSelector.addClass('hidden'); } if (this.model.get('config')) { this.$box.addClass('config-block'); diff --git a/app/styles/design.css b/app/styles/design.css index 0751fe757..30534479d 100644 --- a/app/styles/design.css +++ b/app/styles/design.css @@ -125,7 +125,6 @@ left: 0; right: 0; bottom: 0; - width: 80%; } .generic-block label { From c0c2b181ee183cde3d58103e5469af9e1ff0dfc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Mon, 23 Jan 2017 13:26:18 +0100 Subject: [PATCH 006/124] Use Node extract-zip instead of unzip --- app/package.json | 4 ++-- app/scripts/factories/node.factory.js | 4 ++-- app/scripts/services/tools.service.js | 9 +++++---- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/app/package.json b/app/package.json index db4d53c43..d8eaf2420 100644 --- a/app/package.json +++ b/app/package.json @@ -25,6 +25,7 @@ "node": ">= 0.10.0" }, "dependencies": { + "extract-zip": "^1.6.0", "fs-extra": "^1.0.0", "glob": "^7.1.1", "is-online": "^5.2.0", @@ -38,8 +39,7 @@ "ssh-exec": "^2.0.0", "sudo-prompt": "^6.2.0", "svgo": "^0.7.1", - "tarball-extract": "0.0.3", - "unzip": "^0.1.11" + "tarball-extract": "0.0.3" }, "readme": "../README.md", "keywords": [ diff --git a/app/scripts/factories/node.factory.js b/app/scripts/factories/node.factory.js index 7f0facf3d..77f98ea69 100644 --- a/app/scripts/factories/node.factory.js +++ b/app/scripts/factories/node.factory.js @@ -49,8 +49,8 @@ angular.module('icestudio') .factory('nodeLangInfo', function() { return require('node-lang-info'); }) - .factory('nodeUnzip', function() { - return require('unzip'); + .factory('nodeExtract', function() { + return require('extract-zip'); }) .factory('SVGO', function() { return require('svgo'); diff --git a/app/scripts/services/tools.service.js b/app/scripts/services/tools.service.js index ffece9ce5..8ef849af2 100644 --- a/app/scripts/services/tools.service.js +++ b/app/scripts/services/tools.service.js @@ -17,7 +17,7 @@ angular.module('icestudio') nodeChildProcess, nodeSSHexec, nodeRSync, - nodeUnzip) { + nodeExtract) { var currentAlert = null; var taskRunning = false; @@ -570,9 +570,10 @@ angular.module('icestudio') this.addCollection = function(filepath) { var name = utils.basename(filepath); - nodeFs.createReadStream(filepath) - .pipe(nodeUnzip.Extract({ path: utils.COLLECTIONS_DIR })) - .on('close', function() { + nodeExtract(filepath, { dir: utils.COLLECTIONS_DIR }, function (err) { + if (err) { + throw err; + } resources.loadCollections(); alertify.success(gettextCatalog.getString('Collection file {{name}} added', { name: utils.bold(name) })); }); From 804002fd59cf267b7b707e599d0668fc4b394564 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Mon, 23 Jan 2017 20:15:51 +0100 Subject: [PATCH 007/124] Copy hidden files in the packaging --- gruntfile.js | 1 + 1 file changed, 1 insertion(+) diff --git a/gruntfile.js b/gruntfile.js index 1b98a9099..d510a70ef 100644 --- a/gruntfile.js +++ b/gruntfile.js @@ -70,6 +70,7 @@ module.exports = function(grunt) { files: [ { expand: true, + dot: true, cwd: 'app', dest: 'dist/tmp', src: [ From f0fd9ab63d78798f7820262ae189aadc5d24f7b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Mon, 23 Jan 2017 20:40:19 +0100 Subject: [PATCH 008/124] Rename plugins->graphics. Improve SVG image adjustment --- README.md | 2 +- app/index.html | 10 +++++----- app/resources/images/icestudio-logo.ico | Bin 0 -> 370070 bytes .../connectors/joint.connectors.js | 0 .../dia/joint.dia.command.js | 0 .../routers/joint.routers.js | 0 .../shapes/joint.shapes.js | 2 +- .../{plugins => graphics}/ui/joint.ui.js | 0 8 files changed, 7 insertions(+), 7 deletions(-) create mode 100644 app/resources/images/icestudio-logo.ico rename app/scripts/{plugins => graphics}/connectors/joint.connectors.js (100%) rename app/scripts/{plugins => graphics}/dia/joint.dia.command.js (100%) rename app/scripts/{plugins => graphics}/routers/joint.routers.js (100%) rename app/scripts/{plugins => graphics}/shapes/joint.shapes.js (99%) rename app/scripts/{plugins => graphics}/ui/joint.ui.js (100%) diff --git a/README.md b/README.md index 38f932ff8..803309d8e 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ NOTE: in Mac OS X this command also generates a **dmg** package. |:----------:|:------------------------------------------:| | English | ![Progress](http://progressed.io/bar/100) | | Spanish | ![Progress](http://progressed.io/bar/100) | -| Basque | ![Progress](http://progressed.io/bar/88) | +| Basque | ![Progress](http://progressed.io/bar/100) | | French | ![Progress](http://progressed.io/bar/72) | | Galician | ![Progress](http://progressed.io/bar/47) | diff --git a/app/index.html b/app/index.html index 1587487d7..8c1f5cefa 100644 --- a/app/index.html +++ b/app/index.html @@ -55,11 +55,11 @@ - - - - - + + + + + diff --git a/app/resources/images/icestudio-logo.ico b/app/resources/images/icestudio-logo.ico new file mode 100644 index 0000000000000000000000000000000000000000..f8dbf862df8a51556c25cd1fce3f59e6983b5108 GIT binary patch literal 370070 zcmeF42b@&Z^~dR5DPjYGWr3xMy;tlVPzvhNFXye)!M$dw~YuZ==EQIS=RCLWA>jt>zkB7hb`Ac;1kT zFX+$lE1rRP2C|p|@SY0m;7k?YhCMI{9>FF!1;a%elz?~F*iK_djh!@h)!0L0PmR4a z_SV=Z9Q*3%hyRSf^F6-5o5n60J8P&Ky+#iW&d+sJm2P|l-qKoQD~*;Ko-e?c;8Exc zo+o%-rjO!RJOl9zXa;I(fOm9haDp5~o}!B*XOX$cUvLJ_J8OVJ@CY`+DH!glaj;Hy zSk0O>j?_3>qqjyMjlLTFH2P}{jEq5g?}z)mMvVbFPd|;m8pmlItI(;F`Oz%h3u2plCzB^ju z1dTBoV>QNUjMtbDj)^)>N--wu+>>>VNjlf0x^-$z)R<7GR?Ts>YSkR0zm3*7USqg^ zJ6Ho=0gr$`9H{|s=&1o;KzD#g!K>g21sd=%_yKwh`T;%#cmceRIf;3JCLw-pkTU>% zgZZe8$9@J6;G#eSyl<*nqqdF%s8aJ$cl0IM3K%;)$T8rz}skunM zo3Am4-)l_On5@D1ct1^p&!-Ed)5UwH)~QnyUNKSE8ms#~L7yM5zYo!XFQ7LZtpQI! zS3pnMQ3HO^Q3HJjn_)8zcpp3vUKsNNO+x(IAZ9?2Fu4Xzd%Ygn2j;zu@1`&9B>e0t znSOxe?GeKIvBFe;eFwZFYr#9%LIzJ2UgvDyto}*e+i!Zo-rKjl=7_zvy7SlrT0b)I zkT%baII`fC6MDCQdwkyxf1BLD<3FYh?DVgxgF5|t+Tc$An=z#5`3Wcl>JXv4tOxI;P#x1EahEy#Rh0>jj#G_*JcD0Gb8!(6HC-!90Gl4jSEsja`KIeT4TzG|->H$3P7) zjqu*9$v7u zMhgvm2G|JbOz;9tTl|V=ATMVC+Vr?b);H0BhM{F>x|=Ytvv9I+{d%? zJXC)hzeV%<3-{=;*=5J}Z2iCqz1qJubzrA&W(_O)WzO)inq1%W2##}xbzVDDya@ea*pcmCJAALL?%TFw(<@|a zEZ1{kPhd05(wL)Xo&!ISZi7vMPhyk?_6+lYfAJs4xJGj#v0|q&r5x>wjO&XG)yI zcuImtI3APGExg{qb*9VSKuFJdRjbD6&IOyCdeq)q-*WuX1uuaSu!4?lc=Pf+l)w5Lcnf7O-|^TEdBU)w zKs+OuHmER|I-q?prGG&%sc$&bAngzn-#ot&-CCg zcTNeOyLDpl`1L0Q_g*zDxb@=x!S(0#3a&imu;9Eodj}_t+g@1iW;|fpz(V5>@EiDx z=L_(!2oDQ+jI&9&*LmUx-hP=mwDV6Rk12Rd{)5|FHmiS@=4o!o3+Bm2m?!@sd||T2 zSPlG#*htt1JvDaK=%!Jiv4uucjVK?`sp3~W0~Im@$+*X7-9h^O4)V$GtM`XUhJpM3 zZMSSR=a_w4-6FmI{RHN*mxYB;PI?ltBWw>B zt|td?3CExRWqI(;U(XJ{ef#|2$M;qSKYw^hu;!CXgSCIVG+6WTs^FK8F0t|RhZpN_ zZv6DYMFGSA{}+AE_kPug{`Th&E)ITp@511J|94*Suh&)tpFFoLc>TfI!85mv5AM2b zu=vg4!HOAs1Pe!Zw|kx0P1;q;odb0WHdVi7+ae^k3S_T8c7%}pAa z&tSIZZ;p8BJn>QZ0y@D&4eSGSg5DbV3HH#~R--@zy({JenuPe3^BF*%pkI3X9z7r2 zca*H(QNDX{Z}MLH{;1u1v^Y<)?JxMm(0$F0_n439^In$+E9l;nrE`P(1*5tJX9-ux z-=}UI8@&Dag5c{{&NPgJ*|oy=FT(auA6{hm{QjK_f^XkBUpQr)7yS3Fb0dTQpJ$)* zw{(N=xZ!_W{N3+#?(g5d(D(~{<5yjK%_pmL?Ui<&FJ3&=_`*ZK8yQ@6>S4k1NjsSy z0Pli_VSj*ko)@0L^D;aSgg;oEL35>etN#C*?R}KT{0k8Ej|3R)u#mi5r&b!U=7TD z|E@4D8DEU=Vtn#>MUVf5*Dk*Pxw@D0gYU$9&=I&-&lCRr#yP85ewVZDRX zrtD(RJEea+(`Pg%&Q4gD7p$Foyz?0-rm$vgpTdu@50tlbvgUY}<{H}oK7f8e{9ug6 zFb({vo)2`_dtwIo2k;Zd@d8ak{3`Pdc-%uj_?foQ2koWfZ!cM-ygai*k5TMUrfO~Q5x@t~2hhWa8FbaaPe{CgxFNn8bU4;Ee#JA8 zX$GJZ=momjOcc{rxbH3-pSbk?8ohSkw#BKVdbM9YYgp%BOur9hy~jMVA6X9_gLlS~ zG1~>dJL8Dp>6^y~U%q&n$#v}QAK$+y#JkhIk*npwVLtuF>j{n@TwuNi_#1Y_hfgmJ z9{TO^!K%gL1)3vx;`BisERN~tjCl*~gSGQQonZRlqVJ?*J>6!@M(1k{h!vm@EKJe| zunX`H9H6nYMv(@#8a~>nk5K=OU-1l-m;o>Ip%b(5W#4y}eZQ+{ZEt;c#EzYtEgsXS z@YT7Yd;;&H(_f)ihL`pDz@{jc#%IVpd}e=t`EuJ$`MxGZ2;e&4u^Ys)y??IwHorPy1Gl$EhCX8Ssvt z&yhSl9nHT7FCbp$<6ajn+&@@-#}u=9vE}jILFf47v6IV-nGN-Ko)-{faPxJs*+vgv zJ1RJ1+HRUx_-SD{*N(^98puCrepdK^;)h4?yY(Ylw`E$h+0q5D3Fhhjbd3ob@>-$akSxorFn#Pjrix8|GF8_@rtZEixwL~vh1-h+E= zSNvI5pMFH}*N5hqpZ*uc;PHz?+l&nrySaWY4B6=LFCGae{K2_P3k@ zcr1K3;v-1t1LRZ5Pxy~Ldu(>2^Z??9bMX!6{an4rM@Xyyn}8f5Y&qft_y^Giu=j}Z z#JYecVIz5A{}Cf@Dt~?})xlGnvy=XRkZNf!R2=#%)AL6p1){Ajs zpY|ViYrpAbT1)8yH5Qsp;PQ+o>9dg<$7vj-fiBQN1HS-vpN|pfZT#9mXTa=V3Svk5Vj_K3>g|KGU>-h=XLu=q3+PnVjUh-WnGy24T@ATMM*5{f}d0t8! z5gz;M{j zswOO$4^~|1YU%plnLY39`_RUBH7?kp zbA}fg?%#iUNkGmlzALY@=9=!~^Of|Kpbx-f;RECyzH;x(;Nryxn129X;Nk_vd4=c% z_y{NU>-hIwx;DR7>pVxgfc%4I8_d-ICu$t8LC!!=jU6-!G&a*fHee6LJV2AM0lZj8 zuut5lsdD#Pqwnke_UfxKabo`tZ-p^@FY^<=JuhcgEj=)J>(P1gQGt6W?_=A(Bt|QP zXE}cBe1*ghpSfj1aK_YKgUQM>#vhaH8whQJwSx|8^Fs4Y>Yl1Krw4&_frMY+1bs$~ z;7|>GzMV9vCBP>X#|JbC)%*qa(dQcIO>^mk1zLafy*;HLUMiaTF(UIz==%7Z$X$iT zEXP&(_Fz87edXgmnYVbmfLw}yy}ly2>+-?DBGn;Hlb(h>5U~lYCkna%{=ps{n_i{0 zHy%LFfZos07_ULz5Wc~^G@uW1hKLWuJ^@Wab$yA?qsKK;u6|4Pqv@#kT?)2tbewu< z{TVx7bt!9#<9Yaxx|&^yJqb;H^Tyd0mxBJ#L6hk){(sf-0{DRQ6RrwAef|{l5l)qV zko>ENE`V=fP1px*%`yA6dR#Jq7{PpW0UfC)z&Ggaf&KKphX%O*?r4}>bdccb!EPb<5R&l641pKx)KJ;2@v2Wb!sF3`Xy;A4d7 zlkqFRXMi;_KVRd9by{nEk^4IjKD^B}${DeKnW6*p?;+>m1^AMwV}JOXktR>5XM?6T zgk1b|xY^J?_qG`}0reI1N+1`Lx~qsTU^xTu0QEoq->%(TTqoL?Eq^faf%$qrU4!_* zAPsy1yJ&RKpq|kC1@$(5<>L$p`1twi>A0zS>!H83Z`HEl!DEgsdeSBLyYFn>VHJt6?z>!AnF$g*ME)r6Fww5 znXlZ>1sd1|#0R`j;7GmSO#}UrJb}g<*p#szph?K5mvlJ8zHGXt>Qhf{SHa%fZ8lQ< z?LRhKKZ1Q|fxZ;jiS)}tb`W2t?minDi@%fK{K44-=UINnXX=r3#mR>l4>_HDS{C31K1?VqB)dDNVsFMj*B%h!!@o!_`F>-wO_dA|U*9em&C3t;=f z1Mm$JADGaudRL^I1+PxKG&n){ZZ+|aOp`0DQC## za#KerI_pMYk9%yp+4Z6&{DSZR@`k5sjM9Jy?5jckSj+=7303vdz7Zz(H)&M2 zxz?$*j@v1&|IoU&zI?sbSF^kI&85y1{393Em!{(1S1;ayt_R;Icks?j2U&a|>JwmJ z6Xk?huaU#{*y=qIo! zNe}Qm0G}XnfjBm(NvOz|_^@GLwH#o7^DgZ+*$c1mC$07IGKZDp0ww~AdHUyfDpCPwA z%=TJPuF&6KIz2dh=AI@u!X5(NFR1un*MQ!`qC3+Abo4x6q}~%7BtL*0p_Urt3dcM^ zlaS+A?Pizm75u;t-PMb z%FAf6N#nX5wU_MU$=GMD(D`q>cz|;GE;e01misaOv*lUF0~8xzzmkg<^)wwa$rdnw z0Q;GXr00aOLGlA8Ymg^+qz3f@1sd3=^qYuzfF@x*U$s=n)38Ck+Rba$s@ZNrzYh0@ z*iYE|==}Kk@40fQ^@w53Tc0A zeZ|NRAWv|j#!!vJG+YPOZVDT_C9j|cIELC z+XweC_G9dOOqcbD&Ik{{H+V66fM_d{D`+*u>{GbccANb{G&xH;DfI&IfC(DZ4IZpP z{{VW3V6VpYLYjoKzVP+S*IKvr77fv*nyL@~8Od^=b-~wn%Y}Wl2c_$^A7ekpepxY} z{C%$n{OhkPg7fC?ldK0=PAPeU-3l#N*!l^HMyVU5cDSF${u<;76BmqQ0-A(!ylS`J zydl2+#zPKkJ2mvhByxJu!{E*I;>FL;8pig1jQwORm-qk9C%7{B>XkEs721y-`_k(H z^cg14RDI(Atv({>ibicOLLFyloS@N1V=oOK6NvqRnv62MY8AF>jPBH^PtVo^G~Yjj z_&46`<^I*$LzjHcZ{vL4IJZIkzr0va_d9F=>cYQJtr&Z{%~1VFm|yOCib)1d?APH7 z#if>uR<#GA^$;O9z{dpWDO#X`4qH!y{FV5Xc?N2>+PncV)W-Yn*m6hp<^Ckp`>l=- z?9-2%I1TxHaeP1bbe|3n8{_A$Zs_9Ry{8rh3r2M-kt^(LhesS$@Ve-AfpSyv1rQgc zpCJCgLp9icw6zBHf^m+pCLz;Hy*uhQs$Zu`{RTB#O&QqnPf?p6KR-R4PMf@o)$gMB z$2xzE|BB{IK2PxF`({|ZK#7_m^$)Pzfc;uMBD(gu0r&&QYYfonsS*1EGzq1>wAXg+ zwp%qs?{BO)-<8SOrWBvjSKx@>GJ$d8U#Ga&~O-i1S z)eP>jZE-(A(J;P%6E&z4z!%U_gWRAvFF=!!;#IR_nyAFA{S5W$V_q%BAHKJK zT`%7|!|FC-?8msQY~GAdkh;O!R}HW|M-u+P5dSlV6n)pBb(0H4!}G%Z1-vhS+@PH_ z$P2)yO$89BeP<0T`cH`Y(BDT+fBWFxtA>Wyk861_0|sK=J;A)D5Ty#fQQ^Jp-s0#1}wr&|r;&G`efVF+oj2^ria!+D)6(X;`mL z?Uw4n|?9erNFaeBYEM$``2=;HzE(_OQ$brbykbrefEH|p#6{=PG(?H>H|wX;UqE57g+UfgOO{+h}Wq3R*R;t>>?|T~V{v@}0aD+Wd() zKlyzRYJTa{68rmNoj>3B$N!{{5re*bnXeT}_yVk__~66Zt`>c3?{Uiuzz!IrL2lq4 z8ig7gbzYG2r!6m1yz;_mtPh_GzLiUt9u)kb{vG+Y?(zM^ya*cbzJSlQ$H>yL+uNQp z-WT9wf;)F<{#)T3J79s1!~=$FP$x)UkgpL!4sKMh{dU~kVj#nhEI3qt6W_zb_UD_a zIR8@Z+w|G$<;Gv*9G@6(`O0(AOX(r@y!INShoHwly@Zit6Z?1kLiOt>3)~B32hcCT z=LPMn_eC0;X%PF`XncWP3O1##x9+A*>Nc0&zd9Q0XU?d#c;=P~vECp1`>KWR_g~cv z;}4|g9QCM)ynrw_u$oHt`{?~w zs&8kU+Z)ULe3kjh_hoi~>{NOPo;+bk%Tx1uK1&V^aDWE6 z!JBJr6g7f-^wgX<8XHAkpyHy$`)W)Z)bW}S`(d7s z^Z7k|?Z{Z~k8xjZa39qL&;!t0uRQtCV5;>CNbDzIy#x2&ezO}y`s^!)4lqT78o`4! zd|u!N9S=NupH`OdFs66mAtClHZvtHjo1dCqdiF-=G=3l30o8>D4FBpE@X50$S*@_u z3A#EK^98DJ?cWhM-P~#V>BLEp#S@xfqJ{)y|!pkAN)6%Ii%>ZsO%^9H*-kG z;I#*4g>`-Tm*>OUMn`p_$qn&7^SQ^H)d(pbaOY)%l6`?|sS`eC->n}K2G9YB2ciQE z(>O$9Ta67iCurcIZA|v7u6J07{o*_y)%IL-=23=yFqTg=i*NV4cg_!fl0V{S?Gs@` z{l(+HBbBR{c)DQT{Xka)Z~2rL3U8u5=wGj|(4IoOMC${rMwlAGty?x&A*|1n4nUuP zu^Q9}lM_^+v7zb!wd>Ta*;xI2p7;3oxzqUkK7MXlUi$o9-toEWIj}f*{ejsw-g;zy z@Q+u{wBAsq@hWn}@C{`ep0-OnS9Sb5&zAn#olaHSb8(KJ#dE)V`-0$W?a}d-_V8w} z4*EaAkLl5sPji^;3-qx7Umw)>z}8O*=hO$`3nV8fssn7H|8G!!fH8gAJK5j2!zhn^ zj=r|pks(tLy0lXhzcfBvrAQoVl(Q!j~)!edK zgH06Qd&%RU`ItGRQ?P9O4w9v3RERzeJ-~~T=>i?i%cNAa$M>P9d&HL}^=%i-kZ&Tq zZ|AQZ?)s3Sw|uFXFYCPSHC!Kg>K6Ze+oa&Z-yR>_tNlr$ zk@<`!_iJZz4i@SF!a zL!R(CavA#KyPgKz?^Y@YVVTht^yYg!l;8p4U-ToRA5pUGLFeZ>VE^1Xdt0A!^1>$d zZChgS*{CD72yQs9cd%vyt8ro;k^R3t{fJ;(@787q6XS^v_&;+<5BF5~_ZV7z$7K6G z`eIlol8774AKA^$dHU2{gMYtqPKony5AgYKWjCGm3rQ^9<7m6`DLtu1|FK->TqGH;oG>h4P8>B##B!cb;Cx zw_V&nQr4|GE{&SS)5_I!>5-9 zKkB=wc+mRH1-@f=&TG<5h;Mn`<^2Ht+@8Yd0MVSlp2B@+jcQ*Ppt?S*Yf>NIz9D`R z{k+KYrFJ^kvfuIC3xiLeKgIg9fIqG^O<0A_;KTeMy?(lA20QX9 zM(n~9S&v7q8)aBeo=0RgSY^G^@nSy5Hn{P;K0)xA^QS;ppRGP6xbHW^f|cU?i%#fn zc7v_G<{Vv&xrc|(9$q9rLhlNhf0ISxEthFOL1-`BKfuKT26y^)^JewW6xN9akP}E< zz>yj|YP8a5tWj-i19$D(jNS${rRzKx(*LjE30`=L@}$3hm7YEoBl|yA4u+RCVAa3p z$QnR1yhr{(6WC}MF4)gx!f#GHJmlA@v7f-d^!HSB%bLUYp{Zy+NHP!L9t{5`|2I(l zpC$ZjepA^{ZXUZDw(q)paIjXgJ{kYWe`4vG@XvXioz~U-wfG#^6Am9O^AGRm?{>X# z?U{QU`oH-T^!LwH$BWLu`mK-LkN!5H11Pt}=>U$Vybf^G-djH)jIfU&I=}>t{u=ve zbkv|fKwXV$_fnkG`kF`w*e^==CO5Svtaq+uzn^#358q32pVsJoMBhUP{2y6>T$nbn zeOkHie`na$bD*0-qfxpwo-dyRb_cvA6YZD8|Ay1goH@Y$zcvf}GY5Z6p#yn6>JIJg z>g^~$|EZ3%RX>6q0G`1;z6Fo*RNv<_>=M5F2gMO8GLDFCu~PCS;s>xj#DtH3Xm9J2 z1@QUO0jLcep>e224-N8xs0*s8QLSF8cQCz0@&4<>xwkxj_%J&6r_Z09b6y+8KlA;c zH_r*qnYEYGyQBl8qXGZ9=?0-*;6F=uH2n7~Fuy)JYBK(jBUek0$OiwnT|6Mv|2J0r z7X&v;Ucc@`3tj5DUZ)NNsSnvH=#4?rQw%_@ml&(3<== zTrbOkHeP#fZ>@F3_Ve@d=Oftxi^Xqd4(*s$4rijLbbo^u(MhpQ*Tw(&MoaW8&IHQdp+)^3yv+(Z$I6dl;v}8`(gXzd;CTAT9o&AS(2-x zmq%w!-`#kQyT0%*xiqFv;d{dP0)Yk_V6q0a!96v+ACQ{4YNG?3Jhr>>t7(Ia=7wux zvfsV$61@Aw!V1;3{F)&@kSWv^pm)q3)~QszK3f{{^8c2V>&h)+8N{2eT7Fp8_}?Jo zf7#++`u9y2IQ)Zozka+Yj#HW)mF=4O?|B_zn*6)Z{*gX2xsN~lXYfaO3%blB*N#d> z6KlH`Y`RJqr&nOq4@i%&tu?A!K48u5Rp%@H<}cx#uYuoT`>a}`o_;y@vG_Sob#(K7 zN${6Dru7)){NgEhbj~Mo-TPd?2O+lCxUn->#zH<-ZxVRb^NArqdLolb7Fzi z1dtDSphg#sYLg2f-QWEF^7~yKuFo$~tbA?zc;q(b=jS)m(fN`t30{yMkSkk&=OSkC z{OyxXxBHLm4Sx^tgH=lpEEWIwwQt{m<$pH#$0tOrG@3_A4iEY=`8g4vdAWIj!@kQk zdi1){miy^>jbF2hK5{*t@%0C1o9ytq2s{9P<$gQ0yjfVs4@gbGXpN&Z$OXU#Xry7- z(lIYzE2eB`K8gVcwP`tLSm!T7{D*$O_KNZJTgS&!(Q_U>zvlrj-963d2tMFtLU^xb zqAz|+yy;B&bg1{FMva(*t1mdu`k7?XTQ_|9U%KrM-D4kMCt&M?5wOquZ~l6=)%Y;S z>$^^@FTVaKREvNvSlQT*(jnI-FWl1xHUPZ=hH1daRt`iejLAF)z)dj1IYb7I9>B0c-dhiuChGah~eE(Vy;teQlf# z;A|3f0rkcCLHU4Z3HNi;*Z_GQ2Us|&o9QRYzwo{NO!sH*&lA7<_U+2E`BTlk|BN-j z?}I*)8~TC=bWmPH534Kq;)T43VB>$Pd|hgI zs25y5Y3H1+%~a)c-Jsmy%JTJjI!kp#_A@UZJ#&)f9w8TzJ$}D{19slxR^fl9@&RY* zI7)+j;2ku!(jX3ymvO*T#&2u)HTnGVIJ`hU%aTeH-NVRMjl=8hA@r~5F3Cv0KI{HE)X?= zc^L;#O|#vX{CT zrl{{Ln`g-P?|A%EYne&@M==cMy}gvqW{Q44oA;~(^GyGcpFg~)Qe)5Q)(9SyDi^?P zfT2Y{s88k!;e57u0Q~_6Y3!##JwS7fysQUUHf~$PzxeHy;kqPr|MTbVlNUS}nk4st zoPFpE-+eYTR8il7SCAWr?~DF|++RAHscxT_2mh=|u5ce+Gx#xlo*F#*%vEOoNvd`8 zbMuMrWr1|Xh@NP^^8I(({1)LH8-QFOUk^Z!aQcGeT|L0UQ7+$i$YE_8Y2M!p=YEap zw~FnMBU{z^Ek2%%Ux_)Vrw{iRrIX5j53S>WSS8=rcd9*3MJv_yGf(66|HxMUSLFON z*AtFyBc1yI>&cc6^NI~{$>IYNehzE^dIM?S;J*mx^aYrsbz2DiQPPfmMoZ>w2p);oJYB8_m9HM-n z(emDd$N$x*bJAy8T=O8g1DRO>0Gt@On4gr*+~9FD2uEnaLRy=|EyoH z)=BUH*#Oi869?QwqdrevLdWzNuaX5&b{$9Nh~yy-YZtB^`iVK_KT~=iUC58kKA$hzxj{9H)-X#J=21_uNZ9o_3@whSV3ui zTyXY(kIl~;dPF878+k6C0Y2t!0PPd=)zjhzvj_W|& z@0$?+meY)FOa5k6+y2S;vDiyeUuG{qpf%Qkd#9howPzk}{o#lm!^ik7zT1Djes*xN zY&UG%Y~+tWf2QMK?wV?t0XtQhqmn#d+5VsV_Hylq_b;@13v!@bjHPrLooa5FTjrP^ zUSDa?g|*7vf`_{`SYf_g_S_}z3cfwxj-JccYT^H*&mZ@g?K4&6_xxP_Uj0wK4FLYB z2_AJ!!CS&OaX|6_PS7|?gFJw|st45Ey13tvq7r-hu@=O^*~_=8{jJe81Xp1BMdbkF z|A$_nF^_w4mH3?8{kI;SXPBpsjo*2>Mt&->WO99pZTLLSD9x4SJ=X^#bJSOyeq!W@ z5TnFK%okdX&SCO^m^XheJpnme3Q@5cT-`IwF9FcKC{%;EtL)D={TFCTYGvD76z9sJIMS2 z_T~ea zDIdTj;k~~GeE|zKs0YpqAK+;dyP5tkz2%m0ZHsMx=`}w|9^})Uv%cI9G(-Q<>*NbO zd*+@2eX3uO{>|Uf4>+dN!TCLMh&qA~pI&0R!}{Xi)4bPJkzdF(>LrnJneKT`5V$jnRozv!(@p3Ebu_Cm5vXx9{deG4gKTo#}}Bth90wA zo4)UVxBg!1?$c7)^Ok%)o{OC4WFG+aK!@$Q)jh)bv^sTaPSbISMo*0*jl9SMQtu?g z{^(;1i2HvOuCwJZV6#1b{kpxelb^q;{_lCfkD;E8tVf1%Kk4Yq|IO3=yH6|%PFL*C z`)3}#82>{z^|ZfZ-Z3TyfHWIZ^B&tM|AthHKS|~C**B9`#aS$X7icn z0XM4u7jsVjo?TyfPxbt)DTD99!^!nPC*Xe3=lENy-&N&j+!K6@JZax2&HDg{9^UpP zVSToG0?p8oJfLJBpgyX)m)6j15cT@#q4&RpYijX-^tX4!hx1M6imtVz7q?dr@6*Y9 ze&cz9_dC(Qc;1MvCZo!$<7UEui*dVN>C~@Wq+D_Q5!eLyQ!?Fa^ci#Y-sVa^5c7cw zOMkv~Qa~>|^S_6^6-)EKXL{aDe{(*7ea&|duJPeHKA2kX;cpChCO!k^fcwwYT;%dR z&=vioANn18oRrK1nAor5mvw5_S|Wtcln-FEj?@Ct6U^5F8UFPDDtT$m%=agLH85O1 z>+^-(#lC*j#G||Aa=t42T#iRLpCQ*~PoFcU?q+MHnl10MDU)uSiPn?<25-TCMNe}6 zMxQ+PEOItMdYw2|^JIK6lu=;+uMf^iyQj=eN?)@ZF!<#~Y0RWu)j@V_*? z?=a`T(BxG`%vWWe6B*)s@os%CoWHNxw~p?K6Q<7}@aq^of)&k``K4UEA^YEZdzxOk zCym?QY%6GoSXf2Sx%aK1Yany!qfDIwyxn|z!em)+&vzNv2>yL^Zc2O4eDNJ*B=vmv z|7N(w&bc@4l{u(P+Q`+lxNmHe&tEvza?6taNIMiYyGmG}E+61T9gow-*xvI?b7`D`-oSJm{_=9=+#g>aH?-MKJT3KcM9`Ul?OyE-~f%iHQH+s z12Fwt$4Y+*^XB`THn4LYt=lW%+NtJ8HEmj(RQqD(l0GZ)T+k6Z8F^-lPu$jQTyKwO zL!>DcXTJ!(}x@>S_pCkGKkq3A} z@AmHs=kqnDN#G9C@gNO)0=LkpvN{0Gz4;zz4eh+M*2eGeu@*T2uSajrr@X&x*Ag0n zPVqBM?%&Sz^;EQ%?z5IBGjqWlLmSu^@Be9Wz&=UlS5V$1 z*DA~PxIWiJm-w^xbBDK7H$K3;;Lm(Uz46(rG<<%)Kj2sO>R1e5x_p4g3-gC+bkm>? z0Q{RCu4ARXeBSTuVO@HMG+;UZtRwl@)HPH`-#@eiUD}>=#qpR@=^q+o9-u?~Q+HoJ z#PW+W`RP3tzz4c3d(oUOn-=UO<0{qvp%Z)(k6(YH?VZc{vgLD`zQ=j!M}V$U*7|+o zfoILw-RyF75uS}XO};n(?DRe1I{3fYf0_J8=U>e}uHfh4sT;={j=)o<=gYUhF&E4e z^<@$NpXC9opVkjeo76p3;0FK10FThvPGbv=D(eLzdNkj^j*~;$w7tEtbQ}I9l8JNMB^2wi<-zNggr3;kWC9MRW%0RNye24qXVv0PGe=zWm#yrEZ;?MMHbz4*&zAX+a_c#xTHRqbz|Km<{q&IAE?HP zJ`BtOd=Jcn5BMOtCbpkfCGo+s&R?rvF!2 z9kAx#@ISt9huT_`mqOZ1?D6}jo5!t}3@FQ-RpfV(L!6(QQfO|86@#xuKkO10MP#>|5XS0vM@hG{y*^F zPh(Gw0u6eBR(cMgauZDcD?esat$(8D7qozUc;lftiQXCOJ1!`cv0JJ{$8G&TEMyJ^`m=O_MB?j-~sm=(*tD z@af$qf8*o~_vY`HoS(|@OH>B$tLVA)=jU1A=a2pVged>FUK(SME&NDWpQkZNJYb-X z`)CwuPy=9bI2|kPCHZeLM;*7*nkVZ1;r|Oqbq_xO%c%)j>7Oq?W{!n={73n}$5Zl= z_|U8ORbGCz-JATm>8r3h`gG8}}H}=UVJPV$%OmKSq2q*o>7me-(Khw0x- zEf)UsL;eqQ_?P{^w_kG|h5t?#izSA&A^2ojE9eYcCXj7LEQ;syc4)e_EYIiQ-|K&w zY;5GhROKCz>sp!iKX|4`BJsC$c?r(`?-IPPxMW4xe-865KY;ro?pmI?&d1-t$MJ2x zbkB6-?;)-%2YBkhPTw@BSDQXS;D3;g`)YL1XsS{9`v0&*-=70K{$-aM{+Ev3euL=y z1C6jI(Aaz0yO?@i^7FrxZyUc8^vB#9w;x-u4zBzha|C0V{|WSO7B+RMSA6O-RX)&5-;BnbWNTW{Qp(4GxVr%UQPbb7*h0| z_5w(S|0?nS>E0~nsN>Pa_vw07o~pGWkG(Qzvm$Aq`$x{O&lh>V@BnBW8;9Bya)7Tt zr?>W#R=&Gr5*TrILHXnmaYpF)>(|ay&kdIYo-Y3n`rv-37lHpkI~6q-;1BGP)0L?P z0GojPKWceh?MM0VsW|`7#rgiG{{Ocqr+m=~J#6mbGteFDfj{DrYe(5Va<3IN4;6i0 z))4;x)+6&h_Br|*z#lhlT$lYoC**_wZ_e3 z%q4S*&(Zfua{h(lx?snTH;nv$kAL*d5K|$KlfQRp(T}PD_V}j<81eu18dcH*O!w#I z|KTO^-ziwG{oe9rzu)rkY)|jx`q5Jdc@LdM*Tlb1@B#4%WH$VQ8ZPMn+cNS2cn-85 ze0i0fhaA5s-|^Sv$o&ulpvUsxwf6@2misUzC*tj?zJN3A=4b?>TldPdr-Xba4P-3 z)Avmp*1`WbUh==n^MB?JcYFTnc%WZ<@Bce#T=nkz4IP4Cdfz3>e*dh|qtgdu7YL7h zAN_~iJk}F>gg(G_Iy&Kd{2kj59OM5xbJ}hh`3~2I?$NhzI{#R^PNr+5yLP6}xi91} zw)_h1kCZ9z*Utwy{e$`+kpICPW}^!k?lngqyUZPP$XvqXuq#sG-d~d=*Y&l~>uY;t znv;tBo8ezHkkkj<^ndyP{wM#R$3OZ1duX)NXrf`VSjS3xX`jka{&(3ovJU3|Uoxgg z@K5pY>gtnW9k9c$KJ5tM%k2*udB&7_0Dng&gGpky;PM>x{CnZfsfKB=&3PG~hIkL{ zW54rv_ROST7CN?*wd=~d7e3#x|*a2m&x%c;y>d5`!YiGZKOfkRYd*a!l0r)3& zAm_lhk<6cno%r|)eD8%jr`VkFd*+b2Os97xUz6_}ch`NcoByMpG2p)}`d_}R z33Q4qy7#J~0sBVMk0qJjGX3B2f#ROO*z(0a@LWzbxMzN#RrW_Fu0C69ZuYgwyVUkZ z@^!fu{&;*4#KRm9aQkzWcRry*2IqSCnvof8{^a}N|Hy0aBc-49^S9BLkiArkdn0gu z`hG$0=)24j_+>bGpWXBO>oX7226iy)cwSrH=dY^YdAfU1wWZ1VKT`&F{Ac}owH65L zV}$=>bz~plZ8WN+{$FuF<2gF+s5MCR{-j?nzM3yzJgvfP(u!UmcoupqvWq?(_?JCh zdwR>}=;;_tgVEoqhL}2bVus-UAL5aBDxcPTTLTnN2zB~w&K>;@v=6=FLg*$uKlsM41vcH@d>7mP#=q5`cj_OV z;4#cu^0_ko-_J2Q$*vB#;^U#&&SzP_=kf3IKN2|>mj5@Yf5)#>`%e$x6NUd?I#y}_ zpG5xuh%OzpK0k(QWc5GD%a7Cps-ojkWu4!$d>{Gb^q2F@w~su7j?1Exdb;H|yhpDE ztG8X!KX^hler^vC@7pSyj^WSkkB$dOXOw@8edNAVO&l2JnV~C2s^@ocHka2-9~1b} zSIXT+AK`g9e_65b@5%jEw!1Lxcwi^(3DYNd=ZOWjp9uGCb5ahz$8VWe^aXey@fT{8 z;oH?w=XC4j`mEspAitAm)Erv<&$zyYe-rL!hxk8Q#~n4c)@Z0v`TG9|SN}Kr_|7eK zk0pBlv+l$&v7y0HD!QufTUSGLyhRr{5G~_P)jOU@RuMNFNFX#Cfbg%Yr z3+LAVSA5_|z28n_ON}b&|Esm|^}p=<{b{&<<_AEZBZrw>X8b$V0qg1R(aA^Wm*G3) z+@GfXzVS_$kA9tf?Df`g-m=12HhAPa40?jYU(T7eXK>GzLv7E_uUq2f5 zetPOn=r?@Igq@5pfvs%y@UrR#{u$_-T3S!v3ub<%23q<}b(Q;` z_IMV2E_Z0p1z%%n_5bSsG5pAOuL$c?)c<3ofJguT?i!nG)YmX9>R4$nt&PV&dw;za zu9fxwLMJ0n9=VfF@AS`{4@ak?Wad-emgbp=P2Z;;4cP7I1KDCFnVz%#zdwIA@i%`? z{~OkVJOX$XJu=y^`~1241m~%D;pyUIJOeg5m?C~@xD+=1dD0yn?u+cVT+8D+-S^k` zbIy;vC;yMR|L1FAj)m+Tzc#68iM4g>TfUskROhUy&$v(YH1Zf&k5Ff_{(l1xY4fbW zJzo94M(B93Mpuo^G}!;6(qCH71kcg?r^5AB|4+B~-=n`DZGNasYg1A4Sl07;-I)0= zjbHIR=$Y{8kDpy8|KLFhJ9&L+Jo)#~JOf)ndZ6v~NjyCHdzt>v?>#TT2MN~U3C?cr zWPVBa9b#?S*&%!%e+Yga_W5u&Q5dHWZJkW_RMx*q2AUu8w~EJ-@66n@2CPLoneX%g zm$!%?3Yy4wKEVBPuP*NF;TqchALwbt5Nr@LAW#1m_339G%%`uT*E`C4FPw|G!6k|vaxLUzIvMW& z#yl}c_NVMp{%To6AzxqMuoIV+ZV7Z}f> z&Vc(yM*x@E>h9&iBKO5Lz%{kc>~&I%M;BiNbKEQY5nOS~p;niRj<7yFfa}6*sB!rG zFUuuQoa~R%5NpZYpcjCB@~l<~>pnh!?!eqkIJT`~kIIwQck&Vc+!L{wFH~c_K>hQe zN559G{nmCb-1I{5ul;|H)sekFsx0@X82^bJ;DO=2n*Wcv$48ET8J-qhuY7p#W&d5O zBlmt5?%!}U+~w^->(sHcj|ufP&^!5S{(0yLPM!_1T6l(EtMVS9_348PrGp%qs2@sY zhj@FJ`=L*S&y$B}*F*N|&7htaikICOc_=tM8xGtK-J%wv?6 zQ(Y(1XWRoiz(vxDoxZiw_#)RQh5_H9FByJ;Yo&97cfRlM-JcsCFk7|>`v>G>T$=gg zzEaix>6w1sqDB2>!re&u|B3zYps|$(vHyf0PVX!A70ChKOY>YJ2N=I{u6w{o>8-r) z9iLqpd?tBOVD zU{@^a7UFFX%2cYWlp(dtXcig!+5^gC5vG%6YkI7?8UpC% zL**4;uRb{B0D~3ojr%Rjz45&6S=ryLLvX6>P;v*r9J)RE!SK&?_Y?h`XZSAM7Y+X< z=UX2hX*@vBMBg835}vwgtkoG(XXMwWH2sJ9pibiBO6Zvqor`SVGhgVF)J#R{zJ2fS z4+OYr8bgKu19j}I(L$q2Vt=}>y+r%o|6gD4-wUx*ya$+UX!vxt^qkE+tf+ql+#}c^Ux*$C_(4~z2KgKL%it}{8GJL_dy0M!ot?el*BAdh13Z9xoT6F@ z;v(SQ>pR)3BfsZ2>`#i#kS}z}e6fzy{ul54<@Wwy??3JR^Sl5zMzQ}vI#y}lPlx>C ze--cXJmBt-&cZ!F)t^#1+4+Tkr!!Mu36AHN!*2#&*GG;eW07;gPcBs+0r~e)?3Cp_ z^uxf1#GHd!d`avPhHjDRp27ZN<<+ddV~TSBPd7gY@*W=t7~#BSxtHj7`E9Q7&)oU> z=jiN|>~a=7|9}r&zU&a2r|5m=!+Ye@`SXhRSoX31WAOaQIkh@w#Q|QR3_RGGQZprmSRWdAqZC=sY^S=U+Wd zoi3eS9NZe3$k;!~fju^?&Xsn|Vt&pQ%1)J+SqZuM%7%|6jh+&do{^;?;6=jqh&uiAC|Y}9_`$|m*xU;DNz59m#;HU122B<`y;-c?zDXU*|PSlur%3@0}dzJK-onJKUh zmhbNX9lh_b%JP04^4-7Usb>Gr9oD6}X!`4ru66G&j_&G!^2^u4x`Gd4d29c6naw-( za^bv}DziOyqetj$j(8q1xMkyaG`pL6VNc7^??&H)fBe1l+Q>EjiF1H|dgP~jHa>^< zRV@CCZQAIwC%?y+w{pRL0X>7TA4~IFB>z6u|K0PEkFtw&8&?CAPuPbRxgT`38stE6#P|48&|7l&HloFmG=CN;y%@T&HuyU8YFyxw`wnQ=L5_qe(UZ9 zdH{3u&4Tt)(N8v?L1QkK(8c69{WF5kudsXp^q@@VV6CzL>0eL$CztX+d;E8_oYQaK zIJ<)M|IGMj{@{~m&)UPD|G$bEJafwg)%dy?D}T>4ho$|E-{WJYZZIEfQlLY40KE*n z&ggxAeR{UOUsxL^-(TM3{Uu{x^Y49tXM}5EdBEs$D;MmaH+q)mwb)wUO9#Z>{pjhX zmS67Yy);@%^>;81j_J1nKf#a2wZ2w=FPCGKJ_hGvQtg9Bugh({B>D~bPufd%9UOrT zFi_kUo#+l-Oo(ED#MNSjHL1r+27io{9_0`Q-Gu8+mO>eDy;sHD>_Q+@2 z*C;vf7yEyYZChL_ybYG`?+6|9%J%nsK=T{+0`A-|#E0bqGT+cYd4R;DeC{gPsEWZ{ zWB-w(e%Z2vtw*hwJ*K~FzEj~l(`Wpid>itF1KAS9ySWa2H19V@vpMDp2TuRNT(}9>r`1DuGy~a1WzWy232_C>8$1Y#u(te$Yy+&k^+5Xz|#BM%vB~{T-x6}4`9vki{t|jaQI(kJt2MGAN4*w>$u|z$@?3x z(O0(ro;nt2RB6qhuD{Mp^KL#s9cyX45U#@-^g5pDR^{6M`Vl_Bs=>d>ePRCr`Pjgk z*Vn`QEH_Pg>{4g?lVXY<`^ES_*Y*y~4*w_SRQ?lp#i#t2JEvrUf9D_j_HEWDeT{8d zdAGM~&&xvp2mjdTQT#KX#A@GrYEfGMWm(tI^8k9((U+V&r>g8x=jP~q+5USc{IuTo zA8>G+#{{+^s`<~mxPLMem^-}4`vA@f*Tj4P=rlYVxutIL9a;-&PxjN+fUWFD#8_jTI2SUb-H&;!uB@ss4@dHvdFd-VM6BlRbN zmXJ#!otgfxn*Ym$v;MOE57DukMoWz<>G!AWW_+o((tH3q9_62bqm#8=tOpCcJSroz zou~4Ck2OW-XFp}untRCw{diu$x!@^Jsx~fJm-P5Q+xE>$Z~xO98=Yi*@z1=%Z%acc?BGJh((vv1`^|J@Zm6NVOZySf2N->gd0(G+gxCMGk^jWKsO$0ezh85Ij?#RH znVv1x-&i|(iSpd9h5ZaFn+Gtr=xEq}&_HP481}W__qQ!Jtv6es>nq!zem{j8%{1~N z?yvhO@k-fb_~y!YyXy~k90?uQ;FG*ebIq71c%q8_qUVlq<@BTBlcG>B7;BDqm?VBPWH8L1|sWRmg*W#L2 zpLV#_E^|MTxc{pH81??WWc%-;_pLS9>&N7*j(PW*GrW`c1Kbj>hxG#m|5u)Jh{>kP znqM#h9`3kwU?Qi&(@44_Iv9ULW@;Tu#SQ*VzDxh?a^qh-#N_`C(*Gq3$|7(1F4#c# zAm@O3E9zT0AI}lWlTx3<&j-(An9wtke?$Hfy?&rk>I=DES=KLm{onO2a52zz=K$VD zEeZFOspb+{4v)a+3?K08>7Osvk>}+%?A7RdJEY=ex&F+_Cu&hoxBOG|Q+Tz<|55vF zeTx9zw|?DP)caRyukUObjpo<;049cWZTo_;E{jHQYcb9r)I*{yG*q5>=ls5x68)-X z4NG0;>uCf!A^!&-Gxts234hBbCn(ci2i!+#dMnofujGDai+^|<^kcp&VG`YnoI~=6 znCoolJ{1=HXWSD5zrl>bova`2hsu+%nm}POTRpiL|MG2R$gi|H(0(AP=78sfX0BR( zSX4(|ho|w}*e{nWu1l^Tm}WiEgM3d?=7nqd=k!NDXYL+W4pwFP0=#@8KO?z!oc#a4 zc5b)HDZ<^cs`>TxzIm(rd;XyLv|0eoaiQKv;(*0AZnB+R9(9x!EiJ8uKrAJTck27os^A7I7C z0hrq#B{S&zOz#=imwSN@II`x*3i|8P>w+G@pRHbQ{WI8;7TmHns}`4Tb8=4ni^;XS z+ykFod)84Qe!8XPL);6p3jAl{jbP1mec3ecJ-Ntg*uf0B{SK$=%C&I*Q(m_$e*<40 zL%tUn`CRa!Ogx}0_~-oWT3IzD3r0h5WJfNaFd?4W_GCt<9t665l6w4%aLVzw7gNcpCFZUYzNtk|ErW*Zr0A zd#CVpoOJ)Z_xpR?`=fLhvjKFR5Ykw}1^{#Pj|7_)KgW&-fSFH~Tlt1C`uhE|?fCAK z3xhATKQVd&G>qQ;V^|YLEZFG}ml(|;H$4AH#*CiPut(f1nQpue0`) zVLfnBf!==?K35_Rz#3(ebJ@&aw%_qI3BLh9et^~Q-(Bg}I>b9X*`Jr+;dA=ph10Bm zJbrX!AcM7HKyzT3xcS$wo$2USIPmB2N3e_DOb#nFzEHiL@zKp!Z!QKo-^d~I5>w3! z_d+c0ZP6>SMEDVN%DsehTd7B$oojo#r4Kp27HsrnUBKg?x)#3A{d)fA?;SidhuCW1 z)2~_a{J9)J->;E5bNArxgL}aSX?SE#3b>9g8%4pHIV7RsgKxFNx+X zMyccQkp9+iga;t&UVm_w(Y2@XikRnIoYQ!Ka%t$J_mJv)u3K@8#i6b^`4IEl5U(O< z9=(y?xaqFRdBC=plgLVRlITeGJ0Uxf5%llFwq^c2=E6BGRqiw2jIw+`+wbx{XA?Nz z88H+5EkC7@zhK+zUjE2uHcS`O{Du53(;VJ&-D2P|Anm^&lh};lFkC1NUI9EZF2X z=*7s(Y<}mzTUMUIxsku2_xp-xAj5qq6soqDQJ>7fe*<;*zqxdI!JHDIb>!mwK zK77t|uy@4i({@edg?ZUOx>x%b1f~J%^L4OvfAW2+Y=7@{KwZWU!~?tzaBfI{zl1!_ zYJ%Cfp}MRaG@Gm=7Jrsr?w5z&I4^p^Cz5H|;5PX?*az6y=*H9~64T=U$={3qAMDcu zjXu5L7@kp9J^&A(hR62UlD?kG4k*ujM1RBF`2VAC73=P~T?2oOTpyVe^2A(CRtdkK z^~>6Cr_EOiW=E>m2Y$Z-jVj&WQ`ax|OY$nr1?aqsXw6~(I)*xc>%pGN-<9h*ua4() zGU9B@Nz0Tw>@f^q^|;3#2a7kH-^X&F$uF6z+?sTD0H1RW_VK1>kh}-$zoql|Yp46I zzdlFw4Qz4t>1AJ@C|0ZE8dStRGbhNrOSOLtd^XHwT+4CFz>Z(kuUC7t0C$LbebMJ* zOAYq-G92kx?Ot<-7kEG5Lm{nLZ(#B>$h= z-nUy8o2atqwyLfN_NmLF*IZN|Bi=`!zrF{yzFh!6Qhq#f=wwBq#e8BNAhqu5=;dPVan;9S55&ESaqnVZE@!#GaDS6}nX?Bb`X&Blu#DgQ z$s4o8|8$3cFSj`lb}Ia%m=8F+fc1;s3-6(6XuG=jbt;<=`1K?wkvLK~hlxHv?OQdT zF8mxUzuyizR=b#AGWPku=pvN=-RtW8D>|ebxg%CBNrAJmlZ%3kn}t zAsYc*0(-*CFnADlKl|2^3*`AnW#cQKug9Da%VFO^bTmJAUiJ?^vfUGc%h9Uy-%o() zp!dx+s&sEZT|Xx;(OSxyAm|P{e(|np>(tKW^IBHsy?MI9SBlMO`9jK}q*jf1H@O7K zxcB!5e$lP&R1AUM8u$mIxcBc_2Wl9J-zD$W;(ct!2ZU{Oisx^iWbuA-IXTBd(KCH} z@LBq6$H$U9-P?2QAx-=%%8TXuU%P#$W(x(jgM|Mbb!?@P*LA*SVGN=#(*fp<=-NW` zl86ODchtqu?>*K5O43UdKkxv2M#LcL5ew$|3~ci`IOqS--}v{)ad<^(y$_H_$R%>K zxlZ!=O6UXP16=nr@g(-sAUDFt<@|NyWAS=2m&kr{6c&kRQhQ`|zM^xc7d5Wue^pg`@ER>65GJ$0|LuB4S1x0xfzxIzHg!Y$n{J$FEhs z`;#|}&FK3J*TU8T8^QNz;rHq0&8?B+N%$C3@r!i7-55V-zR=Nby5Lx|*`l(a`QLlH zmZu6Q2Po!;?DuxR;l<;px*xSKj`ITyD{7@@{xYOHi+P$KK>Z;%M%pOrGs6cwFG%%U zBea8GSmmfWI zlG$!iyB|I<=Geklgzety@w2yN{|06EC-W@Po!0@DhBRn70bU0vuKi9gdp0)uDa-Ss z5Ac1kPaO=tL2|82!@lPS&?CORix&0_zLC!noj#L}QI>n#SiegxDY+2r?To)RS@!R> zUCWb&ll>+8t7V-}GR8CgUv%bmfGzd@(~#~=2Oz#o{P`Qjo6P?#I^I}mrmWA6jPZ2F z`}?mRZnC~C*iYsG)X2a6_(JpNqu-;$qFa>p-Ztj%vlj4BYMjb9Yn&9(>D#!OrO*`YCFgwPp%`p#9I zJ$XRXlw~XTJ@)+(+QeU|wNh-cpY4^Msm9;mOMEQJAK{nqPU1F6eG6@GtUb5eY>B|U zk7WP0I_Er$K`9CYX9I9NOT_yY5>V37%^UntN(eJ1iqxS=h^!Vp!I)G%?AI|9==L3{t z|A0Yc%a<>mX8o+F5d^cz_NB*jd5$i(*!4h#4_q*RKZ`x$&x(0KdLDq>$A-n0P2T&> z#Jn3~SOAH8dC zzVJBfi4n~WE)Ui-{RY0m`Qb72QjD>mdQFODzst?I`|=?ra(%q)AADF_`g`nKzg{BO zhx-0%(c3FmJV5m3b$|my+B2I7T0@7W&lNh9`2u6V(>it#7(@X${^;obN&HXPPRmlncB^&jDX1MsvFA z0jUX!^EKC96EFXno0SXqH@iQ=m)B4udF0+(UnrdH-mre{jmGDfjxWqj>=|CvPPFz} zNN=VGIRAC$;N2${8b7Vd{aO9G=F`#3|BqGYi*6o~GZmr-xcH3Q$C+HVm?y7`f8)u@ z$z~6;sm1kutcS(>#`m>cpMw?e+fnuX+Xxq%Xlzt^e=;waGa|GDMs!Z{1yUQJ`oJ(R zFqSv#(i=R0xF7pRB-fO{OOe^x>|YY{UE>?@kyNn*K8FW<@bnVn$6Py~_+eGwi`V{#M_s|Npo{=VlW`|2wI^-{<+|34i+G|gS zK^9AazgG1+=hO8)&5`GuEdS9v@uN_q2Oo%HJl%WFK@AXjLyix)-}&4O>ek^mG4|Kx z#q=XYMqQLj??1kCFyN4Ft`&}UmEO-jej7!sKU=;bzo_9~$F8Eye}(jEb^!0;rM53j ztaq-%|G*-cyjJ_#(4QRH177L| z{&jJWJU}ktr@H5gp`jj@sIxY`e^UPr|5vY0twV)}?Ud)wK3*H0zrQqIAR6@XfD-$L zdcPCB!q}6ZT(?;M6w@Jd;c9W02g}CopjcoV>-E7tSJd(cUtSs9d*x8;&jDWud25Ly zbL?t=b~d}7{qxwDhR@+C^xVJiH^bt1Y>J!={2=&4sJ9?)9hu*?R^vOY^UuojBi7$T zvOk*RYk6>`;bLR@yJ`fz9dKJXk5(g$E=x`;G)-*fe{aXOXVeaX2l$xN4d?a_o>jk3 zdU5bMJp$Ob0DT}6{_*X*{y*oKjzMk#d!$`|PA~gCd^dW{_`R!rg!i&{3G&JJNDOJ$ za#{x*(&kEGc!!1!YIo6bGmQosM&~-lugDC@7wGJO5nbA94nOwu$Prm&y=8w_d3%Rd zCmILq^|B1Xn{*R8u zK2k0Q75f9DbWPqVxQ${Me(;?3)CS-1&~Q#m9Qm9ab?Q{2aTd;nZObuwo%p?z{m`J> z!>2?)FZ1`$7*h1F?uDBU5@~m9P_H(7_-ypCz6i`Sz7>snU%>e#d4RK1Pa3gb6{aB|;J(tiJJ{I5%UZT={@vwW|70B;8zie9BbPJrbFSWG}VEBn9xEITa5e|nn5PQCHG zW7Ea}kYUvLfA#Vip+1%RTpP3g4}GC)!b>ky9kkm|ArVhiZol?sAKdA)F6}lwTIAhP zvA(TzjQxFCVm;m0gm=#art3X2%lv`7cQJuNi%+>dgknEcGDe|S=7v20qkSmCxwN`L zX!-JGhq#))*w4R?uhYvGVyeH9>r{64>`~{vUeow8dD_ap5M7>XO^vSh_#C{jtrT z4%}IOKHJv_4DamyftDAbc%kXA^s0LL=J7`7v9G$M?Dlf&%NI`%mdbCMl%tZs|6|vm z5YAs5Q;G6W-}4ZgpM8y!YAw-4*FcB8d$y*RSHUKY>zJ=!psyQaKTCj@^0j&L2TmJ_ zE5-H`yET+uiTwlcYeCoSsf?`q&zrFxP?CmS9N?NW*>|o)tjhdXi%xJo*W#M~lDyN; zA=tnDl7Xi4N9=vmMX8k-V^7G^ZRm$eSfOC!@JnLiU+qA{@)CFfU6Z$4huQ}G)=tQ z^Z?nuel6pplWpv`N-hxoQ|52Be1HG<=zNz~Ej|&?6R{*W@SEQMo8h6~q_}^#=1pgoarUj?lE{0k5bRw(|+b zJOE7N7lEE%yl0xxGj+bi{HdvkWq+~W@9ll&>fWn{O70gLpA6~DXbqizLcb2rY}%;q z4$A9oEnh#_uODN-0sx=llv<0B2Nd$^A+FJHD`W%lf;BG+RmUC%>n@ z`uW6iKLu7R>LcY?8V^v8z^+=Ce}p{1@`LcvIGR2Zl4~Xg8$++*|y+HoabF4?z z2Tv~zex{x>*86P^m>cqYZcI>cMs(g?`2Tyz1I!lieeBqKi9Ep{-oG%e7cd?O&7T`% z-~1vjpPxOHE?U$xIkzWa?@t}r>E+Jtn(QV@-lAEPdgkkkv0rhxF3XvPdCvoO(EGoI zJiz4$$qxW8N1wa;w8O1t=%)`>8vVz!EaU-29kqV%Ci4K+l|0w;=Ij%Ey84v3 ze?Y`9xUpd$n}>5nbA7{Z>bKKv*->)nZ>Kjom(YJ)#P$zs#_#)E@}*VO*dKd4jjE%L7~w5%L9D ze_}nZt}EsN8#)g_-v{^K{PpbMnzN2FeGvMH`1+{jvHl&}pZ5|G?v}!RbNTuj>%Hlh zl}gO<#mY4UTGLt!Ms@Y|0vCil0H0tYJ}7&^)}Q>Bf4#oKa$uZKY~!|xH$)!b^nG%9 zR|W6?X|eW8-b4N%R|i3_o{&DQU#H^9KPsPpmI$xumMt1IZ`P!4T)$r~6jtqzWDC?% zzHpc))OnKD*!u$$u>tZ0@d=zZc^B*7Os+TdAJ-6WI6M%ZPJYvm((@j@ZgenTejxgG zMEraSeShZAqJQ-1*=nFDw{h#u8#Zgws1Ez{#Cbhc3*~ZOefIF8n!>&D0P%u@^#1dZ z*TV}!pMcxf5_{mDD~4E~TWkWS3&b`-b=3zFK0f7r(68qz)$POU-99Jl+WOj;ceVQT z6WedpW~)X`W%I{1eC0-7{`^pSzIcG^DXjfQTMPG3MR-8O9w5$BV7*<~0|#A^J)PhK z)fEF9>wTlY(W`^HhUf2?5-c0PgJB<;AF=gWd(-)q%m4cp&FZr+Pvh2GH1vD(#6JG~ zf$@6Z$(&(M5128eO9QR@g&_|x-7kD^^+WW?K}Wn>x&V2y&`_)kR1bRq+|!pI-u;y{0gwA#D@wtg+u^TpU-Z^Y%_2_*mJ6YzTs z74;MDzYMWo!X6M^SZ&<7v-b+#czCYm3XmUa^rhO+ja9DZ>vN;)TaSYXwe|<&z*me&G{V(+X;JM&af_93;*|o zJOF*Lgl~{Kd1Axtf%W+d%Y!waU=z50y7`KQjp@D&_p;B>^?&*3V(VkDQv2_c@9pcr z!?jKL`1Jp^Z_ z$$Yc(<=_8c=;7_~@7GfeU&Bsqo6wg(#(n<6v_kKU7@_)#86VKGv+#dk$OF7zAdC@o zl{_i3y>M5|*u#41f`4iX$sdgQK;`fOH`gwI4<7vWD`(l>#*0tf*6KT0JLB0drtf6F zd;ha)_OIA&+ZF}jUAS*l*t#+KJTdkw6k+*)a(au3Zj1+Tw|ak~MzoGFv=(Rv_cXHyd~I>e2P%dScs>8ScPyhh51*c5f*>LY-dL`?2w1&11 z-^?LJZwx-PEj{^bb!yY3-nJc@Hqd^2G4Aspnw5HolKd4(z={EPv*Zj>eNC zy1vNajrHi*ckQD&BKTZ=^~fDyfK_NZ)(O(v2gL7~qwf`8 z`9+vV=D+*o!r*50;##V`$>`rOV_kV4#l33zik>*9&l1s*B{>oNbkMu(slvqDj=wUC>@ku zr8ntS5fxU&UQiIb#u8(!F&Y!2QAyOq7!ymPF(!XSjU^gO5~GHlo%jEJ&zn2@W_D&f zMVi_BIn@s*0J1p`E&;`k5;oEH6gKcyE zJ=a)=(E0e!J+Q*NcI)w;aBsMDV|f8TAGycZx4pME%*uOh$%GE`TKM@pX?^n=RvR;@ zWy;t8MoQhG1@xx5e!c*G1JeVuyEGHuZB=o(Los>)9wpa|@dDph?d)Bly~-cIZJzh; zbLVKyyI~as=loSi+8PavEB1^=vn_BxmWQv z%+CFj_S;?3t8+uv=^k1l^sRtK`wrPyq1GGp^}w7wa)RQv(+DD1_9%}Z#}jT%=5>^A&3k$Iluj#-?(NHc?=B59 zCl70zlKXFkq#fFgP|je6cs29%Q@WWR(7Zy{8DW0$8}OzId`cmw7=2*5DCh#lsfb%s zwn!JS=k8N?F7jA^4jCub#aQB3+GpC=2Y#RUn-n*2KE2ap=r;U%;vmFEUw?A5_tTqa zn@p2HU34DCJr~6^~RL?A%*kMYy>;~+soi?av(~OxT+NI?F&=#3*C3MOo zR*Wz=llg@-aKqfrH8t)uOMLpIipf1Zt%A>LeN}#A*NSujF~jyY--tfK1G}eqFK8_B z!#|vFaWH%XWCt0-c774bHFB}UgQ*v&J1Xmuu}*(oE#IfN+^D}?;l1;leC^G=)VouA zj$F9oO-d?U-JTGc<1}CHEjB^>5S2we=g(8}<z3bu;@;|A%oq@Nrsfey)>waN}g2 zzWpm}XLQb=H#VocBk<(oTV-fH&Xn9cgrts#3?e)+@2-fNnd_`&bD+Z<(d0RDCO z-D^E3kJw+Op z>?cef-hDeKd)I9nX8VaTw+^2Mzn;7Z`aGw}ulIfXsC?reMf*oOclz<;^|SK6uUy^n z{d+ZGF0TD89G{b^b+}XUe20+K@sP#<{Q~s`j9<@)^baHltV_N|<>xA2szhy_&Vx44 zWWn_l*k=|QvbX=;(cNW7q0GSV`c2=_we;IO&-ZZ`h#+lU;D*r-doz^>qF(w zeEiad-Y2hIY&mqu?`N+@iq6Ta`{&CSdmp`cf%o3;&h!4JHS!p@{GIkseERMs-eb4U z^&XTja{I+&yrN-|aQc}ln6b8iO>Ko)`sYLq*G(fVQqkAh=9#Q#J zC8!HH586kQAN+!#kH9;Oi?7#w1agt_uh9)0>01Y75j}CH@)77)Y}MN5=bYNtJ5Rau zl>B9VY;Gg@3OvIY1-i%AIi2pO-$72X(|+;Sx;MR{zBv7H&WlQ}JLi7M zZxIIIcgabXeBT&;S8hSZ#yMR&=-zadi&TE0!oH@+nv=J|Ie?++*+cdORf8MAmo2mJb&w&pK6Jmc(z z-LlSGnv(f%5o@QKQceu0zs&dna|{y3wWT8(%4axw)12J-dgclhVu^375JN!Lg0feh zb0_oVc_;WR`rzLxugRZ(Kz_!#vhUN^&dBXO@1*wZml5-~eMz_Uvln&CQpwyrzbHl; z^HAzX3IpG949Nbo9)a|Mzprs3bZnZHS7YPs&dNW`?Wboasjz+#y5U}xr&L~6Aukd6 zq(6bIBJV-@O?2*TJ!2K0sl2K3g39A6cc@$-SzoSr-MIBL^LnkE*15^NF&%77J|50( z3lC2}OZNTD1(Nr#O{P{_-=9Q7m$(zBfYo%%dr)W4QFmkd3pu43cd3)3d$xyRAyS)zfni zJ7s)_Qe$yj7j;cPb3xavE%Uo%X}lssWA`z>eo3&V?xir0!a&(Ez*qu$LH@Rmz5nd&nG5nVPM@1sP4PwPmM*H3wbQz!%im95JuM~gWv5{3$rJ|m9|p`{ z@Ygi+^@QaqYu$5O`FUp?D~6Y@7+%Vr z?>~~IzMR593Iiz&q%e@eKnepX45ToS!axcGDGa1AkitL;11SuoFp$DP3Iiz&q%e@e zKnepX45ToS!axcGDGa1AkitL;11SuoFp$DP3Iiz&q%e@eKnepX45ToS!axcGDGa1A zkitL;11SuoFp$DP3Iiz&q%e@eKnepX45ToS!axcGDGa1AkitL;1K(i`c&Wb>1`Zeo z_Gks*5-Qt^Al69wK$zQUX@iqJu-9%U{XpdPw6txBKk&8xdRkij#P|2O`-z(n{d|%N zqMuJv!Cv?Iw6x4b02JL$OG|J+P<=ua1*%U7K%n}B0OSYjFOGqj>Jwlf6ns4`Ejzvi zpU2)WMuNSu_lsdDKlXkx0Ap_#!(bfv#V{CyJ-!v!fH?4rVR&zR{c#Q06MsLh0r9uv z8c+=UI10pJpRWxLQlMD#;u=`2d2tQgTO7U^G2T=BehdQgi{FnyK=Io#2uRTU7z89} zehdNy&sW~B;<<{IO+We0rw`uPi5~%2)rlR z_e;AUVSxN(?^pJIgn^QM-Z4bLuQlk;clz8ExxpQH?u;t%mhY4TNx`-g;nDNX;C zaQ}el?@HL8!6IL!?c08|zuSP%ODcYmKuPlZ_Ie5L`}gycexQ{5W%Qr+B>R48{#zOE z?@ji7Q%@x*@W}n70$N7@IH>?+`}nKu{iF;&*#CY~1}yFVHvj#k3=+9r>izu01SqZl z6}`VF@%s_-dtPbx_a=TnLf)hrIzryk?n0u3(f{Uik3=H5?2favW~@8>5% zP!#=15FAB+5($c;Hwglx=uL7zy9oY-2oBs&h~U9R?Ng0Fa>i7yuGf9}}lYRDD2*;&{uoB`O37Kd+}83Ed0v#|Vv! z(at>=p^^Jp{Aiv)(%1F8YDkEi;-5-gNnxNWFwjLzpRdBstm$bvoaryM)q{_R)c+I) zQW!{KAccVx22vPEVIYNp6b4cl_!eV;j!%XP``u=#u&;G>mBS*XX5hR=uU;*qR?Qlj^=sG6YFf8eR_i*ovT{^9*QuG+Ro8l`^saSSW*?Ql z9QAB3l^%yx&+Jw`E2DE(W_tUK^z_#HUQ?9@Dz#PM4|oW^Nc|lG7!a*9YgEsuQNK>L zx{c~pZ>D3;VKp+l>3Uy*ceKh7l@nD)tDK}VQDutCbd?z@Gb2aNxi(FO`xEtzld5H9 zj1o@_tx+wbzj&gDu65A87Ao~s&>`rFl#UP*4%A;pMtVkry44PA*04sy26d{n)o(&x z(EC`Gkt$PkZIPbY(6LRO^ZVpAyn0ZtCbyp0zxjP*j%oSGxZ_$qdh+1bzc@){+_9}5 z9X+V!Plp}V?17_uG`^!}rv}%xYf58qQ^#*-wxymAyc`CEh)6=I)w~SReR(15$ zwH%cuDz#M54Jlp_686Jidc%6vtG8}ivw`$^8+{*|AE|PJ=rF5o^IDsa?bGyzi9=ie zeBRicmsU>g^x^thdH+3aPM7fJd0o9TqNR(sd0tUDL+4iJ>luok(>sNF_lxC|JH0=9 zRQ5~ak;jkf*66mT4QpJe`ZlVZssc~Ut(KKOMfTzZ@k$^4&QWQof?i132qB@ef9W#9 z*)40;6z-c!_Bsg9z4Xmtjq6umI8w{ChzZ}rsn z-pa|@-l{1%VVxJQozbaKdm$HUZ{)($=XSNix%NcXy^e)E!?P==VRT zDcrSXaQOR|jt~Fj##!Do_bl~Zd}OWn=O<40{_p9tytkg+>b>)ueD7@)&j0qSv%J@S zahmu0hu3(&`r#7qq3dUOw_iLqeC6rKhPN&4Rk&eR=fahfbHb~pwD&g7>KxSmX9~s@Dof=j%uyMob94eeWF3_h9|#Gh{na{5#yqE9P3ici5`BEF zf!#XP+c0f-+n=qOk^31ke)`-l-rDIMB-_wCxA4*pM~8p7V}jB7&1bfHpZ@U@@9Y2D z>4o0j?G?Urt&Z1t1$%dU``)_R``??pbdR#r3co4b3+b6cJ!|ji{HwoR<^A)Oi@m@8 z@=R~f4;FcMUOL{pa81AP`k6jItdoB5ZAUjx_U7Yp$F}@MtEPutCYaZXXIDrUED#@z z)o*+g_yB#7vI{~&qQ7398&q#pzj`CxYo{`>fA>abFC5?g_t<0Tj+{%Mo8I1i*3j@1 z-(TRp`OMkg7k}Adbc4>&^2@(oY1jV!=PSH_|M_wij#qg9dA;OP^t}Jh7k{}z-@3}a z319Gip84yZE#9NI%<<3<*pn5Lv%~0xGZ%D|?dkHrnIqc$zEj&eSE~){gSzt~)beq_o|6cYH8EzOYsR#$k!X7&-?H!qpc;g7P>-e$?V>~eU`v<{(**Y*$p z?3Po!cYk}1$uKm5-q7Et^W{ckeph9IZyX=-UFs}+=Ni)w@ZXQGo$hU4c?9;MP;iE~ zn2zdNID2&VEA3m?-l;aNlRkhC=IS_1zjIZHDbQc|PXEbv)=)nUneQMyKY!s#?Vm@# zL-$qk$2QE$Exa!O1n;GvuJh$wct>|TnsBU2T9@TpMRhw~xYqmEYnOSy{m}|<=NZSz zHsMP;KIl>?ANBbr4XV?3SR*~KQg&gg&ikui6X+{^=X8Pk+xU0b!zPN^96MupyL;t} z7og`?PitSePQLmr=a2IKCfkc`#(qN^Q8<-ynpjYB>$V$<~7mLji=5K&3%8($MJ!e^JFwncHQx2LB!8|hG5M3~LZ1$h!`j$>5lAK)8(<9R@_()45dG(o2|kdJrS>2sv-6_Z~vDZB9I z?ISH#gP#1C`E^y>Z#&p@57zH|7<9o0FKqXAZ5iS%7Y>MF%J1E$v8NvhM{BdP(pSqz zWSp|A3UR}4EDuZ`)+XbKt_{ewXgYsf&i9Gcsy`P}|0qoS1RGC$1^Ec#>f3w{pVLR6 z-S_UCq_}na@M&}MY>aBefEM@!>tzephzBO>oN!R_vZ!^6UwGCasb>hrV-ML8df=*%L99tH2^~NfOESzl+4-i9! z2haoGC|fXROm_MSM>eBB+hp0K4nKwVlKFzIi+fruow(h1N#+ytfFH|%2N;_|2dtan z`-KyRwEiVLpnky`DSvDM{s2DVA^$gTTu%D*6Wc=jy2~bZxZlzKoMnBycb+@Pe4u~5 z_TAC6*zx7o?|nn#N{$DJ6O25%#ZQDw`iAri=o=C*K4fD= zvSH9ZO)>oK#B|iJH=X~ks`Re0co1(q?d5*Sa zJt8mog!f)G$znvr2^By2vxt3~=uf`kaFtdnW^Z*oXn)(6cQ;$KYHFt&p}q3(3U0q> zjOl#W&n;`aQ_uOl@}JlJBQc_#XAF+UiboB!_z?c!ViBDD0dhzVnjMg?H`;4Vb1F8U zyoU=`A7%b5u&3x>e%W_20PctrzOPs@;|21kl#AS@Ah%uJouc__<&ls>LR{dW*nuS{ zcQC#iGq6=N`F?L=^YQs!e`2$Z+ox=P#cA(&!0dqf1dJIeFCxsCnPlxP(R{OFgDX{r z>70DR1D;cQ&XR5>`)j6mzJ@$Kjo%kMblnV(al91mD^L3%4=`?cn`|oi&iDd~3p^~E zlS4$_06C=xG&ZQ5YvVV?cm_)ED_5(dck#LbVRRN_xDNJ;9ziypTpKEi1NR;{ zVC;VPXx7N)BCV7^I9l{4E@bkk<9`49@#TX|_phIs`#9sdYo>Py zz5KKFRnpI=z4#8~=+kb8hv1QC{v9a)seq3i{9bv_sXtia-{&53Ks&g{@AzlR^V{za zTw`+($RR=pXx_Vg7JHP|H%kyr&N=dNt9?#QW@`@QBm@_*2CDDAn=)XY6 z-YWam50JbYe<|KK8QI4lx$E+ijh1Ec`Di!1^31)b<~T%mkw-a|it zSNMRrA@Ih5p#SrtKX3sbGQhmktIr%7g%SE_ZW=z3-3LzK{mBipytic2?zm)}m%psH z&pU#B&9si*g{%8Tc%Wq8+35iIMmE57z|4{DUJ%WR3C+>5o63F}Bi=AO*JOXqjLw52 z?G9^>Y?)=>@$OxhpJX^g$Ad%hlJPQgYc*zIV_y;8GCVW)$G4xpZ*keifkt!QrGVQ5 zNq^)Y`op)b{+(L}8*kD^WP#j1;Nm`Qp~zmEUV~rIZ(Jk)Svu5oTxl}o^p>lGxFB_G zJSFezHqB~YD!MOIOlYu?*cKGO$^#i=m zW!un?Wx*qQ5Z?(t#P{AJ9vXjKtH(t1m8?IYW420_`hgnPF@Dk<=ay>IKjAy^csNLx zWONT+r+w(~Ph{_)qheZ7e1MZ+0e9#F{3qvIa0FlIeC|_x`FDNK()ln5PkGLR_CH?! zuiSjB@izTX{C@IXka6G(w#l8t1DfNewjE`C1m^>n6&`pDUy}JU#D!N*?fehv|1(AR z`I=9{ngdnp2TC?=oJsxtWf9#Udiw6AW!C+{c7aFu=F>l2;$603ps!0M`wln3Blv^X z&p)`z^FFvy^0C9aXiY!ki-SdfVzEa5h`vJ3k$2=59~R#~UiZhhiTf97oP?MT{(|H0 zAf8G(=Xu7}iMPsrSX}tXt_^Py-Iq!SkSEkwrOIr;1*i5fpHjBw=kS~6GKKJGeVtYQ zSSRqq1D|MZ1+AwbS?QEeR-iHUVJDxtXPM3Q_s2qV^*nt~C;tb||BI(Tu%9b=$G3&{ z(XpBYvJbrI17}I!N}v#812R z!qMTX@VDWgo6Z{{dGdYbpq#jK#{)2x-C@!PsuXp_y2?dCX!9Dd1MoZw%H?spQ^P9$rmRUR4E@oI>p{!IUMnSjt(B|6n26b-`!f53f_RH;c?nZn_0)EvbmA0IRR{}NrnD7byCN7 zMDvZx3z(wPUgwp{4^z8*zi)P4Ungrny=iu(;RdJJ{*M*6$8HiUC05S5Ys5mq3pxUP zzxIpMV{E>|wL5}yqVItN*m;+^ zPOLG9pMyRiUcFUq{^!dVNBrlC_gVcu=`|kf+|ifT`h;I=T=)XfeYX08-F2)~EKuz; z{?T!11kVMGKbWs28deU!z7PAiU$M5@V)KW|c{JPb>ZOJoeltdp2+zT5z=jV*ynq-o zzeYz3Gcw4@ZsNod#=T7kuUTj&N{y3b|3VjY<`Sm8hED~ltkjW=S`&RW#Hz>UdPM*lm$ z;Tyz0z%#xqF%9nV`-2zG_kN&QF*Hr&_knk=2kk1f!QRpbd;GR}_APj|tg!MNIu+ib zZ_2t}=yJYEea@G2N9qgm1LxQ(Y!>zNJ%3!vw@L5-dZ8+451#lw-@*qFe_B6K^Tvn; zF4UUXLv$oRw8DR9&+lUPU+wxeI#%(T5cqNQtBNCSK;F=0@BqJmcJnD(ORqaTZ8p=% z82APE$RR$Jqm5nHJ)cHhqrSwY>yOshz#$$41> zU#>m?yt>Yy(2U9jo&14&3zy~TnT;f0z$N6Sc>+<0-APyzJyQW@C}52jzpg z+!@#3jej3LAoqqb6>J3XB8Tt~eQQS{@wWcEApFf(#K@oa>=NzBh#~U{p&>csqAH= zV|A5s|2g_AcCj8@${rUuT!Qb4z*?4f$Psu> zXRG~~0OO10l{xCipV}rpgzUmg@KJ1^y|@qT>mol+ggzoSLj7{@5v@ND-;|~86?q0- zgbgHCCZ4r*vIq2Ne7gufo&Ah-tWXRfO8+@sw&RP**DmnaT`t{@R-_%lcW93v$MNZo zj0Nup&%rx7ne{WA>|?u;J*Tt5QShCT&Z*1%DUIp-egHX2eqV$9#M1l-;BhdaOMtDU z`YLGm*4mAQNS1{eI)9vEZoW7=i9{ZoXnS27kV9j zVa?qlo9&OY5ewp8`RMQWsjpTJANfc45w0&nu5W4dr(MA0`Wxt<(%O&=7iGE5x3O>R zVIepz1~6)1OV$yaq47X+f-5u*7?A%PeErw6peSaUTpr5;bFyb>3;1Qt2=auXUC`f3 z^d9f<47u3AiLG+J-}TBJ{9hUK)8fDF@{F+m(4Txo*6o8glhKpw@EY_W?!(+oWTq@U z6JLK>e^V#9_w+}#_khuVtoF2u(4SnOiqhZwf7!oV>3`)f>r>$8DVH2aDJ$IZ&pA7W zkLkx$x>(a1kVOJQh`&@D9 zMDZ`@1M)6(C#HSJC1b50%NQqov%h#C3bXRCW%mkMH=ljYMEB|JYgh&JzYSb!TyDS8 zA3X_uFIw9_s>g}DLQit0$uq|n#5M@Ghy>S1nhY+Rzb4B#m znyb)%n)bM?3j43vzxlrf?1cla<7F_2!-||E`+mP`wc(xkxzpnu!6md{jp&bFq)*_F z6HymDfnNIYb<<5pgL~d_I)L$H>=rt*Bp#sui0smjI9>hr;CE<0attp)YvMof9Xtyx z{}4>~2{zyFbTJ@5ChYXcUg_tu&XFn)4xvBa<@=j77ne1o*waw-pTa(;I##a#H>b;P z+ON5%1#X@db(ROl(w=oZ%zj*FF6?1)3ofCd&j-*W&v-+*hqm_)^rElE*cUV=_Tgy8 zJN|p*2P$5y{A_f0QS8XgKlG2lhyFlMqvxF;>AvIhlE23oF+hAxd=p}SjN>zQ?bq+e zD|ye?LADl-&G#VJbAR~GMt}Jm#2thqv;W%Tl6B)IvDdAR6SY3a_zwO?mp{yi=kn6;R8np&f!ILs`di1yj$>&eTsjshWRU;(E1`gsPuwM#Kc#wlEmw`*@{ zGEvt1d=LMZ@etK%{Xh0O72Qu_zhfOM8vl*ZKW}3M{{{Hv__kHm2EGG5kYn^I1w2v6 zr6u0)#@ZYY6pih4iqI-nPv9@W6VN2c1C}Ex+FrhKkjCJrdQX0Tq4JCtSS}{xbQU`o zAH}?*Tr|cYsoU`cM_}N2$Dc0laMc-pwnK`D61CXzQ{^MId?VU|3 zdfvh0jt^cl&2WTnp)IU=ioL*2GfsftKq(7-OMAv}rF!s9Br6vG?W_HcMfVd$|H_R2 zx$&P9Xt%~R3V(gysirUCxzgY&(|zH9al3rQS{c81@A>mgFYl9%L0-JSU+=vwzc${k zJ3KqOI{siib^6@oR==(I0k$0d4DHd+jt4jomf&^1$vfD2#vQ!BUuU+JIlc5P;1!+^ z!VrW5U*x~?5x-U&@Hx0I6a~bht?h#L;v#Uampg(quI5WH~*uw02E&30U{coaU zg~tEYCes^=DRdLA*#A`HoU^NB{MW(!tGkz&EIOTihUAW%A7VM+hIm_WzE5$S`|~RC zzj%kf8~PO;0uP~AhylVMz|S%M8}2uG$LIxeH6Ofon(?>$7BqxjtkVrVLEGZq=Llb* zUs$UL`Q{zwXt*2&<~hL2@X_l}Zmtp@aD6AW(~bXr*|2W)jiUR}ivLzP2VCtjzR|Iv z=>Jc2rTXDvUoTfq{=p5tGxMII3Hk+j1UJwL{Bn^q1fEXRSF}7Wy+5_`M|b zcYU>FuqUFwgAaM8&p~YXrtKq2Y?nGwN4lPj*$G< zb#$!gQM7PAV14Pb53pxik0)Fju9M-=r~OXL?Pq+IJYVP=q`5mc+Dh)}!}<1t{NPJ0 z-!?dBkn_@fY3eoq?SmUM@6zdizYj=1|4Hp54}am4CL7yuxGu8){@4&WVLc{vIsF92 zVH5e7u3hd(UjSR;>z%|lCm9~&uWK!hFnCxo)pCDliQZjn9+p|5xPLsa2wt-xqQ`^C zo#ZdXTsz-(757KuVROHs0q-F9#7)tI#96SB&>H>1(cy>d;Ff+jc;!31yIpaeSo(KP zN`Ghx-TXfIZu1j}3%PiK<1yCnapQ}?$U6>hcPxqi=rCl$!AMTHgR!VRKELqW#S3X4 zd3*FX${SMwpTv`l-%iQ1ao?FE+U*hjPLS-k)mlK6vi*(^1h4Y}mV+nF+YHry(nLO;#n7Cb`BMDh>4pe6DJj)>Re zpSgD(U1*c@+a6Fq65i$=>I>4OB>KanPA5W_OV(+P8u|Z@pOG8l$gKII^^@$n<9+TC z8*=ZLhDUcl3XklFd>FI++E<&k!H;a(utq~izseqkr$`(qudC=4f+mX5giEuR4o;5X z#PTXMz6Z~P3%-Nzh5pw%HWt4kHt%@A(ck58@eSfSjN{-h`1te31?k^A{P7&?X+lF{ z%l9cJ2(4Wo+|k6*pQF15JlmG`l3re@{>cvOpF1Alh@9es0yD7*3iAu#EmuG7b!$|? z-|#m2jyaCZABFFEpMD3&B`3AE`}ht)oRxM?JJD;hJ>j+bj`IHhBf2jUy}Qcyt5D8Q zGG5sb@dJdXXOK6Ig@@3^<%wal)&cW-Sg!z@GA~;3M4O*Q|JD2GW{-VuR;X{hS$G4t z@W@?P`0GnzzwsLxPg^{`mCr|$$**o_V_)DO{`gw`cz6vvjb9E;p)s@w(mc^Q&(J>J zA%63!Spx;8BW^kq90!x&E0(`Pw#5Jl3><8OL?C5-DCZi6BP4rqq$!d8uLp=f8om6fDOos zWVnDenaUdrq>acWF*yq7*b(>$eMZ5TLN?IJwQrzXgLLQI+1?=iX%l&VT(fgMNBtD)0B&H!-ZKA(_%3`&VXOz5 zBEP^7AL0)z5szX6kbnG3c+zhJ`6GVY!2#zKN87R-P4^3TKi9gW;#JG}R?hD((XUsl zrZp=Z^DTk?&IYgs&=(QEtU~<&pU!>`l{=@+_-elIb%pnm_1 z!aJ@&8}JWroO`O~<4JEY?guTQspA1`06amA*3qA5pfj?J&2jH=q|isfz9av@Ks|-; z`1}iB0t0XZKLsAem)s*e@jJ-~IW5rM(be}!{kSAH1;5DI823zBkDA}An6JjcXXJj` zux>T{{tnF=)~Hm>uM9kJZ$wT)_*LlSAdSijd!py~&VPktk`KBm`hw)YV z(wd)WbBFx3MT2zL7_a0IdEKeGqtM&YmviO`V4I!Zfe(CM@Ym>s2VQaO9;hy(8?>_$ zJD=C_0DOUbL;I@24>+Sv@Z#2iW`8iP{qM1&?6B&D|#)6-?DrI{33&l*D`M#{X`!eJ%F$Hy$eR!7!d38L2LX9pT@RtiODMC zyo@6-=Fj`+18fCj!G8a~eD>MtwyLB(xZr!(m>bR+Zg7f^!pu_DSby_6wX>?E&!3F$ z()&&a%#G-P5Nl-Y4;>JsZCTG9e&|o23(&v&-tzM>uv6rBGDpB4SM=?J^=CCUjQoG9 zJ`K5m@B(_9d`Dt}RjtFz+Mf95;BkCz8^h3CPiUY%KXbi1Lf`+bT00lt_Okwl2k-}dUsAbO^4s78a&fTzzFseiGips!^96{(5W|Q5 z&ewzPeCuG#J#7aD#<7_Xqduzod|f=P%f?vmAmw>9Yto?lezE&O`g5L02hf*y{ok_E zX@5Qkt{fkr(^&)N0*xcY(>~(I+B{!miE$?2;vM%5{JH%JYgxYyJd9t_x0L-d**~st z(=8%NyUuOv91y!7sHA)pQCMAkJ^jW#f0uOUvNrJh?wi4;0yY?(w`IUVm*%j*!y_8`~ILG z?;-w6ydIeY@B3RvmG*t$Al^lsQ)AU3_5htds@=V!QwNRpS9*-Utn`k5PB@JCg1K#l z+mAM?Kd6{}I5^j(w6^Wf`|tqv=z7KO(Es#Tp)J$+Yl zt^}X?eA;{K-NyB+4-q{YDvn#cbZ9-SRn01lO(7sAJ;uf0EMTb6GZ=>XP8 z?&O`lq^HGz*qg`YoSNR!SOamxXSF98d6~>PLf0N_*$005FqZS6@loiH4jVq81-ac# zdUbA4p>`@}01qcXiecWll*qE}nZ@2J$eu9c;E@ckk?kk?G-Z^5JZgZN(9 zEylb`YwP~J@ALrjO#dl1|JTnkVqO!r0$xFP@y`B)skCpS_n8mpkMCuhzpuTQ{!*`Y zRv+!}Q@4NjMhDd257Il)dB6?~7X1rkXX&4YS(64Hrah&#d4JxAp70_3NX`K2d^CF_tda z3)tt7^`ePc9|}Ccd>i6?jLW0{@e9b+D{n5#{^C{V?=d$3`5~@?K0SGGYu0nEqgd}D zVe^ym*`_%W9+;iC1No92*hf5tcs#oGVDo_MN0VD?I9ViR^D)Wq^J>yfXGb6bvU|fIU`1U^#t{Z7@w?m%xjwg%<=5$U!ZBDmp zI)4-%P)uzfdp)5C4yG-@w$rC#Js`#b$qOiWtL*y&REEaM-o@v@M|w|Vjo2bb`=t{) zJS3bp@7AGSjaehw9a8?Dqjj=lkq+omTjx(jc%a~7sckE&Hla)4zNAU-$Um++Ao{bk@cHeXm9W8(@t#rjiSBdi6{@u$%6+TbN&E6A?wH- z%=nt|fckmlBp;0UK6T=6kh4aPqjK5F<11kNL9~A;Gc&!J;yYOrhqgYb@jXY&iXI&g zoHjSFy3X&12WY2`Vb=GdKZcAQj6T=FsFSurcl=Ot5}r_Au<*g&UFPd8o!H?{;ky3l zK`k@C>9lt|0}ljZgjdoxRNhEPx~`Bl`^cvyH|Jp4gZ-`JgWso(`1tsH-`AR!%1d1M9jYZZqnVD263YNHF0$%7@A zK{{3Sx$9ffM)G$UU!?8y?==4O#VMn+7lX5Nmv+xuJEQZriuR5dB0S(?M91j&yVwKG zi7U{!VIleQ_+_lygM54&_<%M*d;DE^>L)kOBJNPAoO84F+Ee)rt=BLR9BJL&Lo%-C z=vP%o(cSSt6P@1|Sznv=r9$lALT)R*A#r%04-Sp|tMcdspYED>EW1X&CUb_EtHJoa z+8n0ME2nh2{iwqmvp*NKH{DoOI6l0IN-evTHpfV^V-L*^Ht4=ipJ(Wm8+#RKuIvsdJr)1M~?hV^R1*UXm=;p=d|dRnKO zPVC>J1voo@d5_F%HuwEj+WPYF1iC=$Rv8|2K1}8J=mL#>gy93N?H96j*)-P--jcp|cn%)jx}7Mt5Fzb>S?WaxSuXIwus_gC}BGcqSo z`hooYAg!w6+{J60oYQB853%jcGhzKD`7ZS13#?CPplMT_ohR(J3T;7WjyTJOElmgZtVD3Eg=!xZ{7qAf=k;fpt&=J_>*iz(E z@@(*)KOfi4E%kYcZ{ou{JA_U8t>Rb8%?Yy}j&yv1;u+|8lXvy$o>?)a)8wAH^(;5{ zqLn=|D9PHLx*m%Gbb;dot!c;kNRUfwdu^rC*pMyn-Y=V(7GjYSo(rYYAn22&uPvw&wcUafcpYs9y=wE}cKs@qlHd#d+- zU0bR?e0v8E$BS0>%s^iSucgkb4gt^Jp z?0XO~`}tfnalB9r?^|YfOP9PiP`?r#alFbL%~xXWq6<~7R=Gjt)=1&}YTe(i=hmvs zmQEkMVOCyet*cTmQ8TtK>Xs?K%2L0sSlx-9PhH!;7!b{Dd`U<8j+yemWAsC1YCmgH zw{+?BEcHXO#1|Qwe^X`Ot_)nMcT2_qF(vVW#T6oa0dE8gJ_RLs&7H$T8Cp{>MeCB$ zoVuIBKnepX45ToS!axcGDGa1AkitL;11SuoFz^k)fOlyA<)_EM^?6!ajo<|k8~FV- zzq5-j?B#c6(eHeI7rhX+pJ{{L1NORd?%nUb{{3i;d;H%~ko=#~4@N$h<~|s5_p|*E z3V8o7Qo&yLJ5s?O_d5b{zWW`i(EW^57_BZXZ5tIttBU{-_?^xDNbC5D0T2Zz@_TR5 z_YI)HZv$vg(S7@!U-a7m4E#0}7v1&&4*uTec>(aP0WrS^$27+p6ljjoIMAHZf2+ZP z=30YG{4E6R4Z>U6Zy_Y+dHvoKe7~~4h2WU)CHS2mtWWn3^zYK(EAf5>@Uu59@E4E& z(&(l8G4zlBoq%6r_)Yg?erFf|9mB5)_&J8(6Uc)XXkJ2j$}f5#UjFR&o}&9gPYi>q zxy6465Mufv(7V|SqThiAZR38R0rsTh~J^UswzNQL$xinT`o(P5g+#6_L-#S_ksK%aKhsTdUeOXYt^hivR#Wh3%a$hx4BPlgA0%7(qKp5y!u!6>{S1<+;(-(ZPBFG z`s&rQ=IYteD*aVDYrb^TV|zD^Un3*HxM8j1M9a-%1~z+O z+OQU{%^%a|)8&)ehsaG~zQ~yi54XZO*OpJpDVR69^(T{uHvh{BM>Tm&>qG3Ud04gO zYTXEx?hWczYr;5nptXkt9(HXzHvN`s=NK&M8Ck6d^k}kV($E%Gk+c3cU zf%XMsucViLx{fsh!dib{Ys+sh)Ve5z%H0c-pZDS;Yqek2eD6-}?Qog)5ar$ZV_JVE z{5{*LZQU!RGEY(42WSt(=E~19JZNu(gNB1eCuOH!lYd-<9$Aft^=rQRl##9fM85Ej z(+1k!6MLWCTBteUp~APXEmY2GSb3*m%U4xSD)ZndetxRWCkyk8a#RbI+g=Es*h}n+ zjYqS7M0nZ>E#El2WBu!dgE<))Y5iI>KCD527Y^v@S&I%&_X?L9?gt;yV)UHRZC+4* zo%TFCF|2jC!fIDYb7HNniIr-e~Io<(J;6wN;)H?6s>+^@Pc9J)B zSgTjtw5WZN8azSt?{sv}#s@Sn@3eV`8_jFhu3Jz0^IWxjVs^n*+Us07%7vOYP)KgE zYcuWRyds5n!3Xd1P36EBYE6mo6`KZX@2c#thaTDNVadeOtjzR&ZJO1>rtJ5>GZ%I< zTo38jrt`w_?S8G?8P?VhX`X)w7=0R6yv@n*9i+B>64Y=d%zjOQ1eoOk4@6SF_LUi&m?UO~vs zV~(fMf%*+E*!miOzM}BzGY5N%PipsRo91=kgJIe)s!H_1!jn6iu3I?i@HT4uo6ucz zFhb0$Kqe1VTa(oT4xoefi8DS}Ft+Xc_3PAN?~Hx}dNhvMX8o$b-`HbX8UKu)G&@ti z+2hE&*19WT4$7g>c83$64|bX?pbw^>*qXg#me#1A(TQf)tX?rWAQzVRS557>Re7}R zwOqhFET@B9{fEL4n3%Vx{XI0FFxNYwUvt)2J6U^XnvbM@Rk?J*s%f2#cQ(xF-dA(` zzs7gfy1n6KYuPwk9klnH2Y;6rPx0^bn|3m!q4^D4`q(@2Gk$iJ)_oVvQ; zqkggGaIkkYvd#YRb4N6>dDyJ0$3DTVq0YRnAPyoh7GAIY0M%#uv)X@3z1r1?LGV{1 z53HY+YqZ~ZN|$4`ei!@ChiR|FuY>Omtvk^CL7p*f?{ zn-yq3{bBmQtYyl+p=2*5&992itpQ$Q8SuN?bD=WrSN^uOpY`O}tB-kL z)PXJH-Wl41`%jN;G*}YZW77lbJC7aI>}fT9r22{RaU@U@{S)ntH?BVWgnGjJyQcHi z|8%_X+RqXBWKX%rMdwwTqp1031`~Z?@V5J`q0zl)-+cOPlizqe2EXUdqx_|{q-ZPO zV_yT-(&C)9pE0kS&5LARC16NolYp7N^P@MPq8Lh>e=7Dy9Ld(C(J`S6C@ z2iID+v=F_M2yS!~bM=vT_@8;2(1bV^au2M~p5tHD7Wf4kx^^Zz!aMN#JDRhJJkegh z!8fUg?`vHtJHLG6AZt6Y#DCA>QR{7oHDAc<(W(AUwYxLKR^t=4pZNmZ*Pg%^u&%LW zs^CAOb^N#a&G*p@cWA9Q=8fKY>3G?I^S$5xXl0Z~kp=c9hJLgiea(A`@ae8OJh3hY zdoV%=$M1X_y{9?Rws$pkv+lWjHxZn=R!A)E#C}a4Rnz)^`iqm2+5xRyWcRc;`xET@ zC%YEa`-#3yAAotbig9^i?Z5lAU}3*i_yF327oQh8c;}qj$8-XEDyXZ1xa51kylb&N z3qMhOKJsz!%stDzLha=llD(B)_Wt?G#pZV;^M}w0#M17(bgZYCDt2J>QQaCPTxVTz zRpW`fc1)2}~ zEeFlZHF?%tT;ms~S7<+LY5nENGmLMTubIEBkFA}G4TC4xFFvwAfI~5jz+ruB`sszLpwaQ2SlXG`{_&`{eKShj%q+JPch*kuAHAkII_E>?O^9GtdQ| z0RNn$)6NzDORj94fq0$GbKd*cdk^fMYW~DU+NX$i(-!J?IzjW+J$!N25~t5w7TBNx z>#m_UHO6v|+Mm!568)|HlGzcgpDiD=5TCxZIvk8&$xg7(PWXcx>>b+7yU-T<$NX)0 z8@_OHLPslKPIr6AH@was@9fX(+Q_$%Gf&@UpYJcr0T+D6_yW3^aVE7tp$&BH*Szov z@T&Bh#W+fmFTd?OOb4+x#l1TxnJ)^@gLn87`m>IV=AzpkRC}b~peJo|?dKey@zGo6 z7>}H{;t20E?P<$>ZX19>az|s6Gn%1o^|C!4BMV{wU)K)+B+pA8L&X_I~9a&+shYVviv61N1HbyE(SD zy^|s8F0K8>|IhpU-#fQHu|XFjPuPCxzXA9h+AB-?uVftq4;l^eg(dr+$QNLJKj=gO zW@Mg%f9&iVcI<4)1N*gKyLFhgm1~rX*Y!7l$JTcc9`UXD7BJnTb=N-DnjYSVH=5r; z{r`OJPqB6UyQKDywS=_b8H>-ymIq#76;%y`_^O2;Stwn@C1Cw5U~w>8|=LNaqCaf-{X6GpWSA5 z;gOqWduK{VvMvt&;zWHP+VAY&%NlcKY&pASods&Y`M5g9{0YZ~kJ~O9Q%ku2yZKs4 z{2z28_KEdX(A8R>#pvYvg77hI1ZK|Jqm4b-feD}auj<<(1FW^8xxB*3c$0C)68@t# zzOawjEXMlqeet3B4t5DY(#Zn-;gVzk-lPviKW_fmHlNg~U4wD-7`g|MARIXVch9|| zg;#?AYu}a*Vmuoosdrkd+`nj%;;_v`pJo){F)<;3^>B~8NkL{xUFFdp++J5*0UC%vWhDTmkOamRt zS}o{1d}eS3KfqVunm!t~6?`S*1L=qm`3H&t+^?pa@1rPSvi7_F?`Yu0ZaTWrZVL5K zA236M#p7C8JG8d9(dM!Z1C4*NQRpz_gY}1y7Yg`)c9oWx9dB%3~3;| ze?VUh`GF>`{SlpFF{z8z^fx*F-Gi&Voo5U--yB}`{(imPUvg4wYv0?y$v2$5FTa&N z`e-w}3@y=Z#L+m%)_tNr9sEIm3fki{6KjNj*yj`b|Iqa_tWSVXfUn@}VNef7ct8C3 zZF4>4p?uoBNu9y){?3iP6Skdu{0UcvBkdP@EBlE`$H&CI9Uhzy11|tC`{7~3pVB_b z(290o3toI=y>!|Ri(#T4%_fR(;AQASK~KO-+}keSkF_R=t>9CY>o6Y zzKP+JdVm9);ou4$buYx&=#=AI{!C3Xzo)b@QP+Oa<9O(#b`?7REck8Cu>upF$MtQt1loGu))HyYGyJWZnr+XC2c7}n^36+(1NgXLY$i6g z!|#8+=I=`cKVY}%=X;;s<~=3Zg5KcZb@addC*%lRpwE5Z%KxTo8|{U!leODDN4?U| zg*Tly+?zA1^~)OhXhXZSpLm(tFPStM5N#$wFZti0l60)Ax1=NOkuLGVvU9)t@oJ0R zI^IW*zyG`K#?$ySJX_MYO1kH0&p04^0gfKjYBg>5_WJp0oR{Tq#bke9CiO?3yk_h1 z;4`5fC<&I}J?I7w>BD0ez$>~By$75e8Q<~kWXXPg@VoKn$e!#+hp_r(Fx?mXzV4dT!qkxqg#)oJy&k%V{LJ5E9+6PnH)$jor^!9K4}3ogva9BSC-%C zEp#{iI{NzPeA>_W4!`+sMR_0m5(}2BYX6Nq_K~NrpXU8-cDd*t#EoJm=_C83r+XjY znyG%+9<6f^d(sWQVe88XRx>p|EVC{$R@sF^f75yf7!w$-S3|+ALNbRlH?%Axv z>RLaybK5!UGj<$5 z;9>az`2H38j_SrQ!q2(>oD)3q(x#8>V0&(CTUxbi?~6~ZZ=^WZK;htXV*AJf`T)I? ztgXqe!vnATP7jO#Q6x?Q@ikI^eg!>Tx!t}+}bKHo<+ z?A=SG52O?KVHe=*^2$R++U?7U_E(dhSHHS|zP8%_&VrM3kD`s|Y3=>0Xlt@>XpGEo zp#2V8sSoxvW3d|R4#{tkkL2e@l@|}mXdJxGH<1x&j_hfi!S)_FbyCir`4hUDf46n< zfmxSUYv6<9^!J`SFH^R0yZnO?J`#SScp>U50&it-;o8iRcFTVZX^*84W8vbzeOiw> zf6|2GjUH-ymFT);bdSG|e;6vvYS4MB+4^cR$t=uEtz__#YYJtYo3W+1(%Mt@r zKGL&Vw{whZ&;=`cX3iSv`|0l5!8oejnTp97|9KzZE1Yy0EIiyHp7)x4Nk4enxQ^SmUuk#+q7q1 z1RrkQQnyBF^oSl;n=92{DGj%&`zZ_rF;IrJ3PRcbL;1FBg{O5R?84`fr4#nZ6XXHD zs)H@Mur0EdLUv?P2Cb5y$L)o^k>ehYTFSwW`anLv^#MCXkK5cD4w2*F$f_0b$Lz2D z_ewj~k33h}v9#}GCxK&d9K74&+7oG~Yp;%b09<(3%Ta4l7@mYL16264iUt?q)!~^J zZ6yr7pvUb&J*L07HJ0pJ(tll4Yj2f#D(PuC`meuE{^0=pN7p4Q2cY`$SHbwnkY0_` zUwB}Asd~Cmz3SGsWaEwHuf4)=q_3LRDdWQzPE7=JHGLJI13!!0Kysvu`5KWnBzj+u z@4vrum-oo{_@yPmdYja(nQ?5VR@ImE?Of}^LEY=#a$K)^cOTcY-n|35)w!cbhnhRv zHm!EXVb#-T2&(>qyOq`uh~|7o<^cu2w_ku?baMOjU1uHb>ZzX7qQ;Qp`_{i|=J1v; zub$Z5w03~Ec4pp}lZUqW%TZnG+|!^=7GoIws%ND) z+_JQ%eN%IO-M6b8{w)jqzKlMc-Z!_-sL4Z{J$CxM!}r~O@feL!t+f0@_1l=IZvB1w z{8rRIXWU6~o$v=QoM+GS&ZaqC_l+9R_=T2@tLN*RM>ninEnyB)RWxMtJimX?qf>*N z$wQjmedfX*;h)_+TVv!Ggf)(*IL$ST>4)iG6H9XaZFi&}&OM&tS&Cw{`o=7c<#!Ja zJF?-kwQFXbDp)(7xzHaYDQ!%(tnIpJxexp3qnn(3>cpH6f2f>VjT;s!H-ua?8;5uC zuV8E_(K+pKZK5seQx~ew8UCSiK^Bf{_esarwXPCe{g3V4xH|A@SF1|#(-o_I*ry)f zd?WKR$hie>_yjmA65Dp|a%}+ziVqb2{-IUmz=V$MQtw`YetfS^buFi>y1%{(%_-O4 zd5b%mzMD0?#Wr#;71Lzxfl{vb05aZDPvElPHS94&G2Aad4cKnb&4UG=v z0$dNTpV8i6pFN`GYH|m(XI??UTf39aQ>+a9?p^Sr_*PhQQAit(>RRs}fztooA0J=l zJ|68{ImM5k%^lTxxca_f;k}TY^NNl!I{3uP3NcI66H~Q$t&kOYaz@KPs9nFz5+|L=&EPLyc zUjHHg4jm63DDcPqS5NVlp47(j8pyvPhlYFPIWa%7tnj<}j==xD3r5*^G2d9C{DFI} znrQs!>cz(WLwh-Hn$zv;CiSb~FPYC5?|Uo0V&esKM>bnU-l^i!QNDMymrQxL3ubaH zsZaVLl84=yz22iT;+~H`0uy$cc^r!Qcn@AX&AvhGo^L{1>53RR;(2_HJLE6*?^@?> z!QToR#KW(6so`I-)JMsqQA}NPv|{rSkg4;P7q?XTcjR14Ml9!qyg}`+YP5ilp?f^+ zyi48{^1{2oOnxu<``E@M<6A37dw{+XJ67%B$3L8Md<*7U84uN~Q?%}zi)~Flwr&HB znS8_;ICKMF4i3(#o47x`%AAiU?pR=SU|d1%ws}>`Pq#S}L0;k<+L3ST_=R^EJ0Mq< zoHF(uXU-;Lb@BBA3x4amnR#E-tdTw){0IL;OT+#2q4kd-f0O4O+`%K?B|jG3ci+y* z#skVF^Df^w(DJ&MDW{PbIIuXK3+&|lkpoUn#8S;GCNGK{OU2JEUxxfK=thahzpG#0 z316)KL%U|xHVbys@j9A46a4mj_KA%rGlnl*72_Sg1N^|wybE9@Ru2uh4sS4)L2f5w zeopVY`WdSM_8(m{)t)7H@uz~DHqj2|w8y{W-sSt!tCqLcQ~fx>?dIz`_)T`_jcmFK z7^F9g$*%hrN7|q@D{NkZWXtmE$U6Y<=n&?(aUI@`*dfcyUNp9)_vrWLV3+J3&%mqX zt@F)b9l>+H0Z!@1sV{oF;E$PWC;S`y%3<9C45h)(x0(C?j`C-Lf!}(^dsudXd@1&= z#SW1-P2rgrlq(DTkKHQS5x?^e*V!MH_9n{}s6CKz{-ga}OxY`Dd(`Yvcowe@yP) z?g;GM!~UJG^?cj;%Pu$kLYOfdqi%n3w+RH;5Ypv{dv@t(i7s5g!&V^#5^GM2>J8aP;%wqBYXns zUDf074^NIX_L$cuy46-sxJoeXKp&)aL>8m zu=A{4u_v@GJXOG&g|y@fUw2{9|u{8JSUBnt${d_!7Nhc!=*X_haGM7Djt)Km7w>A|D_B=gWwXjf^v&5&A%b=N?#L zwh8{`dD;#CfD>e$ISc$oKi#SIAfb`tp{W1xZoUR{crJ|0#UC{Ytz* ze+?b<;!oDu{2hD){8HM+T<-^@@8KWl35k*LfD(3hkJ`h;H2wv2noca2tG& zHcFeE&FIRkPaDveV6tjPASMt`uroosuV;)lS1--d3yTXS>a`?ByL&j7pR zr-1zDX~UYI27Ko&tyqqW{4v*tv*~k-4$8eNotGkd&-*|&1K$)mOoVH94VnQvaX!U{ zLcp$=@j1YL-DbaE&Ur=t#6zwPtI3IyJcN;H*@F`K37F71*o~mRaWHda{uy$r_SrXC z7ix>*t*4A=xfWP2UQsc;$G1~`^GF-ooFJKgA9<7w3SpzE8yK+j@o@4x(KXjEGr08) z*|HG6$m*#b-c`K&1nS?quxem;Z4uno2G!A6GI68qQX%uK#j9c3fxjeto6qOq^V{Xy zJg$Lro{>CQ+cjrBOq?%&Y46a&v8}F~J-VIE#oIi$YGHS6kdKrp*iDBm7~P_m*cpTA$UHe|8VPtV@&@uUyy<=pkG96M)9Rj z#E*|F&bMaf2~FBk@5~`}(pkrLMP%*pvcgiC=V*g$YYeU?A6uv4^q~#<qq-4)WHrgx50JMnKxSxVydM;`Ijr%raG> zb_DYh-M-+#V}{n=D%FQ6Q)S9=nmkkP8iVaR*Ty-HMRuTB}(D&wxp zh9%M?#JdvaAB`Q-78oi8$B@2_(l1(bM8@!e9o=_YVnQ?7nE- zpnp7g?F_|u*M#3v|NQ;mo$Gy|G0MHqo~^#|T8$e`_s&_-|ML@%YWz@*%ryD;)h(V` z+SsmNW6}Q^C$`S;ysKz)#>vMtKK-gQhI~Pv2j5b*pBxwFTVVG?TF1-Ixu!m@a%HYm zoO!)>$%bS8r(C4(>l?O)NbMSB%RiblxQU~4UG3TN5WdvDw{{h3Tsb5^$>!A)H^h$( z7Ux^(8Ot*&&^u4uG0&Satj!+TuOYk01T^bZh(C>7DPfy5GBgA2Auf zoqo)Y^F(!W%{}MNc~|r@7^VzkUcbMlYrR^<*Y{Se*L;OjhBsaD3&p}{H~lrf$(n5R z8Hi())aRj3!81HdtPeaG48jAn&^YBCg28w)xW2aXIt(XyjnZ0NyX}O3(9c!9hI8@> zh($3LPaKHxW9o><0oPbZhL{)YhA^fl-_)MtTjB?J@+(CLCxP%uLt97bp#GEj@R~cb zP3s^NbGLb#tU1B>0%LEisX`ppjm6S72LtESMI4bhIP=npA78J2F6*z+9`#>C$i;~M zO&(PBrXLvZ-%ymlUrn*b*CVtm1kc2a8AoNEMB<_J`7QTCabLzoT>YGj&K~|c>&e`# zXNl7gW56%xTi^iMJa^@QPgH+DaG<%)8IJCwkH{LpS`vySTEB~L^B(I@;?oo3Wgb59 z&`5hC{N~42h_A5DD9;clWjqzX8b07#;K)-y7#)?}ByAP-U%R<)hGH>>dyVg`Bqv6C zBxe0!Xbw&2$sb!KzPm>2abHD{lIjO$?}Hm{O;Y*57HegkoiP#mO->GJFJmvv zMI#
mO}-8H8-`4;sf$FiGGt9~1+(ipe3Uwp8ObsrT=isB!hd0O9OOqMyzi~|sR zWIPi-fFD>Vky!9U*Uhjo1)gDyjqxht`3?sT|Jcn_#^n51_2WaOJO0!ByH(gY_|L%% zelOi{w8inskDy)j8Cj2j?-EPjsr5CH6Yx$MW@JX#@1pOIfLuQB@J^y2WjS_t`jD67jy!TXf3+mU}x>mR7 zXgnafI+wNCeH!@d((62`L%<1R5qujP1@6%~;LYeF9Y|ZD4SI)nU2K|f0^{@(TkfLn zA73%VjYwxGh z+r))k-R@bAMt6OS_2nlV+vs%aI%|Pn7w5shS06jgU{d_yUR!r4;(r9c$GPe9k8iO& zEn;@wN4F$gQ!Zlr_Yrs3-0P^!1?vo+&l%Cw=%8*?EwyWp!H~fJ@^P@!#+_LQ&5aj1 zI3sv3#D}47`EtKfJ$0#TSl@b%--A_0&liqqX*iI9t|eV_KRhj+gwI=`@0BX=5?|7_04=F9p1-yUC>F=td8`(C2@5?zygr!$|Ddwtuc zHAgm;zw;LB)V(M@iLS<8QLvlniJvNOUjD(G@}u$(Z;{rFcQ+|-bou0H|A|ZG`jcJD zTt20{qgjp7M;>;xboCnP?W?8te?U2NLHAu6msmZrZw+$DEH8TV!hz}XouV>cu1ez` zRxUxh^uOt-xLd`3zW%H2=^BU6(i#&E=3>uO^n!~+VY6g=vasD0d>F1lFYP^5u6iwu zz`w7v{m??bg=DrVZZY_4g&tJu6N*r&vA3vX?veizEV`E&xL<>JBJXGOEel@|@*#W? z8tl10;uH;3a#W&mic&DG{T4F*@L0^+Z1@r`_e-Z~X&V>zPss0^7xs16Yj2#_d(rKe zPI%;@8%}xk!Ru!K`G)P|eld4^-e$etl5dO{6e}~AZWw6q&K}w3h=*^S{f3SED}N5% z&RRwO{8EzrJtzzpzcnt>l$;!V1-=We>;D z|8&iJ|2pLyE-E~#OFiqWte+F*<7&#&dJ%o6@r8YiC&%{#@KLqSz1B|H?cI6B6l|@v z1z41uFoU&&wN63-{tW(tpVQ{&Q*ll{4|y>1(+U{pnmfL;*$m!SU*>kfC|$QBgdV{5 z-Fewb*5{&L`X6U#E(iSxwaL~)7ET}IUH+cFXX&%(OO1Jk{wn_r9~Jw7E+JR${#{eF z_sQAb-&DSq{$w4&2d_WnHN9%IRc`jP=vj}c=*Btp z|KX1`tNzLqF2ec#b?6*J6pard8@)v=zsDdZo6cH*)85zF7-@uyw%4n zK*wTxc%QKaa-;BF@Kw1cy09LFw`pFlMZ6pNtFC^-OZbGM{XSsP(YsE$)vRxeuR{B{ zN5A*s8)mZxtMwJ->lp88UEU*p`=b^A!oN`)u`>l)2cl5xe`+jw7vp9H^w*!g@6>-Q zFUIDXKOF0TRK7q9OS|GFH}E5ck8jIS;lpWO$TrDyD&2>fa8 zTl!r?Yje*~j_F;>dHW4Tdo1218JW&Ko imageSelector[0].height) { + if (imageSelector[0].width / imageSelector[0].height > 1.5) { imageSelector.css('width', '80%'); } else { diff --git a/app/scripts/plugins/ui/joint.ui.js b/app/scripts/graphics/ui/joint.ui.js similarity index 100% rename from app/scripts/plugins/ui/joint.ui.js rename to app/scripts/graphics/ui/joint.ui.js From 8cbf59fd4c21a8e8e2a4a71dbc74fc351c0ca7b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Mon, 23 Jan 2017 22:00:19 +0100 Subject: [PATCH 009/124] Add Initial NSIS script --- app/resources/images/icestudio-logo.ico | Bin 0 -> 370070 bytes scripts/windows_installer.nsi | 119 ++++++++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 app/resources/images/icestudio-logo.ico create mode 100755 scripts/windows_installer.nsi diff --git a/app/resources/images/icestudio-logo.ico b/app/resources/images/icestudio-logo.ico new file mode 100644 index 0000000000000000000000000000000000000000..f8dbf862df8a51556c25cd1fce3f59e6983b5108 GIT binary patch literal 370070 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 Date: Tue, 24 Jan 2017 10:03:00 +0100 Subject: [PATCH 010/124] Add wget:python task to grunt --- gruntfile.js | 40 +++++++++++++++++++++++------------ package.json | 1 + scripts/windows_installer.nsi | 3 ++- 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/gruntfile.js b/gruntfile.js index 1b98a9099..04ec4e668 100644 --- a/gruntfile.js +++ b/gruntfile.js @@ -7,12 +7,12 @@ module.exports = function(grunt) { if (DARWIN) { var platforms = ['osx32', 'osx64']; var options = { scope: ['devDependencies', 'darwinDependencies'] }; - var distCommands = ['nwjs', 'toolchain', 'appdmg', 'compress:osx32', 'compress:osx64']; + var distCommands = ['appdmg', 'compress:osx32', 'compress:osx64']; } else { var platforms = ['linux32', 'linux64', 'win32', 'win64']; var options = { scope: ['devDependencies'] }; - var distCommands = ['nwjs', 'toolchain', 'compress:linux32', 'compress:linux64', 'compress:win32', 'compress:win64']; + var distCommands = ['compress:linux32', 'compress:linux64', 'compress:win32', 'compress:win64']; } function all(dir) { @@ -48,7 +48,7 @@ module.exports = function(grunt) { } }, - // Executes nw application + // Execute nw application exec: { nw: 'nw app', stop_NW: 'killall nw || killall nwjs || taskkill /F /IM nw.exe || (exit 0)' @@ -64,7 +64,7 @@ module.exports = function(grunt) { } }, - // Copies dist files + // Copy dist files copy: { dist: { files: [ @@ -83,7 +83,7 @@ module.exports = function(grunt) { { expand: true, cwd: 'app/bower_components/bootstrap/fonts', - dest: 'dist/tmp/fonts/', + dest: 'dist/tmp/fonts', src: '*.*' } ] @@ -107,12 +107,12 @@ module.exports = function(grunt) { } }, - // Performs rewrites based on filerev and the useminPrepare configuration + // Rewrite based on filerev and the useminPrepare configuration usemin: { html: ['dist/tmp/index.html'] }, - // Executes nw-build packaging + // Execute nw-build packaging nwjs: { options: { version: '0.12.3', @@ -127,7 +127,7 @@ module.exports = function(grunt) { src: ['dist/tmp/**'] }, - // Creates standalone toolchains for each platform + // Create standalone toolchains for each platform toolchain: { options: { apioMin: _package.apio.min, @@ -137,7 +137,7 @@ module.exports = function(grunt) { } }, - // ONLY MAC: generates a dmg package + // ONLY MAC: generate a dmg package appdmg: { options: { basepath: '.', @@ -179,7 +179,7 @@ module.exports = function(grunt) { files: [{ expand: true, cwd: 'dist/icestudio/linux32/', - src: ['icestudio', 'icudtl.dat', 'nw.pak', 'toolchain/*.*', ].concat(appFiles), + src: ['icestudio', 'icudtl.dat', 'nw.pak', 'toolchain/*.*'].concat(appFiles), dest: '<%=pkg.name%>-<%=pkg.version%>-linux32' }] }, @@ -240,7 +240,7 @@ module.exports = function(grunt) { } }, - // Watches files for changes and runs tasks based on the changed files + // Watch files for changes and runs tasks based on the changed files watch: { scripts: { files: [ @@ -261,7 +261,18 @@ module.exports = function(grunt) { } }, - // Empties folders to start fresh + // Wget Python installer + wget: { + python: { + options: { + overwrite: false + }, + src: 'https://www.python.org/ftp/python/2.7.13/python-2.7.13.amd64.msi', + dest: 'cache/python/python-2.7.13.amd64.msi' + } + }, + + // Empty folders to start fresh clean: { tmp: ['.tmp', 'dist/tmp'], dist: ['dist'], @@ -327,7 +338,10 @@ module.exports = function(grunt) { 'json-minify', 'uglify', 'cssmin', - 'usemin' + 'usemin', + 'nwjs', + 'toolchain', + 'wget:python' ] .concat(distCommands) .concat([ diff --git a/package.json b/package.json index 3573a3174..ae2c56388 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "grunt-nw-builder": "^3.1.0", "grunt-replace": "^1.0.1", "grunt-usemin": "^3.1.1", + "grunt-wget": "^0.1.3", "grunt-wiredep": "^3.0.1", "load-grunt-tasks": "^3.5.0", "npm-platform-dependencies": "0.0.12", diff --git a/scripts/windows_installer.nsi b/scripts/windows_installer.nsi index 2089810d2..ab0503ae3 100755 --- a/scripts/windows_installer.nsi +++ b/scripts/windows_installer.nsi @@ -2,9 +2,10 @@ !define VERSION "0.3.0-dev" !define ARCH "win64" !define DIST "..\dist" +!define CACHE "..\cache" !define APP "${DIST}\icestudio\${ARCH}" !define PYTHON "python-2.7.13.amd64.msi" -!define PYPATH "${APP}\python\${PYTHON}" +!define PYPATH "${CACHE}\python\${PYTHON}" !define ICON "${APP}\resources\images\icestudio-logo.ico" # define name of installer From 19e419277385ec4b23e129e4ae8eba7a39979398 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Tue, 24 Jan 2017 10:22:36 +0100 Subject: [PATCH 011/124] Move .build & .cache dirs to ~/.icestudio --- app/scripts/services/tools.service.js | 17 ++++++------- app/scripts/services/utils.service.js | 36 +++++++++++++-------------- 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/app/scripts/services/tools.service.js b/app/scripts/services/tools.service.js index 8ef849af2..dc694837d 100644 --- a/app/scripts/services/tools.service.js +++ b/app/scripts/services/tools.service.js @@ -24,7 +24,6 @@ angular.module('icestudio') var toolchain = { apio: '-', installed: false, disabled: false }; this.toolchain = toolchain; - const buildPath = '_build'; // Check if the toolchain is installed checkToolchain(); @@ -33,7 +32,7 @@ angular.module('icestudio') updateToolchainInfo(); // Remove _build directory on start - nodeFse.removeSync(buildPath); + nodeFse.removeSync(utils.BUILD_DIR); this.verifyCode = function() { this.apio(['verify']); @@ -67,7 +66,7 @@ angular.module('icestudio') var message = 'start_' + commands[0]; currentAlert = alertify.notify(gettextCatalog.getString(message), 'message', 100000); $('body').addClass('waiting'); - nodeProcess.chdir(buildPath); + nodeProcess.chdir(utils.BUILD_DIR); check = this.syncResources(code); try { if (check) { @@ -130,14 +129,14 @@ angular.module('icestudio') } this.generateCode = function() { - if (!nodeFs.existsSync(buildPath)) { - nodeFs.mkdirSync(buildPath); + if (!nodeFs.existsSync(utils.BUILD_DIR)) { + nodeFs.mkdirSync(utils.BUILD_DIR); } project.update(); var verilog = compiler.generate('verilog', project.get()); var pcf = compiler.generate('pcf', project.get()); - nodeFs.writeFileSync(nodePath.join(buildPath, 'main.v'), verilog, 'utf8'); - nodeFs.writeFileSync(nodePath.join(buildPath, 'main.pcf'), pcf, 'utf8'); + nodeFs.writeFileSync(nodePath.join(utils.BUILD_DIR, 'main.v'), verilog, 'utf8'); + nodeFs.writeFileSync(nodePath.join(utils.BUILD_DIR, 'main.pcf'), pcf, 'utf8'); return verilog; }; @@ -185,7 +184,7 @@ angular.module('icestudio') currentAlert.setContent(gettextCatalog.getString('Synchronize remote files ...')); nodeRSync({ src: nodeProcess.cwd() + '/', - dest: remoteHostname + ':' + buildPath + '/', + dest: remoteHostname + ':' + utils.BUILD_DIR + '/', ssh: true, recursive: true, delete: true, @@ -194,7 +193,7 @@ angular.module('icestudio') }, function (error, stdout, stderr/*, cmd*/) { if (!error) { currentAlert.setContent(gettextCatalog.getString('Execute remote {{label}} ...', { label: label })); - nodeSSHexec('cd ' + buildPath + '; ' + (['apio'].concat(commands)).join(' '), remoteHostname, + nodeSSHexec('cd ' + utils.BUILD_DIR + '; ' + (['apio'].concat(commands)).join(' '), remoteHostname, function (error, stdout, stderr) { processExecute(label, callback, error, stdout, stderr); }); diff --git a/app/scripts/services/utils.service.js b/app/scripts/services/utils.service.js index e08240df3..b64b526c3 100644 --- a/app/scripts/services/utils.service.js +++ b/app/scripts/services/utils.service.js @@ -22,37 +22,37 @@ angular.module('icestudio') const WIN32 = Boolean(process.platform.indexOf('win32') > -1); const DARWIN = Boolean(process.platform.indexOf('darwin') > -1); - const CACHE = '_cache'; - - const VENV = 'virtualenv-15.0.1'; - const VENV_DIR = nodePath.join(CACHE, VENV); - const VENV_TARGZ = nodePath.join('resources', 'virtualenv', VENV + '.tar.gz'); - + const LOCALE_DIR = nodePath.join('resources', 'locale'); const SAMPLE_DIR = nodePath.join('resources', 'sample'); this.SAMPLE_DIR = SAMPLE_DIR; - const LOCALE_DIR = nodePath.join('resources', 'locale'); + const BASE_DIR = process.env.HOME || process.env.USERPROFILE; + const ICESTUDIO_DIR = nodePath.join(BASE_DIR, '.icestudio'); + this.ICESTUDIO_DIR = ICESTUDIO_DIR; + const COLLECTIONS_DIR = nodePath.join(ICESTUDIO_DIR, 'collections'); + this.COLLECTIONS_DIR = COLLECTIONS_DIR; + const APIO_HOME_DIR = nodePath.join(ICESTUDIO_DIR, 'apio'); + const PROFILE_PATH = nodePath.join(ICESTUDIO_DIR, 'profile.json'); + this.PROFILE_PATH = PROFILE_PATH; + const CACHE_DIR = nodePath.join(ICESTUDIO_DIR, '.cache'); + const BUILD_DIR = nodePath.join(ICESTUDIO_DIR, '.build'); + this.BUILD_DIR = BUILD_DIR; + + const VENV = 'virtualenv-15.0.1'; + const VENV_DIR = nodePath.join(CACHE_DIR, VENV); + const VENV_TARGZ = nodePath.join('resources', 'virtualenv', VENV + '.tar.gz'); const APP_DIR = nodePath.dirname(process.execPath); const TOOLCHAIN_DIR = nodePath.join(APP_DIR, 'toolchain'); this.TOOLCHAIN_DIR = TOOLCHAIN_DIR; const DEFAULT_APIO = 'default-apio'; - const DEFAULT_APIO_DIR = nodePath.join(CACHE, DEFAULT_APIO); + const DEFAULT_APIO_DIR = nodePath.join(CACHE_DIR, DEFAULT_APIO); const DEFAULT_APIO_TARGZ = nodePath.join(TOOLCHAIN_DIR, DEFAULT_APIO + '.tar.gz'); const DEFAULT_APIO_PACKAGES = 'default-apio-packages'; const DEFAULT_APIO_PACKAGES_TARGZ = nodePath.join(TOOLCHAIN_DIR, DEFAULT_APIO_PACKAGES + '.tar.gz'); - const BASE_DIR = process.env.HOME || process.env.USERPROFILE; - const ICESTUDIO_DIR = nodePath.join(BASE_DIR, '.icestudio'); - this.ICESTUDIO_DIR = ICESTUDIO_DIR; - const COLLECTIONS_DIR = nodePath.join(ICESTUDIO_DIR, 'collections'); - this.COLLECTIONS_DIR = COLLECTIONS_DIR; - const APIO_HOME_DIR = nodePath.join(ICESTUDIO_DIR, 'apio'); - const PROFILE_PATH = nodePath.join(ICESTUDIO_DIR, 'profile.json'); - this.PROFILE_PATH = PROFILE_PATH; - const ENV_DIR = _getEnvDir(nodePath.join(ICESTUDIO_DIR, 'venv')); const ENV_BIN_DIR = nodePath.join(ENV_DIR, WIN32 ? 'Scripts' : 'bin'); const ENV_PIP = nodePath.join(ENV_BIN_DIR, 'pip'); @@ -129,7 +129,7 @@ angular.module('icestudio') }; this.extractVirtualEnv = function(callback) { - this.extractTargz(VENV_TARGZ, CACHE, callback); + this.extractTargz(VENV_TARGZ, CACHE_DIR, callback); }; function disableClick(e) { From 2808891e34468d3d4992e85e24abb9e584c9e8fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Tue, 24 Jan 2017 11:13:25 +0100 Subject: [PATCH 012/124] NSIS: uninstall previous version --- scripts/windows_installer.nsi | 47 +++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/scripts/windows_installer.nsi b/scripts/windows_installer.nsi index ab0503ae3..a426f15f4 100755 --- a/scripts/windows_installer.nsi +++ b/scripts/windows_installer.nsi @@ -1,6 +1,10 @@ -!define NAME "Icestudio" -!define VERSION "0.3.0-dev" -!define ARCH "win64" +!define NAME "Icestudio" +!ifndef VERSION + !define VERSION "dev" +!endif +!ifndef ARCH + !define ARCH "win64" +!endif !define DIST "..\dist" !define CACHE "..\cache" !define APP "${DIST}\icestudio\${ARCH}" @@ -50,14 +54,38 @@ RequestExecutionLevel admin !insertmacro MUI_LANGUAGE "English" +Function .onInit + + ReadRegStr $R0 HKLM \ + "Software\Microsoft\Windows\CurrentVersion\Uninstall\${NAME}" \ + "UninstallString" + StrCmp $R0 "" done + + MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION \ + "${NAME} is already installed. $\n$\nClick OK to remove the \ + previous version or Cancel to cancel this upgrade." \ + IDOK uninst + Abort + + # run the uninstaller + uninst: + ExecWait '$R0 _?=$INSTDIR' + + done: + +FunctionEnd + + Section "Install Python" Call ValidatePythonVersion Pop $R0 ${If} $R0 != "0" - MessageBox MB_YESNO "Python 2.7.13 will be installed. Do you want to continue?" IDYES continue - Quit + MessageBox MB_YESNO \ + "Python 2.7.13 will be installed. Do you want to continue?" \ + IDYES continue + Quit continue: # define output path @@ -89,12 +117,16 @@ Section "${NAME} ${VERSION}" File /r "${APP}\" # define the uninstaller name + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${NAME}" "DisplayName" "${NAME}" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${NAME}" "UninstallString" '"$INSTDIR\uninstaller.exe"' + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${NAME}" "NoModify" 1 + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${NAME}" "NoRepair" 1 WriteUninstaller $INSTDIR\uninstaller.exe # define shortcut CreateDirectory "$SMPROGRAMS\{NAME}" CreateShortCut "$SMPROGRAMS\{NAME}\${NAME}.lnk" "$INSTDIR\icestudio.exe" "" "$INSTDIR\resources\images\icestudio-logo.ico" 0 - CreateShortCut "$SMPROGRAMS\{NAME}\Uninstall ${NAME}.lnk" "$INSTDIR\uninstaller.exe" + CreateShortCut "$SMPROGRAMS\{NAME}\Uninstall ${NAME}.lnk" "$INSTDIR\uninstaller.exe" "" "$INSTDIR\uninstaller.exe" 0 SectionEnd @@ -109,12 +141,13 @@ FunctionEnd Section "Uninstall" # delete the uninstaller + DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${NAME}" Delete $INSTDIR\uninstaller.exe # delete the installed files - RMDir /r $INSTDIR Delete "$SMPROGRAMS\{NAME}\${NAME}.lnk" Delete "$SMPROGRAMS\{NAME}\Uninstall ${NAME}.lnk" Delete "$SMPROGRAMS\{NAME}" + RMDir /r $INSTDIR SectionEnd From 32f17cdbc65608458318d1922b236eb86cb25852 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Tue, 24 Jan 2017 11:25:22 +0100 Subject: [PATCH 013/124] NSIS: select app files --- gruntfile.js | 5 ++--- scripts/windows_installer.nsi | 14 +++++++++++++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/gruntfile.js b/gruntfile.js index 04ec4e668..013bccc6f 100644 --- a/gruntfile.js +++ b/gruntfile.js @@ -12,7 +12,7 @@ module.exports = function(grunt) { else { var platforms = ['linux32', 'linux64', 'win32', 'win64']; var options = { scope: ['devDependencies'] }; - var distCommands = ['compress:linux32', 'compress:linux64', 'compress:win32', 'compress:win64']; + var distCommands = ['wget:python', 'compress:linux32', 'compress:linux64', 'compress:win32', 'compress:win64']; } function all(dir) { @@ -340,8 +340,7 @@ module.exports = function(grunt) { 'cssmin', 'usemin', 'nwjs', - 'toolchain', - 'wget:python' + 'toolchain' ] .concat(distCommands) .concat([ diff --git a/scripts/windows_installer.nsi b/scripts/windows_installer.nsi index a426f15f4..b7ce816d9 100755 --- a/scripts/windows_installer.nsi +++ b/scripts/windows_installer.nsi @@ -114,7 +114,19 @@ Section "${NAME} ${VERSION}" SetOutPath $INSTDIR # install app files - File /r "${APP}\" + File "${APP}\icestudio.exe" + File "${APP}\icudtl.dat" + File "${APP}\nw.pak" + File /r "${APP}\toolchain" + + File "${APP}\index.html" + File "${APP}\package.json" + File /r "${APP}\fonts" + File /r "${APP}\node_modules" + File /r "${APP}\resources" + File /r "${APP}\scripts" + File /r "${APP}\styles" + File /r "${APP}\views" # define the uninstaller name WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${NAME}" "DisplayName" "${NAME}" From 637795ba3231973cb731b3583121ac714d59b375 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Tue, 24 Jan 2017 12:17:36 +0100 Subject: [PATCH 014/124] Add NSIS to grunt/travis --- .travis.yml | 3 ++- gruntfile.js | 14 +++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index a7abd9274..b3097cbbe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ os: before_install: - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo dpkg --add-architecture i386; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get update -q; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install -y wine; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install -y wine nsis; fi install: - npm install @@ -25,6 +25,7 @@ deploy: file: - dist/*.zip - dist/*.dmg + - dist/*.exe file_glob: true skip_cleanup: true on: diff --git a/gruntfile.js b/gruntfile.js index 013bccc6f..30a2b699b 100644 --- a/gruntfile.js +++ b/gruntfile.js @@ -1,18 +1,16 @@ module.exports = function(grunt) { var os = require('os'); - // Load apio info - var _package = require('./package.json'); const DARWIN = Boolean(os.platform().indexOf('darwin') > -1); if (DARWIN) { var platforms = ['osx32', 'osx64']; var options = { scope: ['devDependencies', 'darwinDependencies'] }; - var distCommands = ['appdmg', 'compress:osx32', 'compress:osx64']; + var distCommands = ['compress:osx32', 'compress:osx64', 'appdmg']; } else { var platforms = ['linux32', 'linux64', 'win32', 'win64']; var options = { scope: ['devDependencies'] }; - var distCommands = ['wget:python', 'compress:linux32', 'compress:linux64', 'compress:win32', 'compress:win64']; + var distCommands = ['compress:linux32', 'compress:linux64', 'compress:win32', 'compress:win64', 'wget:python', 'exec:nsis32', 'exec:nsis64']; } function all(dir) { @@ -51,7 +49,9 @@ module.exports = function(grunt) { // Execute nw application exec: { nw: 'nw app', - stop_NW: 'killall nw || killall nwjs || taskkill /F /IM nw.exe || (exit 0)' + stop_NW: 'killall nw || killall nwjs || taskkill /F /IM nw.exe || (exit 0)', + nsis32: 'makensis -DARCH=win32 -DVERSION=<%=pkg.version%> scripts/windows_installer.nsi', + nsis64: 'makensis -DARCH=win64 -DVERSION=<%=pkg.version%> scripts/windows_installer.nsi' }, // Reads HTML for usemin blocks to enable smart builds that automatically @@ -130,8 +130,8 @@ module.exports = function(grunt) { // Create standalone toolchains for each platform toolchain: { options: { - apioMin: _package.apio.min, - apioMax: _package.apio.max, + apioMin: '<%=pkg.apio.min%>', + apioMax: '<%=pkg.apio.max%>', buildDir: 'dist/', platforms: platforms } From 2712591439c2c8a2372cc73ef94fbd987a2e3743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Tue, 24 Jan 2017 14:38:17 +0100 Subject: [PATCH 015/124] Register .ice files in Windows installer --- app/package.json | 2 +- package.json | 2 +- scripts/FileAssociation.nsh | 190 ++++++++++++++++++++++++++++++++++ scripts/windows_installer.nsi | 31 ++++-- 4 files changed, 214 insertions(+), 11 deletions(-) create mode 100644 scripts/FileAssociation.nsh diff --git a/app/package.json b/app/package.json index d8eaf2420..d92d92629 100644 --- a/app/package.json +++ b/app/package.json @@ -1,6 +1,6 @@ { "name": "icestudio", - "version": "0.3.0-beta2", + "version": "0.3.0-beta3-dev", "description": "Experimental graphic editor for open FPGAs", "author": "Jesús Arroyo Torrens ", "repository": "https://github.com/FPGAwars/icestudio", diff --git a/package.json b/package.json index ae2c56388..27c7d2244 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "icestudio", - "version": "0.3.0-beta2", + "version": "0.3.0-beta3-dev", "description": "Experimental graphic editor for open FPGAs", "author": "Jesús Arroyo Torrens ", "repository": "https://github.com/FPGAwars/icestudio", diff --git a/scripts/FileAssociation.nsh b/scripts/FileAssociation.nsh new file mode 100644 index 000000000..cc2f98583 --- /dev/null +++ b/scripts/FileAssociation.nsh @@ -0,0 +1,190 @@ +/* +_____________________________________________________________________________ + + File Association +_____________________________________________________________________________ + + Based on code taken from http://nsis.sourceforge.net/File_Association + + Usage in script: + 1. !include "FileAssociation.nsh" + 2. [Section|Function] + ${FileAssociationFunction} "Param1" "Param2" "..." $var + [SectionEnd|FunctionEnd] + + FileAssociationFunction=[RegisterExtension|UnRegisterExtension] + +_____________________________________________________________________________ + + ${RegisterExtension} "[executable]" "[extension]" "[description]" + +"[executable]" ; executable which opens the file format + ; +"[extension]" ; extension, which represents the file format to open + ; +"[description]" ; description for the extension. This will be display in Windows Explorer. + ; + + + ${UnRegisterExtension} "[extension]" "[description]" + +"[extension]" ; extension, which represents the file format to open + ; +"[description]" ; description for the extension. This will be display in Windows Explorer. + ; + +_____________________________________________________________________________ + + Macros +_____________________________________________________________________________ + + Change log window verbosity (default: 3=no script) + + Example: + !include "FileAssociation.nsh" + !insertmacro RegisterExtension + ${FileAssociation_VERBOSE} 4 # all verbosity + !insertmacro UnRegisterExtension + ${FileAssociation_VERBOSE} 3 # no script +*/ + + +!ifndef FileAssociation_INCLUDED +!define FileAssociation_INCLUDED + +!include Util.nsh + +!verbose push +!verbose 3 +!ifndef _FileAssociation_VERBOSE + !define _FileAssociation_VERBOSE 3 +!endif +!verbose ${_FileAssociation_VERBOSE} +!define FileAssociation_VERBOSE `!insertmacro FileAssociation_VERBOSE` +!verbose pop + +!macro FileAssociation_VERBOSE _VERBOSE + !verbose push + !verbose 3 + !undef _FileAssociation_VERBOSE + !define _FileAssociation_VERBOSE ${_VERBOSE} + !verbose pop +!macroend + + + +!macro RegisterExtensionCall _EXECUTABLE _EXTENSION _DESCRIPTION + !verbose push + !verbose ${_FileAssociation_VERBOSE} + Push `${_DESCRIPTION}` + Push `${_EXTENSION}` + Push `${_EXECUTABLE}` + ${CallArtificialFunction} RegisterExtension_ + !verbose pop +!macroend + +!macro UnRegisterExtensionCall _EXTENSION _DESCRIPTION + !verbose push + !verbose ${_FileAssociation_VERBOSE} + Push `${_EXTENSION}` + Push `${_DESCRIPTION}` + ${CallArtificialFunction} UnRegisterExtension_ + !verbose pop +!macroend + + + +!define RegisterExtension `!insertmacro RegisterExtensionCall` +!define un.RegisterExtension `!insertmacro RegisterExtensionCall` + +!macro RegisterExtension +!macroend + +!macro un.RegisterExtension +!macroend + +!macro RegisterExtension_ + !verbose push + !verbose ${_FileAssociation_VERBOSE} + + Exch $R2 ;exe + Exch + Exch $R1 ;ext + Exch + Exch 2 + Exch $R0 ;desc + Exch 2 + Push $0 + Push $1 + + ReadRegStr $1 HKCR $R1 "" ; read current file association + StrCmp "$1" "" NoBackup ; is it empty + StrCmp "$1" "$R0" NoBackup ; is it our own + WriteRegStr HKCR $R1 "backup_val" "$1" ; backup current value +NoBackup: + WriteRegStr HKCR $R1 "" "$R0" ; set our file association + + ReadRegStr $0 HKCR $R0 "" + StrCmp $0 "" 0 Skip + WriteRegStr HKCR "$R0" "" "$R0" + WriteRegStr HKCR "$R0\shell" "" "open" + WriteRegStr HKCR "$R0\DefaultIcon" "" "$R2,0" +Skip: + WriteRegStr HKCR "$R0\shell\open\command" "" '"$R2" "%1"' + WriteRegStr HKCR "$R0\shell\edit" "" "Edit $R0" + WriteRegStr HKCR "$R0\shell\edit\command" "" '"$R2" "%1"' + + Pop $1 + Pop $0 + Pop $R2 + Pop $R1 + Pop $R0 + + !verbose pop +!macroend + + + +!define UnRegisterExtension `!insertmacro UnRegisterExtensionCall` +!define un.UnRegisterExtension `!insertmacro UnRegisterExtensionCall` + +!macro UnRegisterExtension +!macroend + +!macro un.UnRegisterExtension +!macroend + +!macro UnRegisterExtension_ + !verbose push + !verbose ${_FileAssociation_VERBOSE} + + Exch $R1 ;desc + Exch + Exch $R0 ;ext + Exch + Push $0 + Push $1 + + ReadRegStr $1 HKCR $R0 "" + StrCmp $1 $R1 0 NoOwn ; only do this if we own it + ReadRegStr $1 HKCR $R0 "backup_val" + StrCmp $1 "" 0 Restore ; if backup="" then delete the whole key + DeleteRegKey HKCR $R0 + Goto NoOwn + +Restore: + WriteRegStr HKCR $R0 "" $1 + DeleteRegValue HKCR $R0 "backup_val" + DeleteRegKey HKCR $R1 ;Delete key with association name settings + +NoOwn: + + Pop $1 + Pop $0 + Pop $R1 + Pop $R0 + + !verbose pop +!macroend + +!endif # !FileAssociation_INCLUDED diff --git a/scripts/windows_installer.nsi b/scripts/windows_installer.nsi index b7ce816d9..fe42f0b65 100755 --- a/scripts/windows_installer.nsi +++ b/scripts/windows_installer.nsi @@ -28,7 +28,8 @@ RequestExecutionLevel admin !include MUI2.nsh -!include Library.nsh +!include FileFunc.nsh +!include FileAssociation.nsh !define MUI_ICON "${ICON}" !define MUI_BGCOLOR FFFFFF @@ -89,7 +90,7 @@ Section "Install Python" continue: # define output path - SetOutPath $INSTDIR\python + SetOutPath "$INSTDIR\python" # copy Python msi File ${PYPATH} @@ -133,19 +134,26 @@ Section "${NAME} ${VERSION}" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${NAME}" "UninstallString" '"$INSTDIR\uninstaller.exe"' WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${NAME}" "NoModify" 1 WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${NAME}" "NoRepair" 1 - WriteUninstaller $INSTDIR\uninstaller.exe + WriteUninstaller "$INSTDIR\uninstaller.exe" + + # write start menu entries for all users + SetShellVarContext all # define shortcut CreateDirectory "$SMPROGRAMS\{NAME}" - CreateShortCut "$SMPROGRAMS\{NAME}\${NAME}.lnk" "$INSTDIR\icestudio.exe" "" "$INSTDIR\resources\images\icestudio-logo.ico" 0 - CreateShortCut "$SMPROGRAMS\{NAME}\Uninstall ${NAME}.lnk" "$INSTDIR\uninstaller.exe" "" "$INSTDIR\uninstaller.exe" 0 + CreateShortCut "$SMPROGRAMS\{NAME}\${NAME}.lnk" "$INSTDIR\icestudio.exe" + CreateShortCut "$SMPROGRAMS\{NAME}\Uninstall ${NAME}.lnk" "$INSTDIR\uninstaller.exe" + + # register .ice files + ${registerExtension} "$INSTDIR\icestudio.exe" ".ice" "Icestudio project" + ${RefreshShellIcons} SectionEnd Function LaunchLink - Exec $INSTDIR\icestudio.exe + Exec "$INSTDIR\icestudio.exe" FunctionEnd @@ -154,12 +162,17 @@ Section "Uninstall" # delete the uninstaller DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${NAME}" - Delete $INSTDIR\uninstaller.exe + Delete "$INSTDIR\uninstaller.exe" + + # write start menu entries for all users + SetShellVarContext all # delete the installed files - Delete "$SMPROGRAMS\{NAME}\${NAME}.lnk" - Delete "$SMPROGRAMS\{NAME}\Uninstall ${NAME}.lnk" Delete "$SMPROGRAMS\{NAME}" RMDir /r $INSTDIR + # unregister .ice files + ${unregisterExtension} ".ice" "Icestudio project" + ${RefreshShellIcons} + SectionEnd From ed451a07afe9af1787eaadbd8d4e8203a9129edd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Tue, 24 Jan 2017 20:42:47 +0100 Subject: [PATCH 016/124] Add Initialize rules --- app/resources/boards/icezum/rules.json | 36 +++++++++ app/scripts/services/boards.service.js | 4 +- app/scripts/services/compiler.service.js | 98 ++++++++++++++++++++---- 3 files changed, 124 insertions(+), 14 deletions(-) create mode 100644 app/resources/boards/icezum/rules.json diff --git a/app/resources/boards/icezum/rules.json b/app/resources/boards/icezum/rules.json new file mode 100644 index 000000000..04ded3287 --- /dev/null +++ b/app/resources/boards/icezum/rules.json @@ -0,0 +1,36 @@ +{ + "initialize": [ + { + "pin": "95", + "bit": "0" + }, + { + "pin": "96", + "bit": "0" + }, + { + "pin": "97", + "bit": "0" + }, + { + "pin": "98", + "bit": "0" + }, + { + "pin": "99", + "bit": "0" + }, + { + "pin": "101", + "bit": "0" + }, + { + "pin": "102", + "bit": "0" + }, + { + "pin": "104", + "bit": "0" + } + ] +} diff --git a/app/scripts/services/boards.service.js b/app/scripts/services/boards.service.js index 4fb0c7e2d..fd65ab02c 100644 --- a/app/scripts/services/boards.service.js +++ b/app/scripts/services/boards.service.js @@ -19,10 +19,12 @@ angular.module('icestudio') if (!content.startsWith('_')) { var info = readJSONFile(contentPath, 'info.json'); var pinout = readJSONFile(contentPath, 'pinout.json'); + var rules = readJSONFile(contentPath, 'rules.json'); boards.push({ 'name': content, 'info': info, - 'pinout': pinout + 'pinout': pinout, + 'rules': rules }); } } diff --git a/app/scripts/services/compiler.service.js b/app/scripts/services/compiler.service.js index de706c7f1..478aab1a2 100644 --- a/app/scripts/services/compiler.service.js +++ b/app/scripts/services/compiler.service.js @@ -1,7 +1,8 @@ 'use strict'; angular.module('icestudio') - .service('compiler', function(nodeSha1, + .service('compiler', function(boards, + nodeSha1, _package) { this.generate = function(target, project) { @@ -321,6 +322,30 @@ angular.module('icestudio') return null; } + function getInitPins(blocks) { + var initPins = []; + var usedPins = []; + + for (var i in blocks) { + var block = blocks[i]; + if (block.type === 'basic.input' || + block.type === 'basic.output') { + for (var p in block.data.pins) { + usedPins.push(block.data.virtual ? '' : block.data.pins[p].value); + } + } + } + + var allInitPins = boards.selectedBoard.rules.initialize; + for (var j in allInitPins) { + if (usedPins.indexOf(allInitPins[j].pin) === -1) { + initPins.push(allInitPins[j]); + } + } + + return initPins; + } + function verilogCompiler(name, project) { var data; var code = ''; @@ -329,17 +354,45 @@ angular.module('icestudio') project.design && project.design.graph) { - var graph = project.design.graph; + var blocks = project.design.graph.blocks; var dependencies = project.dependencies; // Main module if (name) { + + var params = getParams(project); + var ports = getPorts(project); + var content = getContent(name, project); + + if (name === 'main') { + + // Initialize pins + + var initPins = getInitPins(blocks); + var n = initPins.length; + + if (n > 0) { + // Declare m port + ports.out.push({ + name: 'm', + range: '[0:' + (n-1) + ']' + }); + // Generate port value + var value = n.toString() + '\'b'; + for (var j in initPins) { + value += initPins[j].bit; + } + // Assign m port + content += '\nassign m = ' + value + ';'; + } + } + data = { name: name, - params: getParams(project), - ports: getPorts(project), - content: getContent(name, project) + params: params, + ports: ports, + content: content }; code += module(data); } @@ -352,8 +405,8 @@ angular.module('icestudio') // Code modules - for (var i in graph.blocks) { - var block = graph.blocks[i]; + for (var i in blocks) { + var block = blocks[i]; if (block) { if (block.type === 'basic.code') { data = { @@ -373,33 +426,52 @@ angular.module('icestudio') function pcfCompiler(project) { var code = ''; - var graph = project.design.graph; + var blocks = project.design.graph.blocks; + var pin, value; - for (var i in graph.blocks) { - var block = graph.blocks[i]; + for (var i in blocks) { + var block = blocks[i]; if (block.type === 'basic.input' || block.type === 'basic.output') { if (block.data.pins.length > 1) { for (var p in block.data.pins) { - var pin = block.data.pins[p]; + pin = block.data.pins[p]; + value = block.data.virtual ? '' : pin.value; code += 'set_io '; code += digestId(block.id); code += '[' + pin.index + '] '; - code += block.data.virtual ? '' : pin.value; + code += value; code += '\n'; } } else { + pin = block.data.pins[0]; + value = block.data.virtual ? '' : pin.value; code += 'set_io '; code += digestId(block.id); code += ' '; - code += block.data.virtual ? '' : block.data.pins[0].value; + code += value; code += '\n'; } } } + // Set port of initialized pins + var initPins = getInitPins(blocks); + if (initPins.length > 1) { + for (var j in initPins) { + code += 'set_io m[' + j + '] '; + code += initPins[j].pin; + code += '\n'; + } + } + else { + code += 'set_io m '; + code += initPins[0].pin; + code += '\n'; + } + return code; } From 8a8d004048df6593312561e57f3d9b77a1cc57e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Wed, 25 Jan 2017 02:31:17 +0100 Subject: [PATCH 017/124] Cover paths with " to avoid spaces --- app/scripts/services/utils.service.js | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/app/scripts/services/utils.service.js b/app/scripts/services/utils.service.js index b64b526c3..d9f71107c 100644 --- a/app/scripts/services/utils.service.js +++ b/app/scripts/services/utils.service.js @@ -57,7 +57,7 @@ angular.module('icestudio') const ENV_BIN_DIR = nodePath.join(ENV_DIR, WIN32 ? 'Scripts' : 'bin'); const ENV_PIP = nodePath.join(ENV_BIN_DIR, 'pip'); const ENV_APIO = nodePath.join(ENV_BIN_DIR, WIN32 ? 'apio.exe' : 'apio'); - const APIO_CMD = (WIN32 ? 'set' : 'export') + ' APIO_HOME_DIR=' + APIO_HOME_DIR + (WIN32 ? '& ' : '; ') + ENV_APIO; + const APIO_CMD = (WIN32 ? 'set' : 'export') + ' APIO_HOME_DIR=' + APIO_HOME_DIR + (WIN32 ? '& ' : '; ') + coverPath(ENV_APIO); const SYSTEM_APIO = '/usr/bin/apio'; function _getEnvDir(defaultEnvDir) { @@ -178,7 +178,9 @@ angular.module('icestudio') if (!nodeFs.existsSync(ENV_DIR)) { nodeFs.mkdirSync(ENV_DIR); this.executeCommand( - [this.getPythonExecutable(), nodePath.join(VENV_DIR, 'virtualenv.py'), ENV_DIR], callback); + [this.getPythonExecutable(), + coverPath(nodePath.join(VENV_DIR, 'virtualenv.py')), + coverPath(ENV_DIR)], callback); } else { callback(); @@ -203,7 +205,8 @@ angular.module('icestudio') var self = this; nodeGlob(nodePath.join(DEFAULT_APIO_DIR, '*.*'), {}, function (error, files) { if (!error) { - self.executeCommand([ENV_PIP, 'install', '-U', '--no-deps'].concat(files), callback); + files = files.map(function(item) { return coverPath(item); }); + self.executeCommand([coverPath(ENV_PIP), 'install', '-U', '--no-deps'].concat(files), callback); } }); }; @@ -227,7 +230,7 @@ angular.module('icestudio') }; this.installOnlineApio = function(callback) { - this.executeCommand([ENV_PIP, 'install', '-U', 'apio">=' + _package.apio.min + ',<' + _package.apio.max + '"'], callback); + this.executeCommand([coverPath(ENV_PIP), 'install', '-U', 'apio">=' + _package.apio.min + ',<' + _package.apio.max + '"'], callback); }; this.apioInstall = function(_package, callback) { @@ -244,7 +247,7 @@ angular.module('icestudio') alertify.notify('Using system wide apio', 'message', 5); } this.toolchainDisabled = true; - return candidateApio; + return coverPath(candidateApio); } this.toolchainDisabled = false; return APIO_CMD; @@ -925,12 +928,12 @@ angular.module('icestudio') this.newWindow = function(filepath) { var execPath = process.execPath; - var command = [ '"' + execPath + '"' ]; + var command = [ coverPath(execPath) ]; if (execPath.endsWith('nw') || execPath.endsWith('nw.exe') || execPath.endsWith('nwjs Helper')) { - command.push('"' + nodePath.dirname(process.mainModule.filename) + '"'); + command.push(coverPath(nodePath.dirname(process.mainModule.filename))); } if (filepath) { - command.push('"' + filepath + '"'); + command.push(coverPath(filepath)); } /*var win = window.get(); var position = { @@ -945,4 +948,8 @@ angular.module('icestudio') }); }; + function coverPath(filepath) { + return '"' + filepath + '"'; + } + }); From 44584d7077ee1151695f8d02a45689aa005554ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Wed, 25 Jan 2017 04:41:03 +0100 Subject: [PATCH 018/124] Using new apio --project-dir argument --- app/package.json | 4 ++-- app/scripts/factories/node.factory.js | 3 --- app/scripts/services/tools.service.js | 21 ++++++++------------- app/scripts/services/utils.service.js | 1 + package.json | 4 ++-- 5 files changed, 13 insertions(+), 20 deletions(-) diff --git a/app/package.json b/app/package.json index d92d92629..93421624e 100644 --- a/app/package.json +++ b/app/package.json @@ -18,8 +18,8 @@ "icon": "resources/images/icestudio-logo.png" }, "apio": { - "min": "0.2.0", - "max": "0.2.1" + "min": "0.2.1", + "max": "0.2.2" }, "engines": { "node": ">= 0.10.0" diff --git a/app/scripts/factories/node.factory.js b/app/scripts/factories/node.factory.js index 77f98ea69..bc6242951 100644 --- a/app/scripts/factories/node.factory.js +++ b/app/scripts/factories/node.factory.js @@ -22,9 +22,6 @@ angular.module('icestudio') .factory('nodeChildProcess', function() { return require('child_process'); }) - .factory('nodeProcess', function() { - return require('process'); - }) .factory('nodeTarball', function() { return require('tarball-extract'); }) diff --git a/app/scripts/services/tools.service.js b/app/scripts/services/tools.service.js index dc694837d..d0eb39859 100644 --- a/app/scripts/services/tools.service.js +++ b/app/scripts/services/tools.service.js @@ -13,7 +13,6 @@ angular.module('icestudio') nodeFse, nodeOs, nodePath, - nodeProcess, nodeChildProcess, nodeSSHexec, nodeRSync, @@ -31,7 +30,7 @@ angular.module('icestudio') // Update toolchain information updateToolchainInfo(); - // Remove _build directory on start + // Remove build directory on start nodeFse.removeSync(utils.BUILD_DIR); this.verifyCode = function() { @@ -39,11 +38,11 @@ angular.module('icestudio') }; this.buildCode = function() { - this.apio(['build', '--board', boards.selectedBoard.name]); + this.apio(['build', '-b', boards.selectedBoard.name]); }; this.uploadCode = function() { - this.apio(['upload', '--board', boards.selectedBoard.name]); + this.apio(['upload', '-b', boards.selectedBoard.name]); }; this.apio = function(commands) { @@ -66,7 +65,6 @@ angular.module('icestudio') var message = 'start_' + commands[0]; currentAlert = alertify.notify(gettextCatalog.getString(message), 'message', 100000); $('body').addClass('waiting'); - nodeProcess.chdir(utils.BUILD_DIR); check = this.syncResources(code); try { if (check) { @@ -92,7 +90,6 @@ angular.module('icestudio') catch(e) { } finally { - nodeProcess.chdir('..'); } } else { @@ -104,9 +101,7 @@ angular.module('icestudio') function checkToolchain(callback) { var apio = utils.getApioExecutable(); toolchain.disabled = utils.toolchainDisabled; - nodeChildProcess.exec([ - 'cd', utils.SAMPLE_DIR, (process.platform === 'win32' ? '&' : ';'), - apio, 'clean'].join(' '), function(error/*, stdout, stderr*/) { + nodeChildProcess.exec([apio, 'clean', '-p', utils.SAMPLE_DIR].join(' '), function(error/*, stdout, stderr*/) { if (!toolchain.disabled) { toolchain.installed = !error; if (callback) { @@ -183,8 +178,8 @@ angular.module('icestudio') if (remoteHostname) { currentAlert.setContent(gettextCatalog.getString('Synchronize remote files ...')); nodeRSync({ - src: nodeProcess.cwd() + '/', - dest: remoteHostname + ':' + utils.BUILD_DIR + '/', + src: utils.BUILD_DIR + '/', + dest: remoteHostname + ':.build/', ssh: true, recursive: true, delete: true, @@ -193,7 +188,7 @@ angular.module('icestudio') }, function (error, stdout, stderr/*, cmd*/) { if (!error) { currentAlert.setContent(gettextCatalog.getString('Execute remote {{label}} ...', { label: label })); - nodeSSHexec('cd ' + utils.BUILD_DIR + '; ' + (['apio'].concat(commands)).join(' '), remoteHostname, + nodeSSHexec((['apio'].concat(commands).concat(['-p', '.build'])).join(' '), remoteHostname, function (error, stdout, stderr) { processExecute(label, callback, error, stdout, stderr); }); @@ -206,7 +201,7 @@ angular.module('icestudio') else { var apio = utils.getApioExecutable(); toolchain.disabled = utils.toolchainDisabled; - nodeChildProcess.exec(([apio].concat(commands)).join(' '), { maxBuffer: 5000 * 1024 }, + nodeChildProcess.exec(([apio].concat(commands).concat(['-p', utils.coverPath(utils.BUILD_DIR)])).join(' '), { maxBuffer: 5000 * 1024 }, function(error, stdout, stderr) { // console.log(error, stdout, stderr); processExecute(label, callback, error, stdout, stderr); diff --git a/app/scripts/services/utils.service.js b/app/scripts/services/utils.service.js index d9f71107c..f58830ce7 100644 --- a/app/scripts/services/utils.service.js +++ b/app/scripts/services/utils.service.js @@ -948,6 +948,7 @@ angular.module('icestudio') }); }; + this.coverPath = coverPath; function coverPath(filepath) { return '"' + filepath + '"'; } diff --git a/package.json b/package.json index 27c7d2244..c314fa55a 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,8 @@ "postinstall": "npmpd && cd app && npm install && bower install && cd ../tasks && npm install" }, "apio": { - "min": "0.2.0", - "max": "0.2.1" + "min": "0.2.1", + "max": "0.2.2" }, "devDependencies": { "bower": "^1.8.0", From c2fa15a9f4286992325dd8797633c38a17329276 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Wed, 25 Jan 2017 13:09:51 +0100 Subject: [PATCH 019/124] Check valid apio version --- app/package.json | 1 + app/scripts/factories/node.factory.js | 3 ++ app/scripts/services/tools.service.js | 61 +++++++++++++++------------ app/views/menu.html | 4 +- 4 files changed, 41 insertions(+), 28 deletions(-) diff --git a/app/package.json b/app/package.json index 93421624e..cd914263a 100644 --- a/app/package.json +++ b/app/package.json @@ -35,6 +35,7 @@ "path": "^0.12.7", "rmdir": "^1.2.0", "rsyncwrapper": "^2.0.0", + "semver": "^5.3.0", "sha1": "^1.1.1", "ssh-exec": "^2.0.0", "sudo-prompt": "^6.2.0", diff --git a/app/scripts/factories/node.factory.js b/app/scripts/factories/node.factory.js index bc6242951..3b9c844b7 100644 --- a/app/scripts/factories/node.factory.js +++ b/app/scripts/factories/node.factory.js @@ -49,6 +49,9 @@ angular.module('icestudio') .factory('nodeExtract', function() { return require('extract-zip'); }) + .factory('nodeSemver', function() { + return require('semver'); + }) .factory('SVGO', function() { return require('svgo'); }); diff --git a/app/scripts/services/tools.service.js b/app/scripts/services/tools.service.js index d0eb39859..59f85e385 100644 --- a/app/scripts/services/tools.service.js +++ b/app/scripts/services/tools.service.js @@ -16,7 +16,9 @@ angular.module('icestudio') nodeChildProcess, nodeSSHexec, nodeRSync, - nodeExtract) { + nodeExtract, + nodeSemver, + _package) { var currentAlert = null; var taskRunning = false; @@ -27,9 +29,6 @@ angular.module('icestudio') // Check if the toolchain is installed checkToolchain(); - // Update toolchain information - updateToolchainInfo(); - // Remove build directory on start nodeFse.removeSync(utils.BUILD_DIR); @@ -89,38 +88,49 @@ angular.module('icestudio') } catch(e) { } - finally { - } } else { alertify.notify(gettextCatalog.getString('Toolchain not installed. Please, install the toolchain'), 'error', 30); + taskRunning = false; } } + else { + taskRunning = false; + } }; function checkToolchain(callback) { var apio = utils.getApioExecutable(); toolchain.disabled = utils.toolchainDisabled; - nodeChildProcess.exec([apio, 'clean', '-p', utils.SAMPLE_DIR].join(' '), function(error/*, stdout, stderr*/) { - if (!toolchain.disabled) { - toolchain.installed = !error; + if (!toolchain.disabled) { + nodeChildProcess.exec([apio, '--version'].join(' '), function(error, stdout/*, stderr*/) { + if (error) { + toolchain.apio = ''; + toolchain.installed = false; if (callback) { - callback(toolchain.installed); + callback(); + } + } + else { + toolchain.apio = stdout.match(/apio,\sversion\s(.+)/i)[1]; + toolchain.installed = nodeSemver.gte(toolchain.apio, _package.apio.min) && + nodeSemver.lt(toolchain.apio, _package.apio.max); + if (toolchain.installed) { + nodeChildProcess.exec([apio, 'clean', '-p', utils.SAMPLE_DIR].join(' '), function(error/*, stdout, stderr*/) { + toolchain.installed = !error; + if (callback) { + callback(); + } + }); + } + else { + if (callback) { + callback(); + } } } }); - } - - function updateToolchainInfo() { - var apio = utils.getApioExecutable(); - nodeChildProcess.exec([apio, '--version'].join(' '), function(error, stdout/*, stderr*/) { - if (error) { - toolchain.apio = '-'; - } - else { - toolchain.apio = stdout.match(/apio,\sversion\s(.+)/i)[1]; - } - }); + } } this.generateCode = function() { @@ -203,7 +213,6 @@ angular.module('icestudio') toolchain.disabled = utils.toolchainDisabled; nodeChildProcess.exec(([apio].concat(commands).concat(['-p', utils.coverPath(utils.BUILD_DIR)])).join(' '), { maxBuffer: 5000 * 1024 }, function(error, stdout, stderr) { - // console.log(error, stdout, stderr); processExecute(label, callback, error, stdout, stderr); }); } @@ -337,6 +346,7 @@ angular.module('icestudio') alertify.confirm(gettextCatalog.getString('The toolchain will be removed. Do you want to continue?'), function() { utils.removeToolchain(); + toolchain.apio = ''; toolchain.installed = false; alertify.success(gettextCatalog.getString('Toolchain removed')); }); @@ -513,11 +523,10 @@ angular.module('icestudio') } function installationCompleted(callback) { - checkToolchain(function(installed) { - if (installed) { + checkToolchain(function() { + if (toolchain.installed) { updateProgress(gettextCatalog.getString('Installation completed'), 100); alertify.success(gettextCatalog.getString('Toolchain installed')); - updateToolchainInfo(); } else { errorProgress(gettextCatalog.getString('Toolchain not installed')); diff --git a/app/views/menu.html b/app/views/menu.html index dd448c97b..6ee48ba17 100644 --- a/app/views/menu.html +++ b/app/views/menu.html @@ -245,8 +245,8 @@ {{ 'Reset default' | translate }} -
  • -
  • +
  • +
  • {{ 'Apio ' + toolchain.apio }}
  • From 73e6c642b1c80c4fdd46b26428cb4c8a8e02b90a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Wed, 25 Jan 2017 15:33:40 +0100 Subject: [PATCH 020/124] Fix Sync files --- app/scripts/services/tools.service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/scripts/services/tools.service.js b/app/scripts/services/tools.service.js index 59f85e385..958856f03 100644 --- a/app/scripts/services/tools.service.js +++ b/app/scripts/services/tools.service.js @@ -167,7 +167,7 @@ angular.module('icestudio') var match; while (match = pattern.exec(code)) { var file = match[1]; - var destPath = nodePath.join('.', file); + var destPath = nodePath.join(utils.BUILD_DIR, file); var origPath = nodePath.join(utils.dirname(project.path), file); // Copy included file From 2b32a0a0f628b839ec21e1ba258df1597606a115 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Wed, 25 Jan 2017 16:26:39 +0100 Subject: [PATCH 021/124] Optimize initPins detection --- app/scripts/services/compiler.service.js | 15 ++++++++------- app/scripts/services/project.service.js | 2 +- app/scripts/services/tools.service.js | 7 +++++-- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/app/scripts/services/compiler.service.js b/app/scripts/services/compiler.service.js index 478aab1a2..d76acdd21 100644 --- a/app/scripts/services/compiler.service.js +++ b/app/scripts/services/compiler.service.js @@ -5,17 +5,17 @@ angular.module('icestudio') nodeSha1, _package) { - this.generate = function(target, project) { + this.generate = function(target, project, opt) { var code = ''; switch(target) { case 'verilog': code += header('//'); code += '`default_nettype none\n'; - code += verilogCompiler('main', project); + code += verilogCompiler('main', project, opt); break; case 'pcf': code += header('#'); - code += pcfCompiler(project); + code += pcfCompiler(project, opt); break; case 'testbench': code += header('//'); @@ -322,6 +322,7 @@ angular.module('icestudio') return null; } + this.getInitPins = getInitPins; function getInitPins(blocks) { var initPins = []; var usedPins = []; @@ -346,7 +347,7 @@ angular.module('icestudio') return initPins; } - function verilogCompiler(name, project) { + function verilogCompiler(name, project, opt) { var data; var code = ''; @@ -369,7 +370,7 @@ angular.module('icestudio') // Initialize pins - var initPins = getInitPins(blocks); + var initPins = opt && opt.initPins || getInitPins(blocks); var n = initPins.length; if (n > 0) { @@ -424,7 +425,7 @@ angular.module('icestudio') return code; } - function pcfCompiler(project) { + function pcfCompiler(project, opt) { var code = ''; var blocks = project.design.graph.blocks; var pin, value; @@ -458,7 +459,7 @@ angular.module('icestudio') } // Set port of initialized pins - var initPins = getInitPins(blocks); + var initPins = opt && opt.initPins || getInitPins(blocks); if (initPins.length > 1) { for (var j in initPins) { code += 'set_io m[' + j + '] '; diff --git a/app/scripts/services/project.service.js b/app/scripts/services/project.service.js index 2fad3f39b..c52d63441 100644 --- a/app/scripts/services/project.service.js +++ b/app/scripts/services/project.service.js @@ -488,7 +488,7 @@ angular.module('icestudio') this.name = name; graph.resetBreadcrumbs(name); } - var title = (this.changed ? '●' : '') + this.name + ' ─ Icestudio'; + var title = (this.changed ? '*' : '') + this.name + ' ─ Icestudio'; utils.updateWindowTitle(title); }; diff --git a/app/scripts/services/tools.service.js b/app/scripts/services/tools.service.js index dc694837d..f1df4474c 100644 --- a/app/scripts/services/tools.service.js +++ b/app/scripts/services/tools.service.js @@ -133,8 +133,11 @@ angular.module('icestudio') nodeFs.mkdirSync(utils.BUILD_DIR); } project.update(); - var verilog = compiler.generate('verilog', project.get()); - var pcf = compiler.generate('pcf', project.get()); + var blocks = project.get('design').graph.blocks; + var initPins = compiler.getInitPins(blocks); + var opt = { initPins: initPins }; + var verilog = compiler.generate('verilog', project.get(), opt); + var pcf = compiler.generate('pcf', project.get(), opt); nodeFs.writeFileSync(nodePath.join(utils.BUILD_DIR, 'main.v'), verilog, 'utf8'); nodeFs.writeFileSync(nodePath.join(utils.BUILD_DIR, 'main.pcf'), pcf, 'utf8'); return verilog; From 8a156ad54a3c6c5be89ee3d000a2a01b6c4c134e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Wed, 25 Jan 2017 18:25:21 +0100 Subject: [PATCH 022/124] Add input/output information to the pinouts --- app/resources/boards/generator.py | 11 +++--- app/resources/boards/icezum/pinout.json | 2 +- app/resources/boards/icezum/pinout.pcf | 44 ++++++++++++------------ app/scripts/services/compiler.service.js | 4 +-- 4 files changed, 32 insertions(+), 29 deletions(-) diff --git a/app/resources/boards/generator.py b/app/resources/boards/generator.py index e7feb28ad..2a4c99c87 100755 --- a/app/resources/boards/generator.py +++ b/app/resources/boards/generator.py @@ -1,10 +1,12 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # -- This script generates a board json file from a pcf -# -- Author Jesús Arroyo (C) 2016 +# -- Author Jesús Arroyo (C) 2016-2017 # -- Licence GPLv2 -# set_io --warn-no-port LED1 C8 -> { "name": "LED1", "value": "C8" }, +# set_io LED0 95 # output -> { "name": "LED0", "value": "95", "type": "output" } +# set_io --warn-no-port SW1 10 # input -> { "name": "SW1", "value": "10", "type": "input" } +# set_io --warn-no-port D13 144 -> { "name": "D13", "value": "144", "type": "inout" } import os import re @@ -23,7 +25,7 @@ name = input('Insert board name: ') # eg. icoboard # Regex pattern -pattern = 'set_io\s--warn-no-port\s(?P.*?)\s+(?P.*?)\s' +pattern = 'set_io\s+(--warn-no-port)?\s*(.*?)\s+(.*?)\s+(#+\s+(input|output))?' # Open file with open(os.path.join(name, 'pinout.pcf')) as file: @@ -31,9 +33,10 @@ # Build json pinout = re.findall(pattern, data) + print(pinout) format_pinout = [] for item in pinout: - format_pinout += [{ 'name': item[0], 'value': item[1] }] + format_pinout += [{ 'name': item[1], 'value': item[2], 'type': item[4] or 'inout' }] # Save json file with open(os.path.join(name, 'pinout.json'), 'w') as outfile: diff --git a/app/resources/boards/icezum/pinout.json b/app/resources/boards/icezum/pinout.json index a7fb46502..4b892bc82 100644 --- a/app/resources/boards/icezum/pinout.json +++ b/app/resources/boards/icezum/pinout.json @@ -1 +1 @@ -[{"name": "LED0", "value": "95"}, {"name": "LED1", "value": "96"}, {"name": "LED2", "value": "97"}, {"name": "LED3", "value": "98"}, {"name": "LED4", "value": "99"}, {"name": "LED5", "value": "101"}, {"name": "LED6", "value": "102"}, {"name": "LED7", "value": "104"}, {"name": "SW1", "value": "10"}, {"name": "SW2", "value": "11"}, {"name": "D13", "value": "144"}, {"name": "D12", "value": "143"}, {"name": "D11", "value": "142"}, {"name": "D10", "value": "141"}, {"name": "D9", "value": "139"}, {"name": "D8", "value": "138"}, {"name": "D7", "value": "112"}, {"name": "D6", "value": "113"}, {"name": "D5", "value": "114"}, {"name": "D4", "value": "115"}, {"name": "D3", "value": "116"}, {"name": "D2", "value": "117"}, {"name": "D1", "value": "118"}, {"name": "D0", "value": "119"}, {"name": "DD0", "value": "78"}, {"name": "DD1", "value": "79"}, {"name": "DD2", "value": "80"}, {"name": "DD3", "value": "81"}, {"name": "DD4", "value": "88"}, {"name": "DD5", "value": "87"}, {"name": "GP0", "value": "37"}, {"name": "GP1", "value": "38"}, {"name": "GP2", "value": "39"}, {"name": "GP3", "value": "41"}, {"name": "GP4", "value": "42"}, {"name": "GP5", "value": "43"}, {"name": "GP6", "value": "49"}, {"name": "GP7", "value": "50"}, {"name": "ADC_SCL", "value": "91"}, {"name": "ADC_SDA", "value": "90"}, {"name": "ADc_INT", "value": "93"}, {"name": "CLK", "value": "21"}, {"name": "RES", "value": "66"}, {"name": "DONE", "value": "65"}, {"name": "SS", "value": "71"}, {"name": "MISO", "value": "67"}, {"name": "MOSI", "value": "68"}, {"name": "SCK", "value": "70"}, {"name": "DCD", "value": "1"}, {"name": "DSR", "value": "2"}, {"name": "DTR", "value": "3"}, {"name": "CTS", "value": "4"}, {"name": "RTS", "value": "7"}, {"name": "TX", "value": "8"}, {"name": "RX", "value": "9"}] \ No newline at end of file +[{"type": "output", "name": "LED0", "value": "95"}, {"type": "output", "name": "LED1", "value": "96"}, {"type": "output", "name": "LED2", "value": "97"}, {"type": "output", "name": "LED3", "value": "98"}, {"type": "output", "name": "LED4", "value": "99"}, {"type": "output", "name": "LED5", "value": "101"}, {"type": "output", "name": "LED6", "value": "102"}, {"type": "output", "name": "LED7", "value": "104"}, {"type": "input", "name": "SW1", "value": "10"}, {"type": "input", "name": "SW2", "value": "11"}, {"type": "inout", "name": "D13", "value": "144"}, {"type": "inout", "name": "D12", "value": "143"}, {"type": "inout", "name": "D11", "value": "142"}, {"type": "inout", "name": "D10", "value": "141"}, {"type": "inout", "name": "D9", "value": "139"}, {"type": "inout", "name": "D8", "value": "138"}, {"type": "inout", "name": "D7", "value": "112"}, {"type": "inout", "name": "D6", "value": "113"}, {"type": "inout", "name": "D5", "value": "114"}, {"type": "inout", "name": "D4", "value": "115"}, {"type": "inout", "name": "D3", "value": "116"}, {"type": "inout", "name": "D2", "value": "117"}, {"type": "inout", "name": "D1", "value": "118"}, {"type": "inout", "name": "D0", "value": "119"}, {"type": "inout", "name": "DD0", "value": "78"}, {"type": "inout", "name": "DD1", "value": "79"}, {"type": "inout", "name": "DD2", "value": "80"}, {"type": "inout", "name": "DD3", "value": "81"}, {"type": "inout", "name": "DD4", "value": "88"}, {"type": "inout", "name": "DD5", "value": "87"}, {"type": "inout", "name": "GP0", "value": "37"}, {"type": "inout", "name": "GP1", "value": "38"}, {"type": "inout", "name": "GP2", "value": "39"}, {"type": "inout", "name": "GP3", "value": "41"}, {"type": "inout", "name": "GP4", "value": "42"}, {"type": "inout", "name": "GP5", "value": "43"}, {"type": "inout", "name": "GP6", "value": "49"}, {"type": "inout", "name": "GP7", "value": "50"}, {"type": "inout", "name": "ADC_SCL", "value": "91"}, {"type": "inout", "name": "ADC_SDA", "value": "90"}, {"type": "inout", "name": "ADc_INT", "value": "93"}, {"type": "input", "name": "CLK", "value": "21"}, {"type": "inout", "name": "RES", "value": "66"}, {"type": "inout", "name": "DONE", "value": "65"}, {"type": "output", "name": "SS", "value": "71"}, {"type": "input", "name": "MISO", "value": "67"}, {"type": "output", "name": "MOSI", "value": "68"}, {"type": "output", "name": "SCK", "value": "70"}, {"type": "output", "name": "DCD", "value": "1"}, {"type": "output", "name": "DSR", "value": "2"}, {"type": "input", "name": "DTR", "value": "3"}, {"type": "output", "name": "CTS", "value": "4"}, {"type": "output", "name": "RTS", "value": "7"}, {"type": "output", "name": "TX", "value": "8"}, {"type": "input", "name": "RX", "value": "9"}] \ No newline at end of file diff --git a/app/resources/boards/icezum/pinout.pcf b/app/resources/boards/icezum/pinout.pcf index a3568f334..6dc66a68e 100644 --- a/app/resources/boards/icezum/pinout.pcf +++ b/app/resources/boards/icezum/pinout.pcf @@ -8,18 +8,18 @@ # -- Pinout: https://github.com/FPGAwars/icezum/blob/master/doc/pinout/icezum-pinout.pdf # ------------ User Leds ------------------------------------------------------ -set_io --warn-no-port LED0 95 -set_io --warn-no-port LED1 96 -set_io --warn-no-port LED2 97 -set_io --warn-no-port LED3 98 -set_io --warn-no-port LED4 99 -set_io --warn-no-port LED5 101 -set_io --warn-no-port LED6 102 -set_io --warn-no-port LED7 104 +set_io --warn-no-port LED0 95 # output +set_io --warn-no-port LED1 96 # output +set_io --warn-no-port LED2 97 # output +set_io --warn-no-port LED3 98 # output +set_io --warn-no-port LED4 99 # output +set_io --warn-no-port LED5 101 # output +set_io --warn-no-port LED6 102 # output +set_io --warn-no-port LED7 104 # output # ------------ User push buttons ---------------------------------------------- -set_io --warn-no-port SW1 10 -set_io --warn-no-port SW2 11 +set_io --warn-no-port SW1 10 # input +set_io --warn-no-port SW2 11 # input # ------------ 5v Digital I/O ------------------------------------------------- @@ -90,22 +90,22 @@ set_io --warn-no-port ADC_SDA 90 set_io --warn-no-port ADc_INT 93 # -------------------------- SYSTEM CLOCK ------------------------------------- -set_io --warn-no-port CLK 21 +set_io --warn-no-port CLK 21 # input # -------------------------- FTDI --------------------------------------------- # --- FTDI 0: set_io --warn-no-port RES 66 set_io --warn-no-port DONE 65 -set_io --warn-no-port SS 71 -set_io --warn-no-port MISO 67 -set_io --warn-no-port MOSI 68 -set_io --warn-no-port SCK 70 +set_io --warn-no-port SS 71 # output +set_io --warn-no-port MISO 67 # input +set_io --warn-no-port MOSI 68 # output +set_io --warn-no-port SCK 70 # output # # --- FTDI 1: (Serial port) -set_io --warn-no-port DCD 1 -set_io --warn-no-port DSR 2 -set_io --warn-no-port DTR 3 -set_io --warn-no-port CTS 4 -set_io --warn-no-port RTS 7 -set_io --warn-no-port TX 8 -set_io --warn-no-port RX 9 +set_io --warn-no-port DCD 1 # output +set_io --warn-no-port DSR 2 # output +set_io --warn-no-port DTR 3 # input +set_io --warn-no-port CTS 4 # output +set_io --warn-no-port RTS 7 # output +set_io --warn-no-port TX 8 # output +set_io --warn-no-port RX 9 # input diff --git a/app/scripts/services/compiler.service.js b/app/scripts/services/compiler.service.js index d76acdd21..e3e15476d 100644 --- a/app/scripts/services/compiler.service.js +++ b/app/scripts/services/compiler.service.js @@ -446,7 +446,7 @@ angular.module('icestudio') code += '\n'; } } - else { + else if (block.data.pins.length > 0) { pin = block.data.pins[0]; value = block.data.virtual ? '' : pin.value; code += 'set_io '; @@ -467,7 +467,7 @@ angular.module('icestudio') code += '\n'; } } - else { + else if (initPins.length > 0) { code += 'set_io m '; code += initPins[0].pin; code += '\n'; From fe64c85c7a0a49d1a642c3b0f1bfc8c088384264 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Wed, 25 Jan 2017 18:38:42 +0100 Subject: [PATCH 023/124] Filter pinout choices for input/output ports --- app/scripts/services/blocks.service.js | 4 ++-- app/scripts/services/boards.service.js | 16 ++++++++-------- app/scripts/services/graph.service.js | 2 +- app/scripts/services/utils.service.js | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/app/scripts/services/blocks.service.js b/app/scripts/services/blocks.service.js index f24fb33a0..1bc16c31d 100644 --- a/app/scripts/services/blocks.service.js +++ b/app/scripts/services/blocks.service.js @@ -384,7 +384,7 @@ angular.module('icestudio') position: instance.position, disabled: disabled, rightPorts: rightPorts, - choices: boards.getPinoutHTML() + choices: boards.pinoutInputHTML }); return cell; } @@ -404,7 +404,7 @@ angular.module('icestudio') position: instance.position, disabled: disabled, leftPorts: leftPorts, - choices: boards.getPinoutHTML() + choices: boards.pinoutOutputHTML }); return cell; } diff --git a/app/scripts/services/boards.service.js b/app/scripts/services/boards.service.js index fd65ab02c..babf023bd 100644 --- a/app/scripts/services/boards.service.js +++ b/app/scripts/services/boards.service.js @@ -6,7 +6,8 @@ angular.module('icestudio') nodePath) { const DEFAULT = 'icezum'; - this.pinoutHTML = ''; + this.pinoutInputHTML = ''; + this.pinoutOutputHTML = ''; this.selectedBoard = null; this.currentBoards = loadBoards(nodePath.join('resources', 'boards')); @@ -68,19 +69,18 @@ angular.module('icestudio') } } this.selectedBoard = selectedBoard; - this.pinoutHTML = generateHTMLOptions(this.selectedBoard.pinout); + this.pinoutInputHTML = generateHTMLOptions(this.selectedBoard.pinout, 'input'); + this.pinoutOutputHTML = generateHTMLOptions(this.selectedBoard.pinout, 'output'); utils.rootScopeSafeApply(); return this.selectedBoard.name; }; - this.getPinoutHTML = function() { - return this.pinoutHTML; - }; - - function generateHTMLOptions(pinout) { + function generateHTMLOptions(pinout, type) { var code = ''; for (var i in pinout) { - code += ''; + if (pinout[i].type === type || pinout[i].type === 'inout' ) { + code += ''; + } } return code; } diff --git a/app/scripts/services/graph.service.js b/app/scripts/services/graph.service.js index 490128edf..7cf0db71d 100644 --- a/app/scripts/services/graph.service.js +++ b/app/scripts/services/graph.service.js @@ -537,7 +537,7 @@ angular.module('icestudio') if (type === 'basic.input' || type === 'basic.output') { var view = paper.findViewByModel(cell.id); - cell.set('choices', boards.getPinoutHTML()); + cell.set('choices', (type === 'basic.input') ? boards.pinoutInputHTML : boards.pinoutOutputHTML); view.clearValues(); view.applyChoices(); } diff --git a/app/scripts/services/utils.service.js b/app/scripts/services/utils.service.js index b64b526c3..61bed23f7 100644 --- a/app/scripts/services/utils.service.js +++ b/app/scripts/services/utils.service.js @@ -882,7 +882,7 @@ angular.module('icestudio') this.parsePortLabel = function(data) { // e.g: name[x:y] var match, ret = {}; - var pattern = /([A-Za-z_]+[A-Za-z_0-9]*){0,1}(\[([0-9]+):([0-9]+)\]){0,1}/g; + var pattern = /([A-Za-z_]+[A-Za-z_0-9]*)?(\[([0-9]+):([0-9]+)\])?/g; match = pattern.exec(data); if (match && (match[0] === match.input)) { ret.name = match[1] ? match[1] : ''; @@ -903,7 +903,7 @@ angular.module('icestudio') this.parseParamLabel = function(data) { // e.g: name var match, ret = {}; - var pattern = /([A-Za-z_]+[A-Za-z_0-9]*){0,1}/g; + var pattern = /([A-Za-z_]+[A-Za-z_0-9]*)?/g; match = pattern.exec(data); if (match && (match[0] === match.input)) { ret.name = match[1] ? match[1] : ''; From af7fef2221f6dbd11b5796bbb7a6ae75135a6f8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Wed, 25 Jan 2017 19:03:33 +0100 Subject: [PATCH 024/124] Add input/output information to all the PCFs/pinouts --- .../boards/_iCEblink40-HX1K/pinout.json | 2 +- .../boards/_iCEblink40-HX1K/pinout.pcf | 18 ++--- app/resources/boards/generator.py | 1 - app/resources/boards/go-board/pinout.json | 2 +- app/resources/boards/go-board/pinout.pcf | 72 +++++++++---------- app/resources/boards/iCE40-HX8K/pinout.json | 2 +- app/resources/boards/iCE40-HX8K/pinout.pcf | 44 ++++++------ app/resources/boards/icestick/pinout.json | 2 +- app/resources/boards/icestick/pinout.pcf | 42 +++++------ app/resources/boards/icezum/pinout.json | 2 +- app/resources/boards/icezum/pinout.pcf | 8 +-- app/resources/boards/icezum/rules.json | 9 +++ app/resources/boards/icoboard/pinout.json | 2 +- app/resources/boards/icoboard/pinout.pcf | 8 +-- app/resources/boards/kefir/pinout.json | 2 +- app/resources/boards/kefir/pinout.pcf | 18 ++--- 16 files changed, 121 insertions(+), 113 deletions(-) diff --git a/app/resources/boards/_iCEblink40-HX1K/pinout.json b/app/resources/boards/_iCEblink40-HX1K/pinout.json index 896353c97..41275fb4c 100644 --- a/app/resources/boards/_iCEblink40-HX1K/pinout.json +++ b/app/resources/boards/_iCEblink40-HX1K/pinout.json @@ -1 +1 @@ -[{"name": "CLK", "value": "13"}, {"name": "LED1", "value": "59"}, {"name": "LED2", "value": "56"}, {"name": "LED3", "value": "53"}, {"name": "LED4", "value": "51"}, {"name": "BTN1", "value": "60"}, {"name": "BTN2", "value": "57"}, {"name": "BTN3", "value": "54"}, {"name": "BTN4", "value": "52"}, {"name": "J2U1", "value": "66"}, {"name": "J2U2", "value": "69"}, {"name": "J2U3", "value": "72"}, {"name": "J2U4", "value": "74"}, {"name": "J2U5", "value": "79"}, {"name": "J2U6", "value": "81"}, {"name": "J2U7", "value": "83"}, {"name": "J2U8", "value": "86"}, {"name": "J2D1", "value": "68"}, {"name": "J2D2", "value": "71"}, {"name": "J2D3", "value": "73"}, {"name": "J2D4", "value": "78"}, {"name": "J2D5", "value": "80"}, {"name": "J2D6", "value": "82"}, {"name": "J2D7", "value": "85"}, {"name": "J2D8", "value": "87"}, {"name": "J4U1", "value": "89"}, {"name": "J4U2", "value": "91"}, {"name": "J4U3", "value": "94"}, {"name": "J4U4", "value": "96"}, {"name": "J4U5", "value": "99"}, {"name": "J4U6", "value": "12"}, {"name": "J4U8", "value": "18"}, {"name": "J4D1", "value": "90"}, {"name": "J4D2", "value": "93"}, {"name": "J4D3", "value": "95"}, {"name": "J4D4", "value": "97"}, {"name": "J4D5", "value": "100"}, {"name": "J4D6", "value": "15"}, {"name": "J4D7", "value": "16"}, {"name": "J4D8", "value": "19"}, {"name": "D11", "value": "64"}, {"name": "D10", "value": "62"}, {"name": "D9", "value": "42"}, {"name": "D8", "value": "40"}, {"name": "D7", "value": "36"}, {"name": "D6", "value": "30"}, {"name": "D5", "value": "65"}, {"name": "D4", "value": "63"}, {"name": "D3", "value": "41"}, {"name": "D2", "value": "37"}, {"name": "D1", "value": "34"}, {"name": "D0", "value": "29"}, {"name": "J6R1", "value": "25"}, {"name": "J6R2", "value": "24"}, {"name": "J6R3", "value": "21"}, {"name": "J6R4", "value": "20"}, {"name": "J6L1", "value": "26"}, {"name": "J6L2", "value": "27"}, {"name": "J6L3", "value": "28"}, {"name": "J6L4", "value": "33"}, {"name": "J1R1", "value": "10"}, {"name": "J1R2", "value": "9"}, {"name": "J1R3", "value": "8"}, {"name": "J1R4", "value": "7"}, {"name": "J1L1", "value": "4"}, {"name": "J1L2", "value": "3"}, {"name": "J1L3", "value": "2"}, {"name": "J1L4", "value": "1"}, {"name": "SS", "value": "49"}, {"name": "SO", "value": "45"}, {"name": "SI", "value": "46"}, {"name": "SCK", "value": "48"}] \ No newline at end of file +[{"type": "input", "name": "CLK", "value": "13"}, {"type": "output", "name": "LED1", "value": "59"}, {"type": "output", "name": "LED2", "value": "56"}, {"type": "output", "name": "LED3", "value": "53"}, {"type": "output", "name": "LED4", "value": "51"}, {"type": "input", "name": "BTN1", "value": "60"}, {"type": "input", "name": "BTN2", "value": "57"}, {"type": "input", "name": "BTN3", "value": "54"}, {"type": "input", "name": "BTN4", "value": "52"}, {"type": "inout", "name": "J2U1", "value": "66"}, {"type": "inout", "name": "J2U2", "value": "69"}, {"type": "inout", "name": "J2U3", "value": "72"}, {"type": "inout", "name": "J2U4", "value": "74"}, {"type": "inout", "name": "J2U5", "value": "79"}, {"type": "inout", "name": "J2U6", "value": "81"}, {"type": "inout", "name": "J2U7", "value": "83"}, {"type": "inout", "name": "J2U8", "value": "86"}, {"type": "inout", "name": "J2D1", "value": "68"}, {"type": "inout", "name": "J2D2", "value": "71"}, {"type": "inout", "name": "J2D3", "value": "73"}, {"type": "inout", "name": "J2D4", "value": "78"}, {"type": "inout", "name": "J2D5", "value": "80"}, {"type": "inout", "name": "J2D6", "value": "82"}, {"type": "inout", "name": "J2D7", "value": "85"}, {"type": "inout", "name": "J2D8", "value": "87"}, {"type": "inout", "name": "J4U1", "value": "89"}, {"type": "inout", "name": "J4U2", "value": "91"}, {"type": "inout", "name": "J4U3", "value": "94"}, {"type": "inout", "name": "J4U4", "value": "96"}, {"type": "inout", "name": "J4U5", "value": "99"}, {"type": "inout", "name": "J4U6", "value": "12"}, {"type": "inout", "name": "J4U8", "value": "18"}, {"type": "inout", "name": "J4D1", "value": "90"}, {"type": "inout", "name": "J4D2", "value": "93"}, {"type": "inout", "name": "J4D3", "value": "95"}, {"type": "inout", "name": "J4D4", "value": "97"}, {"type": "inout", "name": "J4D5", "value": "100"}, {"type": "inout", "name": "J4D6", "value": "15"}, {"type": "inout", "name": "J4D7", "value": "16"}, {"type": "inout", "name": "J4D8", "value": "19"}, {"type": "inout", "name": "D11", "value": "64"}, {"type": "inout", "name": "D10", "value": "62"}, {"type": "inout", "name": "D9", "value": "42"}, {"type": "inout", "name": "D8", "value": "40"}, {"type": "inout", "name": "D7", "value": "36"}, {"type": "inout", "name": "D6", "value": "30"}, {"type": "inout", "name": "D5", "value": "65"}, {"type": "inout", "name": "D4", "value": "63"}, {"type": "inout", "name": "D3", "value": "41"}, {"type": "inout", "name": "D2", "value": "37"}, {"type": "inout", "name": "D1", "value": "34"}, {"type": "inout", "name": "D0", "value": "29"}, {"type": "inout", "name": "J6R1", "value": "25"}, {"type": "inout", "name": "J6R2", "value": "24"}, {"type": "inout", "name": "J6R3", "value": "21"}, {"type": "inout", "name": "J6R4", "value": "20"}, {"type": "inout", "name": "J6L1", "value": "26"}, {"type": "inout", "name": "J6L2", "value": "27"}, {"type": "inout", "name": "J6L3", "value": "28"}, {"type": "inout", "name": "J6L4", "value": "33"}, {"type": "inout", "name": "J1R1", "value": "10"}, {"type": "inout", "name": "J1R2", "value": "9"}, {"type": "inout", "name": "J1R3", "value": "8"}, {"type": "inout", "name": "J1R4", "value": "7"}, {"type": "inout", "name": "J1L1", "value": "4"}, {"type": "inout", "name": "J1L2", "value": "3"}, {"type": "inout", "name": "J1L3", "value": "2"}, {"type": "inout", "name": "J1L4", "value": "1"}, {"type": "inout", "name": "SS", "value": "49"}, {"type": "inout", "name": "SO", "value": "45"}, {"type": "inout", "name": "SI", "value": "46"}, {"type": "inout", "name": "SCK", "value": "48"}] \ No newline at end of file diff --git a/app/resources/boards/_iCEblink40-HX1K/pinout.pcf b/app/resources/boards/_iCEblink40-HX1K/pinout.pcf index 49c40d2f2..57051fa83 100644 --- a/app/resources/boards/_iCEblink40-HX1K/pinout.pcf +++ b/app/resources/boards/_iCEblink40-HX1K/pinout.pcf @@ -12,19 +12,19 @@ # -------------------------- System Clock ------------------------------------- -set_io --warn-no-port CLK 13 # JP3.1 [Config: JP2.1 JP2.2 JP2.3 -> NCC=333kHz, NNN=3.33MHz, CCN=33.3MHz] +set_io --warn-no-port CLK 13 # input JP3.1 [Config: JP2.1 JP2.2 JP2.3 -> NCC=333kHz, NNN=3.33MHz, CCN=33.3MHz] # ------------ User Leds ------------------------------------------------------ -set_io --warn-no-port LED1 59 # J12.1 -set_io --warn-no-port LED2 56 # J12.2 -set_io --warn-no-port LED3 53 # J12.3 -set_io --warn-no-port LED4 51 # J12.4 +set_io --warn-no-port LED1 59 # output J12.1 +set_io --warn-no-port LED2 56 # output J12.2 +set_io --warn-no-port LED3 53 # output J12.3 +set_io --warn-no-port LED4 51 # output J12.4 # ------------ Capacitive buttons --------------------------------------------- -set_io --warn-no-port BTN1 60 # Capacitive button 1 -set_io --warn-no-port BTN2 57 # Capacitive button 2 -set_io --warn-no-port BTN3 54 # Capacitive button 3 -set_io --warn-no-port BTN4 52 # Capacitive button 4 +set_io --warn-no-port BTN1 60 # input Capacitive button 1 +set_io --warn-no-port BTN2 57 # input Capacitive button 2 +set_io --warn-no-port BTN3 54 # input Capacitive button 3 +set_io --warn-no-port BTN4 52 # input Capacitive button 4 # ----------------- Pins I/O -------------------------------------------------- set_io --warn-no-port J2U1 66 # J2.1 diff --git a/app/resources/boards/generator.py b/app/resources/boards/generator.py index 2a4c99c87..7382000c9 100755 --- a/app/resources/boards/generator.py +++ b/app/resources/boards/generator.py @@ -33,7 +33,6 @@ # Build json pinout = re.findall(pattern, data) - print(pinout) format_pinout = [] for item in pinout: format_pinout += [{ 'name': item[1], 'value': item[2], 'type': item[4] or 'inout' }] diff --git a/app/resources/boards/go-board/pinout.json b/app/resources/boards/go-board/pinout.json index 3edd96210..1793fa509 100644 --- a/app/resources/boards/go-board/pinout.json +++ b/app/resources/boards/go-board/pinout.json @@ -1 +1 @@ -[{"name": "LED1", "value": "56"}, {"name": "LED2", "value": "57"}, {"name": "LED3", "value": "59"}, {"name": "LED4", "value": "60"}, {"name": "SW1", "value": "53"}, {"name": "SW2", "value": "51"}, {"name": "SW3", "value": "54"}, {"name": "SW4", "value": "52"}, {"name": "S1_A", "value": "3"}, {"name": "S1_B", "value": "4"}, {"name": "S1_C", "value": "93"}, {"name": "S1_D", "value": "91"}, {"name": "S1_E", "value": "90"}, {"name": "S1_F", "value": "1"}, {"name": "S1_G", "value": "2"}, {"name": "S2_A", "value": "100"}, {"name": "S2_B", "value": "99"}, {"name": "S2_C", "value": "97"}, {"name": "S2_D", "value": "95"}, {"name": "S2_E", "value": "94"}, {"name": "S2_F", "value": "8"}, {"name": "S2_G", "value": "96"}, {"name": "CLK", "value": "15"}, {"name": "RX", "value": "73"}, {"name": "TX", "value": "74"}, {"name": "VGA_HS", "value": "26"}, {"name": "VGA_VS", "value": "27"}, {"name": "VGA_R0", "value": "36"}, {"name": "VGA_R1", "value": "37"}, {"name": "VGA_R2", "value": "40"}, {"name": "VGA_G0", "value": "29"}, {"name": "VGA_G1", "value": "30"}, {"name": "VGA_G2", "value": "33"}, {"name": "VGA_B0", "value": "28"}, {"name": "VGA_B1", "value": "41"}, {"name": "VGA_B2", "value": "42"}, {"name": "PMOD1", "value": "65"}, {"name": "PMOD2", "value": "64"}, {"name": "PMOD3", "value": "63"}, {"name": "PMOD4", "value": "62"}, {"name": "PMOD7", "value": "78"}, {"name": "PMOD8", "value": "79"}, {"name": "PMOD9", "value": "80"}, {"name": "PMOD10", "value": "81"}] \ No newline at end of file +[{"type": "output", "name": "LED1", "value": "56"}, {"type": "output", "name": "LED2", "value": "57"}, {"type": "output", "name": "LED3", "value": "59"}, {"type": "output", "name": "LED4", "value": "60"}, {"type": "input", "name": "SW1", "value": "53"}, {"type": "input", "name": "SW2", "value": "51"}, {"type": "input", "name": "SW3", "value": "54"}, {"type": "input", "name": "SW4", "value": "52"}, {"type": "output", "name": "S1_A", "value": "3"}, {"type": "output", "name": "S1_B", "value": "4"}, {"type": "output", "name": "S1_C", "value": "93"}, {"type": "output", "name": "S1_D", "value": "91"}, {"type": "output", "name": "S1_E", "value": "90"}, {"type": "output", "name": "S1_F", "value": "1"}, {"type": "output", "name": "S1_G", "value": "2"}, {"type": "output", "name": "S2_A", "value": "100"}, {"type": "output", "name": "S2_B", "value": "99"}, {"type": "output", "name": "S2_C", "value": "97"}, {"type": "output", "name": "S2_D", "value": "95"}, {"type": "output", "name": "S2_E", "value": "94"}, {"type": "output", "name": "S2_F", "value": "8"}, {"type": "output", "name": "S2_G", "value": "96"}, {"type": "input", "name": "CLK", "value": "15"}, {"type": "input", "name": "RX", "value": "73"}, {"type": "output", "name": "TX", "value": "74"}, {"type": "output", "name": "VGA_HS", "value": "26"}, {"type": "output", "name": "VGA_VS", "value": "27"}, {"type": "output", "name": "VGA_R0", "value": "36"}, {"type": "output", "name": "VGA_R1", "value": "37"}, {"type": "output", "name": "VGA_R2", "value": "40"}, {"type": "output", "name": "VGA_G0", "value": "29"}, {"type": "output", "name": "VGA_G1", "value": "30"}, {"type": "output", "name": "VGA_G2", "value": "33"}, {"type": "output", "name": "VGA_B0", "value": "28"}, {"type": "output", "name": "VGA_B1", "value": "41"}, {"type": "output", "name": "VGA_B2", "value": "42"}, {"type": "inout", "name": "PMOD1", "value": "65"}, {"type": "inout", "name": "PMOD2", "value": "64"}, {"type": "inout", "name": "PMOD3", "value": "63"}, {"type": "inout", "name": "PMOD4", "value": "62"}, {"type": "inout", "name": "PMOD7", "value": "78"}, {"type": "inout", "name": "PMOD8", "value": "79"}, {"type": "inout", "name": "PMOD9", "value": "80"}, {"type": "inout", "name": "PMOD10", "value": "81"}] \ No newline at end of file diff --git a/app/resources/boards/go-board/pinout.pcf b/app/resources/boards/go-board/pinout.pcf index 87a6a8d07..171affdf2 100644 --- a/app/resources/boards/go-board/pinout.pcf +++ b/app/resources/boards/go-board/pinout.pcf @@ -11,53 +11,53 @@ # -- Pinout: https://www.nandland.com/goboard/images/Go_Board_V1.pdf # ------------ User Leds ------------------------------------------------------ -set_io --warn-no-port LED1 56 -set_io --warn-no-port LED2 57 -set_io --warn-no-port LED3 59 -set_io --warn-no-port LED4 60 +set_io --warn-no-port LED1 56 # output +set_io --warn-no-port LED2 57 # output +set_io --warn-no-port LED3 59 # output +set_io --warn-no-port LED4 60 # output # ------------ User push-buttons ---------------------------------------------- -set_io --warn-no-port SW1 53 -set_io --warn-no-port SW2 51 -set_io --warn-no-port SW3 54 -set_io --warn-no-port SW4 52 +set_io --warn-no-port SW1 53 # input +set_io --warn-no-port SW2 51 # input +set_io --warn-no-port SW3 54 # input +set_io --warn-no-port SW4 52 # input # ------------ 7 segments ----------------------------------------------------- -set_io --warn-no-port S1_A 3 -set_io --warn-no-port S1_B 4 -set_io --warn-no-port S1_C 93 -set_io --warn-no-port S1_D 91 -set_io --warn-no-port S1_E 90 -set_io --warn-no-port S1_F 1 -set_io --warn-no-port S1_G 2 +set_io --warn-no-port S1_A 3 # output +set_io --warn-no-port S1_B 4 # output +set_io --warn-no-port S1_C 93 # output +set_io --warn-no-port S1_D 91 # output +set_io --warn-no-port S1_E 90 # output +set_io --warn-no-port S1_F 1 # output +set_io --warn-no-port S1_G 2 # output -set_io --warn-no-port S2_A 100 -set_io --warn-no-port S2_B 99 -set_io --warn-no-port S2_C 97 -set_io --warn-no-port S2_D 95 -set_io --warn-no-port S2_E 94 -set_io --warn-no-port S2_F 8 -set_io --warn-no-port S2_G 96 +set_io --warn-no-port S2_A 100 # output +set_io --warn-no-port S2_B 99 # output +set_io --warn-no-port S2_C 97 # output +set_io --warn-no-port S2_D 95 # output +set_io --warn-no-port S2_E 94 # output +set_io --warn-no-port S2_F 8 # output +set_io --warn-no-port S2_G 96 # output # -------------------------- SYSTEM CLOCK ------------------------------------- -set_io --warn-no-port CLK 15 +set_io --warn-no-port CLK 15 # input # -------------------------- UART --------------------------------------------- -set_io --warn-no-port RX 73 -set_io --warn-no-port TX 74 +set_io --warn-no-port RX 73 # input +set_io --warn-no-port TX 74 # output # -------------------------- VGA ---------------------------------------------- -set_io --warn-no-port VGA_HS 26 -set_io --warn-no-port VGA_VS 27 -set_io --warn-no-port VGA_R0 36 -set_io --warn-no-port VGA_R1 37 -set_io --warn-no-port VGA_R2 40 -set_io --warn-no-port VGA_G0 29 -set_io --warn-no-port VGA_G1 30 -set_io --warn-no-port VGA_G2 33 -set_io --warn-no-port VGA_B0 28 -set_io --warn-no-port VGA_B1 41 -set_io --warn-no-port VGA_B2 42 +set_io --warn-no-port VGA_HS 26 # output +set_io --warn-no-port VGA_VS 27 # output +set_io --warn-no-port VGA_R0 36 # output +set_io --warn-no-port VGA_R1 37 # output +set_io --warn-no-port VGA_R2 40 # output +set_io --warn-no-port VGA_G0 29 # output +set_io --warn-no-port VGA_G1 30 # output +set_io --warn-no-port VGA_G2 33 # output +set_io --warn-no-port VGA_B0 28 # output +set_io --warn-no-port VGA_B1 41 # output +set_io --warn-no-port VGA_B2 42 # output # ------------ PMOD connector ------------------------------------------------- # diff --git a/app/resources/boards/iCE40-HX8K/pinout.json b/app/resources/boards/iCE40-HX8K/pinout.json index a537f2df7..916be13b9 100644 --- a/app/resources/boards/iCE40-HX8K/pinout.json +++ b/app/resources/boards/iCE40-HX8K/pinout.json @@ -1 +1 @@ -[{"name": "LED0", "value": "B5"}, {"name": "LED1", "value": "B4"}, {"name": "LED2", "value": "A2"}, {"name": "LED3", "value": "A1"}, {"name": "LED4", "value": "C5"}, {"name": "LED5", "value": "C4"}, {"name": "LED6", "value": "B3"}, {"name": "LED7", "value": "C3"}, {"name": "CLK", "value": "J3"}, {"name": "MISO", "value": "P12"}, {"name": "MOSI", "value": "P11"}, {"name": "SCK", "value": "R11"}, {"name": "SS", "value": "R12"}, {"name": "RTS", "value": "B13"}, {"name": "RESET", "value": "N11"}, {"name": "DONE", "value": "M10"}, {"name": "TX", "value": "B12"}, {"name": "RX", "value": "B10"}, {"name": "DCD", "value": "B15"}, {"name": "DSR", "value": "B14"}, {"name": "DTR", "value": "A16"}, {"name": "CTS", "value": "A15"}, {"name": "A16", "value": "A16"}, {"name": "A15", "value": "A15"}, {"name": "B15", "value": "B15"}, {"name": "B13", "value": "B13"}, {"name": "B14", "value": "B14"}, {"name": "B12", "value": "B12"}, {"name": "B11", "value": "B11"}, {"name": "A11", "value": "A11"}, {"name": "B10", "value": "B10"}, {"name": "A10", "value": "A10"}, {"name": "C9", "value": "C9"}, {"name": "A9", "value": "A9"}, {"name": "B9", "value": "B9"}, {"name": "B8", "value": "B8"}, {"name": "A7", "value": "A7"}, {"name": "B7", "value": "B7"}, {"name": "C7", "value": "C7"}, {"name": "A6", "value": "A6"}, {"name": "C6", "value": "C6"}, {"name": "B6", "value": "B6"}, {"name": "C5", "value": "C5"}, {"name": "A5", "value": "A5"}, {"name": "C4", "value": "C4"}, {"name": "B5", "value": "B5"}, {"name": "C3", "value": "C3"}, {"name": "B4", "value": "B4"}, {"name": "B3", "value": "B3"}, {"name": "A2", "value": "A2"}, {"name": "A1", "value": "A1"}, {"name": "R15", "value": "R15"}, {"name": "P16", "value": "P16"}, {"name": "P15", "value": "P15"}, {"name": "N16", "value": "N16"}, {"name": "M15", "value": "M15"}, {"name": "M16", "value": "M16"}, {"name": "L16", "value": "L16"}, {"name": "K15", "value": "K15"}, {"name": "K16", "value": "K16"}, {"name": "K14", "value": "K14"}, {"name": "J14", "value": "J14"}, {"name": "G14", "value": "G14"}, {"name": "F14", "value": "F14"}, {"name": "J15", "value": "J15"}, {"name": "H14", "value": "H14"}, {"name": "H16", "value": "H16"}, {"name": "G15", "value": "G15"}, {"name": "G16", "value": "G16"}, {"name": "F15", "value": "F15"}, {"name": "F16", "value": "F16"}, {"name": "E14", "value": "E14"}, {"name": "E16", "value": "E16"}, {"name": "D15", "value": "D15"}, {"name": "D16", "value": "D16"}, {"name": "D14", "value": "D14"}, {"name": "C16", "value": "C16"}, {"name": "B16", "value": "B16"}, {"name": "R16", "value": "R16"}, {"name": "T15", "value": "T15"}, {"name": "T16", "value": "T16"}, {"name": "T13", "value": "T13"}, {"name": "T14", "value": "T14"}, {"name": "N12", "value": "N12"}, {"name": "P13", "value": "P13"}, {"name": "N10", "value": "N10"}, {"name": "M11", "value": "M11"}, {"name": "T11", "value": "T11"}, {"name": "P10", "value": "P10"}, {"name": "T10", "value": "T10"}, {"name": "R10", "value": "R10"}, {"name": "P8", "value": "P8"}, {"name": "P9", "value": "P9"}, {"name": "T9", "value": "T9"}, {"name": "R9", "value": "R9"}, {"name": "T7", "value": "T7"}, {"name": "T8", "value": "T8"}, {"name": "T6", "value": "T6"}, {"name": "R6", "value": "R6"}, {"name": "T5", "value": "T5"}, {"name": "R5", "value": "R5"}, {"name": "R3", "value": "R3"}, {"name": "R4", "value": "R4"}, {"name": "R2", "value": "R2"}, {"name": "T3", "value": "T3"}, {"name": "T1", "value": "T1"}, {"name": "T2", "value": "T2"}, {"name": "R1", "value": "R1"}, {"name": "P1", "value": "P1"}, {"name": "P2", "value": "P2"}, {"name": "N3", "value": "N3"}, {"name": "N2", "value": "N2"}, {"name": "M2", "value": "M2"}, {"name": "M1", "value": "M1"}, {"name": "L3", "value": "L3"}, {"name": "L1", "value": "L1"}, {"name": "K3", "value": "K3"}, {"name": "K1", "value": "K1"}, {"name": "J2", "value": "J2"}, {"name": "J1", "value": "J1"}, {"name": "H2", "value": "H2"}, {"name": "J3", "value": "J3"}, {"name": "G2", "value": "G2"}, {"name": "H1", "value": "H1"}, {"name": "F2", "value": "F2"}, {"name": "G1", "value": "G1"}, {"name": "E2", "value": "E2"}, {"name": "F1", "value": "F1"}, {"name": "D1", "value": "D1"}, {"name": "D2", "value": "D2"}, {"name": "C1", "value": "C1"}, {"name": "C2", "value": "C2"}, {"name": "B1", "value": "B1"}, {"name": "B2", "value": "B2"}] \ No newline at end of file +[{"type": "output", "name": "LED0", "value": "B5"}, {"type": "output", "name": "LED1", "value": "B4"}, {"type": "output", "name": "LED2", "value": "A2"}, {"type": "output", "name": "LED3", "value": "A1"}, {"type": "output", "name": "LED4", "value": "C5"}, {"type": "output", "name": "LED5", "value": "C4"}, {"type": "output", "name": "LED6", "value": "B3"}, {"type": "output", "name": "LED7", "value": "C3"}, {"type": "input", "name": "CLK", "value": "J3"}, {"type": "input", "name": "MISO", "value": "P12"}, {"type": "output", "name": "MOSI", "value": "P11"}, {"type": "output", "name": "SCK", "value": "R11"}, {"type": "output", "name": "SS", "value": "R12"}, {"type": "output", "name": "RTS", "value": "B13"}, {"type": "input", "name": "RESET", "value": "N11"}, {"type": "output", "name": "DONE", "value": "M10"}, {"type": "output", "name": "TX", "value": "B12"}, {"type": "input", "name": "RX", "value": "B10"}, {"type": "output", "name": "DCD", "value": "B15"}, {"type": "output", "name": "DSR", "value": "B14"}, {"type": "input", "name": "DTR", "value": "A16"}, {"type": "output", "name": "CTS", "value": "A15"}, {"type": "inout", "name": "A16", "value": "A16"}, {"type": "inout", "name": "A15", "value": "A15"}, {"type": "inout", "name": "B15", "value": "B15"}, {"type": "inout", "name": "B13", "value": "B13"}, {"type": "inout", "name": "B14", "value": "B14"}, {"type": "inout", "name": "B12", "value": "B12"}, {"type": "inout", "name": "B11", "value": "B11"}, {"type": "inout", "name": "A11", "value": "A11"}, {"type": "inout", "name": "B10", "value": "B10"}, {"type": "inout", "name": "A10", "value": "A10"}, {"type": "inout", "name": "C9", "value": "C9"}, {"type": "inout", "name": "A9", "value": "A9"}, {"type": "inout", "name": "B9", "value": "B9"}, {"type": "inout", "name": "B8", "value": "B8"}, {"type": "inout", "name": "A7", "value": "A7"}, {"type": "inout", "name": "B7", "value": "B7"}, {"type": "inout", "name": "C7", "value": "C7"}, {"type": "inout", "name": "A6", "value": "A6"}, {"type": "inout", "name": "C6", "value": "C6"}, {"type": "inout", "name": "B6", "value": "B6"}, {"type": "inout", "name": "C5", "value": "C5"}, {"type": "inout", "name": "A5", "value": "A5"}, {"type": "inout", "name": "C4", "value": "C4"}, {"type": "inout", "name": "B5", "value": "B5"}, {"type": "inout", "name": "C3", "value": "C3"}, {"type": "inout", "name": "B4", "value": "B4"}, {"type": "inout", "name": "B3", "value": "B3"}, {"type": "inout", "name": "A2", "value": "A2"}, {"type": "inout", "name": "A1", "value": "A1"}, {"type": "inout", "name": "R15", "value": "R15"}, {"type": "inout", "name": "P16", "value": "P16"}, {"type": "inout", "name": "P15", "value": "P15"}, {"type": "inout", "name": "N16", "value": "N16"}, {"type": "inout", "name": "M15", "value": "M15"}, {"type": "inout", "name": "M16", "value": "M16"}, {"type": "inout", "name": "L16", "value": "L16"}, {"type": "inout", "name": "K15", "value": "K15"}, {"type": "inout", "name": "K16", "value": "K16"}, {"type": "inout", "name": "K14", "value": "K14"}, {"type": "inout", "name": "J14", "value": "J14"}, {"type": "inout", "name": "G14", "value": "G14"}, {"type": "inout", "name": "F14", "value": "F14"}, {"type": "inout", "name": "J15", "value": "J15"}, {"type": "inout", "name": "H14", "value": "H14"}, {"type": "inout", "name": "H16", "value": "H16"}, {"type": "inout", "name": "G15", "value": "G15"}, {"type": "inout", "name": "G16", "value": "G16"}, {"type": "inout", "name": "F15", "value": "F15"}, {"type": "inout", "name": "F16", "value": "F16"}, {"type": "inout", "name": "E14", "value": "E14"}, {"type": "inout", "name": "E16", "value": "E16"}, {"type": "inout", "name": "D15", "value": "D15"}, {"type": "inout", "name": "D16", "value": "D16"}, {"type": "inout", "name": "D14", "value": "D14"}, {"type": "inout", "name": "C16", "value": "C16"}, {"type": "inout", "name": "B16", "value": "B16"}, {"type": "inout", "name": "R16", "value": "R16"}, {"type": "inout", "name": "T15", "value": "T15"}, {"type": "inout", "name": "T16", "value": "T16"}, {"type": "inout", "name": "T13", "value": "T13"}, {"type": "inout", "name": "T14", "value": "T14"}, {"type": "inout", "name": "N12", "value": "N12"}, {"type": "inout", "name": "P13", "value": "P13"}, {"type": "inout", "name": "N10", "value": "N10"}, {"type": "inout", "name": "M11", "value": "M11"}, {"type": "inout", "name": "T11", "value": "T11"}, {"type": "inout", "name": "P10", "value": "P10"}, {"type": "inout", "name": "T10", "value": "T10"}, {"type": "inout", "name": "R10", "value": "R10"}, {"type": "inout", "name": "P8", "value": "P8"}, {"type": "inout", "name": "P9", "value": "P9"}, {"type": "inout", "name": "T9", "value": "T9"}, {"type": "inout", "name": "R9", "value": "R9"}, {"type": "inout", "name": "T7", "value": "T7"}, {"type": "inout", "name": "T8", "value": "T8"}, {"type": "inout", "name": "T6", "value": "T6"}, {"type": "inout", "name": "R6", "value": "R6"}, {"type": "inout", "name": "T5", "value": "T5"}, {"type": "inout", "name": "R5", "value": "R5"}, {"type": "inout", "name": "R3", "value": "R3"}, {"type": "inout", "name": "R4", "value": "R4"}, {"type": "inout", "name": "R2", "value": "R2"}, {"type": "inout", "name": "T3", "value": "T3"}, {"type": "inout", "name": "T1", "value": "T1"}, {"type": "inout", "name": "T2", "value": "T2"}, {"type": "inout", "name": "R1", "value": "R1"}, {"type": "inout", "name": "P1", "value": "P1"}, {"type": "inout", "name": "P2", "value": "P2"}, {"type": "inout", "name": "N3", "value": "N3"}, {"type": "inout", "name": "N2", "value": "N2"}, {"type": "inout", "name": "M2", "value": "M2"}, {"type": "inout", "name": "M1", "value": "M1"}, {"type": "inout", "name": "L3", "value": "L3"}, {"type": "inout", "name": "L1", "value": "L1"}, {"type": "inout", "name": "K3", "value": "K3"}, {"type": "inout", "name": "K1", "value": "K1"}, {"type": "inout", "name": "J2", "value": "J2"}, {"type": "inout", "name": "J1", "value": "J1"}, {"type": "inout", "name": "H2", "value": "H2"}, {"type": "inout", "name": "J3", "value": "J3"}, {"type": "inout", "name": "G2", "value": "G2"}, {"type": "inout", "name": "H1", "value": "H1"}, {"type": "inout", "name": "F2", "value": "F2"}, {"type": "inout", "name": "G1", "value": "G1"}, {"type": "inout", "name": "E2", "value": "E2"}, {"type": "inout", "name": "F1", "value": "F1"}, {"type": "inout", "name": "D1", "value": "D1"}, {"type": "inout", "name": "D2", "value": "D2"}, {"type": "inout", "name": "C1", "value": "C1"}, {"type": "inout", "name": "C2", "value": "C2"}, {"type": "inout", "name": "B1", "value": "B1"}, {"type": "inout", "name": "B2", "value": "B2"}] \ No newline at end of file diff --git a/app/resources/boards/iCE40-HX8K/pinout.pcf b/app/resources/boards/iCE40-HX8K/pinout.pcf index 4e5bb62b8..d4b51d6e8 100644 --- a/app/resources/boards/iCE40-HX8K/pinout.pcf +++ b/app/resources/boards/iCE40-HX8K/pinout.pcf @@ -7,38 +7,38 @@ #---------------------------- LEDS -------------------------------------------- -set_io --warn-no-port LED0 B5 -set_io --warn-no-port LED1 B4 -set_io --warn-no-port LED2 A2 -set_io --warn-no-port LED3 A1 -set_io --warn-no-port LED4 C5 -set_io --warn-no-port LED5 C4 -set_io --warn-no-port LED6 B3 -set_io --warn-no-port LED7 C3 +set_io --warn-no-port LED0 B5 # output +set_io --warn-no-port LED1 B4 # output +set_io --warn-no-port LED2 A2 # output +set_io --warn-no-port LED3 A1 # output +set_io --warn-no-port LED4 C5 # output +set_io --warn-no-port LED5 C4 # output +set_io --warn-no-port LED6 B3 # output +set_io --warn-no-port LED7 C3 # output # -------------------------- SYSTEM CLOCK ------------------------------------- -set_io --warn-no-port CLK J3 +set_io --warn-no-port CLK J3 # input # ------------------------------ FTDI 0 --------------------------------------- # SPI -set_io --warn-no-port MISO P12 -set_io --warn-no-port MOSI P11 -set_io --warn-no-port SCK R11 -set_io --warn-no-port SS R12 #J7.1 -set_io --warn-no-port RTS B13 +set_io --warn-no-port MISO P12 # input +set_io --warn-no-port MOSI P11 # output +set_io --warn-no-port SCK R11 # output +set_io --warn-no-port SS R12 # output #J7.1 +set_io --warn-no-port RTS B13 # output -set_io --warn-no-port RESET N11 -set_io --warn-no-port DONE M10 +set_io --warn-no-port RESET N11 # input +set_io --warn-no-port DONE M10 # output # ------------------------------ FTDI 1 --------------------------------------- # UART -set_io --warn-no-port TX B12 -set_io --warn-no-port RX B10 -set_io --warn-no-port DCD B15 -set_io --warn-no-port DSR B14 -set_io --warn-no-port DTR A16 -set_io --warn-no-port CTS A15 +set_io --warn-no-port TX B12 # output +set_io --warn-no-port RX B10 # input +set_io --warn-no-port DCD B15 # output +set_io --warn-no-port DSR B14 # output +set_io --warn-no-port DTR A16 # input +set_io --warn-no-port CTS A15 # output # ************************************ diff --git a/app/resources/boards/icestick/pinout.json b/app/resources/boards/icestick/pinout.json index b41dce4bf..88b22d63a 100644 --- a/app/resources/boards/icestick/pinout.json +++ b/app/resources/boards/icestick/pinout.json @@ -1 +1 @@ -[{"name": "D1", "value": "99"}, {"name": "D2", "value": "98"}, {"name": "D3", "value": "97"}, {"name": "D4", "value": "96"}, {"name": "D5", "value": "95"}, {"name": "IrDA_TX", "value": "105"}, {"name": "IrDA_RX", "value": "106"}, {"name": "SD", "value": "107"}, {"name": "PMOD1", "value": "78"}, {"name": "PMOD2", "value": "79"}, {"name": "PMOD3", "value": "80"}, {"name": "PMOD4", "value": "81"}, {"name": "PMOD7", "value": "87"}, {"name": "PMOD8", "value": "88"}, {"name": "PMOD9", "value": "90"}, {"name": "PMOD10", "value": "91"}, {"name": "TR3", "value": "112"}, {"name": "TR4", "value": "113"}, {"name": "TR5", "value": "114"}, {"name": "TR6", "value": "115"}, {"name": "TR7", "value": "116"}, {"name": "TR8", "value": "117"}, {"name": "TR9", "value": "118"}, {"name": "TR10", "value": "119"}, {"name": "BR3", "value": "62"}, {"name": "BR4", "value": "61"}, {"name": "BR5", "value": "60"}, {"name": "BR6", "value": "56"}, {"name": "BR7", "value": "48"}, {"name": "BR8", "value": "47"}, {"name": "BR9", "value": "45"}, {"name": "BR10", "value": "44"}, {"name": "CLK", "value": "21"}, {"name": "RES", "value": "66"}, {"name": "DONE", "value": "65"}, {"name": "SS", "value": "71"}, {"name": "MISO", "value": "67"}, {"name": "MOSI", "value": "68"}, {"name": "SCK", "value": "70"}, {"name": "DCD", "value": "1"}, {"name": "DSR", "value": "2"}, {"name": "DTR", "value": "3"}, {"name": "CTS", "value": "4"}, {"name": "RTS", "value": "7"}, {"name": "TX", "value": "8"}, {"name": "RX", "value": "9"}] \ No newline at end of file +[{"type": "output", "name": "D1", "value": "99"}, {"type": "output", "name": "D2", "value": "98"}, {"type": "output", "name": "D3", "value": "97"}, {"type": "output", "name": "D4", "value": "96"}, {"type": "output", "name": "D5", "value": "95"}, {"type": "output", "name": "IrDA_TX", "value": "105"}, {"type": "input", "name": "IrDA_RX", "value": "106"}, {"type": "inout", "name": "SD", "value": "107"}, {"type": "inout", "name": "PMOD1", "value": "78"}, {"type": "inout", "name": "PMOD2", "value": "79"}, {"type": "inout", "name": "PMOD3", "value": "80"}, {"type": "inout", "name": "PMOD4", "value": "81"}, {"type": "inout", "name": "PMOD7", "value": "87"}, {"type": "inout", "name": "PMOD8", "value": "88"}, {"type": "inout", "name": "PMOD9", "value": "90"}, {"type": "inout", "name": "PMOD10", "value": "91"}, {"type": "inout", "name": "TR3", "value": "112"}, {"type": "inout", "name": "TR4", "value": "113"}, {"type": "inout", "name": "TR5", "value": "114"}, {"type": "inout", "name": "TR6", "value": "115"}, {"type": "inout", "name": "TR7", "value": "116"}, {"type": "inout", "name": "TR8", "value": "117"}, {"type": "inout", "name": "TR9", "value": "118"}, {"type": "inout", "name": "TR10", "value": "119"}, {"type": "inout", "name": "BR3", "value": "62"}, {"type": "inout", "name": "BR4", "value": "61"}, {"type": "inout", "name": "BR5", "value": "60"}, {"type": "inout", "name": "BR6", "value": "56"}, {"type": "inout", "name": "BR7", "value": "48"}, {"type": "inout", "name": "BR8", "value": "47"}, {"type": "inout", "name": "BR9", "value": "45"}, {"type": "inout", "name": "BR10", "value": "44"}, {"type": "input", "name": "CLK", "value": "21"}, {"type": "input", "name": "RES", "value": "66"}, {"type": "output", "name": "DONE", "value": "65"}, {"type": "output", "name": "SS", "value": "71"}, {"type": "input", "name": "MISO", "value": "67"}, {"type": "output", "name": "MOSI", "value": "68"}, {"type": "output", "name": "SCK", "value": "70"}, {"type": "output", "name": "DCD", "value": "1"}, {"type": "output", "name": "DSR", "value": "2"}, {"type": "input", "name": "DTR", "value": "3"}, {"type": "output", "name": "CTS", "value": "4"}, {"type": "output", "name": "RTS", "value": "7"}, {"type": "output", "name": "TX", "value": "8"}, {"type": "input", "name": "RX", "value": "9"}] \ No newline at end of file diff --git a/app/resources/boards/icestick/pinout.pcf b/app/resources/boards/icestick/pinout.pcf index 06336e8ac..91bd8ad66 100644 --- a/app/resources/boards/icestick/pinout.pcf +++ b/app/resources/boards/icestick/pinout.pcf @@ -18,17 +18,17 @@ # ------------ Red leds ------------------------------------------------------- -set_io --warn-no-port D1 99 -set_io --warn-no-port D2 98 -set_io --warn-no-port D3 97 -set_io --warn-no-port D4 96 +set_io --warn-no-port D1 99 # output +set_io --warn-no-port D2 98 # output +set_io --warn-no-port D3 97 # output +set_io --warn-no-port D4 96 # output # ------------ Green led ------------------------------------------------------ -set_io --warn-no-port D5 95 +set_io --warn-no-port D5 95 # output # ------------ IrDA ----------------------------------------------------------- -set_io --warn-no-port IrDA_TX 105 -set_io --warn-no-port IrDA_RX 106 +set_io --warn-no-port IrDA_TX 105 # output +set_io --warn-no-port IrDA_RX 106 # input #-- SD = 0, enable IrDA set_io --warn-no-port SD 107 @@ -121,22 +121,22 @@ set_io --warn-no-port BR9 45 set_io --warn-no-port BR10 44 # -------------------------- SYSTEM CLOCK ------------------------------------- -set_io --warn-no-port CLK 21 +set_io --warn-no-port CLK 21 # input # -------------------------- FTDI --------------------------------------------- # --- FTDI 0: -set_io --warn-no-port RES 66 -set_io --warn-no-port DONE 65 -set_io --warn-no-port SS 71 -set_io --warn-no-port MISO 67 -set_io --warn-no-port MOSI 68 -set_io --warn-no-port SCK 70 +set_io --warn-no-port RES 66 # input +set_io --warn-no-port DONE 65 # output +set_io --warn-no-port SS 71 # output +set_io --warn-no-port MISO 67 # input +set_io --warn-no-port MOSI 68 # output +set_io --warn-no-port SCK 70 # output # # --- FTDI 1: (Serial port) -set_io --warn-no-port DCD 1 -set_io --warn-no-port DSR 2 -set_io --warn-no-port DTR 3 -set_io --warn-no-port CTS 4 -set_io --warn-no-port RTS 7 -set_io --warn-no-port TX 8 -set_io --warn-no-port RX 9 +set_io --warn-no-port DCD 1 # output +set_io --warn-no-port DSR 2 # output +set_io --warn-no-port DTR 3 # input +set_io --warn-no-port CTS 4 # output +set_io --warn-no-port RTS 7 # output +set_io --warn-no-port TX 8 # output +set_io --warn-no-port RX 9 # input diff --git a/app/resources/boards/icezum/pinout.json b/app/resources/boards/icezum/pinout.json index 4b892bc82..d8d9f691a 100644 --- a/app/resources/boards/icezum/pinout.json +++ b/app/resources/boards/icezum/pinout.json @@ -1 +1 @@ -[{"type": "output", "name": "LED0", "value": "95"}, {"type": "output", "name": "LED1", "value": "96"}, {"type": "output", "name": "LED2", "value": "97"}, {"type": "output", "name": "LED3", "value": "98"}, {"type": "output", "name": "LED4", "value": "99"}, {"type": "output", "name": "LED5", "value": "101"}, {"type": "output", "name": "LED6", "value": "102"}, {"type": "output", "name": "LED7", "value": "104"}, {"type": "input", "name": "SW1", "value": "10"}, {"type": "input", "name": "SW2", "value": "11"}, {"type": "inout", "name": "D13", "value": "144"}, {"type": "inout", "name": "D12", "value": "143"}, {"type": "inout", "name": "D11", "value": "142"}, {"type": "inout", "name": "D10", "value": "141"}, {"type": "inout", "name": "D9", "value": "139"}, {"type": "inout", "name": "D8", "value": "138"}, {"type": "inout", "name": "D7", "value": "112"}, {"type": "inout", "name": "D6", "value": "113"}, {"type": "inout", "name": "D5", "value": "114"}, {"type": "inout", "name": "D4", "value": "115"}, {"type": "inout", "name": "D3", "value": "116"}, {"type": "inout", "name": "D2", "value": "117"}, {"type": "inout", "name": "D1", "value": "118"}, {"type": "inout", "name": "D0", "value": "119"}, {"type": "inout", "name": "DD0", "value": "78"}, {"type": "inout", "name": "DD1", "value": "79"}, {"type": "inout", "name": "DD2", "value": "80"}, {"type": "inout", "name": "DD3", "value": "81"}, {"type": "inout", "name": "DD4", "value": "88"}, {"type": "inout", "name": "DD5", "value": "87"}, {"type": "inout", "name": "GP0", "value": "37"}, {"type": "inout", "name": "GP1", "value": "38"}, {"type": "inout", "name": "GP2", "value": "39"}, {"type": "inout", "name": "GP3", "value": "41"}, {"type": "inout", "name": "GP4", "value": "42"}, {"type": "inout", "name": "GP5", "value": "43"}, {"type": "inout", "name": "GP6", "value": "49"}, {"type": "inout", "name": "GP7", "value": "50"}, {"type": "inout", "name": "ADC_SCL", "value": "91"}, {"type": "inout", "name": "ADC_SDA", "value": "90"}, {"type": "inout", "name": "ADc_INT", "value": "93"}, {"type": "input", "name": "CLK", "value": "21"}, {"type": "inout", "name": "RES", "value": "66"}, {"type": "inout", "name": "DONE", "value": "65"}, {"type": "output", "name": "SS", "value": "71"}, {"type": "input", "name": "MISO", "value": "67"}, {"type": "output", "name": "MOSI", "value": "68"}, {"type": "output", "name": "SCK", "value": "70"}, {"type": "output", "name": "DCD", "value": "1"}, {"type": "output", "name": "DSR", "value": "2"}, {"type": "input", "name": "DTR", "value": "3"}, {"type": "output", "name": "CTS", "value": "4"}, {"type": "output", "name": "RTS", "value": "7"}, {"type": "output", "name": "TX", "value": "8"}, {"type": "input", "name": "RX", "value": "9"}] \ No newline at end of file +[{"type": "output", "name": "LED0", "value": "95"}, {"type": "output", "name": "LED1", "value": "96"}, {"type": "output", "name": "LED2", "value": "97"}, {"type": "output", "name": "LED3", "value": "98"}, {"type": "output", "name": "LED4", "value": "99"}, {"type": "output", "name": "LED5", "value": "101"}, {"type": "output", "name": "LED6", "value": "102"}, {"type": "output", "name": "LED7", "value": "104"}, {"type": "input", "name": "SW1", "value": "10"}, {"type": "input", "name": "SW2", "value": "11"}, {"type": "inout", "name": "D13", "value": "144"}, {"type": "inout", "name": "D12", "value": "143"}, {"type": "inout", "name": "D11", "value": "142"}, {"type": "inout", "name": "D10", "value": "141"}, {"type": "inout", "name": "D9", "value": "139"}, {"type": "inout", "name": "D8", "value": "138"}, {"type": "inout", "name": "D7", "value": "112"}, {"type": "inout", "name": "D6", "value": "113"}, {"type": "inout", "name": "D5", "value": "114"}, {"type": "inout", "name": "D4", "value": "115"}, {"type": "inout", "name": "D3", "value": "116"}, {"type": "inout", "name": "D2", "value": "117"}, {"type": "inout", "name": "D1", "value": "118"}, {"type": "inout", "name": "D0", "value": "119"}, {"type": "inout", "name": "DD0", "value": "78"}, {"type": "inout", "name": "DD1", "value": "79"}, {"type": "inout", "name": "DD2", "value": "80"}, {"type": "inout", "name": "DD3", "value": "81"}, {"type": "inout", "name": "DD4", "value": "88"}, {"type": "inout", "name": "DD5", "value": "87"}, {"type": "inout", "name": "GP0", "value": "37"}, {"type": "inout", "name": "GP1", "value": "38"}, {"type": "inout", "name": "GP2", "value": "39"}, {"type": "inout", "name": "GP3", "value": "41"}, {"type": "inout", "name": "GP4", "value": "42"}, {"type": "inout", "name": "GP5", "value": "43"}, {"type": "inout", "name": "GP6", "value": "49"}, {"type": "inout", "name": "GP7", "value": "50"}, {"type": "output", "name": "ADC_SCL", "value": "91"}, {"type": "inout", "name": "ADC_SDA", "value": "90"}, {"type": "input", "name": "ADC_INT", "value": "93"}, {"type": "input", "name": "CLK", "value": "21"}, {"type": "input", "name": "RES", "value": "66"}, {"type": "output", "name": "DONE", "value": "65"}, {"type": "output", "name": "SS", "value": "71"}, {"type": "input", "name": "MISO", "value": "67"}, {"type": "output", "name": "MOSI", "value": "68"}, {"type": "output", "name": "SCK", "value": "70"}, {"type": "output", "name": "DCD", "value": "1"}, {"type": "output", "name": "DSR", "value": "2"}, {"type": "input", "name": "DTR", "value": "3"}, {"type": "output", "name": "CTS", "value": "4"}, {"type": "output", "name": "RTS", "value": "7"}, {"type": "output", "name": "TX", "value": "8"}, {"type": "input", "name": "RX", "value": "9"}] \ No newline at end of file diff --git a/app/resources/boards/icezum/pinout.pcf b/app/resources/boards/icezum/pinout.pcf index 6dc66a68e..a18a06d8d 100644 --- a/app/resources/boards/icezum/pinout.pcf +++ b/app/resources/boards/icezum/pinout.pcf @@ -85,17 +85,17 @@ set_io --warn-no-port GP6 49 set_io --warn-no-port GP7 50 # -------------------------- I2C ADC ------------------------------------------ -set_io --warn-no-port ADC_SCL 91 +set_io --warn-no-port ADC_SCL 91 # output set_io --warn-no-port ADC_SDA 90 -set_io --warn-no-port ADc_INT 93 +set_io --warn-no-port ADC_INT 93 # input # -------------------------- SYSTEM CLOCK ------------------------------------- set_io --warn-no-port CLK 21 # input # -------------------------- FTDI --------------------------------------------- # --- FTDI 0: -set_io --warn-no-port RES 66 -set_io --warn-no-port DONE 65 +set_io --warn-no-port RES 66 # input +set_io --warn-no-port DONE 65 # output set_io --warn-no-port SS 71 # output set_io --warn-no-port MISO 67 # input set_io --warn-no-port MOSI 68 # output diff --git a/app/resources/boards/icezum/rules.json b/app/resources/boards/icezum/rules.json index 04ded3287..d0a9d9345 100644 --- a/app/resources/boards/icezum/rules.json +++ b/app/resources/boards/icezum/rules.json @@ -1,4 +1,13 @@ { + "connect" : [ + { + "pin": "21", + "port" : { + "name": "clk", + "type": "input" + } + } + ], "initialize": [ { "pin": "95", diff --git a/app/resources/boards/icoboard/pinout.json b/app/resources/boards/icoboard/pinout.json index 35e7dfcbf..f987b1615 100644 --- a/app/resources/boards/icoboard/pinout.json +++ b/app/resources/boards/icoboard/pinout.json @@ -1 +1 @@ -[{"name": "CLK", "value": "R9"}, {"name": "LED1", "value": "C8"}, {"name": "LED2", "value": "F7"}, {"name": "LED3", "value": "K9"}, {"name": "P10", "value": "B4"}, {"name": "P11", "value": "C3"}, {"name": "P12", "value": "A2"}, {"name": "P13", "value": "A5"}, {"name": "P14", "value": "B5"}, {"name": "P15", "value": "B3"}, {"name": "P16", "value": "B6"}, {"name": "P17", "value": "B7"}, {"name": "P20", "value": "B11"}, {"name": "P21", "value": "B10"}, {"name": "P22", "value": "B9"}, {"name": "P23", "value": "D8"}, {"name": "P24", "value": "A11"}, {"name": "P25", "value": "A10"}, {"name": "P26", "value": "A9"}, {"name": "P27", "value": "B8"}, {"name": "P30", "value": "N6"}, {"name": "P31", "value": "L7"}, {"name": "P32", "value": "G5"}, {"name": "P33", "value": "L9"}, {"name": "P34", "value": "N7"}, {"name": "P35", "value": "M8"}, {"name": "P36", "value": "P9"}, {"name": "P37", "value": "N9"}, {"name": "P40", "value": "T11"}, {"name": "P41", "value": "T14"}, {"name": "P42", "value": "T15"}, {"name": "P43", "value": "T9"}, {"name": "P44", "value": "T10"}, {"name": "P45", "value": "T13"}, {"name": "P46", "value": "R14"}, {"name": "P47", "value": "R10"}, {"name": "C00", "value": "C13"}, {"name": "C01", "value": "A15"}, {"name": "C02", "value": "L12"}, {"name": "C03", "value": "J16"}, {"name": "C04", "value": "E9"}, {"name": "C05", "value": "C11"}, {"name": "C06", "value": "P14"}, {"name": "C07", "value": "J15"}, {"name": "C08", "value": "B14"}, {"name": "C09", "value": "E10"}, {"name": "C10", "value": "T16"}, {"name": "C11", "value": "J14"}, {"name": "C12", "value": "A16"}, {"name": "C13", "value": "F9"}, {"name": "C14", "value": "F14"}, {"name": "C15", "value": "K16"}, {"name": "C16", "value": "D15"}, {"name": "C17", "value": "B16"}, {"name": "C18", "value": "G13"}, {"name": "C19", "value": "G14"}, {"name": "C20", "value": "D16"}, {"name": "C21", "value": "G11"}, {"name": "C22", "value": "K15"}, {"name": "C23", "value": "L16"}, {"name": "C24", "value": "F16"}, {"name": "C25", "value": "D10"}, {"name": "C26", "value": "H14"}, {"name": "C27", "value": "H13"}, {"name": "C28", "value": "G15"}, {"name": "C29", "value": "D11"}, {"name": "C30", "value": "M16"}, {"name": "C31", "value": "H12"}, {"name": "C32", "value": "G10"}, {"name": "C33", "value": "E11"}, {"name": "C34", "value": "M15"}, {"name": "C35", "value": "N16"}, {"name": "C36", "value": "J10"}, {"name": "C37", "value": "F12"}, {"name": "C38", "value": "J11"}, {"name": "C39", "value": "R16"}, {"name": "C40", "value": "G16"}, {"name": "C41", "value": "E14"}, {"name": "C42", "value": "J13"}, {"name": "C43", "value": "K12"}, {"name": "C44", "value": "H16"}, {"name": "C45", "value": "F11"}, {"name": "C46", "value": "K13"}, {"name": "C47", "value": "K14"}, {"name": "C48", "value": "F15"}, {"name": "C49", "value": "G12"}, {"name": "C50", "value": "L11"}, {"name": "C51", "value": "M9"}, {"name": "C52", "value": "E16"}, {"name": "C53", "value": "B13"}, {"name": "C54", "value": "N12"}, {"name": "C55", "value": "L14"}, {"name": "C56", "value": "D14"}, {"name": "C57", "value": "C12"}, {"name": "C58", "value": "M12"}, {"name": "C59", "value": "M14"}, {"name": "C60", "value": "C16"}, {"name": "C61", "value": "B12"}, {"name": "C62", "value": "R15"}, {"name": "C63", "value": "L10"}] \ No newline at end of file +[{"type": "input", "name": "CLK", "value": "R9"}, {"type": "output", "name": "LED1", "value": "C8"}, {"type": "output", "name": "LED2", "value": "F7"}, {"type": "output", "name": "LED3", "value": "K9"}, {"type": "inout", "name": "P10", "value": "B4"}, {"type": "inout", "name": "P11", "value": "C3"}, {"type": "inout", "name": "P12", "value": "A2"}, {"type": "inout", "name": "P13", "value": "A5"}, {"type": "inout", "name": "P14", "value": "B5"}, {"type": "inout", "name": "P15", "value": "B3"}, {"type": "inout", "name": "P16", "value": "B6"}, {"type": "inout", "name": "P17", "value": "B7"}, {"type": "inout", "name": "P20", "value": "B11"}, {"type": "inout", "name": "P21", "value": "B10"}, {"type": "inout", "name": "P22", "value": "B9"}, {"type": "inout", "name": "P23", "value": "D8"}, {"type": "inout", "name": "P24", "value": "A11"}, {"type": "inout", "name": "P25", "value": "A10"}, {"type": "inout", "name": "P26", "value": "A9"}, {"type": "inout", "name": "P27", "value": "B8"}, {"type": "inout", "name": "P30", "value": "N6"}, {"type": "inout", "name": "P31", "value": "L7"}, {"type": "inout", "name": "P32", "value": "G5"}, {"type": "inout", "name": "P33", "value": "L9"}, {"type": "inout", "name": "P34", "value": "N7"}, {"type": "inout", "name": "P35", "value": "M8"}, {"type": "inout", "name": "P36", "value": "P9"}, {"type": "inout", "name": "P37", "value": "N9"}, {"type": "inout", "name": "P40", "value": "T11"}, {"type": "inout", "name": "P41", "value": "T14"}, {"type": "inout", "name": "P42", "value": "T15"}, {"type": "inout", "name": "P43", "value": "T9"}, {"type": "inout", "name": "P44", "value": "T10"}, {"type": "inout", "name": "P45", "value": "T13"}, {"type": "inout", "name": "P46", "value": "R14"}, {"type": "inout", "name": "P47", "value": "R10"}, {"type": "inout", "name": "C00", "value": "C13"}, {"type": "inout", "name": "C01", "value": "A15"}, {"type": "inout", "name": "C02", "value": "L12"}, {"type": "inout", "name": "C03", "value": "J16"}, {"type": "inout", "name": "C04", "value": "E9"}, {"type": "inout", "name": "C05", "value": "C11"}, {"type": "inout", "name": "C06", "value": "P14"}, {"type": "inout", "name": "C07", "value": "J15"}, {"type": "inout", "name": "C08", "value": "B14"}, {"type": "inout", "name": "C09", "value": "E10"}, {"type": "inout", "name": "C10", "value": "T16"}, {"type": "inout", "name": "C11", "value": "J14"}, {"type": "inout", "name": "C12", "value": "A16"}, {"type": "inout", "name": "C13", "value": "F9"}, {"type": "inout", "name": "C14", "value": "F14"}, {"type": "inout", "name": "C15", "value": "K16"}, {"type": "inout", "name": "C16", "value": "D15"}, {"type": "inout", "name": "C17", "value": "B16"}, {"type": "inout", "name": "C18", "value": "G13"}, {"type": "inout", "name": "C19", "value": "G14"}, {"type": "inout", "name": "C20", "value": "D16"}, {"type": "inout", "name": "C21", "value": "G11"}, {"type": "inout", "name": "C22", "value": "K15"}, {"type": "inout", "name": "C23", "value": "L16"}, {"type": "inout", "name": "C24", "value": "F16"}, {"type": "inout", "name": "C25", "value": "D10"}, {"type": "inout", "name": "C26", "value": "H14"}, {"type": "inout", "name": "C27", "value": "H13"}, {"type": "inout", "name": "C28", "value": "G15"}, {"type": "inout", "name": "C29", "value": "D11"}, {"type": "inout", "name": "C30", "value": "M16"}, {"type": "inout", "name": "C31", "value": "H12"}, {"type": "inout", "name": "C32", "value": "G10"}, {"type": "inout", "name": "C33", "value": "E11"}, {"type": "inout", "name": "C34", "value": "M15"}, {"type": "inout", "name": "C35", "value": "N16"}, {"type": "inout", "name": "C36", "value": "J10"}, {"type": "inout", "name": "C37", "value": "F12"}, {"type": "inout", "name": "C38", "value": "J11"}, {"type": "inout", "name": "C39", "value": "R16"}, {"type": "inout", "name": "C40", "value": "G16"}, {"type": "inout", "name": "C41", "value": "E14"}, {"type": "inout", "name": "C42", "value": "J13"}, {"type": "inout", "name": "C43", "value": "K12"}, {"type": "inout", "name": "C44", "value": "H16"}, {"type": "inout", "name": "C45", "value": "F11"}, {"type": "inout", "name": "C46", "value": "K13"}, {"type": "inout", "name": "C47", "value": "K14"}, {"type": "inout", "name": "C48", "value": "F15"}, {"type": "inout", "name": "C49", "value": "G12"}, {"type": "inout", "name": "C50", "value": "L11"}, {"type": "inout", "name": "C51", "value": "M9"}, {"type": "inout", "name": "C52", "value": "E16"}, {"type": "inout", "name": "C53", "value": "B13"}, {"type": "inout", "name": "C54", "value": "N12"}, {"type": "inout", "name": "C55", "value": "L14"}, {"type": "inout", "name": "C56", "value": "D14"}, {"type": "inout", "name": "C57", "value": "C12"}, {"type": "inout", "name": "C58", "value": "M12"}, {"type": "inout", "name": "C59", "value": "M14"}, {"type": "inout", "name": "C60", "value": "C16"}, {"type": "inout", "name": "C61", "value": "B12"}, {"type": "inout", "name": "C62", "value": "R15"}, {"type": "inout", "name": "C63", "value": "L10"}] \ No newline at end of file diff --git a/app/resources/boards/icoboard/pinout.pcf b/app/resources/boards/icoboard/pinout.pcf index 52ee25490..c421f1b0f 100644 --- a/app/resources/boards/icoboard/pinout.pcf +++ b/app/resources/boards/icoboard/pinout.pcf @@ -8,7 +8,7 @@ # ------------ System clock --------------------------------------------------- -set_io --warn-no-port CLK R9 +set_io --warn-no-port CLK R9 # input # ------------ LEDs map ------------------------------------------------------- @@ -29,11 +29,11 @@ set_io --warn-no-port CLK R9 # -- LED3: Red led # ------------| Green leds |--------------------------------------------------- -set_io --warn-no-port LED1 C8 -set_io --warn-no-port LED2 F7 +set_io --warn-no-port LED1 C8 # output +set_io --warn-no-port LED2 F7 # output # ------------| Red led |------------------------------------------------------ -set_io --warn-no-port LED3 K9 +set_io --warn-no-port LED3 K9 # output # ------------ PMOD connectors ------------------------------------------------ diff --git a/app/resources/boards/kefir/pinout.json b/app/resources/boards/kefir/pinout.json index 04ee67661..1a9d46ba9 100644 --- a/app/resources/boards/kefir/pinout.json +++ b/app/resources/boards/kefir/pinout.json @@ -1 +1 @@ -[{"name": "BTN1", "value": "136"}, {"name": "BTN2", "value": "134"}, {"name": "BTN3", "value": "128"}, {"name": "BTN4", "value": "122"}, {"name": "LED1", "value": "137"}, {"name": "LED2", "value": "135"}, {"name": "LED3", "value": "129"}, {"name": "LED4", "value": "121"}, {"name": "CLK", "value": "49"}, {"name": "SS_B", "value": "71"}, {"name": "AD_CS", "value": "101"}, {"name": "AD_Din", "value": "102"}, {"name": "AD_Dout", "value": "104"}, {"name": "AD_Clk", "value": "105"}, {"name": "USB_Vp_o", "value": "112"}, {"name": "USB_Vm_o", "value": "113"}, {"name": "USB_Vm_i", "value": "114"}, {"name": "USB_Vp_i", "value": "115"}, {"name": "USB_nOE", "value": "116"}, {"name": "ARDU00", "value": "138"}, {"name": "ARDU01", "value": "141"}, {"name": "ARDU02", "value": "143"}, {"name": "ARDU03", "value": "1"}, {"name": "RESET_P2", "value": "73"}, {"name": "Milk_TXD", "value": "37"}, {"name": "Milk_RXD", "value": "38"}] \ No newline at end of file +[{"type": "input", "name": "BTN1", "value": "136"}, {"type": "input", "name": "BTN2", "value": "134"}, {"type": "input", "name": "BTN3", "value": "128"}, {"type": "input", "name": "BTN4", "value": "122"}, {"type": "output", "name": "LED1", "value": "137"}, {"type": "output", "name": "LED2", "value": "135"}, {"type": "output", "name": "LED3", "value": "129"}, {"type": "output", "name": "LED4", "value": "121"}, {"type": "input", "name": "CLK", "value": "49"}, {"type": "inout", "name": "SS_B", "value": "71"}, {"type": "inout", "name": "AD_CS", "value": "101"}, {"type": "inout", "name": "AD_Din", "value": "102"}, {"type": "inout", "name": "AD_Dout", "value": "104"}, {"type": "inout", "name": "AD_Clk", "value": "105"}, {"type": "inout", "name": "USB_Vp_o", "value": "112"}, {"type": "inout", "name": "USB_Vm_o", "value": "113"}, {"type": "inout", "name": "USB_Vm_i", "value": "114"}, {"type": "inout", "name": "USB_Vp_i", "value": "115"}, {"type": "inout", "name": "USB_nOE", "value": "116"}, {"type": "inout", "name": "ARDU00", "value": "138"}, {"type": "inout", "name": "ARDU01", "value": "141"}, {"type": "inout", "name": "ARDU02", "value": "143"}, {"type": "inout", "name": "ARDU03", "value": "1"}, {"type": "inout", "name": "RESET_P2", "value": "73"}, {"type": "inout", "name": "Milk_TXD", "value": "37"}, {"type": "inout", "name": "Milk_RXD", "value": "38"}] \ No newline at end of file diff --git a/app/resources/boards/kefir/pinout.pcf b/app/resources/boards/kefir/pinout.pcf index 6be09fb5c..610078356 100644 --- a/app/resources/boards/kefir/pinout.pcf +++ b/app/resources/boards/kefir/pinout.pcf @@ -3,19 +3,19 @@ # ----------------------------------------------------------------------------- # Capacitive-sense buttons -set_io --warn-no-port BTN1 136 -set_io --warn-no-port BTN2 134 -set_io --warn-no-port BTN3 128 -set_io --warn-no-port BTN4 122 +set_io --warn-no-port BTN1 136 # input +set_io --warn-no-port BTN2 134 # input +set_io --warn-no-port BTN3 128 # input +set_io --warn-no-port BTN4 122 # input # LED outputs -set_io --warn-no-port LED1 137 -set_io --warn-no-port LED2 135 -set_io --warn-no-port LED3 129 -set_io --warn-no-port LED4 121 +set_io --warn-no-port LED1 137 # output +set_io --warn-no-port LED2 135 # output +set_io --warn-no-port LED3 129 # output +set_io --warn-no-port LED4 121 # output # Clock input -set_io --warn-no-port CLK 49 +set_io --warn-no-port CLK 49 # input # SPI Flash enable control set_io --warn-no-port SS_B 71 From ea90d52f0d037f821280fa6b24cf81699bbb86ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Thu, 26 Jan 2017 17:14:29 +0100 Subject: [PATCH 025/124] Apply rules for input unconnected ports --- app/resources/boards/icezum/rules.json | 11 +- app/scripts/services/compiler.service.js | 227 ++++++++++++++++++++--- 2 files changed, 206 insertions(+), 32 deletions(-) diff --git a/app/resources/boards/icezum/rules.json b/app/resources/boards/icezum/rules.json index d0a9d9345..0d10eb53a 100644 --- a/app/resources/boards/icezum/rules.json +++ b/app/resources/boards/icezum/rules.json @@ -1,14 +1,11 @@ { - "connect" : [ + "input" : [ { - "pin": "21", - "port" : { - "name": "clk", - "type": "input" - } + "port" : "clk", + "pin": "21" } ], - "initialize": [ + "output": [ { "pin": "95", "bit": "0" diff --git a/app/scripts/services/compiler.service.js b/app/scripts/services/compiler.service.js index e3e15476d..f250b657b 100644 --- a/app/scripts/services/compiler.service.js +++ b/app/scripts/services/compiler.service.js @@ -322,25 +322,116 @@ angular.module('icestudio') return null; } + this.getInitPorts = getInitPorts; + function getInitPorts(project) { + // Find not connected input wire ports to initialize + + var i, j; + var initPorts = []; + var inputPorts = []; + var ncInputPorts = []; + var blocks = project.design.graph.blocks; + var wires = project.design.graph.wires; + var dependencies = project.dependencies; + + // Find all input ports in: + // - Code blocks + // - Generic blocks + for (i in blocks) { + var block = blocks[i]; + if (block) { + if (block.type === 'basic.code') { + // Code block + for (j in block.data.ports.in) { + if (!block.data.ports.in[j].range) { + inputPorts.push({ + block: block.id, + port: block.data.ports.in[j].name + }); + } + } + // block.data.ports.in + } + else if (!block.type.startsWith('basic.')) { + // Generic block + var genericBlock = dependencies[block.type]; + var subBlocks = genericBlock.design.graph.blocks; + for (j in subBlocks) { + if (subBlocks[j].type === 'basic.input' && !subBlocks[j].data.range) { + inputPorts.push({ + block: block.id, + port: subBlocks[j].id, + name: subBlocks[j].data.name + }); + } + } + } + } + } + + //console.log('INPUT PORTS', inputPorts); + + // Filter not connected input ports + for (i in inputPorts) { + var connected = false; + var inputPort = inputPorts[i]; + for (j in wires) { + var target = wires[j].target; + if (target.block === inputPort.block && target.port === inputPort.port) { + connected = true; + break; + } + } + if (!connected) { + ncInputPorts.push(inputPort); + } + } + + //console.log('NC INPUT PORTS', ncInputPorts); + + // Filter ports defined in rules + var allInitPorts = boards.selectedBoard.rules.input; + for (i in allInitPorts) { + for (j in ncInputPorts) { + if (ncInputPorts[j].name === allInitPorts[i].port) { + initPorts.push({ + block: ncInputPorts[j].block, + port: ncInputPorts[j].port, + name: ncInputPorts[j].name, + pin: allInitPorts[i].pin + }); + } + } + } + + console.log('INIT PORTS', initPorts); + + return initPorts; + } + this.getInitPins = getInitPins; function getInitPins(blocks) { + // Find not used output pins to initialize + + var i; var initPins = []; var usedPins = []; - for (var i in blocks) { + // Find all set output pins + for (i in blocks) { var block = blocks[i]; - if (block.type === 'basic.input' || - block.type === 'basic.output') { + if (block.type === 'basic.output') { for (var p in block.data.pins) { usedPins.push(block.data.virtual ? '' : block.data.pins[p].value); } } } - var allInitPins = boards.selectedBoard.rules.initialize; - for (var j in allInitPins) { - if (usedPins.indexOf(allInitPins[j].pin) === -1) { - initPins.push(allInitPins[j]); + // Filter pins defined in rules + var allInitPins = boards.selectedBoard.rules.output; + for (i in allInitPins) { + if (usedPins.indexOf(allInitPins[i].pin) === -1) { + initPins.push(allInitPins[i]); } } @@ -348,8 +439,7 @@ angular.module('icestudio') } function verilogCompiler(name, project, opt) { - var data; - var code = ''; + var i, data, block, code = ''; if (project && project.design && @@ -362,13 +452,72 @@ angular.module('icestudio') if (name) { + // Initialize input ports + + if (name === 'main') { + + var initPorts = opt && opt.initPorts || getInitPorts(project); + for (i in initPorts) { + var initPort = initPorts[i]; + + // Find existing input block with the initPort value + var found = false; + var source = { + block: initPort.name, + port: 'out' + }; + for (i in blocks) { + block = blocks[i]; + if (block.type === 'basic.input' && !block.data.range && !block.data.virtual) { + if (initPort.pin === block.data.pins[0].value) { + found = true; + source.block = block.id; + break; + } + } + } + + if (!found) { + // Add imaginary input block with the initPort value + project.design.graph.blocks.push({ + id: initPort.name, + type: 'basic.input', + data: { + name: initPort.name, + pins: [ + { + index: '0', + value: initPort.pin + } + ], + virtual: false + } + }); + } + + // Add imaginary wire between the imaginary input block and the initPort + project.design.graph.wires.push({ + source: { + block: source.block, + port: source.port + }, + target: { + block: initPort.block, + port: initPort.port + } + }); + } + } + var params = getParams(project); var ports = getPorts(project); var content = getContent(name, project); + // Initialize output pins + if (name === 'main') { - // Initialize pins + // Initialize output pins var initPins = opt && opt.initPins || getInitPins(blocks); var n = initPins.length; @@ -376,16 +525,16 @@ angular.module('icestudio') if (n > 0) { // Declare m port ports.out.push({ - name: 'm', + name: 'vinit', range: '[0:' + (n-1) + ']' }); // Generate port value var value = n.toString() + '\'b'; - for (var j in initPins) { - value += initPins[j].bit; + for (i in initPins) { + value += initPins[i].bit; } // Assign m port - content += '\nassign m = ' + value + ';'; + content += '\nassign vinit = ' + value + ';'; } } @@ -406,15 +555,15 @@ angular.module('icestudio') // Code modules - for (var i in blocks) { - var block = blocks[i]; + for (i in blocks) { + block = blocks[i]; if (block) { if (block.type === 'basic.code') { data = { name: name + '_' + digestId(block.id), params: block.data.params, ports: block.data.ports, - content: block.data.code + content: block.data.code.replace(/\n+/g, '\n').replace(/\n$/g, '') }; code += module(data); } @@ -426,12 +575,12 @@ angular.module('icestudio') } function pcfCompiler(project, opt) { - var code = ''; + var i, j, block, code = ''; var blocks = project.design.graph.blocks; var pin, value; - for (var i in blocks) { - var block = blocks[i]; + for (i in blocks) { + block = blocks[i]; if (block.type === 'basic.input' || block.type === 'basic.output') { @@ -458,17 +607,45 @@ angular.module('icestudio') } } - // Set port of initialized pins + // Declare init input ports + + var initPorts = opt && opt.initPorts || getInitPorts(project); + for (i in initPorts) { + var initPort = initPorts[i]; + + // Find existing input block with the initPort value + var found = false; + for (j in blocks) { + block = blocks[j]; + if (block.type === 'basic.input' && !block.data.range && !block.data.virtual) { + if (initPort.pin === block.data.pins[0].value) { + found = true; + break; + } + } + } + + if (!found) { + code += 'set_io v'; + code += initPorts[i].name; + code += ' '; + code += initPorts[i].pin; + code += '\n'; + } + } + + // Declare init output pins + var initPins = opt && opt.initPins || getInitPins(blocks); if (initPins.length > 1) { - for (var j in initPins) { - code += 'set_io m[' + j + '] '; - code += initPins[j].pin; + for (i in initPins) { + code += 'set_io vinit[' + i + '] '; + code += initPins[i].pin; code += '\n'; } } else if (initPins.length > 0) { - code += 'set_io m '; + code += 'set_io vinit '; code += initPins[0].pin; code += '\n'; } From b3b6a7d743d84e7ca712e78b44a1df18aae9e28f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Thu, 26 Jan 2017 17:45:24 +0100 Subject: [PATCH 026/124] Optimize initPorts detection --- app/scripts/services/compiler.service.js | 15 +++++++++++---- app/scripts/services/tools.service.js | 7 ++++--- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/app/scripts/services/compiler.service.js b/app/scripts/services/compiler.service.js index f250b657b..e00d15b0e 100644 --- a/app/scripts/services/compiler.service.js +++ b/app/scripts/services/compiler.service.js @@ -404,18 +404,19 @@ angular.module('icestudio') } } - console.log('INIT PORTS', initPorts); + //console.log('INIT PORTS', initPorts); return initPorts; } this.getInitPins = getInitPins; - function getInitPins(blocks) { + function getInitPins(project) { // Find not used output pins to initialize var i; var initPins = []; var usedPins = []; + var blocks = project.design.graph.blocks; // Find all set output pins for (i in blocks) { @@ -519,7 +520,7 @@ angular.module('icestudio') // Initialize output pins - var initPins = opt && opt.initPins || getInitPins(blocks); + var initPins = opt && opt.initPins || getInitPins(project); var n = initPins.length; if (n > 0) { @@ -609,9 +610,14 @@ angular.module('icestudio') // Declare init input ports + var used = []; var initPorts = opt && opt.initPorts || getInitPorts(project); for (i in initPorts) { var initPort = initPorts[i]; + if (used.indexOf(initPort.pin) !== -1) { + break; + } + used.push(initPort.pin); // Find existing input block with the initPort value var found = false; @@ -620,6 +626,7 @@ angular.module('icestudio') if (block.type === 'basic.input' && !block.data.range && !block.data.virtual) { if (initPort.pin === block.data.pins[0].value) { found = true; + used.push(initPort.pin); break; } } @@ -636,7 +643,7 @@ angular.module('icestudio') // Declare init output pins - var initPins = opt && opt.initPins || getInitPins(blocks); + var initPins = opt && opt.initPins || getInitPins(project); if (initPins.length > 1) { for (i in initPins) { code += 'set_io vinit[' + i + '] '; diff --git a/app/scripts/services/tools.service.js b/app/scripts/services/tools.service.js index f1df4474c..c67aa0a56 100644 --- a/app/scripts/services/tools.service.js +++ b/app/scripts/services/tools.service.js @@ -133,9 +133,10 @@ angular.module('icestudio') nodeFs.mkdirSync(utils.BUILD_DIR); } project.update(); - var blocks = project.get('design').graph.blocks; - var initPins = compiler.getInitPins(blocks); - var opt = { initPins: initPins }; + var opt = { + initPorts: compiler.getInitPorts(project.get()), + initPins: compiler.getInitPins(project.get()) + }; var verilog = compiler.generate('verilog', project.get(), opt); var pcf = compiler.generate('pcf', project.get(), opt); nodeFs.writeFileSync(nodePath.join(utils.BUILD_DIR, 'main.v'), verilog, 'utf8'); From 6db51074064acf9396cb5156caec3129228c9f9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Thu, 26 Jan 2017 17:59:01 +0100 Subject: [PATCH 027/124] Apply rules also in Code block input ports --- app/scripts/services/compiler.service.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/scripts/services/compiler.service.js b/app/scripts/services/compiler.service.js index e00d15b0e..11819ba6d 100644 --- a/app/scripts/services/compiler.service.js +++ b/app/scripts/services/compiler.service.js @@ -393,11 +393,11 @@ angular.module('icestudio') var allInitPorts = boards.selectedBoard.rules.input; for (i in allInitPorts) { for (j in ncInputPorts) { - if (ncInputPorts[j].name === allInitPorts[i].port) { + if (ncInputPorts[j].name === allInitPorts[i].port || ncInputPorts[j].port === allInitPorts[i].port) { initPorts.push({ block: ncInputPorts[j].block, port: ncInputPorts[j].port, - name: ncInputPorts[j].name, + name: ncInputPorts[j].name || ncInputPorts[j].port, pin: allInitPorts[i].pin }); } From 9fb45691947323b4c88b5f7cd9ad1864c73b8935 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Thu, 26 Jan 2017 18:10:31 +0100 Subject: [PATCH 028/124] Fix shortcut directory in the windows intaller --- scripts/windows_installer.nsi | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/windows_installer.nsi b/scripts/windows_installer.nsi index fe42f0b65..9770b29d0 100755 --- a/scripts/windows_installer.nsi +++ b/scripts/windows_installer.nsi @@ -140,9 +140,9 @@ Section "${NAME} ${VERSION}" SetShellVarContext all # define shortcut - CreateDirectory "$SMPROGRAMS\{NAME}" - CreateShortCut "$SMPROGRAMS\{NAME}\${NAME}.lnk" "$INSTDIR\icestudio.exe" - CreateShortCut "$SMPROGRAMS\{NAME}\Uninstall ${NAME}.lnk" "$INSTDIR\uninstaller.exe" + CreateDirectory "$SMPROGRAMS\${NAME}" + CreateShortCut "$SMPROGRAMS\${NAME}\${NAME}.lnk" "$INSTDIR\icestudio.exe" + CreateShortCut "$SMPROGRAMS\${NAME}\Uninstall ${NAME}.lnk" "$INSTDIR\uninstaller.exe" # register .ice files ${registerExtension} "$INSTDIR\icestudio.exe" ".ice" "Icestudio project" @@ -168,7 +168,7 @@ Section "Uninstall" SetShellVarContext all # delete the installed files - Delete "$SMPROGRAMS\{NAME}" + Delete "$SMPROGRAMS\${NAME}" RMDir /r $INSTDIR # unregister .ice files From e25a06b578dfe3ba89881b18b2a077827e1b044e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Fri, 27 Jan 2017 08:45:44 +0100 Subject: [PATCH 029/124] Keep the pan&zoom state updated in the command manager --- app/scripts/graphics/dia/joint.dia.command.js | 26 +++++++-------- app/scripts/services/graph.service.js | 33 ++++++++++--------- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/app/scripts/graphics/dia/joint.dia.command.js b/app/scripts/graphics/dia/joint.dia.command.js index bd9d7dd92..a5626aad9 100755 --- a/app/scripts/graphics/dia/joint.dia.command.js +++ b/app/scripts/graphics/dia/joint.dia.command.js @@ -29,6 +29,8 @@ joint.dia.CommandManager = Backbone.Model.extend({ listen: function() { + this.listenTo(this.graph, 'state', this.updateState, this); + this.listenTo(this.graph, 'all', this.addCommand, this); this.listenTo(this.graph, 'batch:start', this.initBatchCommand, this); @@ -46,6 +48,10 @@ joint.dia.CommandManager = Backbone.Model.extend({ return cmd; }, + updateState: function(state) { + this.state = state; + }, + addCommand: function(cmdName, cell, graph, options) { if (this.zeroProject && cmdName === 'board') { @@ -248,8 +254,9 @@ joint.dia.CommandManager = Backbone.Model.extend({ break; case 'remove': - this.graph.addCell(cmd.data.attributes); - break; + cmd.data.attributes.state = this.state; + this.graph.addCell(cmd.data.attributes); + break; case 'board': this.triggerBoard(cmd.data.previous); @@ -307,6 +314,7 @@ joint.dia.CommandManager = Backbone.Model.extend({ switch (cmd.action) { case 'add': + cmd.data.attributes.state = this.state; this.graph.addCell(cmd.data.attributes); break; @@ -342,33 +350,23 @@ joint.dia.CommandManager = Backbone.Model.extend({ this.listen(); }, - undo: function(state) { + undo: function() { var command = this.undoStack.pop(); this.triggerChange(); if (command) { - - if (command.action === 'add') { - command.data.attributes.state = state; - } - this.revertCommand(command); this.redoStack.push(command); } }, - redo: function(state) { + redo: function() { var command = this.redoStack.pop(); if (command) { - - if (command.action === 'add') { - command.data.attributes.state = state; - } - this.applyCommand(command); this.undoStack.push(command); this.triggerChange(); diff --git a/app/scripts/services/graph.service.js b/app/scripts/services/graph.service.js index 490128edf..b0d6780cf 100644 --- a/app/scripts/services/graph.service.js +++ b/app/scripts/services/graph.service.js @@ -261,22 +261,25 @@ angular.module('icestudio') onPan: function(newPan) { state.pan = newPan; selectionView.options.state = state; - - var cells = graph.getCells(); - - _.each(cells, function(cell) { - if (!cell.isLink()) { - cell.attributes.state = state; - var elementView = paper.findViewByModel(cell); - // Pan blocks - elementView.updateBox(); - // Pan selection boxes - selectionView.updateBox(elementView.model); - } - }); + graph.trigger('state', state); + updateCellBoxes(); } }); + function updateCellBoxes() { + var cells = graph.getCells(); + _.each(cells, function(cell) { + if (!cell.isLink()) { + cell.attributes.state = state; + var elementView = paper.findViewByModel(cell); + // Pan blocks + elementView.updateBox(); + // Pan selection boxes + selectionView.updateBox(elementView.model); + } + }); + } + // Command Manager commandManager = new joint.dia.CommandManager({ @@ -446,12 +449,12 @@ angular.module('icestudio') this.undo = function() { disableSelected(); - commandManager.undo(state); + commandManager.undo(); }; this.redo = function() { disableSelected(); - commandManager.redo(state); + commandManager.redo(); }; this.clearAll = function() { From 1b2948ee6046ada09a784ca0c227ff997d5a15a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Fri, 27 Jan 2017 12:25:38 +0100 Subject: [PATCH 030/124] Basic GUI rules detection. Style improvements --- app/resources/boards/icezum/rules.json | 5 +- app/scripts/graphics/shapes/joint.shapes.js | 130 +++++++++++++++----- app/scripts/services/blocks.service.js | 24 +++- app/scripts/services/project.service.js | 3 + 4 files changed, 128 insertions(+), 34 deletions(-) diff --git a/app/resources/boards/icezum/rules.json b/app/resources/boards/icezum/rules.json index 0d10eb53a..b297d6b0c 100644 --- a/app/resources/boards/icezum/rules.json +++ b/app/resources/boards/icezum/rules.json @@ -2,7 +2,10 @@ "input" : [ { "port" : "clk", - "pin": "21" + "pin": { + "name": "CLK", + "value": "21" + } } ], "output": [ diff --git a/app/scripts/graphics/shapes/joint.shapes.js b/app/scripts/graphics/shapes/joint.shapes.js index 5a076ef8e..9dc13a51f 100644 --- a/app/scripts/graphics/shapes/joint.shapes.js +++ b/app/scripts/graphics/shapes/joint.shapes.js @@ -3,6 +3,7 @@ var os = require('os'); var sha1 = require('sha1'); +const WIRE_WIDTH = 1.5; const DARWIN = Boolean(os.platform().indexOf('darwin') > -1); if (DARWIN) { @@ -18,7 +19,12 @@ joint.shapes.ice = {}; joint.shapes.ice.Model = joint.shapes.basic.Generic.extend({ markup: '', - portMarkup: '', + portMarkup: ' \ + \ + \ + \ + \ + ', defaults: joint.util.deepSupplement({ type: 'ice.Model', @@ -70,7 +76,19 @@ joint.shapes.ice.Model = joint.shapes.basic.Generic.extend({ }, '.port-wire': { stroke: '#888', - 'stroke-width': 2 + 'stroke-width': WIRE_WIDTH + }, + '.port-default': { + display: 'none', + x: '-40', + y: '-10', + width: '20', + height: '20', + rx: '3', + ry: '3', + stroke: '#888', + 'stroke-width': 1, + fill: '#FBFBC9' } } }, joint.shapes.basic.Generic.prototype.defaults), @@ -113,6 +131,7 @@ joint.shapes.ice.Model = joint.shapes.basic.Generic.extend({ var portLabelSelector = portSelector + '>.port-label'; var portWireSelector = portSelector + '>.port-wire'; var portBodySelector = portSelector + '>.port-body'; + var portDefaultSelector = portSelector + '>.port-default'; attrs[portSelector] = { ref: '.body' @@ -131,13 +150,20 @@ joint.shapes.ice.Model = joint.shapes.basic.Generic.extend({ } }; + if (port.default && port.default.apply) { + attrs[portDefaultSelector] = { + display: 'visible' + }; + } + if ((type === 'leftPorts') || (type === 'topPorts')) { attrs[portSelector]['pointer-events'] = 'none'; attrs[portWireSelector]['pointer-events'] = 'none'; } - var offset = (port.size && port.size > 1) ? 6 : 0; + var offset = (port.size && port.size > 1) ? 6 : 1; var pos = Math.round((index + 0.5) / total * port.gridUnits) / port.gridUnits; + var lineX = (port.default && port.default.apply) ? '-20' : '0'; switch (type) { case 'left': @@ -147,7 +173,7 @@ joint.shapes.ice.Model = joint.shapes.basic.Generic.extend({ attrs[portLabelSelector]['y'] = -5-offset; attrs[portLabelSelector]['text-anchor'] = 'end'; attrs[portWireSelector]['y'] = pos; - attrs[portWireSelector]['d'] = 'M 0 0 L 16 0'; + attrs[portWireSelector]['d'] = 'M ' + lineX + ' 0 L 16 0'; break; case 'right': attrs[portSelector]['ref-dx'] = 8; @@ -162,7 +188,7 @@ joint.shapes.ice.Model = joint.shapes.basic.Generic.extend({ attrs[portSelector]['ref-y'] = -8; attrs[portSelector]['ref-x'] = pos; attrs[portLabelSelector]['dx'] = 5+offset; - attrs[portLabelSelector]['y'] = 4; + attrs[portLabelSelector]['y'] = 2; attrs[portLabelSelector]['text-anchor'] = 'start'; attrs[portWireSelector]['x'] = pos; attrs[portWireSelector]['d'] = 'M 0 0 L 0 16'; @@ -171,7 +197,7 @@ joint.shapes.ice.Model = joint.shapes.basic.Generic.extend({ attrs[portSelector]['ref-dy'] = 8; attrs[portSelector]['ref-x'] = pos; attrs[portLabelSelector]['dx'] = 5+offset; - attrs[portLabelSelector]['y'] = -4; + attrs[portLabelSelector]['y'] = -2; attrs[portLabelSelector]['text-anchor'] = 'start'; attrs[portWireSelector]['x'] = pos; attrs[portWireSelector]['d'] = 'M 0 0 L 0 -16'; @@ -239,23 +265,28 @@ joint.shapes.ice.ModelView = joint.dia.ElementView.extend({ }, updateBox: function() { - var port, wireWidth; + var i, port; var bbox = this.model.getBBox(); var state = this.model.get('state'); var leftPorts = this.model.get('leftPorts'); var rightPorts = this.model.get('rightPorts'); // Render ports width - this.$('.port-wire').css('stroke-width', 2 * state.zoom); - for (var i in leftPorts) { + var width = WIRE_WIDTH * state.zoom; + this.$('.port-wire').css('stroke-width', width); + this.$('.port-default').css('stroke-width', width); + // Render buses + for (i in leftPorts) { port = leftPorts[i]; - wireWidth = (port.size > 1) ? 8 : 2; - this.$('#port-wire-' + port.id).css('stroke-width', wireWidth * state.zoom); + if (port.size > 1) { + this.$('#port-wire-' + port.id).css('stroke-width', width * 4); + } } - for (var o in rightPorts) { - port = rightPorts[o]; - wireWidth = (port.size > 1) ? 8 : 2; - this.$('#port-wire-' + port.id).css('stroke-width', wireWidth * state.zoom); + for (i in rightPorts) { + port = rightPorts[i]; + if (port.size > 1) { + this.$('#port-wire-' + port.id).css('stroke-width', width * 4); + } } /*if (this.$box.css('z-index') > this.model.attributes.zindex) { @@ -338,6 +369,7 @@ joint.shapes.ice.GenericView = joint.shapes.ice.ModelView.extend({ mouseupcard: function(/*evt, x, y*/) { this.down = false; + this.mouseovercard(); }, mousedowncard: function(/*evt, x, y*/) { @@ -774,23 +806,27 @@ joint.shapes.ice.CodeView = joint.shapes.ice.ModelView.extend({ }, updateBox: function() { - var port, wireWidth; + var i, port; var bbox = this.model.getBBox(); var state = this.model.get('state'); var leftPorts = this.model.get('leftPorts'); var rightPorts = this.model.get('rightPorts'); // Render ports width - this.$('.port-wire').css('stroke-width', 2 * state.zoom); - for (var i in leftPorts) { + var width = WIRE_WIDTH * state.zoom; + this.$('.port-wire').css('stroke-width', width); + // Render buses + for (i in leftPorts) { port = leftPorts[i]; - wireWidth = (port.size > 1) ? 8 : 2; - this.$('#port-wire-' + port.id).css('stroke-width', wireWidth * state.zoom); + if (port.size > 1) { + this.$('#port-wire-' + port.id).css('stroke-width', width * 4); + } } - for (var o in rightPorts) { - port = rightPorts[o]; - wireWidth = (port.size > 1) ? 8 : 2; - this.$('#port-wire-' + port.id).css('stroke-width', wireWidth * state.zoom); + for (i in rightPorts) { + port = rightPorts[i]; + if (port.size > 1) { + this.$('#port-wire-' + port.id).css('stroke-width', width * 4); + } } this.$box.css({ width: bbox.width * state.zoom, @@ -993,6 +1029,26 @@ joint.shapes.ice.Wire = joint.dia.Link.extend({ '' ].join(''), + toolMarkup: [ + '', + '', + '', + '', + 'Remove link.', + '', + '' + ].join(''), + + vertexMarkup: [ + '', + '', + '', + '', + 'Remove vertex.', + '', + '' + ].join(''), + defaults: joint.util.deepSupplement({ type: 'ice.Wire', @@ -1015,8 +1071,7 @@ joint.shapes.ice.Wire = joint.dia.Link.extend({ '.connection': { 'stroke-width': 2, stroke: '#888' - }, - '.marker-vertex': { r: 8 } + } }, router: { name: 'ice' }, @@ -1033,18 +1088,29 @@ joint.shapes.ice.WireView = joint.dia.LinkView.extend({ var self = this; setTimeout(function() { - var portName = self.model.get('source').port; + var i, port, portName = self.model.get('source').port; var rightPorts = self.sourceView.model.get('rightPorts'); + var bottomPorts = self.sourceView.model.get('bottomPorts'); // Initialize wire properties - var port; - for (var o in rightPorts) { - port = rightPorts[o]; + for (i in rightPorts) { + port = rightPorts[i]; + if (portName === port.id) { + self.model.attributes.size = port.size; // For wire size connection validation + self.$('.connection').css('stroke-width', WIRE_WIDTH * ((port.size > 1) ? 4 : 1)); + self.model.label(0, {attrs: { text: { text: (port.size > 1) ? '' + port.size + '' : '' } } }); + self.model.bifurcationMarkup = self.model.bifurcationMarkup.replace(/<%= r %>/g, WIRE_WIDTH * ((port.size > 1) ? 4 : 3)); + self.update(); + break; + } + } + for (i in bottomPorts) { + port = bottomPorts[i]; if (portName === port.id) { self.model.attributes.size = port.size; // For wire size connection validation - self.$('.connection').css('stroke-width', (port.size > 1) ? 8 : 2); + self.$('.connection').css('stroke-width', WIRE_WIDTH * ((port.size > 1) ? 4 : 1)); self.model.label(0, {attrs: { text: { text: (port.size > 1) ? '' + port.size + '' : '' } } }); - self.model.bifurcationMarkup = self.model.bifurcationMarkup.replace(/<%= r %>/g, (port.size > 1) ? 8 : 4); + self.model.bifurcationMarkup = self.model.bifurcationMarkup.replace(/<%= r %>/g, WIRE_WIDTH * ((port.size > 1) ? 4 : 3)); self.update(); break; } diff --git a/app/scripts/services/blocks.service.js b/app/scripts/services/blocks.service.js index 1bc16c31d..729b6da5b 100644 --- a/app/scripts/services/blocks.service.js +++ b/app/scripts/services/blocks.service.js @@ -485,6 +485,24 @@ angular.module('icestudio') return cell; } + function hasInputRule(port) { + var _default; + var rules = boards.selectedBoard.rules; + if (rules) { + var allInitPorts = rules.input; + if (allInitPorts) { + for (var i in allInitPorts) { + if (port === allInitPorts[i].port){ + _default = allInitPorts[i].pin; + _default.apply = true; + break; + } + } + } + } + return _default; + } + function loadGeneric(instance, block) { var i; var leftPorts = []; @@ -498,10 +516,14 @@ angular.module('icestudio') var item = block.design.graph.blocks[i]; if (item.type === 'basic.input') { data = block.design.graph.blocks[i].data; + if (!item.data.range) { + data.default = hasInputRule(item.data.name); + } leftPorts.push({ id: item.id, label: item.data.name + (item.data.range || ''), - size: data.pins ? data.pins.length : (data.size || 1) + size: data.pins ? data.pins.length : (data.size || 1), + default: data.default }); } else if (item.type === 'basic.output') { diff --git a/app/scripts/services/project.service.js b/app/scripts/services/project.service.js index c52d63441..00dfccb8b 100644 --- a/app/scripts/services/project.service.js +++ b/app/scripts/services/project.service.js @@ -477,6 +477,9 @@ angular.module('icestudio') var newSubDependencies = findSubDependencies(allDependencies[type]); subDependencies = subDependencies.concat(newSubDependencies); } + else if (type === 'basic.input') { + delete blocks[i].data.default; + } } return _.unique(subDependencies); } From b40a5434dac7d2618a56c62f91bd9ab18b7699ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Fri, 27 Jan 2017 12:36:54 +0100 Subject: [PATCH 031/124] Toogle select without Ctrl --- app/scripts/graphics/shapes/joint.shapes.js | 2 +- app/scripts/services/graph.service.js | 7 +---- app/styles/design.css | 34 ++++++++++++++------- 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/app/scripts/graphics/shapes/joint.shapes.js b/app/scripts/graphics/shapes/joint.shapes.js index 9dc13a51f..40ea37908 100644 --- a/app/scripts/graphics/shapes/joint.shapes.js +++ b/app/scripts/graphics/shapes/joint.shapes.js @@ -274,7 +274,7 @@ joint.shapes.ice.ModelView = joint.dia.ElementView.extend({ // Render ports width var width = WIRE_WIDTH * state.zoom; this.$('.port-wire').css('stroke-width', width); - this.$('.port-default').css('stroke-width', width); + this.$('.port-default').css('stroke-width', state.zoom); // Render buses for (i in leftPorts) { port = leftPorts[i]; diff --git a/app/scripts/services/graph.service.js b/app/scripts/services/graph.service.js index 7cf0db71d..d559e32b9 100644 --- a/app/scripts/services/graph.service.js +++ b/app/scripts/services/graph.service.js @@ -13,7 +13,6 @@ angular.module('icestudio') var z = { index: 100 }; - var ctrlPressed = false; var graph = null; var paper = null; @@ -39,10 +38,6 @@ angular.module('icestudio') // Functions - $(document).on('keydown', function(event) { - ctrlPressed = event.keyCode === 17; - }); - this.getState = function() { // Clone state return utils.clone(state); @@ -319,7 +314,7 @@ angular.module('icestudio') }); } // Toggle selection - if ((evt.which === 3) && (evt.ctrlKey || evt.metaKey)) { + if (evt.which === 3) { var cell = selection.get($(evt.target).data('model')); selection.reset(selection.without(cell)); selectionView.destroySelectionBox(paper.findViewByModel(cell)); diff --git a/app/styles/design.css b/app/styles/design.css index 30534479d..e5e816020 100644 --- a/app/styles/design.css +++ b/app/styles/design.css @@ -75,7 +75,7 @@ position: absolute; background: #C0DFEB; border-radius: 5px; - border: 2px solid #888; + border: 1px solid #888; pointer-events: none; -webkit-user-select: none; user-select: none; @@ -141,7 +141,7 @@ position: absolute; background: #E2FBC9; border-radius: 5px; - border: 2px solid #888; + border: 1px solid #888; pointer-events: none; -webkit-user-select: none; user-select: none; @@ -162,7 +162,7 @@ position: absolute; background: #FBFBC9; border-radius: 5px; - border: 2px solid #888; + border: 1px solid #888; pointer-events: none; -webkit-user-select: none; user-select: none; @@ -171,7 +171,7 @@ .fpga-port label { display: block; - margin-top: 4px; + margin-top: 5px; margin-bottom: 5px; text-align: center; font-size: 13px; @@ -189,7 +189,7 @@ .fpga-port div .select2 { position: relative; - width: 82px; + width: 84px; left: 5px; margin-bottom: 4px; pointer-events: auto; @@ -199,6 +199,14 @@ font-size: 13px; } +.select2-container--default .select2-selection--single { + border: 1px solid #888; +} + +.select2-dropdown { + border: 1px solid #888; +} + .bigdrop { width: 92px !important; font-size: 13px; @@ -208,7 +216,7 @@ position: absolute; background: #FBF0C9; border-radius: 5px; - border: 2px solid #888; + border: 1px solid #888; pointer-events: none; -webkit-user-select: none; user-select: none; @@ -225,7 +233,8 @@ .constant-block label { display: block; - margin-top: 8px; + margin-top: 5px; + margin-bottom: 5px; text-align: center; font-size: 13px; color: #444; @@ -238,8 +247,11 @@ text-align: center; left: 5px; bottom: 5px; - width: 82px; + width: 84px; + height: 28px; font-size: 13px; + border-radius: 4px; + border: 1px solid #888; pointer-events: auto; } @@ -247,7 +259,7 @@ position: absolute; background: #C0DFEB; border-radius: 5px; - border: 2px solid #888; + border: 1px solid #888; pointer-events: none; -webkit-user-select: none; user-select: none; @@ -272,7 +284,7 @@ position: absolute; background: #DDD; border-radius: 5px; - border: 2px solid #888; + border: 1px solid #888; pointer-events: none; -webkit-user-select: none; user-select: none; @@ -298,7 +310,7 @@ } .highlight { - box-shadow: 0 0 30px 0 rgba(200,200,200,1); + box-shadow: 0 0 8px 8px rgba(42, 118, 198, 0.2); } .selectionarea { From 5940069078dfe7389b445f379b73f9bd5f8efe86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Fri, 27 Jan 2017 13:33:21 +0100 Subject: [PATCH 032/124] Colored highlight in blocks --- app/scripts/services/graph.service.js | 68 +++++++++++++++++++++++---- app/styles/design.css | 20 +++++++- 2 files changed, 77 insertions(+), 11 deletions(-) diff --git a/app/scripts/services/graph.service.js b/app/scripts/services/graph.service.js index d559e32b9..610e9b1a6 100644 --- a/app/scripts/services/graph.service.js +++ b/app/scripts/services/graph.service.js @@ -331,7 +331,7 @@ angular.module('icestudio') // Right button selection.add(cellView.model); selectionView.createSelectionBox(cellView); - cellView.$box.removeClass('highlight'); + unhighlight(cellView); } // Update wires on obstacles var cells = graph.getCells(); @@ -407,11 +407,9 @@ angular.module('icestudio') paper.on('cell:mouseover', function(cellView/*, evt*/) { if (!self.mousedown) { if (!cellView.model.isLink()) { - cellView.$box.addClass('highlight'); - if (cellView && !cellView.model.isLink()) { - if (cellView.$box.css('z-index') < z.index) { - cellView.$box.css('z-index', ++z.index); - } + highlight(cellView); + if (cellView.$box.css('z-index') < z.index) { + cellView.$box.css('z-index', ++z.index); } } } @@ -420,7 +418,7 @@ angular.module('icestudio') paper.on('cell:mouseout', function(cellView/*, evt*/) { if (!self.mousedown) { if (!cellView.model.isLink()) { - cellView.$box.removeClass('highlight'); + unhighlight(cellView); } } }); @@ -578,7 +576,7 @@ angular.module('icestudio') var cellView = paper.findViewByModel(cell); selection.add(cell); selectionView.createSelectionBox(cellView); - cellView.$box.removeClass('highlight'); + unhighlight(cellView); } }); } @@ -592,11 +590,63 @@ angular.module('icestudio') var cellView = paper.findViewByModel(cell); selection.add(cell); selectionView.createSelectionBox(cellView); - cellView.$box.removeClass('highlight'); + unhighlight(cellView); } }); }; + function highlight(cellView) { + if (cellView) { + switch(cellView.model.attributes.type) { + case 'ice.Input': + case 'ice.Output': + if (cellView.model.attributes.data.virtual) { + cellView.$box.addClass('highlight-green'); + } + else { + cellView.$box.addClass('highlight-yellow'); + } + break; + case 'ice.Constant': + cellView.$box.addClass('highlight-orange'); + break; + case 'ice.Code': + case 'ice.Generic': + cellView.$box.addClass('highlight-blue'); + break; + case 'ice.Info': + cellView.$box.addClass('highlight-gray'); + break; + } + } + } + + function unhighlight(cellView) { + if (cellView) { + switch(cellView.model.attributes.type) { + case 'ice.Input': + case 'ice.Output': + if (cellView.model.attributes.data.virtual) { + cellView.$box.removeClass('highlight-green'); + } + else { + cellView.$box.removeClass('highlight-yellow'); + } + break; + case 'ice.Constant': + cellView.$box.removeClass('highlight-orange'); + break; + case 'ice.Code': + case 'ice.Generic': + cellView.$box.removeClass('highlight-blue'); + break; + case 'ice.Info': + cellView.$box.removeClass('highlight-gray'); + break; + } + } + } + this.hasSelection = function() { return selection.length > 0; }; diff --git a/app/styles/design.css b/app/styles/design.css index e5e816020..74882250f 100644 --- a/app/styles/design.css +++ b/app/styles/design.css @@ -309,8 +309,24 @@ fill: yellow; } -.highlight { - box-shadow: 0 0 8px 8px rgba(42, 118, 198, 0.2); +.highlight-yellow { + box-shadow: 0 0 8px 8px rgba(248, 248, 160, 0.8); +} + +.highlight-green { + box-shadow: 0 0 8px 8px rgba(217, 250, 184, 0.8); +} + +.highlight-orange { + box-shadow: 0 0 8px 8px rgba(250, 235, 184, 0.8); +} + +.highlight-blue { + box-shadow: 0 0 8px 8px rgba(216, 235, 243, 0.8); +} + +.highlight-gray { + box-shadow: 0 0 8px 8px rgba(221, 221, 221, 0.8); } .selectionarea { From fc332bd477b2f8e0df3565a2d34ebec68cee4f8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Fri, 27 Jan 2017 14:24:24 +0100 Subject: [PATCH 033/124] Close expanded combo on zoom --- app/scripts/services/graph.service.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/scripts/services/graph.service.js b/app/scripts/services/graph.service.js index 610e9b1a6..c389d2a56 100644 --- a/app/scripts/services/graph.service.js +++ b/app/scripts/services/graph.service.js @@ -248,8 +248,12 @@ angular.module('icestudio') /*beforeZoom: function(oldzoom, newzoom) { },*/ onZoom: function(scale) { - state.zoom = scale; - // Already rendered in pan + state.zoom = scale; // Already rendered in pan + + // Close expanded combo + if (document.activeElement.className === 'select2-search__field') { + $('select').select2('close'); + } }, /*beforePan: function(oldpan, newpan) { },*/ From 9b61960660b56c46a7642cd6f5b24238ecab17df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Fri, 27 Jan 2017 14:57:25 +0100 Subject: [PATCH 034/124] Validate Constant block name on edit --- app/scripts/services/blocks.service.js | 34 +++++++++++++++++--------- app/styles/design.css | 2 +- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/app/scripts/services/blocks.service.js b/app/scripts/services/blocks.service.js index 729b6da5b..fb7e3983a 100644 --- a/app/scripts/services/blocks.service.js +++ b/app/scripts/services/blocks.service.js @@ -58,7 +58,7 @@ angular.module('icestudio') gettextCatalog.getString('FPGA pin') ], [ config._default, - false + true ], function(evt, values) { var labels = values[0].replace(/ /g, '').split(','); @@ -738,18 +738,28 @@ angular.module('icestudio') block.data.local ], function(evt, values) { - var name = values[0].replace(/ /g, ''); + var label = values[0].replace(/ /g, ''); var local = values[1]; - // Edit block - if (block.data.name !== name || - block.data.local !== local) { - // Edit block - var data = utils.clone(block.data); - data.name = name; - data.local = local; - cellView.model.set('data', data); - cellView.apply(); - alertify.success(gettextCatalog.getString('Block updated')); + // Validate values + var paramInfo = utils.parseParamLabel(label); + if (paramInfo) { + var name = paramInfo.name; + evt.cancel = false; + if (block.data.name !== name || + block.data.local !== local) { + // Edit block + var data = utils.clone(block.data); + data.name = name; + data.local = local; + cellView.model.set('data', data); + cellView.apply(); + alertify.success(gettextCatalog.getString('Block updated')); + } + } + else { + evt.cancel = true; + alertify.notify(gettextCatalog.getString('Wrong parameter name {{name}}', { name: label }), 'warning', 3); + return; } }); } diff --git a/app/styles/design.css b/app/styles/design.css index 74882250f..93f99cc5c 100644 --- a/app/styles/design.css +++ b/app/styles/design.css @@ -246,7 +246,7 @@ position: absolute; text-align: center; left: 5px; - bottom: 5px; + margin-bottom: 5px; width: 84px; height: 28px; font-size: 13px; From f3cbfc17f003578ebdd9f1f362653988b3e02c77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Fri, 27 Jan 2017 18:00:08 +0100 Subject: [PATCH 035/124] Improve label render in wires --- app/scripts/graphics/shapes/joint.shapes.js | 104 ++++++++++++++------ app/scripts/services/graph.service.js | 47 +++++---- app/styles/design.css | 44 ++++----- samples/complex.ice | 44 +++++---- 4 files changed, 145 insertions(+), 94 deletions(-) diff --git a/app/scripts/graphics/shapes/joint.shapes.js b/app/scripts/graphics/shapes/joint.shapes.js index 40ea37908..4eb8153bf 100644 --- a/app/scripts/graphics/shapes/joint.shapes.js +++ b/app/scripts/graphics/shapes/joint.shapes.js @@ -72,10 +72,10 @@ joint.shapes.ice.Model = joint.shapes.basic.Generic.extend({ magnet: true }, '.port-label': { - fill: '#888' + fill: '#777' }, '.port-wire': { - stroke: '#888', + stroke: '#777', 'stroke-width': WIRE_WIDTH }, '.port-default': { @@ -86,7 +86,7 @@ joint.shapes.ice.Model = joint.shapes.basic.Generic.extend({ height: '20', rx: '3', ry: '3', - stroke: '#888', + stroke: '#777', 'stroke-width': 1, fill: '#FBFBC9' } @@ -161,7 +161,7 @@ joint.shapes.ice.Model = joint.shapes.basic.Generic.extend({ attrs[portWireSelector]['pointer-events'] = 'none'; } - var offset = (port.size && port.size > 1) ? 6 : 1; + var offset = (port.size && port.size > 1) ? 4 : 1; var pos = Math.round((index + 0.5) / total * port.gridUnits) / port.gridUnits; var lineX = (port.default && port.default.apply) ? '-20' : '0'; @@ -279,13 +279,13 @@ joint.shapes.ice.ModelView = joint.dia.ElementView.extend({ for (i in leftPorts) { port = leftPorts[i]; if (port.size > 1) { - this.$('#port-wire-' + port.id).css('stroke-width', width * 4); + this.$('#port-wire-' + port.id).css('stroke-width', width * 3); } } for (i in rightPorts) { port = rightPorts[i]; if (port.size > 1) { - this.$('#port-wire-' + port.id).css('stroke-width', width * 4); + this.$('#port-wire-' + port.id).css('stroke-width', width * 3); } } @@ -819,13 +819,13 @@ joint.shapes.ice.CodeView = joint.shapes.ice.ModelView.extend({ for (i in leftPorts) { port = leftPorts[i]; if (port.size > 1) { - this.$('#port-wire-' + port.id).css('stroke-width', width * 4); + this.$('#port-wire-' + port.id).css('stroke-width', width * 3); } } for (i in rightPorts) { port = rightPorts[i]; if (port.size > 1) { - this.$('#port-wire-' + port.id).css('stroke-width', width * 4); + this.$('#port-wire-' + port.id).css('stroke-width', width * 3); } } @@ -1006,7 +1006,7 @@ joint.shapes.ice.InfoView = joint.dia.ElementView.extend({ joint.shapes.ice.Wire = joint.dia.Link.extend({ markup: [ - '', + '', '', '', '', @@ -1017,9 +1017,16 @@ joint.shapes.ice.Wire = joint.dia.Link.extend({ '' ].join(''), + labelMarkup: [ + '' + ].join(''), + bifurcationMarkup: [ '', - '', + '', '' ].join(''), @@ -1060,8 +1067,9 @@ joint.shapes.ice.Wire = joint.dia.Link.extend({ text: { text: '', 'font-weight': 'bold', - 'font-size': '150%', - 'y': '12px' + 'font-size': '13px', + 'text-anchor': 'middle', + 'y': '4px' } } } @@ -1069,8 +1077,8 @@ joint.shapes.ice.Wire = joint.dia.Link.extend({ attrs: { '.connection': { - 'stroke-width': 2, - stroke: '#888' + 'stroke-width': WIRE_WIDTH, + stroke: '#777' } }, @@ -1090,27 +1098,21 @@ joint.shapes.ice.WireView = joint.dia.LinkView.extend({ setTimeout(function() { var i, port, portName = self.model.get('source').port; var rightPorts = self.sourceView.model.get('rightPorts'); - var bottomPorts = self.sourceView.model.get('bottomPorts'); // Initialize wire properties for (i in rightPorts) { port = rightPorts[i]; if (portName === port.id) { self.model.attributes.size = port.size; // For wire size connection validation - self.$('.connection').css('stroke-width', WIRE_WIDTH * ((port.size > 1) ? 4 : 1)); - self.model.label(0, {attrs: { text: { text: (port.size > 1) ? '' + port.size + '' : '' } } }); - self.model.bifurcationMarkup = self.model.bifurcationMarkup.replace(/<%= r %>/g, WIRE_WIDTH * ((port.size > 1) ? 4 : 3)); - self.update(); - break; - } - } - for (i in bottomPorts) { - port = bottomPorts[i]; - if (portName === port.id) { - self.model.attributes.size = port.size; // For wire size connection validation - self.$('.connection').css('stroke-width', WIRE_WIDTH * ((port.size > 1) ? 4 : 1)); - self.model.label(0, {attrs: { text: { text: (port.size > 1) ? '' + port.size + '' : '' } } }); - self.model.bifurcationMarkup = self.model.bifurcationMarkup.replace(/<%= r %>/g, WIRE_WIDTH * ((port.size > 1) ? 4 : 3)); + if (port.size > 1) { + self.$('.connection').css('stroke-width', WIRE_WIDTH * 3); + self.model.label(0, {attrs: { text: { text: port.size } } }); + self.model.bifurcationMarkup = self.model.bifurcationMarkup.replace(/<%= r %>/g, WIRE_WIDTH * 4); + } + else { + self.model.bifurcationMarkup = self.model.bifurcationMarkup.replace(/<%= r %>/g, WIRE_WIDTH * 2); + } + self.update(); break; } @@ -1142,6 +1144,50 @@ joint.shapes.ice.WireView = joint.dia.LinkView.extend({ return this; }, + renderLabels: function() { + if (!this._V.labels) { + return this; + } + + this._labelCache = {}; + var $labels = $(this._V.labels.node).empty(); + + var labels = this.model.get('labels') || []; + if (!labels.length) { + return this; + } + + var labelTemplate = joint.util.template(this.model.get('labelMarkup') || this.model.labelMarkup); + // This is a prepared instance of a vectorized SVGDOM node for the label element resulting from + // compilation of the labelTemplate. The purpose is that all labels will just `clone()` this + // node to create a duplicate. + var labelNodeInstance = V(labelTemplate()); + + _.each(labels, function(label, idx) { + + var labelNode = labelNodeInstance.clone().node; + V(labelNode).attr('label-idx', idx); + this._labelCache[idx] = V(labelNode); + + var $text = $(labelNode).find('text'); + var textAttributes = _.extend({ 'text-anchor': 'middle', 'font-size': 13 }, joint.util.getByPath(label, 'attrs/text', '/')); + + $text.attr(_.omit(textAttributes, 'text')); + + if (label.attrs.text.text) { + $(labelNode).removeClass('hidden'); + } + + if (!_.isUndefined(textAttributes.text)) { + V($text[0]).text(textAttributes.text + '', { annotations: textAttributes.annotations }); + } + $labels.append(labelNode); + + }, this); + + return this; + }, + updateConnection: function(opt) { opt = opt || {}; diff --git a/app/scripts/services/graph.service.js b/app/scripts/services/graph.service.js index c389d2a56..2a01352cb 100644 --- a/app/scripts/services/graph.service.js +++ b/app/scripts/services/graph.service.js @@ -219,6 +219,23 @@ angular.module('icestudio') } }); + // Command Manager + + commandManager = new joint.dia.CommandManager({ + paper: paper, + graph: graph + }); + + // Selection View + + selection = new Backbone.Collection(); + selectionView = new joint.ui.SelectionView({ + paper: paper, + graph: graph, + model: selection, + state: state + }); + paper.options.enabled = true; paper.options.warningTimer = false; @@ -276,23 +293,6 @@ angular.module('icestudio') } }); - // Command Manager - - commandManager = new joint.dia.CommandManager({ - paper: paper, - graph: graph - }); - - // Selection View - - selection = new Backbone.Collection(); - selectionView = new joint.ui.SelectionView({ - paper: paper, - graph: graph, - model: selection, - state: state - }); - // Events this.mousedown = false; @@ -349,15 +349,14 @@ angular.module('icestudio') } }); - paper.on('cell:pointerdown', function(/*cellView*/) { + paper.on('cell:pointerdown', function(cellView) { self.mousedown = true; - /*if (paper.options.enabled) { - if (!cellView.model.isLink()) { - if (cellView.$box.css('z-index') < z.index) { - cellView.$box.css('z-index', ++z.index); - } + if (paper.options.enabled) { + if (cellView.model.isLink()) { + // Unhighlight source block of the wire + unhighlight(paper.findViewByModel(cellView.model.attributes.source.id)); } - }*/ + } }); paper.on('cell:pointerdblclick', function(cellView/*, evt, x, y*/) { diff --git a/app/styles/design.css b/app/styles/design.css index 93f99cc5c..64c3d0d67 100644 --- a/app/styles/design.css +++ b/app/styles/design.css @@ -75,7 +75,7 @@ position: absolute; background: #C0DFEB; border-radius: 5px; - border: 1px solid #888; + border: 1px solid #444; pointer-events: none; -webkit-user-select: none; user-select: none; @@ -87,7 +87,7 @@ width: 120px; color: #333; background-color: white; - border: 1px solid #888; + border: 1px solid #444; text-align: center; border-radius: 5px; padding: 5px 5px; @@ -106,7 +106,7 @@ margin-left: -6px; border-width: 6px; border-style: solid; - border-color: #888 transparent transparent transparent; + border-color: #444 transparent transparent transparent; } .generic-block .tooltip-large { @@ -132,7 +132,7 @@ margin-top: 20px; text-align: center; font-size: 14px; - color: #444; + color: #222; text-overflow: ellipsis; overflow: hidden; } @@ -141,7 +141,7 @@ position: absolute; background: #E2FBC9; border-radius: 5px; - border: 1px solid #888; + border: 1px solid #444; pointer-events: none; -webkit-user-select: none; user-select: none; @@ -150,10 +150,10 @@ .virtual-port label { display: block; - margin-top: 20px; + margin-top: 22px; text-align: center; - font-size: 0.9em; - color: #444; + font-size: 13px; + color: #222; text-overflow: ellipsis; overflow: hidden; } @@ -162,7 +162,7 @@ position: absolute; background: #FBFBC9; border-radius: 5px; - border: 1px solid #888; + border: 1px solid #444; pointer-events: none; -webkit-user-select: none; user-select: none; @@ -175,7 +175,7 @@ margin-bottom: 5px; text-align: center; font-size: 13px; - color: #444; + color: #222; text-overflow: ellipsis; overflow: hidden; } @@ -200,11 +200,11 @@ } .select2-container--default .select2-selection--single { - border: 1px solid #888; + border: 1px solid #777; } .select2-dropdown { - border: 1px solid #888; + border: 1px solid #777; } .bigdrop { @@ -216,7 +216,7 @@ position: absolute; background: #FBF0C9; border-radius: 5px; - border: 1px solid #888; + border: 1px solid #444; pointer-events: none; -webkit-user-select: none; user-select: none; @@ -237,7 +237,7 @@ margin-bottom: 5px; text-align: center; font-size: 13px; - color: #444; + color: #222; text-overflow: ellipsis; overflow: hidden; } @@ -251,7 +251,7 @@ height: 28px; font-size: 13px; border-radius: 4px; - border: 1px solid #888; + border: 1px solid #777; pointer-events: auto; } @@ -259,7 +259,7 @@ position: absolute; background: #C0DFEB; border-radius: 5px; - border: 1px solid #888; + border: 1px solid #444; pointer-events: none; -webkit-user-select: none; user-select: none; @@ -284,7 +284,7 @@ position: absolute; background: #DDD; border-radius: 5px; - border: 1px solid #888; + border: 1px solid #444; pointer-events: none; -webkit-user-select: none; user-select: none; @@ -310,23 +310,23 @@ } .highlight-yellow { - box-shadow: 0 0 8px 8px rgba(248, 248, 160, 0.8); + box-shadow: 0 0 8px 8px rgba(248, 248, 160, 0.5); } .highlight-green { - box-shadow: 0 0 8px 8px rgba(217, 250, 184, 0.8); + box-shadow: 0 0 8px 8px rgba(217, 250, 184, 0.5); } .highlight-orange { - box-shadow: 0 0 8px 8px rgba(250, 235, 184, 0.8); + box-shadow: 0 0 8px 8px rgba(250, 235, 184, 0.5); } .highlight-blue { - box-shadow: 0 0 8px 8px rgba(216, 235, 243, 0.8); + box-shadow: 0 0 8px 8px rgba(216, 235, 243, 0.5); } .highlight-gray { - box-shadow: 0 0 8px 8px rgba(221, 221, 221, 0.8); + box-shadow: 0 0 8px 8px rgba(221, 221, 221, 0.5); } .selectionarea { diff --git a/samples/complex.ice b/samples/complex.ice index 614039ab1..7d955fec5 100644 --- a/samples/complex.ice +++ b/samples/complex.ice @@ -36,7 +36,7 @@ "id": "00824624-3f88-4f18-9983-3e45d52b19c7", "type": "88119a9ec745ebab0cf9098d339d2bbdd803db40", "position": { - "x": 344, + "x": 376, "y": 200 } }, @@ -44,7 +44,7 @@ "id": "bf4b5914-c791-42d3-b876-df0e03d5a9a4", "type": "dd6af852895fb14362cdc5cb5f47c76353d7c7ad", "position": { - "x": 528, + "x": 536, "y": 200 } }, @@ -88,8 +88,8 @@ "virtual": false }, "position": { - "x": 152, - "y": 280 + "x": 120, + "y": 296 } }, { @@ -114,7 +114,7 @@ }, "position": { "x": 712, - "y": 280 + "y": 296 } } ], @@ -150,17 +150,6 @@ "port": "in" } }, - { - "source": { - "block": "ceadef15-e833-414a-93e7-aea6f85a882b", - "port": "ef743d41-5941-4831-becd-0d930c4eed54" - }, - "target": { - "block": "00824624-3f88-4f18-9983-3e45d52b19c7", - "port": "95f8c313-6e18-4ee3-b9cf-7266dec53c93" - }, - "size": 4 - }, { "source": { "block": "21e9e7f9-9b8a-4fca-904d-e266f1496454", @@ -170,6 +159,12 @@ "block": "00824624-3f88-4f18-9983-3e45d52b19c7", "port": "f6528039-852b-41f9-aa41-268994b3f631" }, + "vertices": [ + { + "x": 296, + "y": 288 + } + ], "size": 2 }, { @@ -181,15 +176,26 @@ "block": "bf4b5914-c791-42d3-b876-df0e03d5a9a4", "port": "a4058fa5-b66e-4e5e-b542-28d7c3e9d3cd" } + }, + { + "source": { + "block": "ceadef15-e833-414a-93e7-aea6f85a882b", + "port": "ef743d41-5941-4831-becd-0d930c4eed54" + }, + "target": { + "block": "00824624-3f88-4f18-9983-3e45d52b19c7", + "port": "95f8c313-6e18-4ee3-b9cf-7266dec53c93" + }, + "size": 4 } ] }, "state": { "pan": { - "x": -24.6892, - "y": 38.6221 + "x": -54.0302, + "y": 9.0372 }, - "zoom": 1.0164 + "zoom": 1.1006 } }, "dependencies": { From 16ed11be47e38d2706982c5c221886523be79971 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Fri, 27 Jan 2017 21:15:51 +0100 Subject: [PATCH 036/124] Fix empty labels --- app/scripts/graphics/shapes/joint.shapes.js | 4 +- app/scripts/services/blocks.service.js | 2 +- app/styles/design.css | 49 ++++++++++++--------- 3 files changed, 31 insertions(+), 24 deletions(-) diff --git a/app/scripts/graphics/shapes/joint.shapes.js b/app/scripts/graphics/shapes/joint.shapes.js index 4eb8153bf..cd70e2888 100644 --- a/app/scripts/graphics/shapes/joint.shapes.js +++ b/app/scripts/graphics/shapes/joint.shapes.js @@ -86,7 +86,7 @@ joint.shapes.ice.Model = joint.shapes.basic.Generic.extend({ height: '20', rx: '3', ry: '3', - stroke: '#777', + stroke: '#555', 'stroke-width': 1, fill: '#FBFBC9' } @@ -1020,7 +1020,7 @@ joint.shapes.ice.Wire = joint.dia.Link.extend({ labelMarkup: [ '' ].join(''), diff --git a/app/scripts/services/blocks.service.js b/app/scripts/services/blocks.service.js index fb7e3983a..ceda5ad39 100644 --- a/app/scripts/services/blocks.service.js +++ b/app/scripts/services/blocks.service.js @@ -332,7 +332,7 @@ angular.module('icestudio') var blockInstance = { id: null, type: type, - position: { x: 6 * gridsize, y: 16 * gridsize } + position: { x: 10 * gridsize, y: 16 * gridsize } }; if (block && block.design && diff --git a/app/styles/design.css b/app/styles/design.css index 64c3d0d67..dbf8ba6a8 100644 --- a/app/styles/design.css +++ b/app/styles/design.css @@ -75,7 +75,7 @@ position: absolute; background: #C0DFEB; border-radius: 5px; - border: 1px solid #444; + border: 1px solid #555; pointer-events: none; -webkit-user-select: none; user-select: none; @@ -87,7 +87,7 @@ width: 120px; color: #333; background-color: white; - border: 1px solid #444; + border: 1px solid #777; text-align: center; border-radius: 5px; padding: 5px 5px; @@ -106,7 +106,7 @@ margin-left: -6px; border-width: 6px; border-style: solid; - border-color: #444 transparent transparent transparent; + border-color: #777 transparent transparent transparent; } .generic-block .tooltip-large { @@ -132,7 +132,7 @@ margin-top: 20px; text-align: center; font-size: 14px; - color: #222; + color: #333; text-overflow: ellipsis; overflow: hidden; } @@ -141,7 +141,7 @@ position: absolute; background: #E2FBC9; border-radius: 5px; - border: 1px solid #444; + border: 1px solid #555; pointer-events: none; -webkit-user-select: none; user-select: none; @@ -153,7 +153,7 @@ margin-top: 22px; text-align: center; font-size: 13px; - color: #222; + color: #333; text-overflow: ellipsis; overflow: hidden; } @@ -162,7 +162,7 @@ position: absolute; background: #FBFBC9; border-radius: 5px; - border: 1px solid #444; + border: 1px solid #555; pointer-events: none; -webkit-user-select: none; user-select: none; @@ -171,11 +171,12 @@ .fpga-port label { display: block; - margin-top: 5px; - margin-bottom: 5px; + margin-top: 6px; + margin-bottom: 6px; text-align: center; font-size: 13px; - color: #222; + height: 18px; + color: #333; text-overflow: ellipsis; overflow: hidden; } @@ -191,12 +192,17 @@ position: relative; width: 84px; left: 5px; - margin-bottom: 4px; + margin-bottom: 6px; pointer-events: auto; } .select2-selection__rendered { + line-height: 24px !important; +} + +.select2-selection { font-size: 13px; + height: 26px !important; } .select2-container--default .select2-selection--single { @@ -208,15 +214,15 @@ } .bigdrop { - width: 92px !important; font-size: 13px; + width: 92px !important; } .constant-block { position: absolute; background: #FBF0C9; border-radius: 5px; - border: 1px solid #444; + border: 1px solid #555; pointer-events: none; -webkit-user-select: none; user-select: none; @@ -228,16 +234,17 @@ top: 0; right: 5px; font-size: 1.5em; - color: #444; + color: #555; } .constant-block label { display: block; - margin-top: 5px; - margin-bottom: 5px; + margin-top: 6px; + margin-bottom: 6px; text-align: center; font-size: 13px; - color: #222; + height: 18px; + color: #333; text-overflow: ellipsis; overflow: hidden; } @@ -246,9 +253,9 @@ position: absolute; text-align: center; left: 5px; - margin-bottom: 5px; + margin-bottom: 6px; width: 84px; - height: 28px; + height: 26px; font-size: 13px; border-radius: 4px; border: 1px solid #777; @@ -259,7 +266,7 @@ position: absolute; background: #C0DFEB; border-radius: 5px; - border: 1px solid #444; + border: 1px solid #555; pointer-events: none; -webkit-user-select: none; user-select: none; @@ -284,7 +291,7 @@ position: absolute; background: #DDD; border-radius: 5px; - border: 1px solid #444; + border: 1px solid #555; pointer-events: none; -webkit-user-select: none; user-select: none; From fcc1f0f5fcc83e62a0072f0d1fc0b353680894d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Mon, 30 Jan 2017 06:17:02 +0100 Subject: [PATCH 037/124] Arrange graphics dir --- app/index.html | 10 +++++----- .../{dia/joint.dia.command.js => joint.command.js} | 0 .../graphics/{connectors => }/joint.connectors.js | 0 app/scripts/graphics/{routers => }/joint.routers.js | 0 .../graphics/{ui/joint.ui.js => joint.selection.js} | 0 app/scripts/graphics/{shapes => }/joint.shapes.js | 0 6 files changed, 5 insertions(+), 5 deletions(-) rename app/scripts/graphics/{dia/joint.dia.command.js => joint.command.js} (100%) rename app/scripts/graphics/{connectors => }/joint.connectors.js (100%) rename app/scripts/graphics/{routers => }/joint.routers.js (100%) rename app/scripts/graphics/{ui/joint.ui.js => joint.selection.js} (100%) rename app/scripts/graphics/{shapes => }/joint.shapes.js (100%) diff --git a/app/index.html b/app/index.html index 8c1f5cefa..2461945d0 100644 --- a/app/index.html +++ b/app/index.html @@ -55,11 +55,11 @@ - - - - - + + + + + diff --git a/app/scripts/graphics/dia/joint.dia.command.js b/app/scripts/graphics/joint.command.js similarity index 100% rename from app/scripts/graphics/dia/joint.dia.command.js rename to app/scripts/graphics/joint.command.js diff --git a/app/scripts/graphics/connectors/joint.connectors.js b/app/scripts/graphics/joint.connectors.js similarity index 100% rename from app/scripts/graphics/connectors/joint.connectors.js rename to app/scripts/graphics/joint.connectors.js diff --git a/app/scripts/graphics/routers/joint.routers.js b/app/scripts/graphics/joint.routers.js similarity index 100% rename from app/scripts/graphics/routers/joint.routers.js rename to app/scripts/graphics/joint.routers.js diff --git a/app/scripts/graphics/ui/joint.ui.js b/app/scripts/graphics/joint.selection.js similarity index 100% rename from app/scripts/graphics/ui/joint.ui.js rename to app/scripts/graphics/joint.selection.js diff --git a/app/scripts/graphics/shapes/joint.shapes.js b/app/scripts/graphics/joint.shapes.js similarity index 100% rename from app/scripts/graphics/shapes/joint.shapes.js rename to app/scripts/graphics/joint.shapes.js From fb885601051f2ca0c4fa24b2b386ba5e50e13e03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Mon, 30 Jan 2017 08:59:25 +0100 Subject: [PATCH 038/124] React to connections in a default port --- app/scripts/services/graph.service.js | 52 ++++++++++++++++++++++++++- app/styles/design.css | 6 ++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/app/scripts/services/graph.service.js b/app/scripts/services/graph.service.js index 2a01352cb..f5e9d1fcb 100644 --- a/app/scripts/services/graph.service.js +++ b/app/scripts/services/graph.service.js @@ -124,7 +124,7 @@ angular.module('icestudio') height: 1000, model: graph, gridSize: gridsize, - snapLinks: { radius: 15 }, + snapLinks: { radius: 16 }, linkPinning: false, embeddingMode: false, //markAvailable: true, @@ -438,6 +438,56 @@ angular.module('icestudio') } }*/ }); + + graph.on('add change:source change:target', function(cell) { + if (cell.isLink() && cell.get('source').id) { + var target = cell.get('target'); + if (target.id) { + // Connected to a port + cell.attributes.lastTarget = target; + updatePortDefault(target, false); + } + else { + // Moving the wire connection + target = cell.get('lastTarget'); + updatePortDefault(target, true); + } + //console.log('Connect link', cell, target); + } + }); + + graph.on('remove', function(cell) { + if (cell.isLink()) { + var target = cell.get('target'); + if (!target.id) { + target = cell.get('lastTarget'); + } + updatePortDefault(target, true); + //console.log('Remove link', cell); + } + }); + + function updatePortDefault(target, value) { + if (target) { + var block = graph.getCell(target.id); + if (block) { + var ports = block.get('leftPorts'); + for (var i in ports) { + if (ports[i].id === target.port && ports[i].default) { + ports[i].default.apply = value; + var selector = '.leftPorts>.port' + i + '>.port-default'; + block.attributes.attrs[selector].display = value ? 'visible' : 'none'; + paper.findViewByModel(block.id).update(); + //block.processPorts(); + //block.trigger('process:ports'); + break; + } + } + //block.updatePortsAttrs(); + } + } + } + }; this.undo = function() { diff --git a/app/styles/design.css b/app/styles/design.css index dbf8ba6a8..633210bba 100644 --- a/app/styles/design.css +++ b/app/styles/design.css @@ -316,6 +316,12 @@ fill: yellow; } +.joint-highlight-stroke.joint-theme-default { + fill: yellow; + opacity: 0.5; + stroke-width: 0px; +} + .highlight-yellow { box-shadow: 0 0 8px 8px rgba(248, 248, 160, 0.5); } From eee7b945045c739cde992da99436e78c97ed5970 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Mon, 30 Jan 2017 09:47:44 +0100 Subject: [PATCH 039/124] Do not store add-*-remove sequences in the undoStack --- app/scripts/graphics/joint.command.js | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/app/scripts/graphics/joint.command.js b/app/scripts/graphics/joint.command.js index bd9d7dd92..e159e2831 100755 --- a/app/scripts/graphics/joint.command.js +++ b/app/scripts/graphics/joint.command.js @@ -74,13 +74,13 @@ joint.dia.CommandManager = Backbone.Model.extend({ this.redoStack = []; if (!cmd.batch) { - this.undoStack.push(cmd); + this.undoStack.push(cmd); this.triggerChange(); - this.trigger('add', cmd); + this.trigger('add', cmd); } else { this.lastCmdIndex = Math.max(this.lastCmdIndex, 0); - // Commands possible thrown away. Someone might be interested. - this.trigger('batch', cmd); + // Commands possible thrown away. Someone might be interested. + this.trigger('batch', cmd); } }, this); @@ -119,17 +119,20 @@ joint.dia.CommandManager = Backbone.Model.extend({ command.batch = false; } - if (cmdName === 'add' || cmdName === 'remove') { - - // In a batch: delete an "add-remove" sequence if it is applied to the same cell - if (cmdName === 'remove' && this.batchCommand && this.lastCmdIndex > 0) { - var prevCommand = this.batchCommand[this.lastCmdIndex-1]; + // In a batch: delete an "add-*-remove" sequence if it is applied to the same cell + if (cmdName === 'remove' && this.batchCommand && this.lastCmdIndex > 0) { + for (var i = 0; i < this.lastCmdIndex; i++) { + var prevCommand = this.batchCommand[i]; if (prevCommand.action === 'add' && prevCommand.data.id === cell.id) { - this.batchCommand.pop(); - this.batchCommand.pop(); + delete this.batchCommand; + delete this.lastCmdIndex; + delete this.batchLevel; return; } } + } + + if (cmdName === 'add' || cmdName === 'remove') { command.action = cmdName; command.data.id = cell.id; From 18ce4c53ba9ecc65baaa3224a731055f7f2b235c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Mon, 30 Jan 2017 10:11:44 +0100 Subject: [PATCH 040/124] Group "port default" graphics --- app/scripts/graphics/joint.shapes.js | 20 +++++++++++++++----- app/scripts/services/graph.service.js | 3 --- app/styles/design.css | 5 +---- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/app/scripts/graphics/joint.shapes.js b/app/scripts/graphics/joint.shapes.js index cd70e2888..10b92ed95 100644 --- a/app/scripts/graphics/joint.shapes.js +++ b/app/scripts/graphics/joint.shapes.js @@ -20,7 +20,7 @@ joint.shapes.ice.Model = joint.shapes.basic.Generic.extend({ markup: '', portMarkup: ' \ - \ + \ \ \ \ @@ -79,7 +79,9 @@ joint.shapes.ice.Model = joint.shapes.basic.Generic.extend({ 'stroke-width': WIRE_WIDTH }, '.port-default': { - display: 'none', + display: 'none' + }, + '.port-default rect': { x: '-40', y: '-10', width: '20', @@ -89,7 +91,13 @@ joint.shapes.ice.Model = joint.shapes.basic.Generic.extend({ stroke: '#555', 'stroke-width': 1, fill: '#FBFBC9' + }, + '.port-default path': { + d: 'M 0 0 L -20 0', + stroke: '#777', + 'stroke-width': WIRE_WIDTH } + } }, joint.shapes.basic.Generic.prototype.defaults), @@ -163,7 +171,6 @@ joint.shapes.ice.Model = joint.shapes.basic.Generic.extend({ var offset = (port.size && port.size > 1) ? 4 : 1; var pos = Math.round((index + 0.5) / total * port.gridUnits) / port.gridUnits; - var lineX = (port.default && port.default.apply) ? '-20' : '0'; switch (type) { case 'left': @@ -173,7 +180,7 @@ joint.shapes.ice.Model = joint.shapes.basic.Generic.extend({ attrs[portLabelSelector]['y'] = -5-offset; attrs[portLabelSelector]['text-anchor'] = 'end'; attrs[portWireSelector]['y'] = pos; - attrs[portWireSelector]['d'] = 'M ' + lineX + ' 0 L 16 0'; + attrs[portWireSelector]['d'] = 'M 0 0 L 16 0'; break; case 'right': attrs[portSelector]['ref-dx'] = 8; @@ -274,10 +281,11 @@ joint.shapes.ice.ModelView = joint.dia.ElementView.extend({ // Render ports width var width = WIRE_WIDTH * state.zoom; this.$('.port-wire').css('stroke-width', width); - this.$('.port-default').css('stroke-width', state.zoom); // Render buses for (i in leftPorts) { port = leftPorts[i]; + this.$('#port-default-wire-' + port.id).css('stroke-width', width); + this.$('#port-default-rect-' + port.id).css('stroke-width', state.zoom); if (port.size > 1) { this.$('#port-wire-' + port.id).css('stroke-width', width * 3); } @@ -818,6 +826,8 @@ joint.shapes.ice.CodeView = joint.shapes.ice.ModelView.extend({ // Render buses for (i in leftPorts) { port = leftPorts[i]; + this.$('#port-default-wire-' + port.id).css('stroke-width', width); + this.$('#port-default-rect-' + port.id).css('stroke-width', state.zoom); if (port.size > 1) { this.$('#port-wire-' + port.id).css('stroke-width', width * 3); } diff --git a/app/scripts/services/graph.service.js b/app/scripts/services/graph.service.js index f5e9d1fcb..30e027fd2 100644 --- a/app/scripts/services/graph.service.js +++ b/app/scripts/services/graph.service.js @@ -478,12 +478,9 @@ angular.module('icestudio') var selector = '.leftPorts>.port' + i + '>.port-default'; block.attributes.attrs[selector].display = value ? 'visible' : 'none'; paper.findViewByModel(block.id).update(); - //block.processPorts(); - //block.trigger('process:ports'); break; } } - //block.updatePortsAttrs(); } } } diff --git a/app/styles/design.css b/app/styles/design.css index 633210bba..7aac7a0f4 100644 --- a/app/styles/design.css +++ b/app/styles/design.css @@ -313,13 +313,10 @@ } .port-body:hover { - fill: yellow; } .joint-highlight-stroke.joint-theme-default { - fill: yellow; - opacity: 0.5; - stroke-width: 0px; + display: none; } .highlight-yellow { From 1a002378c16870da2e97c73364b54008398d8909 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Mon, 30 Jan 2017 10:22:08 +0100 Subject: [PATCH 041/124] Check default ports also in Code blocks --- app/scripts/services/blocks.service.js | 6 +++++- app/scripts/services/project.service.js | 9 +++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/app/scripts/services/blocks.service.js b/app/scripts/services/blocks.service.js index ceda5ad39..ff8dc8f03 100644 --- a/app/scripts/services/blocks.service.js +++ b/app/scripts/services/blocks.service.js @@ -434,11 +434,15 @@ angular.module('icestudio') for (var i in instance.data.ports.in) { port = instance.data.ports.in[i]; + if (!port.range) { + port.default = hasInputRule(port.name); + } leftPorts.push({ id: port.name, label: port.name + (port.range || ''), size: port.size || 1, - gridUnits: 32 + gridUnits: 32, + default: port.default }); } diff --git a/app/scripts/services/project.service.js b/app/scripts/services/project.service.js index 00dfccb8b..64ad16a9d 100644 --- a/app/scripts/services/project.service.js +++ b/app/scripts/services/project.service.js @@ -429,6 +429,11 @@ angular.module('icestudio') cell.type === 'ice.Info') { delete block.data.deltas; } + if (cell.type === 'ice.Code') { + for (var i in block.data.ports.in) { + delete block.data.ports.in[i].default; + } + } blocks.push(block); } else if (cell.type === 'ice.Wire') { @@ -456,8 +461,8 @@ angular.module('icestudio') if (updateDependencies !== false) { project.dependencies = {}; var types = findSubDependencies(project); - for (var i in types) { - project.dependencies[types[i]] = allDependencies[types[i]]; + for (var t in types) { + project.dependencies[types[t]] = allDependencies[types[t]]; } } From 814e2b2d0cb79af6dd5e7b2a2acba50f6a0ffe29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Mon, 30 Jan 2017 11:06:47 +0100 Subject: [PATCH 042/124] Update compiler to apply the rules.input --- app/resources/boards/icezum/rules.json | 5 +- app/scripts/controllers/design.js | 2 +- app/scripts/services/blocks.service.js | 2 +- app/scripts/services/compiler.service.js | 90 ++++++++---------------- app/scripts/services/graph.service.js | 4 +- app/scripts/services/project.service.js | 18 ++--- 6 files changed, 44 insertions(+), 77 deletions(-) diff --git a/app/resources/boards/icezum/rules.json b/app/resources/boards/icezum/rules.json index b297d6b0c..0d10eb53a 100644 --- a/app/resources/boards/icezum/rules.json +++ b/app/resources/boards/icezum/rules.json @@ -2,10 +2,7 @@ "input" : [ { "port" : "clk", - "pin": { - "name": "CLK", - "value": "21" - } + "pin": "21" } ], "output": [ diff --git a/app/scripts/controllers/design.js b/app/scripts/controllers/design.js index 2d4db5053..a6ca7500b 100644 --- a/app/scripts/controllers/design.js +++ b/app/scripts/controllers/design.js @@ -46,7 +46,7 @@ angular.module('icestudio') } $rootScope.$on('updateProject', function(event, callback) { - project.update(callback, false); + project.update({ deps: false }, callback); }); $rootScope.$on('breadcrumbsBack', function(/*event*/) { diff --git a/app/scripts/services/blocks.service.js b/app/scripts/services/blocks.service.js index ff8dc8f03..b8f4bda10 100644 --- a/app/scripts/services/blocks.service.js +++ b/app/scripts/services/blocks.service.js @@ -497,7 +497,7 @@ angular.module('icestudio') if (allInitPorts) { for (var i in allInitPorts) { if (port === allInitPorts[i].port){ - _default = allInitPorts[i].pin; + _default = allInitPorts[i]; _default.apply = true; break; } diff --git a/app/scripts/services/compiler.service.js b/app/scripts/services/compiler.service.js index 11819ba6d..d0076b674 100644 --- a/app/scripts/services/compiler.service.js +++ b/app/scripts/services/compiler.service.js @@ -328,13 +328,10 @@ angular.module('icestudio') var i, j; var initPorts = []; - var inputPorts = []; - var ncInputPorts = []; var blocks = project.design.graph.blocks; - var wires = project.design.graph.wires; var dependencies = project.dependencies; - // Find all input ports in: + // Find all not connected input ports: // - Code blocks // - Generic blocks for (i in blocks) { @@ -343,10 +340,13 @@ angular.module('icestudio') if (block.type === 'basic.code') { // Code block for (j in block.data.ports.in) { - if (!block.data.ports.in[j].range) { - inputPorts.push({ + var inPort = block.data.ports.in[j]; + if (inPort.default && inPort.default.apply) { + initPorts.push({ block: block.id, - port: block.data.ports.in[j].name + port: inPort.name, + name: inPort.default.port, + pin: inPort.default.pin }); } } @@ -357,11 +357,14 @@ angular.module('icestudio') var genericBlock = dependencies[block.type]; var subBlocks = genericBlock.design.graph.blocks; for (j in subBlocks) { - if (subBlocks[j].type === 'basic.input' && !subBlocks[j].data.range) { - inputPorts.push({ + var subBlock = subBlocks[j]; + if (subBlock.type === 'basic.input' && + subBlock.data.default && subBlock.data.default.apply) { + initPorts.push({ block: block.id, - port: subBlocks[j].id, - name: subBlocks[j].data.name + port: subBlock.id, + name: subBlock.data.default.port, + pin: subBlock.data.default.pin }); } } @@ -369,43 +372,6 @@ angular.module('icestudio') } } - //console.log('INPUT PORTS', inputPorts); - - // Filter not connected input ports - for (i in inputPorts) { - var connected = false; - var inputPort = inputPorts[i]; - for (j in wires) { - var target = wires[j].target; - if (target.block === inputPort.block && target.port === inputPort.port) { - connected = true; - break; - } - } - if (!connected) { - ncInputPorts.push(inputPort); - } - } - - //console.log('NC INPUT PORTS', ncInputPorts); - - // Filter ports defined in rules - var allInitPorts = boards.selectedBoard.rules.input; - for (i in allInitPorts) { - for (j in ncInputPorts) { - if (ncInputPorts[j].name === allInitPorts[i].port || ncInputPorts[j].port === allInitPorts[i].port) { - initPorts.push({ - block: ncInputPorts[j].block, - port: ncInputPorts[j].port, - name: ncInputPorts[j].name || ncInputPorts[j].port, - pin: allInitPorts[i].pin - }); - } - } - } - - //console.log('INIT PORTS', initPorts); - return initPorts; } @@ -469,12 +435,13 @@ angular.module('icestudio') }; for (i in blocks) { block = blocks[i]; - if (block.type === 'basic.input' && !block.data.range && !block.data.virtual) { - if (initPort.pin === block.data.pins[0].value) { - found = true; - source.block = block.id; - break; - } + if (block.type === 'basic.input' && + !block.data.range && + !block.data.virtual && + initPort.pin === block.data.pins[0].value) { + found = true; + source.block = block.id; + break; } } @@ -496,7 +463,7 @@ angular.module('icestudio') }); } - // Add imaginary wire between the imaginary input block and the initPort + // Add imaginary wire between the input block and the initPort project.design.graph.wires.push({ source: { block: source.block, @@ -623,12 +590,13 @@ angular.module('icestudio') var found = false; for (j in blocks) { block = blocks[j]; - if (block.type === 'basic.input' && !block.data.range && !block.data.virtual) { - if (initPort.pin === block.data.pins[0].value) { - found = true; - used.push(initPort.pin); - break; - } + if (block.type === 'basic.input' && + !block.data.range && + !block.data.virtual && + initPort.pin === block.data.pins[0].value) { + found = true; + used.push(initPort.pin); + break; } } diff --git a/app/scripts/services/graph.service.js b/app/scripts/services/graph.service.js index 30e027fd2..13a223e55 100644 --- a/app/scripts/services/graph.service.js +++ b/app/scripts/services/graph.service.js @@ -441,6 +441,7 @@ angular.module('icestudio') graph.on('add change:source change:target', function(cell) { if (cell.isLink() && cell.get('source').id) { + // Link connected var target = cell.get('target'); if (target.id) { // Connected to a port @@ -452,18 +453,17 @@ angular.module('icestudio') target = cell.get('lastTarget'); updatePortDefault(target, true); } - //console.log('Connect link', cell, target); } }); graph.on('remove', function(cell) { if (cell.isLink()) { + // Link removed var target = cell.get('target'); if (!target.id) { target = cell.get('lastTarget'); } updatePortDefault(target, true); - //console.log('Remove link', cell); } }); diff --git a/app/scripts/services/project.service.js b/app/scripts/services/project.service.js index 64ad16a9d..0334c3b83 100644 --- a/app/scripts/services/project.service.js +++ b/app/scripts/services/project.service.js @@ -276,7 +276,7 @@ angular.module('icestudio') this.updateTitle(name); sortGraph(); - this.update(); + this.update({ full: true }); utils.saveFile(filepath, project, function() { alertify.success(gettextCatalog.getString('Project {{name}} saved', { name: utils.bold(name) })); }, true); @@ -405,12 +405,14 @@ angular.module('icestudio') }); }; - this.update = function(callback, updateDependencies) { + this.update = function(opt, callback) { var graphData = graph.toJSON(); var blocks = []; var wires = []; + opt = opt || {}; + for (var c = 0; c < graphData.cells.length; c++) { var cell = graphData.cells[c]; @@ -429,7 +431,7 @@ angular.module('icestudio') cell.type === 'ice.Info') { delete block.data.deltas; } - if (cell.type === 'ice.Code') { + if (opt.full && cell.type === 'ice.Code') { for (var i in block.data.ports.in) { delete block.data.ports.in[i].default; } @@ -458,9 +460,9 @@ angular.module('icestudio') }; // Update dependencies - if (updateDependencies !== false) { + if (opt.deps !== false) { project.dependencies = {}; - var types = findSubDependencies(project); + var types = findSubDependencies(project, opt); for (var t in types) { project.dependencies[types[t]] = allDependencies[types[t]]; } @@ -471,7 +473,7 @@ angular.module('icestudio') } }; - function findSubDependencies(dependency) { + function findSubDependencies(dependency, opt) { var subDependencies = []; if (dependency) { var blocks = dependency.design.graph.blocks; @@ -479,10 +481,10 @@ angular.module('icestudio') var type = blocks[i].type; if (type.indexOf('basic.') === -1) { subDependencies.push(type); - var newSubDependencies = findSubDependencies(allDependencies[type]); + var newSubDependencies = findSubDependencies(allDependencies[type], opt); subDependencies = subDependencies.concat(newSubDependencies); } - else if (type === 'basic.input') { + else if (opt.full && type === 'basic.input') { delete blocks[i].data.default; } } From 66454b67a36e1d09a00291fc460103ef05b7afd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Mon, 30 Jan 2017 11:25:20 +0100 Subject: [PATCH 043/124] Clearest block borders --- app/scripts/graphics/joint.shapes.js | 4 ++-- app/styles/design.css | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/app/scripts/graphics/joint.shapes.js b/app/scripts/graphics/joint.shapes.js index 10b92ed95..ce8fd4ba0 100644 --- a/app/scripts/graphics/joint.shapes.js +++ b/app/scripts/graphics/joint.shapes.js @@ -88,7 +88,7 @@ joint.shapes.ice.Model = joint.shapes.basic.Generic.extend({ height: '20', rx: '3', ry: '3', - stroke: '#555', + stroke: '#777', 'stroke-width': 1, fill: '#FBFBC9' }, @@ -614,7 +614,7 @@ joint.shapes.ice.ConstantView = joint.shapes.ice.ModelView.extend({ template: '\
    \ -

    *

    \ +

    \ \ \
    \ diff --git a/app/styles/design.css b/app/styles/design.css index 7aac7a0f4..84680964a 100644 --- a/app/styles/design.css +++ b/app/styles/design.css @@ -75,7 +75,7 @@ position: absolute; background: #C0DFEB; border-radius: 5px; - border: 1px solid #555; + border: 1px solid #777; pointer-events: none; -webkit-user-select: none; user-select: none; @@ -141,7 +141,7 @@ position: absolute; background: #E2FBC9; border-radius: 5px; - border: 1px solid #555; + border: 1px solid #777; pointer-events: none; -webkit-user-select: none; user-select: none; @@ -162,7 +162,7 @@ position: absolute; background: #FBFBC9; border-radius: 5px; - border: 1px solid #555; + border: 1px solid #777; pointer-events: none; -webkit-user-select: none; user-select: none; @@ -197,7 +197,7 @@ } .select2-selection__rendered { - line-height: 24px !important; + line-height: 25px !important; } .select2-selection { @@ -206,11 +206,11 @@ } .select2-container--default .select2-selection--single { - border: 1px solid #777; + border: 1px solid #aaa; } .select2-dropdown { - border: 1px solid #777; + border: 1px solid #aaa; } .bigdrop { @@ -222,7 +222,7 @@ position: absolute; background: #FBF0C9; border-radius: 5px; - border: 1px solid #555; + border: 1px solid #777; pointer-events: none; -webkit-user-select: none; user-select: none; @@ -233,7 +233,7 @@ position: absolute; top: 0; right: 5px; - font-size: 1.5em; + font-size: 12px; color: #555; } @@ -258,7 +258,7 @@ height: 26px; font-size: 13px; border-radius: 4px; - border: 1px solid #777; + border: 1px solid #aaa; pointer-events: auto; } @@ -266,7 +266,7 @@ position: absolute; background: #C0DFEB; border-radius: 5px; - border: 1px solid #555; + border: 1px solid #777; pointer-events: none; -webkit-user-select: none; user-select: none; @@ -291,7 +291,7 @@ position: absolute; background: #DDD; border-radius: 5px; - border: 1px solid #555; + border: 1px solid #777; pointer-events: none; -webkit-user-select: none; user-select: none; From 4347bf6d8c69cc162742dab4338511affbf98ab6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Mon, 30 Jan 2017 14:19:10 +0100 Subject: [PATCH 044/124] Add "Board rules" section in Preferences --- app/scripts/controllers/menu.js | 12 +++++++ app/scripts/graphics/joint.shapes.js | 26 ++++++++------ app/scripts/services/graph.service.js | 18 ++++++++-- app/views/menu.html | 49 +++++++++++++++++++-------- 4 files changed, 78 insertions(+), 27 deletions(-) diff --git a/app/scripts/controllers/menu.js b/app/scripts/controllers/menu.js index fe58aec43..89796d359 100644 --- a/app/scripts/controllers/menu.js +++ b/app/scripts/controllers/menu.js @@ -325,6 +325,18 @@ angular.module('icestudio') }); }; + $scope.enableBoardRules = function() { + profile.data.boardRules = true; + graph.refreshBoardRules(); + alertify.success(gettextCatalog.getString('Board rules enabled')); + }; + + $scope.disableBoardRules = function() { + profile.data.boardRules = false; + graph.refreshBoardRules(); + alertify.success(gettextCatalog.getString('Board rules disabled')); + }; + $scope.selectLanguage = function(language) { if (profile.data.language !== language) { profile.data.language = language; diff --git a/app/scripts/graphics/joint.shapes.js b/app/scripts/graphics/joint.shapes.js index ce8fd4ba0..961bfe2a5 100644 --- a/app/scripts/graphics/joint.shapes.js +++ b/app/scripts/graphics/joint.shapes.js @@ -20,7 +20,7 @@ joint.shapes.ice.Model = joint.shapes.basic.Generic.extend({ markup: '', portMarkup: ' \ - \ + \ \ \ \ @@ -158,11 +158,9 @@ joint.shapes.ice.Model = joint.shapes.basic.Generic.extend({ } }; - if (port.default && port.default.apply) { - attrs[portDefaultSelector] = { - display: 'visible' - }; - } + attrs[portDefaultSelector] = { + display: (port.default && port.default.apply) ? 'inline' : 'none' + }; if ((type === 'leftPorts') || (type === 'topPorts')) { attrs[portSelector]['pointer-events'] = 'none'; @@ -275,6 +273,7 @@ joint.shapes.ice.ModelView = joint.dia.ElementView.extend({ var i, port; var bbox = this.model.getBBox(); var state = this.model.get('state'); + var rules = this.model.get('rules'); var leftPorts = this.model.get('leftPorts'); var rightPorts = this.model.get('rightPorts'); @@ -284,8 +283,11 @@ joint.shapes.ice.ModelView = joint.dia.ElementView.extend({ // Render buses for (i in leftPorts) { port = leftPorts[i]; - this.$('#port-default-wire-' + port.id).css('stroke-width', width); - this.$('#port-default-rect-' + port.id).css('stroke-width', state.zoom); + if (port.default && port.default.apply) { + this.$('#port-default-' + port.id).css('display', rules ? 'inline' : 'none'); + this.$('#port-default-wire-' + port.id).css('stroke-width', width); + this.$('#port-default-rect-' + port.id).css('stroke-width', state.zoom); + } if (port.size > 1) { this.$('#port-wire-' + port.id).css('stroke-width', width * 3); } @@ -817,6 +819,7 @@ joint.shapes.ice.CodeView = joint.shapes.ice.ModelView.extend({ var i, port; var bbox = this.model.getBBox(); var state = this.model.get('state'); + var rules = this.model.get('rules'); var leftPorts = this.model.get('leftPorts'); var rightPorts = this.model.get('rightPorts'); @@ -826,8 +829,11 @@ joint.shapes.ice.CodeView = joint.shapes.ice.ModelView.extend({ // Render buses for (i in leftPorts) { port = leftPorts[i]; - this.$('#port-default-wire-' + port.id).css('stroke-width', width); - this.$('#port-default-rect-' + port.id).css('stroke-width', state.zoom); + if (port.default && port.default.apply) { + this.$('#port-default-' + port.id).css('display', rules ? 'inline' : 'none'); + this.$('#port-default-wire-' + port.id).css('stroke-width', width); + this.$('#port-default-rect-' + port.id).css('stroke-width', state.zoom); + } if (port.size > 1) { this.$('#port-wire-' + port.id).css('stroke-width', width * 3); } diff --git a/app/scripts/services/graph.service.js b/app/scripts/services/graph.service.js index 13a223e55..d3ddcb67a 100644 --- a/app/scripts/services/graph.service.js +++ b/app/scripts/services/graph.service.js @@ -5,6 +5,7 @@ angular.module('icestudio') joint, boards, blocks, + profile, utils, gettextCatalog, window) { @@ -476,17 +477,29 @@ angular.module('icestudio') if (ports[i].id === target.port && ports[i].default) { ports[i].default.apply = value; var selector = '.leftPorts>.port' + i + '>.port-default'; - block.attributes.attrs[selector].display = value ? 'visible' : 'none'; - paper.findViewByModel(block.id).update(); + block.attributes.attrs[selector].display = (value && profile.data.boardRules) ? 'inline' : 'none'; break; } } + paper.findViewByModel(block.id).update(); } } } }; + this.refreshBoardRules = function() { + var cells = graph.getCells(); + + _.each(cells, function(cell) { + if (!cell.isLink()) { + cell.attributes.rules = profile.data.boardRules; + var cellView = paper.findViewByModel(cell); + cellView.updateBox(); + } + }); + }; + this.undo = function() { disableSelected(); commandManager.undo(state); @@ -821,6 +834,7 @@ angular.module('icestudio') function addCell(cell) { cell.attributes.state = state; + cell.attributes.rules = profile.data.boardRules; //cell.attributes.zindex = z.index; graph.addCell(cell); if (!cell.isLink()) { diff --git a/app/views/menu.html b/app/views/menu.html index dd448c97b..020d271fd 100644 --- a/app/views/menu.html +++ b/app/views/menu.html @@ -121,48 +121,67 @@ {{ 'Project information' | translate }}
  • -
  • - - {{ 'Remote hostname' | translate }} - - -
  • -
  • +
  • + +
  • +
  • + + {{ 'Remote hostname' | translate }} + + +
  • @@ -186,11 +205,11 @@
    • - {{ 'Default' | translate }} + {{ 'Default' | translate }}  - {{ collection.name }} + {{ collection.name }} 
    • @@ -204,7 +223,7 @@
      • - {{ board.info.label }} + {{ board.info.label }} 
      • From ec44f981b7839fbaafd5522f2b19b49558b84451 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Mon, 30 Jan 2017 15:56:27 +0100 Subject: [PATCH 045/124] Apply "board rules" setting to the compiler --- app/scripts/services/compiler.service.js | 93 ++++++++++++------------ app/scripts/services/profile.service.js | 5 +- app/scripts/services/project.service.js | 3 +- app/scripts/services/tools.service.js | 9 ++- app/views/menu.html | 8 +- 5 files changed, 62 insertions(+), 56 deletions(-) diff --git a/app/scripts/services/compiler.service.js b/app/scripts/services/compiler.service.js index d0076b674..8d715b2ec 100644 --- a/app/scripts/services/compiler.service.js +++ b/app/scripts/services/compiler.service.js @@ -407,6 +407,7 @@ angular.module('icestudio') function verilogCompiler(name, project, opt) { var i, data, block, code = ''; + opt = opt || {}; if (project && project.design && @@ -421,9 +422,9 @@ angular.module('icestudio') // Initialize input ports - if (name === 'main') { + if (name === 'main' && opt.boardRules) { - var initPorts = opt && opt.initPorts || getInitPorts(project); + var initPorts = opt.initPorts || getInitPorts(project); for (i in initPorts) { var initPort = initPorts[i]; @@ -483,11 +484,11 @@ angular.module('icestudio') // Initialize output pins - if (name === 'main') { + if (name === 'main' && opt.boardRules) { // Initialize output pins - var initPins = opt && opt.initPins || getInitPins(project); + var initPins = opt.initPins || getInitPins(project); var n = initPins.length; if (n > 0) { @@ -543,9 +544,9 @@ angular.module('icestudio') } function pcfCompiler(project, opt) { - var i, j, block, code = ''; + var i, j, block, pin, value, code = ''; var blocks = project.design.graph.blocks; - var pin, value; + opt = opt || {}; for (i in blocks) { block = blocks[i]; @@ -575,55 +576,57 @@ angular.module('icestudio') } } - // Declare init input ports + if (opt.boardRules) { + // Declare init input ports - var used = []; - var initPorts = opt && opt.initPorts || getInitPorts(project); - for (i in initPorts) { - var initPort = initPorts[i]; - if (used.indexOf(initPort.pin) !== -1) { - break; - } - used.push(initPort.pin); - - // Find existing input block with the initPort value - var found = false; - for (j in blocks) { - block = blocks[j]; - if (block.type === 'basic.input' && - !block.data.range && - !block.data.virtual && - initPort.pin === block.data.pins[0].value) { - found = true; - used.push(initPort.pin); + var used = []; + var initPorts = opt.initPorts || getInitPorts(project); + for (i in initPorts) { + var initPort = initPorts[i]; + if (used.indexOf(initPort.pin) !== -1) { break; } - } + used.push(initPort.pin); + + // Find existing input block with the initPort value + var found = false; + for (j in blocks) { + block = blocks[j]; + if (block.type === 'basic.input' && + !block.data.range && + !block.data.virtual && + initPort.pin === block.data.pins[0].value) { + found = true; + used.push(initPort.pin); + break; + } + } - if (!found) { - code += 'set_io v'; - code += initPorts[i].name; - code += ' '; - code += initPorts[i].pin; - code += '\n'; + if (!found) { + code += 'set_io v'; + code += initPorts[i].name; + code += ' '; + code += initPorts[i].pin; + code += '\n'; + } } - } - // Declare init output pins + // Declare init output pins - var initPins = opt && opt.initPins || getInitPins(project); - if (initPins.length > 1) { - for (i in initPins) { - code += 'set_io vinit[' + i + '] '; - code += initPins[i].pin; + var initPins = opt.initPins || getInitPins(project); + if (initPins.length > 1) { + for (i in initPins) { + code += 'set_io vinit[' + i + '] '; + code += initPins[i].pin; + code += '\n'; + } + } + else if (initPins.length > 0) { + code += 'set_io vinit '; + code += initPins[0].pin; code += '\n'; } } - else if (initPins.length > 0) { - code += 'set_io vinit '; - code += initPins[0].pin; - code += '\n'; - } return code; } diff --git a/app/scripts/services/profile.service.js b/app/scripts/services/profile.service.js index fb3b6156f..5bb2f5aac 100644 --- a/app/scripts/services/profile.service.js +++ b/app/scripts/services/profile.service.js @@ -8,14 +8,15 @@ angular.module('icestudio') 'language': '', 'remoterHostname': '', 'collection': '', - 'board': '' + 'board': '', + 'boardRules': true }; this.load = function(callback) { var self = this; utils.readFile(utils.PROFILE_PATH, function(data) { if (data) { - self.data = data; + self.data = _.merge({}, self.data); } if (callback) { callback(); diff --git a/app/scripts/services/project.service.js b/app/scripts/services/project.service.js index 0334c3b83..b709b8a24 100644 --- a/app/scripts/services/project.service.js +++ b/app/scripts/services/project.service.js @@ -504,7 +504,8 @@ angular.module('icestudio') this.export = function(target, filepath, message) { this.update(); - var data = compiler.generate(target, project); + var opt = { boardRules: profile.data.boardRules }; + var data = compiler.generate(target, project, opt); utils.saveFile(filepath, data, function() { alertify.success(message); }, false); diff --git a/app/scripts/services/tools.service.js b/app/scripts/services/tools.service.js index c67aa0a56..5a5b2acdc 100644 --- a/app/scripts/services/tools.service.js +++ b/app/scripts/services/tools.service.js @@ -133,10 +133,11 @@ angular.module('icestudio') nodeFs.mkdirSync(utils.BUILD_DIR); } project.update(); - var opt = { - initPorts: compiler.getInitPorts(project.get()), - initPins: compiler.getInitPins(project.get()) - }; + var opt = { boardRules: profile.data.boardRules }; + if (opt.boardRules) { + opt.initPorts = compiler.getInitPorts(project.get()); + opt.initPins = compiler.getInitPins(project.get()); + } var verilog = compiler.generate('verilog', project.get(), opt); var pcf = compiler.generate('pcf', project.get(), opt); nodeFs.writeFileSync(nodePath.join(utils.BUILD_DIR, 'main.v'), verilog, 'utf8'); diff --git a/app/views/menu.html b/app/views/menu.html index 020d271fd..8c7330ff8 100644 --- a/app/views/menu.html +++ b/app/views/menu.html @@ -163,13 +163,13 @@
          -
        • - +
        • + {{ 'Enable' | translate }}
        • -
        • - +
        • + {{ 'Disable' | translate }}
        • From a0d8fdaaff0f4b405952e0556289885272f77460 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Mon, 30 Jan 2017 16:53:38 +0100 Subject: [PATCH 046/124] Add View > Board rules --- app/resources/viewers/plain/pcf.html | 10 +++-- app/resources/viewers/svg/pinout.html | 8 ++-- app/resources/viewers/table/rules.html | 54 ++++++++++++++++++++++++++ app/scripts/controllers/menu.js | 19 +++++++++ app/views/menu.html | 3 ++ 5 files changed, 87 insertions(+), 7 deletions(-) create mode 100644 app/resources/viewers/table/rules.html diff --git a/app/resources/viewers/plain/pcf.html b/app/resources/viewers/plain/pcf.html index 98945b6ae..048f63f1c 100644 --- a/app/resources/viewers/plain/pcf.html +++ b/app/resources/viewers/plain/pcf.html @@ -1,6 +1,7 @@ + PCF + + +
          + + + diff --git a/app/scripts/controllers/menu.js b/app/scripts/controllers/menu.js index 89796d359..ced06ba69 100644 --- a/app/scripts/controllers/menu.js +++ b/app/scripts/controllers/menu.js @@ -387,6 +387,25 @@ angular.module('icestudio') } }; + $scope.showBoardRules = function() { + var board = boards.selectedBoard; + var rules = JSON.stringify(board.rules); + if (rules !== '{}') { + gui.Window.open('resources/viewers/table/rules.html?rules=' + rules, { + title: boards.selectedBoard.info.label + ' - Rules', + focus: true, + toolbar: false, + resizable: false, + width: 500, + height: 500, + icon: 'resources/images/icestudio-logo.png' + }); + } + else { + alertify.notify(gettextCatalog.getString('{{board}} rules not defined', { board: utils.bold(board.info.label) }), 'error', 5); + } + }; + $scope.selectCollection = function(collection) { if (resources.selectedCollection.name !== collection.name) { var name = resources.selectCollection(collection.name); diff --git a/app/views/menu.html b/app/views/menu.html index 8c7330ff8..72721b0ec 100644 --- a/app/views/menu.html +++ b/app/views/menu.html @@ -199,6 +199,9 @@
        • {{ 'Datasheet' | translate }}
        • +
        • + {{ 'Board rules' | translate }} +
        • '); + content.push('
          '); content.push(''); // Restore values $('#label').val(values[0]); - $('#local').prop('checked', values[1]); + $('#check').prop('checked', values[1]); alertify.confirm(content.join('\n')) .set('onok', function(evt) { var values = []; values.push($('#label').val()); - values.push($('#local').prop('checked')); + values.push($('#check').prop('checked')); + if (callback) { + callback(evt, values); + } + }) + .set('oncancel', function(/*evt*/) { + }); + }; + + this.inputcheckbox2prompt = function(messages, values, callback) { + var content = []; + content.push('
          '); + content.push('

          ' + messages[0] + '

          '); + content.push(' '); + content.push('
          '); + content.push('
          '); + content.push('
          '); + content.push('
          '); + // Restore values + $('#label').val(values[0]); + $('#check1').prop('checked', values[1]); + $('#check2').prop('checked', values[2]); + + alertify.confirm(content.join('\n')) + .set('onok', function(evt) { + var values = []; + values.push($('#label').val()); + values.push($('#check1').prop('checked')); + values.push($('#check2').prop('checked')); if (callback) { callback(evt, values); } diff --git a/app/styles/design.css b/app/styles/design.css index d4f5c85ce..6c4926516 100644 --- a/app/styles/design.css +++ b/app/styles/design.css @@ -148,11 +148,20 @@ z-index: 0; } +.virtual-port p { + position: absolute; + top: 0; + right: 5px; + font-size: 13px; + color: #555; +} + .virtual-port label { display: block; margin-top: 22px; text-align: center; font-size: 13px; + height: 18px; color: #333; text-overflow: ellipsis; overflow: hidden; @@ -169,6 +178,14 @@ z-index: 0; } +.fpga-port p { + position: absolute; + top: 0; + right: 5px; + font-size: 13px; + color: #555; +} + .fpga-port label { display: block; margin-top: 6px; From 093cfd263b27dcfbb20bfdea36e4c7180e2b4d22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Tue, 31 Jan 2017 16:38:10 +0100 Subject: [PATCH 055/124] Fix: improve project prune management --- app/scripts/services/project.service.js | 53 +++++++++++++++++-------- 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/app/scripts/services/project.service.js b/app/scripts/services/project.service.js index b709b8a24..d0e0828e0 100644 --- a/app/scripts/services/project.service.js +++ b/app/scripts/services/project.service.js @@ -276,8 +276,8 @@ angular.module('icestudio') this.updateTitle(name); sortGraph(); - this.update({ full: true }); - utils.saveFile(filepath, project, function() { + this.update(); + utils.saveFile(filepath, pruneProject(project), function() { alertify.success(gettextCatalog.getString('Project {{name}} saved', { name: utils.bold(name) })); }, true); }; @@ -405,6 +405,37 @@ angular.module('icestudio') }); }; + function pruneProject (project) { + var _project = utils.clone(project); + + _prune(_project); + for (var d in _project.dependencies) { + _prune(_project.dependencies[d]); + } + + function _prune(_project) { + for (var i in _project.design.graph.blocks) { + var block = _project.design.graph.blocks[i]; + switch (block.type) { + case 'basic.input': + delete block.data.default; + break; + case 'basic.code': + for (var j in block.data.ports.in) { + delete block.data.ports.in[j].default; + } + delete block.data.deltas; + break; + case 'basic.info': + delete block.data.deltas; + break; + } + } + } + + return _project; + } + this.update = function(opt, callback) { var graphData = graph.toJSON(); @@ -427,15 +458,6 @@ angular.module('icestudio') block.type = cell.blockType; block.data = cell.data; block.position = cell.position; - if (cell.type === 'ice.Code' || - cell.type === 'ice.Info') { - delete block.data.deltas; - } - if (opt.full && cell.type === 'ice.Code') { - for (var i in block.data.ports.in) { - delete block.data.ports.in[i].default; - } - } blocks.push(block); } else if (cell.type === 'ice.Wire') { @@ -462,7 +484,7 @@ angular.module('icestudio') // Update dependencies if (opt.deps !== false) { project.dependencies = {}; - var types = findSubDependencies(project, opt); + var types = findSubDependencies(project); for (var t in types) { project.dependencies[types[t]] = allDependencies[types[t]]; } @@ -473,7 +495,7 @@ angular.module('icestudio') } }; - function findSubDependencies(dependency, opt) { + function findSubDependencies(dependency) { var subDependencies = []; if (dependency) { var blocks = dependency.design.graph.blocks; @@ -481,12 +503,9 @@ angular.module('icestudio') var type = blocks[i].type; if (type.indexOf('basic.') === -1) { subDependencies.push(type); - var newSubDependencies = findSubDependencies(allDependencies[type], opt); + var newSubDependencies = findSubDependencies(allDependencies[type]); subDependencies = subDependencies.concat(newSubDependencies); } - else if (opt.full && type === 'basic.input') { - delete blocks[i].data.default; - } } return _.unique(subDependencies); } From 002f60fa1f9cfcb7c10f210485d1908590a1ceb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Tue, 31 Jan 2017 18:18:29 +0100 Subject: [PATCH 056/124] Fix: initialize state in commandManager. Update wires after remove --- app/scripts/controllers/menu.js | 9 +++-- app/scripts/services/graph.service.js | 50 ++++++++++++++------------- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/app/scripts/controllers/menu.js b/app/scripts/controllers/menu.js index 50c0cece6..ced06ba69 100644 --- a/app/scripts/controllers/menu.js +++ b/app/scripts/controllers/menu.js @@ -540,11 +540,10 @@ angular.module('icestudio') var currentUndoStack = []; $(document).on('stackChanged', function(evt, undoStack) { - if (!zeroProject) { - currentUndoStack = undoStack; - project.changed = JSON.stringify(storedUndoStack) !== JSON.stringify(undoStack); - project.updateTitle(); - } + currentUndoStack = undoStack; + project.changed = JSON.stringify(storedUndoStack) !== JSON.stringify(undoStack); + project.updateTitle(); + zeroProject = false; }); function resetChanged() { diff --git a/app/scripts/services/graph.service.js b/app/scripts/services/graph.service.js index caf4548db..0eb9eec91 100644 --- a/app/scripts/services/graph.service.js +++ b/app/scripts/services/graph.service.js @@ -27,13 +27,7 @@ angular.module('icestudio') this.breadcrumbs = [{ name: '', type: '' }]; var gridsize = 8; - var state = { - pan: { - x: 0, - y: 0 - }, - zoom: 1.0 - }; + var state = { pan: { x: 0, y: 0 }, zoom: 1.0 }; const ZOOM_MAX = 2; const ZOOM_MIN = 0.2; @@ -342,26 +336,19 @@ angular.module('icestudio') selectionView.createSelectionBox(cellView); unhighlight(cellView); } - // Update wires on obstacles - var cells = graph.getCells(); - for (var i in cells) { - var cell = cells[i]; - if (cell.isLink()) { - paper.findViewByModel(cell).update(); - } - } + updateWiresOnObstacles(); } } }); - paper.on('cell:pointerdown', function(cellView) { - self.mousedown = true; - if (paper.options.enabled) { - if (cellView.model.isLink()) { - // Unhighlight source block of the wire - unhighlight(paper.findViewByModel(cellView.model.get('source').id)); - } - } + paper.on('cell:pointerdown', function(cellView) { + self.mousedown = true; + if (paper.options.enabled) { + if (cellView.model.isLink()) { + // Unhighlight source block of the wire + unhighlight(paper.findViewByModel(cellView.model.get('source').id)); + } + } }); paper.on('cell:pointerdblclick', function(cellView/*, evt, x, y*/) { @@ -488,8 +475,21 @@ angular.module('icestudio') } } + // Initialize state + graph.trigger('state', state); + }; + function updateWiresOnObstacles() { + var cells = graph.getCells(); + for (var i in cells) { + var cell = cells[i]; + if (cell.isLink()) { + paper.findViewByModel(cell).update(); + } + } + } + this.refreshBoardRules = function() { var cells = graph.getCells(); @@ -781,6 +781,7 @@ angular.module('icestudio') if (selection) { graph.removeCells(selection.models); selectionView.cancelSelection(); + updateWiresOnObstacles(); } }; @@ -870,10 +871,11 @@ angular.module('icestudio') $('body').addClass('waiting'); + this.setState(design.state); + commandManager.stopListening(); this.clearAll(); - this.setState(design.state); setTimeout(function() { var cell; From 76a31f983c94cef83f4040fa057a8d85a11262d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Tue, 31 Jan 2017 18:54:34 +0100 Subject: [PATCH 057/124] Render clock in generic blocks --- app/scripts/graphics/joint.shapes.js | 15 +++++++++++++-- app/scripts/services/blocks.service.js | 3 ++- app/styles/design.css | 8 ++++++++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/app/scripts/graphics/joint.shapes.js b/app/scripts/graphics/joint.shapes.js index f7bd6770b..f062ceac1 100644 --- a/app/scripts/graphics/joint.shapes.js +++ b/app/scripts/graphics/joint.shapes.js @@ -395,15 +395,25 @@ joint.shapes.ice.GenericView = joint.shapes.ice.ModelView.extend({ var image = this.model.get('image'); var label = this.model.get('label'); + var ports = this.model.get('leftPorts'); this.tooltip = this.model.get('tooltip'); this.tooltiptext = this.$box.find('.tooltiptext'); + // Render clocks + for (var i in ports) { + var port = ports[i]; + if (port.clock) { + this.$box.prepend('

          >

          '); + } + } + var imageSelector = this.$box.find('img'); var labelSelector = this.$box.find('label'); if (image) { + // Render image imageSelector.attr('src', 'data:image/svg+xml,' + image); - if (imageSelector[0].width / imageSelector[0].height > 1.5) { + if (imageSelector[0].width / imageSelector[0].height > this.$box.width() / this.$box.height()) { imageSelector.css('width', '80%'); } else { @@ -413,6 +423,7 @@ joint.shapes.ice.GenericView = joint.shapes.ice.ModelView.extend({ labelSelector.addClass('hidden'); } else { + // Render label labelSelector.html(label); labelSelector.removeClass('hidden'); imageSelector.addClass('hidden'); @@ -514,9 +525,9 @@ joint.shapes.ice.IOView = joint.shapes.ice.ModelView.extend({ if (!this.model.get('disabled')) { this.applyChoices(); this.applyValues(); - this.applyClock(); this.applyShape(); } + this.applyClock(); }, applyShape: function() { diff --git a/app/scripts/services/blocks.service.js b/app/scripts/services/blocks.service.js index 9e30b5170..61e96d21a 100644 --- a/app/scripts/services/blocks.service.js +++ b/app/scripts/services/blocks.service.js @@ -582,7 +582,8 @@ angular.module('icestudio') id: item.id, label: item.data.name + (item.data.range || ''), size: data.pins ? data.pins.length : (data.size || 1), - default: data.default + default: data.default, + clock: data.clock }); } else if (item.type === 'basic.output') { diff --git a/app/styles/design.css b/app/styles/design.css index 6c4926516..0d6790ea1 100644 --- a/app/styles/design.css +++ b/app/styles/design.css @@ -82,6 +82,14 @@ z-index: 0; } +.generic-block p { + position: absolute; + top: 3px; + left: -1px; + font-size: 18px; + color: #777; +} + .generic-block .tooltiptext { visibility: hidden; width: 120px; From e90994fa1b60d2358b3db979a9336883e5ec1a6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Tue, 31 Jan 2017 19:10:43 +0100 Subject: [PATCH 058/124] Add clock pin -> "clk" rule --- app/scripts/services/blocks.service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/scripts/services/blocks.service.js b/app/scripts/services/blocks.service.js index 61e96d21a..e69174f98 100644 --- a/app/scripts/services/blocks.service.js +++ b/app/scripts/services/blocks.service.js @@ -576,7 +576,7 @@ angular.module('icestudio') if (item.type === 'basic.input') { data = block.design.graph.blocks[i].data; if (!item.data.range) { - data.default = hasInputRule(item.data.name); + data.default = hasInputRule(item.data.name || (item.data.clock ? 'clk' : '')); } leftPorts.push({ id: item.id, From e477ac989b50fc3a125851ff116c797eb1268d04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Wed, 1 Feb 2017 11:12:25 +0100 Subject: [PATCH 059/124] Refactor shortcuts system. Add shortcuts service --- app/index.html | 1 + app/scripts/controllers/menu.js | 38 ++++++----- app/scripts/services/shortcuts.service.js | 77 +++++++++++++++++++++++ app/scripts/services/utils.service.js | 1 + app/views/menu.html | 4 +- 5 files changed, 102 insertions(+), 19 deletions(-) create mode 100644 app/scripts/services/shortcuts.service.js diff --git a/app/index.html b/app/index.html index 2461945d0..09ad57448 100644 --- a/app/index.html +++ b/app/index.html @@ -67,6 +67,7 @@ + diff --git a/app/scripts/controllers/menu.js b/app/scripts/controllers/menu.js index ced06ba69..2f4a8c323 100644 --- a/app/scripts/controllers/menu.js +++ b/app/scripts/controllers/menu.js @@ -11,6 +11,7 @@ angular.module('icestudio') graph, tools, utils, + shortcuts, gettextCatalog, gui, _package, @@ -566,8 +567,13 @@ angular.module('icestudio') } }); + shortcuts.method('newProject', $scope.newProject); + shortcuts.method('openProject', $scope.openProject); + $(document).on('keydown', function(event) { - if (!promptShown) { + console.log(event); + shortcuts.execute(event); + /*if (!promptShown) { if (graph.isEnabled()) { if (event.ctrlKey) { switch (event.keyCode) { @@ -626,21 +632,19 @@ angular.module('icestudio') } } - if (graph.hasSelection()) { - switch (event.keyCode) { - case 37: // Arrow Left - graph.stepLeft(); - break; - case 38: // Arrow Up - graph.stepUp(); - break; - case 39: // Arrow Right - graph.stepRight(); - break; - case 40: // Arrow Down - graph.stepDown(); - break; - } + switch (event.keyCode) { + case 37: // Arrow Left + graph.stepLeft(); + break; + case 38: // Arrow Up + graph.stepUp(); + break; + case 39: // Arrow Right + graph.stepRight(); + break; + case 40: // Arrow Down + graph.stepDown(); + break; } if (event.keyCode === 46) { // Supr @@ -671,7 +675,7 @@ angular.module('icestudio') if (event.ctrlKey && event.keyCode === 80) { // Ctrl+P // Print and save a window snapshot takeSnapshot(); - } + }*/ }); function takeSnapshot() { diff --git a/app/scripts/services/shortcuts.service.js b/app/scripts/services/shortcuts.service.js new file mode 100644 index 000000000..cfeed8cd2 --- /dev/null +++ b/app/scripts/services/shortcuts.service.js @@ -0,0 +1,77 @@ +'use strict'; + +angular.module('icestudio') + .filter('shortcut', function(shortcuts) { + return function(action) { + return shortcuts.label(action); + }; + }) + .service('shortcuts', function(utils) { + + this.method = function(action, method) { + if (action in shortcuts) { + shortcuts[action].method = method; + } + }; + + this.execute = function(event) { + var method = null; + var system = utils.DARWIN ? 'mac' : 'unix'; + for (var action in shortcuts) { + var command = shortcuts[action][system]; + if (event.keyCode === command.key && + event.ctrlKey === (command.ctrl || false) && + event.metaKey === (command.meta || false)) { + + method = shortcuts[action].method; + break; + } + } + if (method) { + method(); + } + }; + + this.label = function(action) { + var label = ''; + if (action in shortcuts) { + if (utils.DARWIN) { + label = shortcuts[action].mac.label; + } + else { + label = shortcuts[action].unix.label; + } + } + return label; + }; + + var shortcuts = { + newProject: { + method: null, + unix: { + label: 'Ctrl+N', + ctrl: true, + key: 78 + }, + mac: { + label: '⌘+N', + meta: true, + key: 78 + } + }, + openProject: { + method: null, + unix: { + label: 'Ctrl+O', + ctrl: true, + key: 79 + }, + mac: { + label: '⌘+O', + meta: true, + key: 79 + } + }, + }; + + }); diff --git a/app/scripts/services/utils.service.js b/app/scripts/services/utils.service.js index 6171bc38a..8f9e05539 100644 --- a/app/scripts/services/utils.service.js +++ b/app/scripts/services/utils.service.js @@ -21,6 +21,7 @@ angular.module('icestudio') const WIN32 = Boolean(process.platform.indexOf('win32') > -1); const DARWIN = Boolean(process.platform.indexOf('darwin') > -1); + this.DARWIN = DARWIN; const LOCALE_DIR = nodePath.join('resources', 'locale'); const SAMPLE_DIR = nodePath.join('resources', 'sample'); diff --git a/app/views/menu.html b/app/views/menu.html index 39b5b4df2..3863eeb05 100644 --- a/app/views/menu.html +++ b/app/views/menu.html @@ -26,10 +26,10 @@ {{ 'File' | translate }}
          • - {{ 'New' | translate }}Ctrl+N + {{ 'New' | translate }}{{ 'newProject' | shortcut }}
          • - {{ 'Open' | translate }}...Ctrl+O + {{ 'Open' | translate }}...{{ 'openProject' | shortcut }}
          • {{ 'Add as block' | translate }}... From d451e9327ce45709c7e402fe32bfb7fb8325b90c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Wed, 1 Feb 2017 12:29:59 +0100 Subject: [PATCH 060/124] Update all shortcuts to the new system --- app/scripts/controllers/menu.js | 159 ++++++++-------------- app/scripts/services/shortcuts.service.js | 122 +++++++++++++---- app/views/menu.html | 28 ++-- 3 files changed, 165 insertions(+), 144 deletions(-) diff --git a/app/scripts/controllers/menu.js b/app/scripts/controllers/menu.js index 2f4a8c323..fa595addf 100644 --- a/app/scripts/controllers/menu.js +++ b/app/scripts/controllers/menu.js @@ -554,7 +554,7 @@ angular.module('icestudio') zeroProject = false; } - // Shortcuts + // Detect prompt var promptShown = false; @@ -567,115 +567,66 @@ angular.module('icestudio') } }); + alertify.confirm().set({ + onshow: function() { + promptShown = true; + }, + onclose: function() { + promptShown = false; + } + }); + + // Configure all shortcuts + + // -- File shortcuts.method('newProject', $scope.newProject); shortcuts.method('openProject', $scope.openProject); + shortcuts.method('saveProject', $scope.saveProject); + shortcuts.method('quit', $scope.quit); + + // -- Edit + shortcuts.method('undoGraph', $scope.undoGraph); + shortcuts.method('redoGraph', $scope.redoGraph); + shortcuts.method('redoGraph2', $scope.redoGraph); + shortcuts.method('cutSelected', $scope.cutSelected); + shortcuts.method('copySelected', $scope.copySelected); + shortcuts.method('pasteSelected', $scope.pasteSelected); + shortcuts.method('selectAll', $scope.selectAll); + shortcuts.method('resetView', $scope.resetView); + shortcuts.method('fitContent', $scope.fitContent); + + // -- Tools + shortcuts.method('verifyCode', $scope.verifyCode); + shortcuts.method('buildCode', $scope.buildCode); + shortcuts.method('uploadCode', $scope.uploadCode); + + // -- Misc + shortcuts.method('stepUp', graph.stepUp); + shortcuts.method('stepDown', graph.stepDown); + shortcuts.method('stepLeft', graph.stepLeft); + shortcuts.method('stepRight', graph.stepRight); + + shortcuts.method('removeSelected', removeSelected); + shortcuts.method('breadcrumbsBack', function() { + if (!graph.isEnabled()) { + $rootScope.$broadcast('breadcrumbsBack'); + } + }); - $(document).on('keydown', function(event) { - console.log(event); - shortcuts.execute(event); - /*if (!promptShown) { - if (graph.isEnabled()) { - if (event.ctrlKey) { - switch (event.keyCode) { - case 78: // Ctrl+N - $scope.newProject(); - break; - case 79: // Ctrl+O - $scope.openProject(); - break; - case 83: - if (event.shiftKey) { // Ctrl+Shift+S - $scope.saveProjectAs(); - } - else { // Ctrl+S - $scope.saveProject(); - } - break; - case 81: // Ctrl+Q - $scope.quit(); - break; - case 90: - if (event.shiftKey) { // Ctrl+Shift+Z - $scope.redoGraph(); - event.preventDefault(); - } - else { // Ctrl+Z - $scope.undoGraph(); - event.preventDefault(); - } - break; - case 89: // Ctrl+Y - $scope.redoGraph(); - event.preventDefault(); - break; - case 88: // Ctrl+X - $scope.cutSelected(); - break; - case 67: // Ctrl+C - $scope.copySelected(); - break; - case 86: // Ctrl+V - $scope.pasteSelected(); - break; - case 65: // Ctrl+A - $scope.selectAll(); - break; - case 82: // Ctrl+R - $scope.verifyCode(); - break; - case 66: // Ctrl+B - $scope.buildCode(); - break; - case 85: // Ctrl+U - $scope.uploadCode(); - break; - } - } + shortcuts.method('takeSnapshot', takeSnapshot); - switch (event.keyCode) { - case 37: // Arrow Left - graph.stepLeft(); - break; - case 38: // Arrow Up - graph.stepUp(); - break; - case 39: // Arrow Right - graph.stepRight(); - break; - case 40: // Arrow Down - graph.stepDown(); - break; - } + // Detect shortcuts - if (event.keyCode === 46) { // Supr - removeSelected(); - } - } - if (event.ctrlKey) { - switch (event.keyCode) { - case 48: // Ctrl+0 - $scope.resetView(); - break; - case 70: // Ctrl+F - $scope.fitContent(); - break; - } - } - if (event.keyCode === 8) { // Back - if (!graph.isEnabled()) { - $rootScope.$broadcast('breadcrumbsBack'); - } - else { - if (process.platform === 'darwin') { - removeSelected(); - } - } - } + $(document).on('keydown', function(event) { + var opt = { + prompt: promptShown, + disabled: !graph.isEnabled() + }; + + var ret = shortcuts.execute(event, opt); + if (ret.preventDefault) { + event.preventDefault(); } - if (event.ctrlKey && event.keyCode === 80) { // Ctrl+P - // Print and save a window snapshot - takeSnapshot(); - }*/ }); function takeSnapshot() { diff --git a/app/scripts/services/shortcuts.service.js b/app/scripts/services/shortcuts.service.js index cfeed8cd2..89a6e8538 100644 --- a/app/scripts/services/shortcuts.service.js +++ b/app/scripts/services/shortcuts.service.js @@ -9,30 +9,45 @@ angular.module('icestudio') .service('shortcuts', function(utils) { this.method = function(action, method) { + // Configure shortcut method if (action in shortcuts) { - shortcuts[action].method = method; + shortcuts[action]['method'] = method; } }; - this.execute = function(event) { + this.execute = function(event, opt) { + // Execute shortcut method + // Options: + // - opt.prompt: allow shortcut when a prompt is shown + // - opt.disable: allow shortcut when the graph is disabled + + var action = ''; var method = null; var system = utils.DARWIN ? 'mac' : 'unix'; - for (var action in shortcuts) { + var ret = { preventDefault: false }; + for (action in shortcuts) { + var options = shortcuts[action].opt || {}; var command = shortcuts[action][system]; if (event.keyCode === command.key && event.ctrlKey === (command.ctrl || false) && - event.metaKey === (command.meta || false)) { + event.metaKey === (command.meta || false) && + event.shiftKey === (command.shift || false) && + (!opt.prompt || (options.prompt || false)) && + (!opt.disabled || (options.disabled || false))) { method = shortcuts[action].method; + ret.preventDefault = options.preventDefault || false; break; } } if (method) { method(); } + return ret; }; this.label = function(action) { + // Return shortcut label var label = ''; if (action in shortcuts) { if (utils.DARWIN) { @@ -47,31 +62,86 @@ angular.module('icestudio') var shortcuts = { newProject: { - method: null, - unix: { - label: 'Ctrl+N', - ctrl: true, - key: 78 - }, - mac: { - label: '⌘+N', - meta: true, - key: 78 - } + unix: { label: 'Ctrl+N', ctrl: true, key: 78 }, + mac: { label: '⌘+N', meta: true, key: 78 } }, openProject: { - method: null, - unix: { - label: 'Ctrl+O', - ctrl: true, - key: 79 - }, - mac: { - label: '⌘+O', - meta: true, - key: 79 - } + unix: { label: 'Ctrl+O', ctrl: true, key: 79 }, + mac: { label: '⌘+O', meta: true, key: 79 } + }, + saveProject: { + unix: { label: 'Ctrl+S', ctrl: true, key: 83 } + }, + saveProjectAs: { + unix: { label: 'Ctrl+Shift+S', ctrl: true, shift: true, key: 83 } + }, + quit: { + unix: { label: 'Ctrl+Q', ctrl: true, key: 81 } + }, + undoGraph: { + unix: { label: 'Ctrl+Z', ctrl: true, key: 90 }, + opt: { preventDefault: true } + }, + redoGraph: { + unix: { label: 'Ctrl+Y', ctrl: true, key: 89 }, + opt: { preventDefault: true } + }, + redoGraph2: { + unix: { label: 'Ctrl+Shift+Z', ctrl: true, shift: true, key: 90 }, + opt: { preventDefault: true } + }, + cutSelected: { + unix: { label: 'Ctrl+X', ctrl: true, key: 88 } + }, + copySelected: { + unix: { label: 'Ctrl+C', ctrl: true, key: 67 } + }, + pasteSelected: { + unix: { label: 'Ctrl+V', ctrl: true, key: 86 } + }, + selectAll: { + unix: { label: 'Ctrl+A', ctrl: true, key: 65 } }, + resetView: { + unix: { label: 'Ctrl+0', ctrl: true, key: 48 }, + opt: { disabled: true } + }, + fitContent: { + unix: { label: 'Ctrl+F', ctrl: true, key: 70 }, + opt: { disabled: true } + }, + verifyCode: { + unix: { label: 'Ctrl+R', ctrl: true, key: 82 } + }, + buildCode: { + unix: { label: 'Ctrl+B', ctrl: true, key: 66 } + }, + uploadCode: { + unix: { label: 'Ctrl+U', ctrl: true, key: 85 } + }, + stepUp: { + unix: { label: 'Arrow up', key: 38 } + }, + stepDown: { + unix: { label: 'Arrow down', key: 40 } + }, + stepLeft: { + unix: { label: 'Arrow left', key: 37 } + }, + stepRight: { + unix: { label: 'Arrow right', key: 39 } + }, + removeSelected: { + unix: { label: 'Supr', key: 46 } + }, + breadcrumbsBack: { + unix: { label: 'Back', key: 8 }, + opt: { disabled: true } + }, + takeSnapshot: { + unix: { label: 'Ctrl+P', ctrl: true, key: 80 }, + opt: { prompt: true, disabled: true } + } }; }); diff --git a/app/views/menu.html b/app/views/menu.html index 3863eeb05..fdbcb639a 100644 --- a/app/views/menu.html +++ b/app/views/menu.html @@ -54,10 +54,10 @@
          • - {{ 'Save' | translate }}Ctrl+S + {{ 'Save' | translate }}{{ 'saveProject' | shortcut }}
          • - {{ 'Save as' | translate }}...Ctrl+Shift+S + {{ 'Save as' | translate }}...{{ 'saveProjectAs' | shortcut }}
          • - {{ 'Quit' | translate }}Ctrl+Q + {{ 'Quit' | translate }}{{ 'quit' | shortcut }}
          @@ -88,30 +88,30 @@ {{ 'Edit' | translate }}
          • - {{ 'Undo' | translate }}Ctrl+Z + {{ 'Undo' | translate }}{{ 'undoGraph' | shortcut }}
          • - {{ 'Redo' | translate }}Ctrl+Y + {{ 'Redo' | translate }}{{ 'redoGraph' | shortcut }}
          • - {{ 'Cut' | translate }}Ctrl+X + {{ 'Cut' | translate }}{{ 'cutSelected' | shortcut }}
          • - {{ 'Copy' | translate }}Ctrl+C + {{ 'Copy' | translate }}{{ 'copySelected' | shortcut }}
          • - {{ 'Paste' | translate }}Ctrl+V + {{ 'Paste' | translate }}{{ 'pasteSelected' | shortcut }}
          • - {{ 'Select all' | translate }}Ctrl+A + {{ 'Select all' | translate }}{{ 'selectAll' | shortcut }}
          • - {{ 'Reset view' | translate }}Ctrl+0 + {{ 'Reset view' | translate }}{{ 'resetView' | shortcut }}
          • - {{ 'Fit content' | translate }}Ctrl+F + {{ 'Fit content' | translate }}{{ 'fitContent' | shortcut }}