UNPKG

noide

Version:

Web Editor built on hapi and nes websockets using browserify, superviews.js, bootstrap and the ACE editor.

1,788 lines (1,598 loc) 398 kB
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ var Nes = require('nes/client') var host = window.location.host var client = new Nes.Client('ws://' + host) module.exports = client },{"nes/client":39}],2:[function(require,module,exports){ var noide = require('../noide') var client = require('../client') var fs = require('../fs') var util = require('../util') var config = require('../../config/client') var editor = window.ace.edit('editor') var $shortcuts = window.jQuery('#keyboard-shortcuts').modal({ show: false }) // Set editor options editor.setOptions({ enableSnippets: true, enableBasicAutocompletion: true, enableLiveAutocompletion: false, fontSize: config.ace.fontSize }) function save (file, session) { fs.writeFile(file.path, session.getValue(), function (err, payload) { if (err) { return util.handleError(err) } file.stat = payload.stat noide.markSessionClean(session) }) } editor.commands.addCommands([{ name: 'help', bindKey: { win: 'Ctrl-H', mac: 'Command-H' }, exec: function () { $shortcuts.modal('show') }, readOnly: true }]) editor.setTheme('ace/theme/' + config.ace.theme) editor.commands.addCommands([{ name: 'save', bindKey: { win: 'Ctrl-S', mac: 'Command-S' }, exec: function (editor) { var file = noide.current var session = noide.getSession(file) save(file, session) }, readOnly: false }, { name: 'saveall', bindKey: { win: 'Ctrl-Shift-S', mac: 'Command-Option-S' }, exec: function (editor) { noide.dirty.forEach(function (session) { var file = session.file save(file, session) }) }, readOnly: false }, { name: 'beautify', bindKey: { win: 'Ctrl-B', mac: 'Command-B' }, exec: function (editor) { var file = noide.current var path if (file) { switch (file.ext) { case '.js': path = '/standard-format' break default: } if (path) { client.request({ path: path, payload: { value: editor.getValue() }, method: 'POST' }, function (err, payload) { if (err) { return util.handleError(err) } editor.setValue(payload) }) } } }, readOnly: false }]) module.exports = editor },{"../../config/client":30,"../client":1,"../fs":7,"../noide":12,"../util":27}],3:[function(require,module,exports){ var page = require('page') var patch = require('../patch') var fs = require('../fs') var view = require('./view.html') function FileEditor (el) { var model = { mode: null, file: null, rename: fs.rename, mkfile: function (path) { fs.mkfile(path, function (err, payload) { if (!err) { // Open the new file. Leave a short delay // to allow it to register from the socket setTimeout(function () { page('/file?path=' + payload.relativePath) }, 500) } }) }, mkdir: fs.mkdir } function hide () { model.file = null model.mode = null patch(el, view, model, hide) } function show (file, mode) { model.file = file model.mode = mode patch(el, view, model, hide) var input = el.querySelector('input') input.focus() } this.show = show } FileEditor.prototype.rename = function (file) { this.show(file, 'rename') } FileEditor.prototype.mkfile = function (dir) { this.show(dir, 'mkfile') } FileEditor.prototype.mkdir = function (dir) { this.show(dir, 'mkdir') } var fileEditorEl = document.getElementById('file-editor') var fileEditor = new FileEditor(fileEditorEl) module.exports = fileEditor },{"../fs":7,"../patch":14,"./view.html":4,"page":41}],4:[function(require,module,exports){ var IncrementalDOM = require('incremental-dom') var patch = IncrementalDOM.patch var elementOpen = IncrementalDOM.elementOpen var elementVoid = IncrementalDOM.elementVoid var elementClose = IncrementalDOM.elementClose var skip = IncrementalDOM.skip var currentElement = IncrementalDOM.currentElement var text = IncrementalDOM.text module.exports = (function () { var hoisted1 = ["class", "file-editor"] var hoisted2 = ["class", "form-group"] var hoisted3 = ["for", "name"] var hoisted4 = ["class", "input-group"] var hoisted5 = ["type", "text", "class", "form-control input-sm", "id", "filename"] var hoisted6 = ["class", "input-group-btn"] var hoisted7 = ["class", "btn btn-primary btn-sm", "type", "submit"] var hoisted8 = ["class", "btn btn-secondary btn-sm", "type", "button"] var hoisted9 = ["class", "form-group"] var hoisted10 = ["for", "name"] var hoisted11 = ["class", "input-group"] var hoisted12 = ["type", "text", "class", "form-control input-sm", "id", "dirname"] var hoisted13 = ["class", "input-group-btn"] var hoisted14 = ["class", "btn btn-primary btn-sm", "type", "submit"] var hoisted15 = ["class", "btn btn-secondary btn-sm", "type", "button"] var hoisted16 = ["class", "form-group"] var hoisted17 = ["for", "name"] var hoisted18 = ["class", "input-group"] var hoisted19 = ["type", "text", "class", "form-control input-sm", "id", "rename"] var hoisted20 = ["class", "input-group-btn"] var hoisted21 = ["class", "btn btn-primary btn-sm", "type", "submit"] var hoisted22 = ["class", "btn btn-secondary btn-sm", "type", "button"] return function fileEditor (model, hide) { var file = model.file if (file) { elementOpen("div", null, hoisted1) if (model.mode === 'mkfile') { elementOpen("form", "mkfile", null, "onsubmit", function ($event) { $event.preventDefault(); var $element = this; model.mkfile(this.filename.value); hide()}) elementOpen("div", null, hoisted2) elementOpen("label", null, hoisted3) text("Add new file") elementClose("label") elementOpen("div", null, hoisted4) elementOpen("input", null, hoisted5, "value", file.relativePath ? file.relativePath + '/' : '') elementClose("input") elementOpen("span", null, hoisted6) elementOpen("button", null, hoisted7) text("OK") elementClose("button") elementOpen("button", null, hoisted8, "onclick", function ($event) { $event.preventDefault(); var $element = this; hide()}) text("Cancel") elementClose("button") elementClose("span") elementClose("div") elementClose("div") elementClose("form") } if (model.mode === 'mkdir') { elementOpen("form", "mkdir", null, "onsubmit", function ($event) { $event.preventDefault(); var $element = this; model.mkdir(this.dirname.value); hide()}) elementOpen("div", null, hoisted9) elementOpen("label", null, hoisted10) text("Add new folder") elementClose("label") elementOpen("div", null, hoisted11) elementOpen("input", null, hoisted12, "value", file.relativePath ? file.relativePath + '/' : '') elementClose("input") elementOpen("span", null, hoisted13) elementOpen("button", null, hoisted14) text("OK") elementClose("button") elementOpen("button", null, hoisted15, "onclick", function ($event) { $event.preventDefault(); var $element = this; hide()}) text("Cancel") elementClose("button") elementClose("span") elementClose("div") elementClose("div") elementClose("form") } if (model.mode === 'rename') { elementOpen("form", "rename", null, "onsubmit", function ($event) { $event.preventDefault(); var $element = this; model.rename(file.relativePath, this.rename.value); hide()}) elementOpen("div", null, hoisted16) elementOpen("label", null, hoisted17) text("Rename") elementClose("label") elementOpen("div", null, hoisted18) elementOpen("input", null, hoisted19, "value", file.relativePath) elementClose("input") elementOpen("span", null, hoisted20) elementOpen("button", null, hoisted21) text("OK") elementClose("button") elementOpen("button", null, hoisted22, "onclick", function ($event) { $event.preventDefault(); var $element = this; hide()}) text("Cancel") elementClose("button") elementClose("span") elementClose("div") elementClose("div") elementClose("form") } elementClose("div") } } })(); },{"incremental-dom":37}],5:[function(require,module,exports){ var patch = require('../patch') var fs = require('../fs') var util = require('../util') var fileEditor = require('../file-editor') var view = require('./view.html') var copied var $ = window.jQuery var path = require('path') function FileMenu (el) { var $el = $(el) $el.on('mouseleave', function () { hide() }) function callback (err, payload) { if (err) { return util.handleError(err) } } function resetPasteBuffer () { copied = null } function setPasteBuffer (file, action) { hide() copied = { file: file, action: action } } function showPaste (file) { if (copied) { var sourcePath = copied.file.relativePath.toLowerCase() var sourceDir = copied.file.relativeDir.toLowerCase() var destinationDir = (file.isDirectory ? file.relativePath : file.relativeDir).toLowerCase() var isDirectory = copied.file.isDirectory if (!isDirectory) { // Always allow pasteing of a file unless it's a move operation (cut) and the destination dir is the same return copied.action !== 'cut' || destinationDir !== sourceDir } else { // Allow pasteing directories if not into self a decendent if (destinationDir.indexOf(sourcePath) !== 0) { // and or if the operation is move (cut) the parent dir too return copied.action !== 'cut' || destinationDir !== sourceDir } } } return false } function rename (file) { hide() resetPasteBuffer() fileEditor.rename(file) } function paste (file) { hide() if (copied && copied.file) { var action = copied.action var source = copied.file resetPasteBuffer() var pastePath = file.isDirectory ? file.path : file.dir if (action === 'copy') { fs.copy(source.path, path.resolve(pastePath, source.name), callback) } else if (action === 'cut') { fs.rename(source.path, path.resolve(pastePath, source.name), callback) } } } function mkfile (file) { hide() resetPasteBuffer() fileEditor.mkfile(file.isDirectory ? file : file.parent) } function mkdir (file) { hide() resetPasteBuffer() fileEditor.mkdir(file.isDirectory ? file : file.parent) } function remove (file) { var path = file.relativePath hide() resetPasteBuffer() if (window.confirm('Delete [' + path + ']')) { fs.remove(path, callback) } } var model = { x: 0, y: 0, file: null, rename: rename, paste: paste, mkfile: mkfile, mkdir: mkdir, remove: remove, showPaste: showPaste, setPasteBuffer: setPasteBuffer } function hide () { model.file = null patch(el, view, model) } function show (x, y, file) { model.x = x model.y = y model.file = file patch(el, view, model) } this.show = show } var fileMenuEl = document.getElementById('file-menu') var fileMenu = new FileMenu(fileMenuEl) module.exports = fileMenu },{"../file-editor":3,"../fs":7,"../patch":14,"../util":27,"./view.html":6,"path":31}],6:[function(require,module,exports){ var IncrementalDOM = require('incremental-dom') var patch = IncrementalDOM.patch var elementOpen = IncrementalDOM.elementOpen var elementVoid = IncrementalDOM.elementVoid var elementClose = IncrementalDOM.elementClose var skip = IncrementalDOM.skip var currentElement = IncrementalDOM.currentElement var text = IncrementalDOM.text module.exports = (function () { var hoisted1 = ["class", "dropdown-menu"] var hoisted2 = ["class", "dropdown-header"] var hoisted3 = ["class", "fa fa-pencil"] var hoisted4 = ["class", "divider"] var hoisted5 = ["class", "fa fa-scissors"] var hoisted6 = ["class", "fa fa-copy"] var hoisted7 = ["class", "fa fa-paste"] var hoisted8 = ["class", "divider"] var hoisted9 = ["class", "fa fa-file"] var hoisted10 = ["class", "fa fa-folder"] var hoisted11 = ["class", "divider"] var hoisted12 = ["class", "fa fa-trash"] return function fileMenu (model) { var file = model.file if (file) { elementOpen("ul", null, hoisted1, "style", { top: model.y, left: model.x }) elementOpen("li", "header", hoisted2) text("" + (file.name) + "") elementClose("li") elementOpen("li", "rename") elementOpen("a", null, null, "onclick", function ($event) { $event.preventDefault(); var $element = this; model.rename(file)}) elementOpen("i", null, hoisted3) elementClose("i") text(" Rename") elementClose("a") elementClose("li") elementOpen("li", "divider-1", hoisted4) elementClose("li") elementOpen("li", "cut") elementOpen("a", null, null, "onclick", function ($event) { $event.preventDefault(); var $element = this; model.setPasteBuffer(file, 'cut')}) elementOpen("i", null, hoisted5) elementClose("i") text(" Cut") elementClose("a") elementClose("li") elementOpen("li", "copy") elementOpen("a", null, null, "onclick", function ($event) { $event.preventDefault(); var $element = this; model.setPasteBuffer(file, 'copy')}) elementOpen("i", null, hoisted6) elementClose("i") text(" Copy") elementClose("a") elementClose("li") if (model.showPaste(file)) { elementOpen("li", "paste") elementOpen("a", null, null, "onclick", function ($event) { $event.preventDefault(); var $element = this; model.paste(file)}) elementOpen("i", null, hoisted7) elementClose("i") text(" Paste") elementClose("a") elementClose("li") } elementOpen("li", "divider-2", hoisted8) elementClose("li") elementOpen("li", "mkfile") elementOpen("a", null, null, "onclick", function ($event) { $event.preventDefault(); var $element = this; model.mkfile(file)}) elementOpen("i", null, hoisted9) elementClose("i") text(" Add new file") elementClose("a") elementClose("li") elementOpen("li", "mkdir") elementOpen("a", null, null, "onclick", function ($event) { $event.preventDefault(); var $element = this; model.mkdir(file)}) elementOpen("i", null, hoisted10) elementClose("i") text(" Add new folder") elementClose("a") elementClose("li") elementOpen("li", "divider-3", hoisted11) elementClose("li") elementOpen("li", "delete") elementOpen("a", null, null, "onclick", function ($event) { $event.preventDefault(); var $element = this; model.remove(file)}) elementOpen("i", null, hoisted12) elementClose("i") text(" Delete") elementClose("a") elementClose("li") elementClose("ul") } } })(); },{"incremental-dom":37}],7:[function(require,module,exports){ var client = require('./client') function readFile (path, callback) { client.request({ path: '/readfile?path=' + path, method: 'GET' }, callback) } function writeFile (path, contents, callback) { client.request({ path: '/writefile', payload: { path: path, contents: contents }, method: 'PUT' }, callback) } function mkdir (path, callback) { client.request({ path: '/mkdir', payload: { path: path }, method: 'POST' }, callback) } function mkfile (path, callback) { client.request({ path: '/mkfile', payload: { path: path }, method: 'POST' }, callback) } function copy (source, destination, callback) { client.request({ path: '/copy', payload: { source: source, destination: destination }, method: 'POST' }, callback) } function rename (oldPath, newPath, callback) { client.request({ path: '/rename', payload: { oldPath: oldPath, newPath: newPath }, method: 'PUT' }, callback) } function remove (path, callback) { client.request({ path: '/remove', payload: { path: path }, method: 'DELETE' }, callback) } module.exports = { mkdir: mkdir, mkfile: mkfile, copy: copy, readFile: readFile, writeFile: writeFile, rename: rename, remove: remove } },{"./client":1}],8:[function(require,module,exports){ var extend = require('extend') function File (data) { extend(this, data) if (this.isDirectory) { this.expanded = false } } Object.defineProperties(File.prototype, { isFile: { get: function () { return !this.isDirectory } } }) module.exports = File },{"extend":36}],9:[function(require,module,exports){ var page = require('page') var qs = require('querystring') var fs = require('./fs') var client = require('./client') var util = require('./util') var splitter = require('./splitter') var editor = require('./editor') var linter = require('./standard') var noide = require('./noide') var watch = require('./watch') var workspacesEl = document.getElementById('workspaces') window.onbeforeunload = function () { if (noide.dirty.length) { return 'Unsaved changes will be lost - are you sure you want to leave?' } } client.connect(function (err) { if (err) { return util.handleError(err) } client.request('/watched', function (err, payload) { if (err) { return util.handleError(err) } noide.load(payload) // Save state on page unload window.onunload = function () { noide.saveState() } // Build the tree pane require('./tree') // Build the recent list pane require('./recent') // Build the procsses pane var processesView = require('./processes') // Subscribe to watched file changes // that happen on the file system watch() // Subscribe to editor changes and // update the recent files views editor.on('input', function () { noide.onInput() }) /* Initialize the splitters */ function resizeEditor () { editor.resize() processesView.editor.resize() } splitter(document.getElementById('sidebar-workspaces'), false, resizeEditor) splitter(document.getElementById('workspaces-info'), true, resizeEditor) splitter(document.getElementById('main-footer'), true, resizeEditor) /* Initialize the standardjs linter */ linter() function setWorkspace (className) { workspacesEl.className = className || 'welcome' } page('*', function (ctx, next) { // Update current file state noide.current = null next() }) page('/', function (ctx) { setWorkspace() }) page('/file', function (ctx, next) { var relativePath = qs.parse(ctx.querystring).path var file = noide.getFile(relativePath) if (!file) { return next() } var session = noide.getSession(file) function setSession () { setWorkspace('editor') // Update state noide.current = file if (!noide.hasRecent(file)) { noide.addRecent(file) } // Set the editor session editor.setSession(session.editSession) editor.resize() editor.focus() } if (session) { setSession() } else { fs.readFile(relativePath, function (err, payload) { if (err) { return util.handleError(err) } session = noide.addSession(file, payload.contents) setSession() }) } }) page('*', function (ctx) { setWorkspace('not-found') }) page({ hashbang: true }) }) }) },{"./client":1,"./editor":2,"./fs":7,"./noide":12,"./processes":16,"./recent":21,"./splitter":23,"./standard":24,"./tree":25,"./util":27,"./watch":28,"page":41,"querystring":35}],10:[function(require,module,exports){ var util = require('./util') var client = require('./client') function run (command, name, callback) { if (typeof name === 'function') { callback = name name = command } if (!name) { name = command } client.request({ path: '/io', payload: { name: name, command: command }, method: 'POST' }, function (err, payload) { if (err) { util.handleError(err) } callback && callback(err, payload) }) } module.exports = { run: run } },{"./client":1,"./util":27}],11:[function(require,module,exports){ module.exports = function (file) { var modes = { '.js': 'ace/mode/javascript', '.css': 'ace/mode/css', '.scss': 'ace/mode/scss', '.less': 'ace/mode/less', '.html': 'ace/mode/html', '.htm': 'ace/mode/html', '.ejs': 'ace/mode/html', '.json': 'ace/mode/json', '.md': 'ace/mode/markdown', '.coffee': 'ace/mode/coffee', '.jade': 'ace/mode/jade', '.php': 'ace/mode/php', '.py': 'ace/mode/python', '.sass': 'ace/mode/sass', '.txt': 'ace/mode/text', '.typescript': 'ace/mode/typescript', '.gitignore': 'ace/mode/gitignore', '.xml': 'ace/mode/xml' } return modes[file.ext] } },{}],12:[function(require,module,exports){ var page = require('page') var Fso = require('./fso') var Session = require('./session') var observable = require('./observable') var storageKey = 'noide' // The current active file var current // The map of files var files = null // The map of recent files var recent = new Map() // The map of sessions var sessions = new Map() var noide = { get current () { return current }, set current (value) { current = value this.emitChangeCurrent({ detail: current }) }, load: function (payload) { this.cwd = payload.cwd files = new Map(payload.watched.map(function (item) { return [item.relativePath, new Fso(item)] })) var storage = window.localStorage.getItem(storageKey) storage = storage ? JSON.parse(storage) : {} var file, i if (storage.recent) { for (i = 0; i < storage.recent.length; i++) { file = files.get(storage.recent[i]) if (file) { recent.set(file, false) } } } if (storage.expanded) { for (i = 0; i < storage.expanded.length; i++) { file = files.get(storage.expanded[i]) if (file && file.isDirectory) { file.expanded = true } } } }, saveState: function (files) { var storage = { recent: this.recent.map(function (item) { return item.relativePath }), expanded: this.files.filter(function (item) { return item.expanded }).map(function (item) { return item.relativePath }) } window.localStorage.setItem(storageKey, JSON.stringify(storage)) }, get files () { return Array.from(files.values()) }, get sessions () { return Array.from(sessions.values()) }, get recent () { return Array.from(recent.keys()) }, get dirty () { return this.sessions.filter(function (item) { return item.isDirty }) }, addFile: function (data) { var file = new Fso(data) files.set(file.relativePath, file) this.emitAddFile(file) return file }, getFile: function (path) { return files.get(path) }, hasFile: function (path) { return files.has(path) }, hasRecent: function (file) { return recent.has(file) }, addRecent: function (file) { recent.set(file, true) this.emitAddRecent(file) }, getSession: function (file) { return sessions.get(file) }, getRecent: function (file) { return recent.get(file) }, hasSession: function (file) { return sessions.has(file) }, addSession: function (file, contents) { var session = new Session(file, contents) sessions.set(file, session) return session }, markSessionClean: function (session) { session.markClean() this.emitChangeSessionDirty(session) }, onInput: function () { var file = noide.current var session = this.getSession(file) if (session) { var isClean = session.isClean if (isClean && session.isDirty) { session.isDirty = false this.emitChangeSessionDirty(session) } else if (!isClean && !session.isDirty) { session.isDirty = true this.emitChangeSessionDirty(session) } } }, removeFile: function (file) { // Remove from files if (this.hasFile(file.relativePath)) { files.delete(file.relativePath) this.emitRemoveFile(file) } // Remove session if (this.hasSession(file)) { sessions.delete(file) } // Remove from recent files if (this.hasRecent(file)) { recent.delete(file) this.emitRemoveRecent(file) } // If it's the current file getting removed, // navigate back to the previous session/file if (current === file) { if (sessions.size) { // Open the first session page('/file?path=' + this.sessions[0].file.relativePath) } else if (recent.size) { // Open the first recent file page('/file?path=' + this.recent[0].relativePath) } else { page('/') } } }, closeFile: function (file) { var session = this.getSession(file) var close = session && session.isDirty ? window.confirm('There are unsaved changes to this file. Are you sure?') : true if (close) { // Remove from recent files recent.delete(file) this.emitRemoveRecent(file) if (session) { // Remove session sessions.delete(file) // If it's the current file getting closed, // navigate back to the previous session/file if (current === file) { if (sessions.size) { // Open the first session page('/file?path=' + this.sessions[0].file.relativePath) } else if (recent.size) { // Open the first recent file page('/file?path=' + this.recent[0].relativePath) } else { page('/') } } } } } } window.noide = noide module.exports = observable(noide, { 'Change': 'change', 'AddFile': 'addfile', 'RemoveFile': 'removefile', 'AddRecent': 'addRecent', 'RemoveRecent': 'removerecent', 'ChangeCurrent': 'changecurrent', 'ChangeSessionDirty': 'changesessiondirty' }) },{"./fso":8,"./observable":13,"./session":22,"page":41}],13:[function(require,module,exports){ var $ = window.jQuery function Observable (eventsObj) { var _listeners = {} function listeners (event) { return event ? _listeners[event] || (_listeners[event] = $.Callbacks()) : _listeners } var on = function (event, fn) { listeners(event).add(fn) } var off = function (event, fn) { listeners(event).remove(fn) } var emit = function (event, data) { listeners(event).fireWith(this, [data]) } // Add event attach/detatch handler functions var events = Object.keys(eventsObj) events.forEach(function (event) { this['on' + event] = function (fn) { on.call(this, eventsObj[event], fn) } this['off' + event] = function (fn) { off.call(this, eventsObj[event], fn) } this['emit' + event] = function (data) { data.type = eventsObj[event] emit.call(this, eventsObj[event], data) } }, this) // this.on = on // this.off = off // this.emit = emit this.events = eventsObj } /* * Make a Constructor become an observable and * exposes event names as constants */ // module.exports = function (Constructor, events) { // /* // * Mixin Observable into Constructor prototype // */ // var p = typeof Constructor === 'function' ? Constructor.prototype : Constructor // $.extend(p, new Observable(events)) // // return Constructor // } module.exports = function (ctorOrObj, events) { if (!ctorOrObj) { return new Observable(events) } /* * Mixin Observable into Constructor prototype */ var p = typeof ctorOrObj === 'function' ? ctorOrObj.prototype : ctorOrObj $.extend(p, new Observable(events)) return ctorOrObj } },{}],14:[function(require,module,exports){ var IncrementalDOM = require('incremental-dom') var patch = IncrementalDOM.patch // Fix up the element `value` attribute IncrementalDOM.attributes.value = function (el, name, value) { el.value = value } module.exports = function (el, view, data) { var args = Array.prototype.slice.call(arguments) if (args.length <= 3) { patch(el, view, data) } else { patch(el, function () { view.apply(this, args.slice(2)) }) } } },{"incremental-dom":37}],15:[function(require,module,exports){ var IncrementalDOM = require('incremental-dom') var patch = IncrementalDOM.patch var elementOpen = IncrementalDOM.elementOpen var elementVoid = IncrementalDOM.elementVoid var elementClose = IncrementalDOM.elementClose var skip = IncrementalDOM.skip var currentElement = IncrementalDOM.currentElement var text = IncrementalDOM.text module.exports = (function () { var hoisted1 = ["class", "control"] var hoisted2 = ["class", "input-group"] var hoisted3 = ["class", "input-group-btn dropup"] var hoisted4 = ["type", "button", "class", "btn btn-default btn-sm dropdown-toggle", "data-toggle", "dropdown"] var hoisted5 = ["class", "caret"] var hoisted6 = ["class", "dropdown-menu"] var hoisted7 = ["class", "dropdown-header"] var hoisted8 = ["href", "#"] var hoisted9 = ["type", "text", "class", "form-control input-sm", "name", "command", "required", "", "autocomplete", "off", "placeholder", ">"] var hoisted10 = ["class", "input-group-btn"] var hoisted11 = ["class", "btn btn-default btn-sm", "type", "submit", "title", "Run command"] var hoisted12 = ["class", "btn btn-success btn-sm", "type", "button", "title", "Remove dead processes"] var hoisted13 = ["class", "nav nav-tabs"] var hoisted14 = ["class", "dropdown-toggle", "data-toggle", "dropdown"] var hoisted15 = ["class", "caret"] var hoisted16 = ["class", "dropdown-menu"] var hoisted17 = ["class", "dropdown-header"] var hoisted18 = ["class", "dropdown-header"] var hoisted19 = ["role", "separator", "class", "divider"] var hoisted20 = ["class", "fa fa-stop"] var hoisted21 = ["class", "fa fa-refresh"] var hoisted22 = ["class", "fa fa-close"] var hoisted23 = ["class", "processes"] var hoisted24 = ["class", "output"] return function description (model, actions, editorIsInitialized) { elementOpen("div", null, hoisted1) elementOpen("form", null, null, "onsubmit", function ($event) { $event.preventDefault(); var $element = this; actions.run(this.command.value)}) elementOpen("div", null, hoisted2) elementOpen("div", null, hoisted3) elementOpen("button", null, hoisted4) text("Task ") elementOpen("span", null, hoisted5) elementClose("span") elementClose("button") elementOpen("ul", null, hoisted6) if (!model.tasks.length) { elementOpen("li", null, hoisted7) text("No npm run-scripts found") elementClose("li") } if (model.tasks.length) { ;(Array.isArray(model.tasks) ? model.tasks : Object.keys(model.tasks)).forEach(function(task, $index) { elementOpen("li", task.name) elementOpen("a", null, hoisted8, "onclick", function ($event) { $event.preventDefault(); var $element = this; actions.setCommand('npm run ' + task.name)}) text("" + (task.name) + "") elementClose("a") elementClose("li") }, model.tasks) } elementClose("ul") elementClose("div") elementOpen("input", null, hoisted9, "value", model.command) elementClose("input") elementOpen("span", null, hoisted10) elementOpen("button", null, hoisted11) text("Run") elementClose("button") if (model.dead.length) { elementOpen("button", null, hoisted12, "onclick", function ($event) { $event.preventDefault(); var $element = this; actions.removeAllDead()}) text("Clear completed") elementClose("button") } elementClose("span") elementClose("div") elementClose("form") elementOpen("ul", null, hoisted13) ;(Array.isArray(model.processes) ? model.processes : Object.keys(model.processes)).forEach(function(process, $index) { elementOpen("li", process.pid, null, "class", process === model.current ? 'dropup active' : 'dropup') elementOpen("a", null, hoisted14, "onclick", function ($event) { $event.preventDefault(); var $element = this; actions.setCurrent(process)}) elementOpen("span", null, null, "class", 'circle ' + (!process.isAlive ? 'dead' : (process.isActive ? 'alive active' : 'alive'))) elementClose("span") text(" \ " + (process.name || process.command) + " \ ") elementOpen("span", null, hoisted15) elementClose("span") elementClose("a") elementOpen("ul", null, hoisted16) if (process.isAlive) { elementOpen("li", null, hoisted17) text("Process [" + (process.pid) + "]") elementClose("li") } if (!process.isAlive) { elementOpen("li", null, hoisted18) text("Process [") elementOpen("s") text("" + (process.pid) + "") elementClose("s") text("]") elementClose("li") } elementOpen("li", null, hoisted19) elementClose("li") elementOpen("li") if (process.isAlive) { elementOpen("a", "kill-tab", null, "onclick", function ($event) { $event.preventDefault(); var $element = this; actions.kill(process)}) elementOpen("i", null, hoisted20) elementClose("i") text(" Stop") elementClose("a") } elementClose("li") if (!process.isAlive) { elementOpen("li") elementOpen("a", "resurrect-tab", null, "onclick", function ($event) { $event.preventDefault(); var $element = this; actions.resurrect(process)}) elementOpen("i", null, hoisted21) elementClose("i") text(" Resurrect") elementClose("a") elementClose("li") elementOpen("li") elementOpen("a", "remove-tab", null, "onclick", function ($event) { $event.preventDefault(); var $element = this; actions.remove(process)}) elementOpen("i", null, hoisted22) elementClose("i") text(" Close") elementClose("a") elementClose("li") } elementClose("ul") elementClose("li") }, model.processes) elementClose("ul") elementClose("div") elementOpen("div", null, hoisted23, "style", {display: model.current ? '' : 'none'}) elementOpen("div", null, hoisted24) if (editorIsInitialized) { skip() } else { } elementClose("div") elementClose("div") } })(); },{"incremental-dom":37}],16:[function(require,module,exports){ var patch = require('../patch') var view = require('./index.html') var model = require('./model') var Task = require('./task') var Process = require('./process') var client = require('../client') var io = require('../io') var fs = require('../fs') var util = require('../util') var config = require('../../config/client') function Processes (el) { var editor, commandEl /** * Sets the isActive state on the process. * Processes are activated when they receive some data. * After a short delay, this is reset to inactive. */ function setProcessActiveState (process, value) { if (process.isActive !== value) { var timeout = process._timeout if (timeout) { clearTimeout(timeout) } process.isActive = value if (process.isActive) { process._timeout = setTimeout(function () { setProcessActiveState(process, false) }, 1500) } render() } } client.subscribe('/io', function (payload) { var process = model.findProcessByPid(payload.pid) if (process) { var session = process.session // Insert data chunk session.insert({ row: session.getLength(), column: 0 }, payload.data) // Move to the end of the output session.getSelection().moveCursorFileEnd() // Set the process active state to true setProcessActiveState(process, true) render() } }, function (err) { if (err) { return util.handleError(err) } }) var actions = { run: function (command, name) { io.run(command, name, function (err, payload) { if (err) { return util.handleError(err) } }) }, remove: function (process) { if (model.remove(process)) { render() } }, removeAllDead: function () { var died = model.removeAllDead() if (died.length) { render() } }, resurrect: function (process) { model.remove(process) this.run(process.command, process.name) render() }, kill: function (process) { client.request({ method: 'POST', path: '/io/kill', payload: { pid: process.pid } }, function (err, payload) { if (err) { util.handleError(err) } }) }, setCommand: function (command) { model.command = command render() commandEl.focus() }, setCurrent: function (process) { model.current = process if (model.current) { editor.setSession(model.current.session) } render() } } function loadPids (procs) { var proc var born = [] // Find any new processes for (var i = 0; i < procs.length; i++) { proc = procs[i] var process = model.processes.find(function (item) { return item.pid === proc.pid }) if (!process) { // New child process found. Add it // and set it's cached buffer into session process = new Process(proc) process.session.setValue(proc.buffer) born.push(process) } } // Shut down processes that have died model.processes.forEach(function (item) { var match = procs.find(function (check) { return item.pid === check.pid }) if (!match) { // item.pid = 0 item.isAlive = false setProcessActiveState(item, false) } }) // Insert any new child processes if (born.length) { Array.prototype.push.apply(model.processes, born) actions.setCurrent(born[0]) } render() } client.subscribe('/io/pids', loadPids, function (err) { if (err) { return util.handleError(err) } }) client.request({ path: '/io/pids' }, function (err, payload) { if (err) { util.handleError(err) } loadPids(payload) }) fs.readFile('package.json', function (err, payload) { if (err) { return } var pkg = {} try { pkg = JSON.parse(payload.contents) } catch (e) {} console.log(pkg) if (pkg.scripts) { var tasks = [] for (var script in pkg.scripts) { if (script.substr(0, 3) === 'pre' || script.substr(0, 4) === 'post') { continue } tasks.push(new Task({ name: script, command: pkg.scripts[script] })) } model.tasks = tasks render() } }) function render () { patch(el, view, model, actions, !!editor) if (!editor) { var outputEl = el.querySelector('.output') commandEl = el.querySelector('input[name="command"]') editor = window.ace.edit(outputEl) // Set editor options editor.setTheme('ace/theme/terminal') editor.setReadOnly(true) editor.setFontSize(config.ace.fontSize) editor.renderer.setShowGutter(false) editor.setHighlightActiveLine(false) editor.setShowPrintMargin(false) } } this.model = model this.render = render Object.defineProperties(this, { editor: { get: function () { return editor } } }) } var processesEl = document.getElementById('processes') var processesView = new Processes(processesEl) processesView.render() module.exports = processesView },{"../../config/client":30,"../client":1,"../fs":7,"../io":10,"../patch":14,"../util":27,"./index.html":15,"./model":17,"./process":18,"./task":19}],17:[function(require,module,exports){ var model = { tasks: [], command: '', processes: [], current: null, get dead () { return this.processes.filter(function (item) { return !item.isAlive }) }, remove: function (process) { var processes = this.processes var idx = processes.indexOf(process) if (idx > -1) { processes.splice(processes.indexOf(process), 1) if (this.current === process) { this.current = processes[0] } return true } return false }, removeAllDead: function () { var dead = this.dead for (var i = 0; i < dead.length; i++) { this.remove(dead[i]) } return dead }, findProcessByPid: function (pid) { return this.processes.find(function (item) { return item.pid === pid }) } } module.exports = model },{}],18:[function(require,module,exports){ var extend = require('extend') var EditSession = window.ace.require('ace/edit_session').EditSession function Process (data) { extend(this, data) var editSession = new EditSession('', 'ace/mode/sh') editSession.setUseWorker(false) this.session = editSession this.isAlive = true this.isActive = false } module.exports = Process },{"extend":36}],19:[function(require,module,exports){ function Task (data) { this.name = data.name this.command = data.command } module.exports = Task },{}],20:[function(require,module,exports){ var IncrementalDOM = require('incremental-dom') var patch = IncrementalDOM.patch var elementOpen = IncrementalDOM.elementOpen var elementVoid = IncrementalDOM.elementVoid var elementClose = IncrementalDOM.elementClose var skip = IncrementalDOM.skip var currentElement = IncrementalDOM.currentElement var text = IncrementalDOM.text module.exports = (function () { var hoisted1 = ["class", "list-group"] var hoisted2 = ["class", "close"] var hoisted3 = ["class", "name icon icon-file-text"] var hoisted4 = ["class", "file-name"] var hoisted5 = ["class", "list-group-item-text"] return function recent (files, current, onClickClose, isDirty) { elementOpen("div", null, hoisted1, "style", {display: files.length ? '' : 'none'}) ;(Array.isArray(files.reverse()) ? files.reverse() : Object.keys(files.reverse())).forEach(function(file, $index) { elementOpen("a", file.relativePath, null, "title", file.relativePath, "href", "/file?path=" + (file.relativePath) + "", "class", 'list-group-item' + (file === current ? ' active' : '')) elementOpen("span", null, hoisted2, "onclick", function ($event) { $event.preventDefault(); var $element = this; onClickClose(file)}) text("×") elementClose("span") elementOpen("span", null, hoisted3, "data-name", file.name, "data-path", file.relativePath) elementClose("span") elementOpen("small", null, hoisted4) text("" + (file.name) + "") elementClose("small") if (isDirty(file)) { elementOpen("span") text("*") elementClose("span") } if (false) { elementOpen("p", null, hoisted5) text("" + ('./' + (file.relativePath !== file.name ? file.relativeDir : '')) + "") elementClose("p") } elementClose("a") }, files.reverse()) elementClose("div") } })(); },{"incremental-dom":37}],21:[function(require,module,exports){ var noide = require('../noide') var patch = require('../patch') var view = require('./index.html') function Recent (el) { function onClickClose (file) { noide.closeFile(file) } function isDirty (file) { var session = noide.getSession(file) return session && session.isDirty } function render () { console.log('Render recent') patch(el, view, noide.recent, noide.current, onClickClose, isDirty) } noide.onAddRecent(render) noide.onRemoveRecent(render) noide.onChangeCurrent(render) noide.onChangeSessionDirty(render) render() } var recentEl = document.getElementById('recent') var recentView = new Recent(recentEl) module.exports = recentView },{"../noide":12,"../patch":14,"./index.html":20}],22:[function(require,module,exports){ var config = require('../config/client') var modes = require('./modes') var EditSession = window.ace.require('ace/edit_session').EditSession var UndoManager = window.ace.require('ace/undomanager').UndoManager function Session (file, contents) { var editSession = new EditSession(contents, modes(file)) editSession.setMode(modes(file)) editSession.setUseWorker(false) editSession.setTabSize(config.ace.tabSize) editSession.setUseSoftTabs(config.ace.useSoftTabs) editSession.setUndoManager(new UndoManager()) this.file = file this.editSession = editSession } Session.prototype.markClean = function () { this.isDirty = false this.editSession.getUndoManager().markClean() } Session.prototype.getValue = function () { return this.editSession.getValue() } Session.prototype.setValue = function (content, markClean) { this.editSession.setValue(content) if (markClean) { this.markClean() } } Object.defineProperties(Session.prototype, { isClean: { get: function () { return this.editSession.getUndoManager().isClean() } } }) module.exports = Session },{"../config/client":30,"./modes":11}],23:[function(require,module,exports){ var w = window var d = document function splitter (handle, collapseNextElement, onEndCallback) { var last var horizontal = handle.classList.contains('horizontal') var el1 = handle.previousElementSibling var el2 = handle.nextElementSibling var collapse = collapseNextElement ? el2 : el1 var toggle = document.createElement('a') toggle.classList.add('toggle') handle.appendChild(toggle) function onDrag (e) { if (horizontal) { var hT, hB var hDiff = e.clientY - last hT = d.defaultView.getComputedStyle(el1, '').getPropertyValue('height') hB = d.defaultView.getComputedStyle(el2, '').getPropertyValue('height') hT = parseInt(hT, 10) + hDiff hB = parseInt(hB, 10) - hDiff el1.style.height = hT + 'px' el2.style.height = hB + 'px' last = e.clientY } else { var wL, wR var wDiff = e.clientX - last wL = d.defaultView.getComputedStyle(el1, '').getPropertyValue('width') wR = d.defaultView.getComputedStyle(el2, '').getPropertyValue('width') wL = parseInt(wL, 10) + wDiff wR = parseInt(wR, 10) - wDiff el1.style.width = wL + 'px' el2.style.width = wR + 'px' last = e.clientX } } function onEndDrag (e) { e.preventDefault() w.removeEventListener('mousemove', onDrag) w.removeEventListener('mouseup', onEndDrag) if (onEndCallback) { onEndCallback() } // noide.editor.resize() // var processes = require('./processes') // processes.editor.resize() } handle.addEventListener('mousedown', function (e) { e.preventDefault() if (e.target === toggle) { collapse.style.display = collapse.style.display === 'none' ? 'block' : 'none' } else if (collapse.style.display !== 'none') { last = horizontal ? e.clientY : e.clientX w.addEventListener('mousemove', onDrag) w.addEventListener('mouseup', onEndDrag) } }) // handle.addEventListener('click', function (e) { // e.preventDefault()