From a51ff50181c3c6477a535c7fc55b757b4417018f Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 12 Apr 2017 12:31:19 +0200 Subject: [PATCH 1/5] Update nightwatch.js --- nightwatch.js | 1 - 1 file changed, 1 deletion(-) diff --git a/nightwatch.js b/nightwatch.js index cce0dc85e8..6a513c6f6d 100644 --- a/nightwatch.js +++ b/nightwatch.js @@ -63,7 +63,6 @@ module.exports = { 'browserName': 'internet explorer', 'javascriptEnabled': true, 'acceptSslCerts': true, - 'platform': 'WIN8.1', 'version': '11', 'build': 'build-' + TRAVIS_JOB_NUMBER, 'tunnel-identifier': 'browsersolidity_tests_' + TRAVIS_JOB_NUMBER From 40f8abe827abf08f3a1b27f43ad8e709d6833a53 Mon Sep 17 00:00:00 2001 From: serapath Date: Mon, 10 Apr 2017 23:42:18 +0700 Subject: [PATCH 2/5] add file explorer --- assets/css/browser-solidity.css | 24 +-- index.html | 9 +- src/app.js | 157 ++++++++------ src/app/editor.js | 21 ++ src/app/file-explorer.js | 318 +++++++++++++++++++++++++++++ src/app/file-panel.js | 173 ++++++++++++++++ test-browser/tests/fileExplorer.js | 40 ++++ 7 files changed, 661 insertions(+), 81 deletions(-) create mode 100755 src/app/file-explorer.js create mode 100644 src/app/file-panel.js create mode 100644 test-browser/tests/fileExplorer.js diff --git a/assets/css/browser-solidity.css b/assets/css/browser-solidity.css index a7f5839845..b09eb240b6 100644 --- a/assets/css/browser-solidity.css +++ b/assets/css/browser-solidity.css @@ -1,3 +1,6 @@ +html { box-sizing: border-box; } +*, *:before, *:after { box-sizing: inherit; } + body { padding: 0; font-size: 12px; @@ -39,11 +42,11 @@ body { left: 0; } -.files-wrapper { +#tabs-bar { position: absolute; overflow: hidden; top: 0; - left: 5em; + left: 200px; right: 3em; } @@ -75,8 +78,6 @@ body { color: #999; } -.newFile, -.uploadFile, .toggleRHP { display: block; float: left; @@ -124,18 +125,6 @@ body { display: inline-block; } -#input { - border-top: 3px solid #F4F6FF; - padding-top: 0.5em; - font-size: 15px; - position: absolute; - top: 2.5em; - left: 0; - right: 0; - bottom: 0; - min-width: 20vw; -} - #righthand-panel { position: absolute; top: 0; @@ -161,6 +150,7 @@ body { float: right; height: 90%; background-color: white; + padding-right: 1%; } #header #menu { @@ -475,7 +465,7 @@ body { bottom: 0; cursor: col-resize; z-index: 999; - border-right: 3px solid #F4F6FF; + border-right: 2px solid #C6CFF7; } #editor .ace-tm .ace_gutter, diff --git a/index.html b/index.html index d1cca62607..e882ab0847 100644 --- a/index.html +++ b/index.html @@ -41,15 +41,16 @@
- - -
+
-
+
+
+
+
diff --git a/src/app.js b/src/app.js index 4da7b9c1fa..2fac32971e 100644 --- a/src/app.js +++ b/src/app.js @@ -1,10 +1,11 @@ -/* global alert, confirm, prompt, FileReader, Option, Worker, chrome */ +/* global alert, confirm, prompt, Option, Worker, chrome */ 'use strict' var async = require('async') var $ = require('jquery') var base64 = require('js-base64').Base64 var swarmgw = require('swarmgw') +var csjs = require('csjs-inject') var QueryParams = require('./app/query-params') var queryParams = new QueryParams() @@ -24,6 +25,7 @@ var FormalVerification = require('./app/formalVerification') var EventManager = require('./lib/eventManager') var StaticAnalysis = require('./app/staticanalysis/staticAnalysisView') var OffsetToLineColumnConverter = require('./lib/offsetToLineColumnConverter') +var FilePanel = require('./app/file-panel') var examples = require('./app/example-contracts') @@ -84,12 +86,6 @@ var run = function () { loadFiles(filesToLoad) } - // -------- check file upload capabilities ------- - - if (!(window.File || window.FileReader || window.FileList || window.Blob)) { - $('.uploadFile').remove() - } - // ------------------ gist load ---------------- var loadingFromGist = gistHandler.handleLoad(queryParams.get(), function (gistId) { @@ -159,26 +155,83 @@ var run = function () { chromeCloudSync() // ----------------- editor ---------------------- - var editor = new Editor(document.getElementById('input')) - // ----------------- tabbed menu ------------------- - $('#options li').click(function (ev) { - var $el = $(this) - selectTab($el) - }) - - var selectTab = function (el) { - var match = /[a-z]+View/.exec(el.get(0).className) - if (!match) return - var cls = match[0] - if (!el.hasClass('active')) { - el.parent().find('li').removeClass('active') - $('#optionViews').attr('class', '').addClass(cls) - el.addClass('active') + // ---------------- FilePanel -------------------- + /**************************************************************************** + @TODO's + 1. I would put a virtual file called Summary as the root entry of the treeview, which displays the list of the files with the size in bytes of each + + 2. drag'n'drop to enable to rename files&folders in the file explorer into different sub folders + + 3. I would put a virtual file called `Summary` as the root entry of the treeview, which displays the list of the files with the size in bytes of each. + + 4. add maybe more tape tests + + 5. gist imports + copy to the browser => phase of writing + + 6. add filemanagement from righthand panel to filepanel compoennt (editing/imports/exports, public gist, load from github, create new project, ... setup load and modify files) + */ + // var sources = { + // 'test/client/credit.sol': '', + // 'src/voting.sol': '', + // 'src/leasing.sol': '', + // 'src/gmbh/contract.sol': false, + // 'src/gmbh/test.sol': false, + // 'src/gmbh/company.sol': false, + // 'src/gmbh/node_modules/ballot.sol': false, + // 'src/ug/finance.sol': false, + // 'app/solidity/mode.sol': true, + // 'app/ethereum/constitution.sol': true + // } + // Object.keys(sources).forEach(function (key) { files.set(key, sources[key]) }) + /****************************************************************************/ + var css = csjs` + .filepanel { + display : flex; + width : 200px; + } + ` + var filepanel = document.querySelector('#filepanel') + filepanel.className = css.filepanel + var FilePanelAPI = { + createName: createNonClashingName, + switchToFile: switchToFile + } + var el = new FilePanel(FilePanelAPI, files) + filepanel.appendChild(el) + var api = el.api + + api.register('ui', function changeLayout (data) { + var value + if (data.type === 'minimize') { + value = -parseInt(window['filepanel'].style.width) + value = (isNaN(value) ? -window['filepanel'].getBoundingClientRect().width : value) + window['filepanel'].style.position = 'absolute' + window['filepanel'].style.left = (value - 5) + 'px' + window['filepanel'].style.width = -value + 'px' + window['tabs-bar'].style.left = '45px' + } else if (data.type === 'maximize') { + value = -parseInt(window['filepanel'].style.left) + 'px' + window['filepanel'].style.position = 'static' + window['filepanel'].style.width = value + window['filepanel'].style.left = '' + window['tabs-bar'].style.left = value + } else { + window['filepanel'].style.width = data.width + 'px' + window['tabs-bar'].style.left = data.width + 'px' } - self.event.trigger('tabChanged', [cls]) - } + }) + api.register('focus', function (path) { + [...window.files.querySelectorAll('.file .name')].forEach(function (span) { + if (span.innerText === path) switchToFile(path) // @TODO: scroll into view + }) + }) + files.event.register('fileRenamed', function (oldName, newName) { + [...window.files.querySelectorAll('.file .name')].forEach(function (span) { + if (span.innerText === oldName) span.innerText = newName + }) + }) // ------------------ gist publish -------------- @@ -221,39 +274,26 @@ var run = function () { }).appendTo('body') }) - // ----------------- file selector------------- - - var $filesEl = $('#files') - var FILE_SCROLL_DELTA = 300 - - $('.newFile').on('click', function () { - var newName = createNonClashingName('Untitled') - if (!files.set(newName, '')) { - alert('Failed to create file ' + newName) - } else { - switchToFile(newName) - } + // ---------------- tabbed menu ------------------ + $('#options li').click(function (ev) { + var $el = $(this) + selectTab($el) }) - // ----------------- file upload ------------- - - $('.inputFile').on('change', function () { - var fileList = $('input.inputFile')[0].files - for (var i = 0; i < fileList.length; i++) { - var name = fileList[i].name - if (!files.exists(name) || confirm('The file ' + name + ' already exists! Would you like to overwrite it?')) { - var fileReader = new FileReader() - fileReader.onload = function (ev) { - if (!files.set(name, ev.target.result)) { - alert('Failed to create file ' + name) - } else { - switchToFile(name) - } - } - fileReader.readAsText(fileList[i]) - } + var selectTab = function (el) { + var match = /[a-z]+View/.exec(el.get(0).className) + if (!match) return + var cls = match[0] + if (!el.hasClass('active')) { + el.parent().find('li').removeClass('active') + $('#optionViews').attr('class', '').addClass(cls) + el.addClass('active') } - }) + self.event.trigger('tabChanged', [cls]) + } + + var $filesEl = $('#files') + var FILE_SCROLL_DELTA = 300 // Switch tab $filesEl.on('click', '.file:not(.active)', function (ev) { @@ -325,6 +365,8 @@ var run = function () { currentFile = file + files.event.trigger('fileFocus', [file]) + if (files.isReadOnly(file)) { editor.openReadOnly(file, files.get(file)) } else { @@ -368,7 +410,6 @@ var run = function () { }) } - var $filesWrapper = $('.files-wrapper') var $scrollerRight = $('.scroller-right') var $scrollerLeft = $('.scroller-left') @@ -381,12 +422,8 @@ var run = function () { return itemsWidth } - // function widthOfHidden () { - // return ($filesWrapper.outerWidth() - widthOfList() - getLeftPosi()) - // } - function widthOfVisible () { - return $filesWrapper.outerWidth() + return document.querySelector('#editor-container').offsetWidth } function getLeftPosi () { diff --git a/src/app/editor.js b/src/app/editor.js index e5f4e59e0a..19475be4d7 100644 --- a/src/app/editor.js +++ b/src/app/editor.js @@ -2,13 +2,34 @@ var EventManager = require('../lib/eventManager') +var csjs = require('csjs-inject') var ace = require('brace') var Range = ace.acequire('ace/range').Range require('../mode-solidity.js') +var css = csjs` + .editor-container { + display : flex; + position : absolute; + top : 2.5em; + left : 0; + right : 0; + bottom : 0; + min-width : 20vw; + } + .ace-editor { + top : 4px; + border-top : 3px solid transparent; + font-size : 15px; + width : 100%; + } +` +document.querySelector('#editor-container').className = css['editor-container'] + function Editor (editorElement) { var editor = ace.edit(editorElement) editorElement.editor = editor // required to access the editor during tests + editorElement.className += ' ' + css['ace-editor'] var event = new EventManager() this.event = event var sessions = {} diff --git a/src/app/file-explorer.js b/src/app/file-explorer.js new file mode 100755 index 0000000000..800afde400 --- /dev/null +++ b/src/app/file-explorer.js @@ -0,0 +1,318 @@ +/* global FileReader, confirm, alert */ +var yo = require('yo-yo') +var csjs = require('csjs-inject') +var Treeview = require('ethereum-remix').ui.TreeView + +var EventManager = require('../lib/eventManager') + +var css = csjs` + .fileexplorer { + box-sizing : border-box; + } + .folder, + .file { + font-size : 14px; + } + .hasFocus { + background-color : #F4F6FF; + } + .rename { + background-color : white; + } + .remove { + align-self : center; + padding-left : 10px; + } + .activeMode { + display : flex; + justify-content : space-between; + margin-right : 10px; + padding-right : 19px; + } + ul { + padding : 0; + } +` +module.exports = fileExplorer + +function fileExplorer (appAPI, files) { + var fileEvents = files.event + var tv = new Treeview({ + extractData: function (value, tree, key) { + var newValue = {} + // var isReadOnly = false + var isFile = false + Object.keys(value).filter(function keep (x) { + if (x === '/content') isFile = true + // if (x === '/readOnly') isReadOnly = true + if (x[0] !== '/') return true + }).forEach(function (x) { newValue[x] = value[x] }) + return { + path: (tree || {}).path ? tree.path + '/' + key : key, + children: isFile ? undefined + : value instanceof Array ? value.map((item, index) => ({ + key: index, value: item + })) : value instanceof Object ? Object.keys(value).map(subkey => ({ + key: subkey, value: value[subkey] + })) : undefined + } + }, + formatSelf: function (key, data) { + return yo`` + } + }) + + var deleteButton = yo` + + + + ` + + fileEvents.register('fileFocus', fileFocus) + fileEvents.register('fileRemoved', fileRemoved) + fileEvents.register('fileRenamed', fileRenamed) + fileEvents.register('fileAdded', fileAdded) + fileEvents.register('fileChanged', fileChanged) + + var filepath = null + var focusElement = null + var textUnderEdit = null + + var element = tv.render(files.listAsTree()) + element.className = css.fileexplorer + + var api = new EventManager() + api.addFile = function addFile (file) { + var name = file.name + if (!files.exists(name) || confirm('The file ' + name + ' already exists! Would you like to overwrite it?')) { + var fileReader = new FileReader() + fileReader.onload = function (event) { + var success = files.set(name, event.target.result) + if (!success) alert('Failed to create file ' + name) + else api.trigger('focus', [name]) + } + fileReader.readAsText(file) + } + } + + function focus (event) { + event.cancelBubble = true + var li = this + if (focusElement === li) return + if (focusElement) focusElement.classList.toggle(css.hasFocus) + focusElement = li + focusElement.classList.toggle(css.hasFocus) + var label = getLabelFrom(li) + var filepath = label.dataset.path + var isFile = label.className.indexOf('file') === 0 + if (isFile) api.trigger('focus', [filepath]) + } + + function hover (event) { + if (event.type === 'mouseout') { + var exitedTo = event.toElement || event.relatedTarget + if (this.contains(exitedTo)) return + this.style.backgroundColor = '' + this.style.paddingRight = '19px' + return this.removeChild(deleteButton) + } + this.style.backgroundColor = '#F4F6FF' + this.style.paddingRight = '0px' + this.appendChild(deleteButton) + } + + function getElement (path) { + var label = element.querySelector(`label[data-path="${path}"]`) + if (label) return getLiFrom(label) + } + + function deletePath (event) { + event.cancelBubble = true + var span = this + var li = span.parentElement.parentElement + var label = getLabelFrom(li) + var path = label.dataset.path + var isFolder = !!~label.className.indexOf('folder') + if (confirm(` + Do you really want to delete "${path}" ? + ${isFolder ? '(and all contained files and folders)' : ''} + `)) { + li.parentElement.removeChild(li) + removeSubtree(files, path) + } + } + + function editModeOn (event) { + var label = this + var li = getLiFrom(label) + var classes = li.className + if (~classes.indexOf('hasFocus') && !label.getAttribute('contenteditable')) { + textUnderEdit = label.innerText + label.setAttribute('contenteditable', true) + label.classList.add(css.rename) + label.focus() + } + } + + function editModeOff (event) { + var label = this + if (event.type === 'blur' || event.which === 27 || event.which === 13) { + var save = textUnderEdit !== label.innerText + if (event.which === 13) event.preventDefault() + if (save && event.which !== 13) save = confirm('Do you want to rename?') + if (save) renameSubtree(label) + else label.innerText = textUnderEdit + label.removeAttribute('contenteditable') + label.classList.remove(css.rename) + } + } + + function renameSubtree (label, dontcheck) { + var oldPath = label.dataset.path + var newPath = oldPath + newPath = newPath.split('/') + newPath[newPath.length - 1] = label.innerText + newPath = newPath.join('/') + if (!dontcheck) { + var allPaths = Object.keys(files.list()) + for (var i = 0, len = allPaths.length, path, err; i < len; i++) { + path = allPaths[i] + if (files.IsReadOnly(path)) { + err = 'path contains readonly elements' + break + } else if (path.indexOf(newPath) === 0) { + err = 'new path is conflicting with another existing path' + break + } + } + } + if (err) { + alert(`couldn't rename - ${err}`) + label.innerText = textUnderEdit + } else { + textUnderEdit = label.innerText + updateAllLabels([getElement(oldPath)], oldPath, newPath) + } + } + + function updateAllLabels (lis, oldPath, newPath) { + lis.forEach(function (li) { + var label = getLabelFrom(li) + var path = label.dataset.path + var newName = path.replace(oldPath, newPath) + label.dataset.path = newName + var isFile = label.className.indexOf('file') === 0 + if (isFile) files.rename(path, newName) + var ul = li.lastChild + if (ul.tagName === 'UL') { + updateAllLabels([...ul.children], oldPath, newPath) + } + }) + } + + function fileChanged (filepath) { } + + function fileFocus (path) { + if (filepath === path) return + filepath = path + var el = getElement(filepath) + expandPathTo(el) + setTimeout(function focusNode () { el.click() }, 0) + } + + function fileRemoved (filepath) { + var li = getElement(filepath) + if (li) li.parentElement.removeChild(li) + } + + function fileRenamed (oldName, newName) { + var li = getElement(oldName) + if (li) { + oldName = oldName.split('/') + newName = newName.split('/') + var index = oldName.reduce(function (idx, key, i) { + return oldName[i] !== newName[i] ? i : idx + }, undefined) + var newKey = newName[index] + var oldPath = oldName.slice(0, index + 1).join('/') + li = getElement(oldPath) + var label = getLabelFrom(li) + label.innerText = newKey + renameSubtree(label, true) + } + } + + function fileAdded (filepath) { + var el = tv.render(files.listAsTree()) + el.className = css.fileexplorer + element.parentElement.replaceChild(el, element) + element = el + fileFocus(filepath) + appAPI.switchToFile(filepath) + } + + element.api = api + return element +} +/****************************************************************************** + HELPER FUNCTIONS +******************************************************************************/ +function adaptEnvironment (label, focus, hover) { + var li = getLiFrom(label) + li.style.position = 'relative' + var span = li.firstChild + // add focus + li.addEventListener('click', focus) + // add hover + span.classList.add(css.activeMode) + span.addEventListener('mouseover', hover) + span.addEventListener('mouseout', hover) +} + +function unadaptEnvironment (label, focus, hover) { + var li = getLiFrom(label) + var span = li.firstChild + li.style.position = undefined + // remove focus + li.removeEventListener('click', focus) + // remove hover + span.classList.remove(css.activeMode) + span.removeEventListener('mouseover', hover) + span.removeEventListener('mouseout', hover) +} + +function getLiFrom (label) { + return label.parentElement.parentElement.parentElement +} + +function getLabelFrom (li) { + return li.children[0].children[1].children[0] +} + +function removeSubtree (files, path) { + var allPaths = Object.keys(files.list()) // @TODO: change `files` + var removePaths = allPaths.filter(function (p) { return ~p.indexOf(path) }) + removePaths.forEach(function (path) { + [...window.files.querySelectorAll('.file .name')].forEach(function (span) { + if (span.innerText === path) { + var li = span.parentElement + li.parentElement.removeChild(li) // delete tab + } + }) + files.remove(path) + }) +} + +function expandPathTo (li) { + while ((li = li.parentElement.parentElement) && li.tagName === 'LI') { + var caret = li.firstChild.firstChild + if (caret.classList.contains('fa-caret-right')) caret.click() // expand + } +} diff --git a/src/app/file-panel.js b/src/app/file-panel.js new file mode 100644 index 0000000000..a296e25804 --- /dev/null +++ b/src/app/file-panel.js @@ -0,0 +1,173 @@ +/* global alert */ +var csjs = require('csjs-inject') +var yo = require('yo-yo') + +var EventManager = require('../lib/eventManager') +var FileExplorer = require('./file-explorer') + +module.exports = filepanel + +var css = csjs` + .container { + display : flex; + flex-direction : row; + width : 100%; + box-sizing : border-box; + } + .fileexplorer { + display : flex; + flex-direction : column; + position : relative; + top : -33px; + width : 100%; + } + .menu { + display : flex; + flex-direction : row; + } + .newFile { + padding : 10px; + } + .uploadFile { + padding : 10px; + } + .toggleLHP { + display : flex; + justify-content : flex-end; + padding : 10px; + width : 100%; + font-weight : bold; + cursor : pointer; + color : black; + } + .isVisible { + position : absolute; + left : 35px; + } + .isHidden { + position : absolute; + height : 99% + left : -101%; + } + .treeview { + height : 100%; + background-color : white; + } + .dragbar { + position : relative; + top : 6px; + cursor : col-resize; + z-index : 999; + border-right : 2px solid #C6CFF7; + } + .ghostbar { + width : 3px; + background-color : #C6CFF7; + opacity : 0.5; + position : absolute; + cursor : col-resize; + z-index : 9999; + top : 0; + bottom : 0; + } +` + +var limit = 60 +var canUpload = window.File || window.FileReader || window.FileList || window.Blob +var ghostbar = yo`
` + +function filepanel (appAPI, files) { + var fileExplorer = new FileExplorer(appAPI, files) + var dragbar = yo`
` + + function template () { + return yo` +
+
+
+ + + + ${canUpload ? yo` + + + + ` : ''} + + + +
+
${fileExplorer}
+
+ ${dragbar} +
+ ` + } + + var api = new EventManager() + var element = template() + element.api = api + fileExplorer.api.register('focus', function (path) { + api.trigger('focus', [path]) + }) + + return element + + function toggle (event) { + var isHidden = element.classList.toggle(css.isHidden) + this.classList.toggle(css.isVisible) + this.children[0].classList.toggle('fa-angle-double-right') + this.children[0].classList.toggle('fa-angle-double-left') + api.trigger('ui', [{ type: isHidden ? 'minimize' : 'maximize' }]) + } + + function uploadFile (event) { + ;[...this.files].forEach(fileExplorer.api.addFile) + } + + function mousedown (event) { + event.preventDefault() + if (event.which === 1) { + moveGhostbar(event) + document.body.appendChild(ghostbar) + document.addEventListener('mousemove', moveGhostbar) + document.addEventListener('mouseup', removeGhostbar) + document.addEventListener('keydown', cancelGhostbar) + } + } + function cancelGhostbar (event) { + if (event.keyCode === 27) { + document.body.removeChild(ghostbar) + document.removeEventListener('mousemove', moveGhostbar) + document.removeEventListener('mouseup', removeGhostbar) + document.removeEventListener('keydown', cancelGhostbar) + } + } + function moveGhostbar (event) { + var rhp = window['righthand-panel'].offsetLeft + var newpos = (event.pageX < limit) ? limit : event.pageX + newpos = (newpos < (rhp - limit)) ? newpos : (rhp - limit) + ghostbar.style.left = newpos + 'px' + } + + function removeGhostbar (event) { + document.body.removeChild(ghostbar) + document.removeEventListener('mousemove', moveGhostbar) + document.removeEventListener('mouseup', removeGhostbar) + document.removeEventListener('keydown', cancelGhostbar) + var width = (event.pageX < limit) ? limit : event.pageX + element.style.width = width + 'px' + api.trigger('ui', [{ type: 'resize', width: width }]) + } + + function createNewFile () { + var newName = appAPI.createName('Untitled') + if (!files.set(newName, '')) { + alert('Failed to create file ' + newName) + } else { + appAPI.switchToFile(newName) + } + } +} diff --git a/test-browser/tests/fileExplorer.js b/test-browser/tests/fileExplorer.js new file mode 100644 index 0000000000..c530b9a422 --- /dev/null +++ b/test-browser/tests/fileExplorer.js @@ -0,0 +1,40 @@ +'use strict' + +var examples = require('../../src/app/example-contracts') +var init = require('../helpers/init') +var sauce = require('./sauce') + +var sources = { + 'sources': { + 'ballot.sol': examples.ballot.content, + 'test/client/credit.sol': '', + 'src/voting.sol': '', + 'src/leasing.sol': '', + 'src/gmbh/contract.sol': false, + 'src/gmbh/test.sol': false, + 'src/gmbh/company.sol': false, + 'src/gmbh/node_modules/ballot.sol': false, + 'src/ug/finance.sol': false, + 'app/solidity/mode.sol': true, + 'app/ethereum/constitution.sol': true + } +} + +module.exports = { + before: function (browser, done) { + init(browser, done) + }, + '@sources': function () { + return sources + }, + 'FileExplorer': function (browser) { + runTests(browser) + }, + tearDown: sauce +} + +function runTests (browser, testData) { + browser + .waitForElementPresent('#filepanel ul li', 10000, true, function () {}) + .end() +} From 448d4dab1e239df2307cb674ef2f6cee45b17328 Mon Sep 17 00:00:00 2001 From: serapath Date: Tue, 11 Apr 2017 01:46:46 +0700 Subject: [PATCH 3/5] add missing dependency csjs --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 1031393df5..440a62a704 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "brace": "^0.8.0", "browserify": "^13.0.0", "browserify-reload": "^1.0.3", + "csjs-inject": "^1.0.1", "csslint": "^1.0.2", "ethereum-remix": "https://github.com/ethereum/remix", "ethereumjs-abi": "https://github.com/ethereumjs/ethereumjs-abi", From 39491d97fe48a0a16947807559fd6a990739d970 Mon Sep 17 00:00:00 2001 From: serapath Date: Tue, 11 Apr 2017 17:09:17 +0700 Subject: [PATCH 4/5] fix remove file bug --- src/app.js | 43 ++++++++++++++---------------------- src/app/execution-context.js | 2 +- src/app/file-explorer.js | 12 +++++----- 3 files changed, 23 insertions(+), 34 deletions(-) diff --git a/src/app.js b/src/app.js index 2fac32971e..68b6a35191 100644 --- a/src/app.js +++ b/src/app.js @@ -159,32 +159,19 @@ var run = function () { // ---------------- FilePanel -------------------- /**************************************************************************** - @TODO's - 1. I would put a virtual file called Summary as the root entry of the treeview, which displays the list of the files with the size in bytes of each - - 2. drag'n'drop to enable to rename files&folders in the file explorer into different sub folders - - 3. I would put a virtual file called `Summary` as the root entry of the treeview, which displays the list of the files with the size in bytes of each. - - 4. add maybe more tape tests - - 5. gist imports + copy to the browser => phase of writing - - 6. add filemanagement from righthand panel to filepanel compoennt (editing/imports/exports, public gist, load from github, create new project, ... setup load and modify files) - */ - // var sources = { - // 'test/client/credit.sol': '', - // 'src/voting.sol': '', - // 'src/leasing.sol': '', - // 'src/gmbh/contract.sol': false, - // 'src/gmbh/test.sol': false, - // 'src/gmbh/company.sol': false, - // 'src/gmbh/node_modules/ballot.sol': false, - // 'src/ug/finance.sol': false, - // 'app/solidity/mode.sol': true, - // 'app/ethereum/constitution.sol': true - // } - // Object.keys(sources).forEach(function (key) { files.set(key, sources[key]) }) + var sources = { + 'test/client/credit.sol': '', + 'src/voting.sol': '', + 'src/leasing.sol': '', + 'src/gmbh/contract.sol': false, + 'src/gmbh/test.sol': false, + 'src/gmbh/company.sol': false, + 'src/gmbh/node_modules/ballot.sol': false, + 'src/ug/finance.sol': false, + 'app/solidity/mode.sol': true, + 'app/ethereum/constitution.sol': true + } + Object.keys(sources).forEach(function (key) { files.set(key, sources[key]) }) /****************************************************************************/ var css = csjs` .filepanel { @@ -232,7 +219,9 @@ var run = function () { if (span.innerText === oldName) span.innerText = newName }) }) - + files.event.register('fileRemoved', function (path) { + if (path === currentFile) currentFile = null + }) // ------------------ gist publish -------------- $('#gist').click(function () { diff --git a/src/app/execution-context.js b/src/app/execution-context.js index fbb2351bf9..a965a5cd71 100644 --- a/src/app/execution-context.js +++ b/src/app/execution-context.js @@ -147,7 +147,7 @@ function ExecutionContext () { selectExEnv.value = executionContext } }) - + selectExEnv.value = executionContext } diff --git a/src/app/file-explorer.js b/src/app/file-explorer.js index 800afde400..095e288fb3 100755 --- a/src/app/file-explorer.js +++ b/src/app/file-explorer.js @@ -140,10 +140,8 @@ function fileExplorer (appAPI, files) { var label = getLabelFrom(li) var path = label.dataset.path var isFolder = !!~label.className.indexOf('folder') - if (confirm(` - Do you really want to delete "${path}" ? - ${isFolder ? '(and all contained files and folders)' : ''} - `)) { + if (isFolder) path += '/' + if (confirm(`Do you really want to delete "${path}" ?`)) { li.parentElement.removeChild(li) removeSubtree(files, path) } @@ -297,8 +295,10 @@ function getLabelFrom (li) { } function removeSubtree (files, path) { - var allPaths = Object.keys(files.list()) // @TODO: change `files` - var removePaths = allPaths.filter(function (p) { return ~p.indexOf(path) }) + var parts = path.split('/') + var isFile = parts[parts.length - 1].length + var removePaths = isFile ? [path] : Object.keys(files.list()).filter(keep) + function keep (p) { return ~p.indexOf(path) } removePaths.forEach(function (path) { [...window.files.querySelectorAll('.file .name')].forEach(function (span) { if (span.innerText === path) { From f09f5a38a34c918ef2ac89fe72ad1c7b3fd27c23 Mon Sep 17 00:00:00 2001 From: serapath Date: Wed, 12 Apr 2017 01:30:57 +0700 Subject: [PATCH 5/5] REFACTOR ui state + production build --- package.json | 30 ++---------------------------- src/app.js | 37 +++++++++++++++++++++---------------- src/app/file-explorer.js | 5 +++-- src/app/storage.js | 12 ++++++------ 4 files changed, 32 insertions(+), 52 deletions(-) diff --git a/package.json b/package.json index 440a62a704..1bc078c332 100644 --- a/package.json +++ b/package.json @@ -5,38 +5,12 @@ "description": "Minimalistic browser-based Solidity IDE", "devDependencies": { "async": "^2.1.2", - - "babel-cli": "^6.16.0", - "babel-eslint": "^7.1.1", - - "babel-plugin-check-es2015-constants": "^6.8.0", - "babel-plugin-transform-es2015-arrow-functions": "^6.8.0", - "babel-plugin-transform-es2015-block-scoped-functions": "^6.8.0", - "babel-plugin-transform-es2015-block-scoping": "^6.18.0", - "babel-plugin-transform-es2015-classes": "^6.18.0", - "babel-plugin-transform-es2015-computed-properties": "^6.8.0", - "babel-plugin-transform-es2015-destructuring": "^6.18.0", - "babel-plugin-transform-es2015-duplicate-keys": "^6.8.0", - "babel-plugin-transform-es2015-for-of": "^6.18.0", - "babel-plugin-transform-es2015-function-name": "^6.9.0", - "babel-plugin-transform-es2015-literals": "^6.8.0", - "babel-plugin-transform-es2015-object-super": "^6.8.0", - "babel-plugin-transform-es2015-parameters": "^6.18.0", - "babel-plugin-transform-es2015-shorthand-properties": "^6.18.0", - "babel-plugin-transform-es2015-spread": "^6.8.0", - "babel-plugin-transform-es2015-sticky-regex": "^6.8.0", - "babel-plugin-transform-es2015-template-literals": "^6.8.0", - "babel-plugin-transform-es2015-unicode-regex": "^6.11.0", - "babel-plugin-transform-regenerator": "^6.16.1", - "babel-polyfill": "^6.22.0", - "babel-plugin-yo-yoify": "^0.3.3", + "babel-polyfill": "^6.22.0", "babel-preset-es2015": "^6.24.0", - "babelify": "^7.3.0", "brace": "^0.8.0", - "browserify": "^13.0.0", "browserify-reload": "^1.0.3", "csjs-inject": "^1.0.1", "csslint": "^1.0.2", @@ -161,6 +135,6 @@ "start": "npm-run-all -lpr serve watch onchange", "test": "standard; npm run csslint; node test/index.js", "test-browser": "npm-run-all -lpr selenium downloadsolc make-mock-compiler serve browsertest", - "watch": "watchify src/index.js -dv --delay 0 -p browserify-reload -o '| npm run sourcemap'" + "watch": "watchify src/index.js --transform-key=development -dv -p browserify-reload -o build/app.js" } } diff --git a/src/app.js b/src/app.js index 68b6a35191..59ddc20b7e 100644 --- a/src/app.js +++ b/src/app.js @@ -46,10 +46,13 @@ window.addEventListener('message', function (ev) { var run = function () { var self = this this.event = new EventManager() - var storage = new Storage() - var files = new Files(storage) - var config = new Config(storage) - var currentFile + var fileStorage = new Storage('sol:') + var files = new Files(fileStorage) + var config = new Config(fileStorage) + var uiStorage = new Storage('sol-ui:') + var ui = new Files(uiStorage) + + ui.set('currentFile', '') // return all the files, except the temporary/readonly ones function packageFiles () { @@ -183,7 +186,8 @@ var run = function () { filepanel.className = css.filepanel var FilePanelAPI = { createName: createNonClashingName, - switchToFile: switchToFile + switchToFile: switchToFile, + ui: ui.event } var el = new FilePanel(FilePanelAPI, files) filepanel.appendChild(el) @@ -220,7 +224,7 @@ var run = function () { }) }) files.event.register('fileRemoved', function (path) { - if (path === currentFile) currentFile = null + if (path === ui.get('currentFile')) ui.set('currentFile', '') }) // ------------------ gist publish -------------- @@ -318,7 +322,7 @@ var run = function () { if (!files.rename(originalName, newName)) { alert('Error while renaming file') } else { - currentFile = null + ui.set('currentFile', '') switchToFile(newName) editor.discard(originalName) } @@ -339,7 +343,7 @@ var run = function () { if (!files.remove(name)) { alert('Error while removing file') } else { - currentFile = null + ui.set('currentFile', '') switchToNextFile() editor.discard(name) } @@ -352,9 +356,7 @@ var run = function () { function switchToFile (file) { editorSyncFile() - currentFile = file - - files.event.trigger('fileFocus', [file]) + ui.set('currentFile', file) if (files.isReadOnly(file)) { editor.openReadOnly(file, files.get(file)) @@ -385,10 +387,10 @@ var run = function () { $filesEl.append($('
  • ' + name + '
  • ')) } - var currentFileOpen = !!currentFile + var currentFileOpen = !!ui.get('currentFile') if (currentFileOpen) { - var active = $('#files .file').filter(function () { return $(this).find('.name').text() === currentFile }) + var active = $('#files .file').filter(function () { return $(this).find('.name').text() === ui.get('currentFile') }) active.addClass('active') } $('#input').toggle(currentFileOpen) @@ -636,7 +638,7 @@ var run = function () { this.fullLineMarker = null if (lineColumnPos) { var source = compiler.lastCompilationResult.data.sourceList[location.file] // auto switch to that tab - if (currentFile !== source) { + if (ui.get('currentFile') !== source) { switchToFile(source) } this.statementMarker = editor.addMarker(lineColumnPos, 'highlightcode') @@ -760,12 +762,12 @@ var run = function () { var rendererAPI = { error: (file, error) => { - if (file === currentFile) { + if (file === ui.get('currentFile')) { editor.addAnnotation(error) } }, errorClick: (errFile, errLine, errCol) => { - if (errFile !== currentFile && files.exists(errFile)) { + if (errFile !== ui.get('currentFile') && files.exists(errFile)) { switchToFile(errFile) } editor.gotoLine(errLine, errCol) @@ -814,6 +816,7 @@ var run = function () { function runCompiler () { editorSyncFile() + var currentFile = ui.get('currentFile') if (currentFile) { var target = currentFile var sources = {} @@ -823,6 +826,7 @@ var run = function () { } function editorSyncFile () { + var currentFile = ui.get('currentFile') if (currentFile) { var input = editor.get(currentFile) files.set(currentFile, input) @@ -834,6 +838,7 @@ var run = function () { var saveTimeout = null function editorOnChange () { + var currentFile = ui.get('currentFile') if (!currentFile) { return } diff --git a/src/app/file-explorer.js b/src/app/file-explorer.js index 095e288fb3..c234e260ac 100755 --- a/src/app/file-explorer.js +++ b/src/app/file-explorer.js @@ -37,6 +37,7 @@ module.exports = fileExplorer function fileExplorer (appAPI, files) { var fileEvents = files.event + var appUI = appAPI.ui var tv = new Treeview({ extractData: function (value, tree, key) { var newValue = {} @@ -75,7 +76,7 @@ function fileExplorer (appAPI, files) { ` - fileEvents.register('fileFocus', fileFocus) + appUI.register('currentFile', fileFocus) fileEvents.register('fileRemoved', fileRemoved) fileEvents.register('fileRenamed', fileRenamed) fileEvents.register('fileAdded', fileAdded) @@ -182,7 +183,7 @@ function fileExplorer (appAPI, files) { var allPaths = Object.keys(files.list()) for (var i = 0, len = allPaths.length, path, err; i < len; i++) { path = allPaths[i] - if (files.IsReadOnly(path)) { + if (files.isReadOnly(path)) { err = 'path contains readonly elements' break } else if (path.indexOf(newPath) === 0) { diff --git a/src/app/storage.js b/src/app/storage.js index cf90410f08..ba965c4af8 100644 --- a/src/app/storage.js +++ b/src/app/storage.js @@ -1,17 +1,17 @@ 'use strict' -function Storage () { +function Storage (prefix) { this.exists = function (name) { return this.get(name) !== null } this.get = function (name) { - return window.localStorage.getItem('sol:' + name) + return window.localStorage.getItem(prefix + name) } this.set = function (name, content) { try { - window.localStorage.setItem('sol:' + name, content) + window.localStorage.setItem(prefix + name, content) } catch (exception) { return false } @@ -19,7 +19,7 @@ function Storage () { } this.remove = function (name) { - window.localStorage.removeItem('sol:' + name) + window.localStorage.removeItem(prefix + name) return true } @@ -40,7 +40,7 @@ function Storage () { this.keys = function () { return safeKeys() // filter any names not including sol: - .filter(function (item) { return item.indexOf('sol:', 0) === 0 }) + .filter(function (item) { return item.indexOf(prefix, 0) === 0 }) // remove sol: from filename .map(function (item) { return item.replace(/^sol:/, '') }) } @@ -49,7 +49,7 @@ function Storage () { safeKeys().forEach(function (name) { if (name.indexOf('sol-cache-file-', 0) === 0) { var content = window.localStorage.getItem(name) - window.localStorage.setItem(name.replace(/^sol-cache-file-/, 'sol:'), content) + window.localStorage.setItem(name.replace(/^sol-cache-file-/, prefix), content) window.localStorage.removeItem(name) } })