<|start_filename|>packages/qualia_reval/server/plugins/jade.js<|end_filename|>
import Plugins from './plugins.js';
import {JadeCompiler} from 'meteor/mquandalle:jade-compiler';
import {SpacebarsCompiler} from 'meteor/spacebars-compiler';

Plugins.add('Jade', {

  extensions: ['jade'],
  locations: ['client'],

  compile({code}) {
    let result = '',
        tree = JadeCompiler.parse(code)
    ;

    tree.forEach(({attrs, children}) => {
      let templateName = attrs.name,
          compiledHTML = SpacebarsCompiler.codeGen(children);
      result += `
          if (Template['${templateName}']) {
            Template['${templateName}'].renderFunction = function() { var view=this; return ${compiledHTML}() };
        }
        `;
    });

    return result;
  },

});


<|start_filename|>packages/qualia_reval/server/plugins/methods.js<|end_filename|>
import Plugins from './plugins.js';

Plugins.add('Methods', {

  extensions: ['js'],

  compile({ code }) {
    if (!code.includes('Meteor.methods')) {
      return code;
    }

    code = code.replace(/Meteor\.methods/g, 'revalMethods');
    return `
      let revalMethods = function(methods) {
        _.each(methods, (handler, name) => {
          if (Meteor.isClient) {
            Meteor.connection._methodHandlers[name] = handler;
          }
          else if (Meteor.isServer) {
            Meteor.server.method_handlers[name] = handler;
          }
        });
      }
    ` + code;
  },

});


<|start_filename|>packages/qualia_reval/client/autocomplete/index.js<|end_filename|>
import './auto-complete.css';
import './auto-complete.js';

import './revalAutocomplete.html';
import './revalAutocomplete.css';
import './revalAutocomplete.js';


<|start_filename|>packages/qualia_reval/client/fiddle/reval_fiddle.css<|end_filename|>
revalFiddle {
    width: 100%;
    height: 100%;
    display: flex;
    flex-wrap: wrap;
    flex-direction: row;
    overflow: hidden;
    font-family: Lato, Arial;
}
revalFiddle revalFiddleFrame {
    position: relative;
    display: block;
    height: 50vh;
    background-image: linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.7)), url(/packages/qualia_reval/assets/blue-polygons.jpg);
}
revalFiddle revalFiddleFrame:only-child {
    height: 100vh;
}

revalFiddle column {
    position: relative;
    width: 50%;
}
revalFiddle column:last-child {
    transition: 1s ease box-shadow;
    box-shadow: -1px 0px 0px 0px #393939
}
revalFiddle revalFiddleFrame codeLabel {
    position: absolute;
    top: 0px;
    right: 12px;
    z-index: 100;
    background-color: #425246;
    color: #FFFFFF;
    padding: 2px 8px;
    font-size: 11px;
    font-weight: bold;
    border-radius: 0px 0px 0px 5px;
}
revalFiddle revalFiddleFrame codeLabel.js {
    background-color: #2c3c44;
}
revalFiddle revalFiddleFrame codeLabel.js.server {
    background-color: #564141;
}
revalFiddle revalFiddleFrame:last-child {
    transition: 1s ease box-shadow;
    box-shadow: 0px -1px 0px 0px #393939
}

revalFiddle revalFiddleFrame iframe {
    padding-top: 5px;
    background-color: #111111;
    position: absolute;
    top: 0px;
    left: 0px;
    border-style: none;
    width: 100%;
    height: 100%;
}

revalFiddle revalFiddleView {
    resize: vertical;
    overflow: auto;
    flex: 1;
    padding: 1.5em;
}

/* Resizing */
revalFiddle resize {
    z-index: 2;
}
revalFiddle column + resize {
    position: relative;
    width: 10px;
    height: 100%;
    margin-left: -4px;
    left: auto;
    cursor: ew-resize;
    margin-right: -6px;
}
revalFiddle revalFiddleFrame + resize {
    position: absolute;
    cursor: ns-resize;
    width: 100%;
    height: 15px;
    margin-top: -7px;
}
revalFiddle resize:hover + column {
    box-shadow: -1px 0px 0px 0px #666666;
}
revalFiddle resize:hover + revalFiddleFrame {
    box-shadow: 0px -1px 0px 0px #666666;
}
body.resize revalFiddleFrame {
    pointer-events: none;
}

/* Autocomplete */
revalFiddle ~ .autocomplete-suggestions {
    text-align: left;
    cursor: default;
    border: 1px solid #ccc;
    border-top: 0;
    border-radius: 5px;
    padding: 3px 0px;
    background: #fff;
    margin-top: 4px;
    box-shadow: 1px 3px 35px rgba(0, 0, 0, 0.35);
    position: absolute;
    display: none;
    z-index: 9999;
    max-height: 184px;
    overflow: hidden;
    overflow-y: auto;
    box-sizing: border-box;
}
revalFiddle ~ .autocomplete-suggestion {
    position: relative;
    padding: 0px 13px;
    line-height: 24px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    font-size: 1em;
    color: #333;
    border-top: 1px solid #EEE;
}


<|start_filename|>packages/qualia_reval/server/plugins/publications.js<|end_filename|>
import Plugins from './plugins.js';

Plugins.add('Publications', {

  extensions: ['js'],

  compile({ code }) {
    if (!code.includes('Meteor.publish')) {
      return code;
    }

    code = code.replace(/Meteor\.publish/g, 'revalPublish');
    return `
      let revalPublish = function(name) {
        delete Meteor.server.publish_handlers[name];
        return Meteor.publish.apply(this, arguments);
      }
      ${code}
      Object.values(Meteor.server.sessions).forEach(session => session.close());
    `;
  },

});


<|start_filename|>plugins/spacemacs.el<|end_filename|>
;; Copy this code somewhere that Emacs will find it. If you have a
;; private layer you could put it there, but just sticking it in
;; ~/.spacemacs should work.
;;
;; If your server isn't located at localhost:3000, you can modify that
;; reference on the `(concat ...` line.

(defun qr--reload ()
  (require 'request)
  (interactive)
  (request
   (concat "http://localhost:3000/reval/reload?filePath=" (buffer-file-name))
   :type "POST"
   :data (buffer-string)
   )
  )
(spacemacs/set-leader-keys "or" 'qr--reload)


<|start_filename|>packages/qualia_reval/client/ui/index.js<|end_filename|>
import './reval_ui.html';
import './reval_ui.css';
import './reval_ui.js';


<|start_filename|>packages/qualia_reval/server/plugins/css.js<|end_filename|>
import Plugins from './plugins.js';

Plugins.add('CSS', {

  extensions: ['css'],
  locations: ['client'],

  compile({filePath, code}) {
    return `
      var filePath = \`${filePath}\`,
          existingCSS = document.getElementById(filePath),
          css = document.createElement("style");

      if (existingCSS) {
        existingCSS.outerHTML = "";
      }

      css.id = filePath;
      css.type = "text/css";
      css.innerHTML = \`${code}\`;
      document.body.appendChild(css);
    `;
  },

});


<|start_filename|>packages/qualia_reval/client/autocomplete/revalAutocomplete.js<|end_filename|>
import { autoComplete } from './auto-complete.js';

Template.revalAutocomplete.onCreated(function() {
  let tpl = this;

  _.extend(tpl, {

    initialize() {

    },

    render() {
      tpl.input = new autoComplete({
        domRoot: tpl.getDOMRoot(),
        selector: tpl.$('input')[0],
        minChars: 1,
        source: tpl.source,
        onSelect: tpl.data.onSelect,
      });
    },

    source(term, suggest) {
      term = term.toLowerCase();

      let choices = tpl.choices(),
          suggestions = []
      ;

      choices.forEach(choice => {
        if (choice.toLowerCase().includes(term)) {
          suggestions.push(choice);
        }
      });

      suggest(suggestions);
    },

    getDOMRoot() {
      return tpl.$('input').closest(document.body).length > 0
        ? document.body
        : document.head
      ;
    },

    choices() {
      return tpl.data.choices || [];
    },

    placeholder() {
      return tpl.data.placeholder || '';
    },

  }).initialize();

});

Template.revalAutocomplete.onRendered(function() {
  let tpl = this;
  tpl.render();
});

Template.revalAutocomplete.helpers({

  $tpl() {
    return Template.instance();
  },

});


<|start_filename|>packages/qualia_reval/client/fiddle/reval_fiddle_view/reval_fiddle_view.js<|end_filename|>
Template.revalFiddleView.onCreated(function() {
  let tpl = this;

  _.extend(tpl, {

    initialize() {

    },

    templateWrapper() {
      return tpl.data.templateWrapper;
    },

    templateName() {
      return tpl.data.templateName;
    },

    templateData() {
      return tpl.data.templateData
        ? JSON.parse(tpl.data.templateData)
        : {}
      ;
    },

  }).initialize();

});

Template.revalFiddleView.helpers({

  $tpl() {
    return Template.instance();
  },

});


<|start_filename|>packages/qualia_reval/server/plugins/blaze_components.js<|end_filename|>
import Plugins from './plugins.js';

Plugins.add('BlazeComponents', {

  extensions: ['js'],
  locations: ['client'],

  compile({code}) {
    let regex = /register\(['"](.*?)['"]\)/gi,
        templateNames = [],
        match
    ;

    while(match = regex.exec(code)) {
      templateNames.push(match[1]);
    }

    _.unique(templateNames).forEach(templateName => {
      code = `
              if (BlazeComponent.components['']['${templateName}']) {
                delete BlazeComponent.components['']['${templateName}'];
              }
              if (Template['${templateName}']) {
                Template['${templateName}'] = new Template('BlazeComponent.${templateName}', Template['${templateName}'].renderFunction);
              }
            ` + code;
    });

    return code;
  },

});


<|start_filename|>packages/qualia_reval/server/plugins/blaze_html.js<|end_filename|>
import Plugins from './plugins.js';
import {SpacebarsCompiler} from 'meteor/spacebars-compiler';

Plugins.add('BlazeHTML', {

  extensions: ['html'],
  locations: ['client'],

  compile({code}) {
    if (code.includes('<body>')) {
      console.warn("\x1b[91mReval\x1b[0m doesn't support reloading the <body> tag. Please move the code to a sub-template.")
    }

    let result = '',
        regex = /<\s*template\s+name\s*=\s*['"](.*?)['"]\s*>([^]*?)<\s*\/\s*template\s*>/gmi,
        match
    ;

    while (match = regex.exec(code)) {
      let templateName = match[1],
          html = match[2],
          compiledHTML
      ;

      try {
        compiledHTML = SpacebarsCompiler.compile(html);
      }
      catch(e) {
        let errorMessage = `Encountered an error while compiling ${templateName}: ${e.message}\n${e.stack}`;
        console.error(errorMessage);
        // return `console.error(\`${errorMessage}\`)`;
        return '';
      }

      result += `
          if (Template['${templateName}']) {
            Template['${templateName}'].renderFunction = function() { var view=this; return ${compiledHTML}() };
        }
        `;
    }

    return result;
  },

});


<|start_filename|>packages/qualia_reval/server/main.js<|end_filename|>
import '../modules.js';
import Plugins from'./plugins';
import Reval from './reval.js';
import './editor';
import './reload.js';

export {Reval, Plugins};


<|start_filename|>packages/qualia_reval/modules.js<|end_filename|>
let RevalModules = {

  initialize() {
    global.RevalModules = this;

    return this;
  },

  getModule(name) {
    return this.getModules()[name];
  },

  getModules() {
    if (!this.modules) {
      this.collectModules();
    }

    return this.modules;
  },

  collectModules() {
    let rootModule = module;
    while (rootModule.parent) {
      rootModule = rootModule.parent;
    }

    this.modules = {};
    this._collectModules(rootModule, this.modules);
  },

  _collectModules(currentModule, modules) {
    if (modules[currentModule.id]) {
      return;
    }
    modules[currentModule.id] = currentModule;

    currentModule.children.forEach(m => {
      this._collectModules(m, modules);
    });
  }

}.initialize();

export { RevalModules };


<|start_filename|>packages/qualia_reval/client/fiddle/index.js<|end_filename|>
import './reval_fiddle_frame/reval_fiddle_frame.html';
import './reval_fiddle_frame/reval_fiddle_frame.js';

import './reval_fiddle_view/reval_fiddle_view.html';
import './reval_fiddle_view/reval_fiddle_view.js';

import './reval_fiddle.html';
import './reval_fiddle.js';
import './reval_fiddle.css';


<|start_filename|>packages/qualia_reval/client/fiddle/reval_fiddle.js<|end_filename|>
import { Reval } from 'meteor/qualia:reval';

Template.revalFiddle.onCreated(function() {
  let tpl = this;

  _.extend(tpl, {

    initialize() {
      Reval.baseElem = 'revalFiddleView';
      Reval.toggleConnection = true;

      tpl.firstFrameURL.set(tpl.templateToURL(tpl.data.templateName, 'html'));
      tpl.secondFrameURL.set(tpl.filePathToURL(tpl.data.filePath));
      tpl.thirdFrameURL.set(tpl.templateToURL(tpl.data.templateName, 'js'));

      tpl.getFiles();
    },

    firstFrameURL: new ReactiveVar(),
    secondFrameURL: new ReactiveVar(),
    thirdFrameURL: new ReactiveVar(),
    allFiles: new ReactiveVar(),

    templateToURL(templateName, extension) {
      return templateName
        ? `/reval/edit?templateName=${templateName}&sourceType=${extension}`
        : ''
      ;
    },

    filePathToURL(filePath) {
      let extension = (filePath || '').split('.').pop();
      return filePath
        ? `/reval/edit?filePath=${filePath}&sourceType=${extension}`
        : ''
      ;
    },

    async getFiles() {
      let response = await fetch('/reval/files'),
          files = JSON.parse(await response.text())
      ;
      tpl.allFiles.set(files);
    },

  }).initialize();
});


Template.revalFiddle.events({

  'mousedown resize'(event, tpl) {
    let direction = $(event.target).attr('data-direction'),
        width = $(window).width(),
        height = $(window).height(),
        $column = $(event.target).closest('column')
    ;
    $('body')
      .addClass('resize')
      .on('mousemove.resize', (event) => {
        requestAnimationFrame(() => {
          if(direction == 'vertical') {
            let percent = (event.pageY / height) * 100;
            $column
              .children('revalFiddleFrame')
                .eq(0)
                  .css('height', `${percent}vh`)
                  .end()
                .eq(1)
                  .css('height', `${100 - percent}vh`)
            ;
          }
          else if (direction == 'horizontal') {
            let percent = (event.pageX / width) * 100;
            tpl.$('revalFiddle')
              .children('column')
                .eq(0)
                  .css('width', `${percent}vw`)
                  .end()
                .eq(1)
                  .css('width', `${100 - percent}vw`)
            ;
          }
        });
        event.preventDefault();
      })
      .one('mouseup.resize', (event) => {
        $('body')
          .removeClass('resize')
          .off('.resize')
        ;
      })
    ;
  },

  'change revalAutocomplete input'(e, tpl) {
    let filePath = $(e.target).val();
    tpl.secondFrameURL.set(tpl.filePathToURL(filePath));
  },

});

Template.revalFiddle.helpers({

  $tpl() {
    return Template.instance();
  },

});


<|start_filename|>packages/qualia_reval/server/ngrok.js<|end_filename|>
import {WebApp} from 'meteor/webapp';
import {execSync, spawn} from 'child_process';
import http from 'http';

class Ngrok {

  start() {
    if (this.ngrok) {
      return;
    }

    execSync(`chmod a+x ${this.getPath()}`);

    this.ngrok = spawn(this.getPath(), ['http', this.getLocalURL()]);
    process.on('exit', () => {
      this.stop();
    });

    this.waitHealthy();
  }

  stop() {
    this.ngrok.kill();
  }

  waitHealthy() {
    if (this.healthy) {
      return;
    }

    console.log('Starting ngrok');
    Meteor.wrapAsync(done => {
      this.checkHealthy(done);
      this.healthy = true;
    })();
  }

  checkHealthy(done) {
    let publicURL;
    try {
      publicURL = this.getPublicURL();
    }
    catch(e) {}

    if (publicURL) {
      console.log('Connected to ngrok');
      done();
    }
    else {
      console.log('Waiting for ngrok to start...');
      Meteor.setTimeout(() => this.checkHealthy(done), 1000);
    }
  }

  getLocalURL() {
    let address = WebApp.httpServer.address();
    return `${address.address}:${address.port}`;
  }

  getPublicURL() {
    return Meteor.wrapAsync(done => {
      let req = http.get('http://localhost:4040/api/tunnels', res => {
        let rawData = '';
        res.setEncoding('utf8');

        res.on('data', chunk => rawData += chunk);
        res.on('end', () => {
          try {
            let parsedData = JSON.parse(rawData),
                tunnel = parsedData.tunnels.filter(tunnel => tunnel.config.addr === this.getLocalURL())[0]
            ;
            done(null, tunnel.public_url);
          } catch (e) {
            done(e);
          }
        });
      });

      req.on('error', e => done(e));
      req.end();
    })();
  }

  getPath() {
    return Assets.absoluteFilePath(`assets/ngrok_${process.platform}`);
  }

}

export default Ngrok;


<|start_filename|>packages/qualia_reval/server/editor/routes.js<|end_filename|>
import {Meteor} from 'meteor/meteor';
import {Blaze} from 'meteor/blaze';
import {Picker} from 'meteor/meteorhacks:picker';

import Editor from './editor.js';
import Utils from '../utils.js';
import Reval from '../reval.js';

let cdnURL = 'https://unpkg.com/monaco-editor@0.11.1/min';

Picker.route('/reval/edit', function(params, request, response) {

  try {
    let filePath = params.query.filePath || Utils.findFilePath(params.query.templateName, params.query.sourceType);
    filePath = Utils.resolvePath(Utils.normalizePath(filePath));

    let code = Reval.getFile(filePath);
    if (!code) {
      throw new Meteor.Error(`Unable to find ${filePath}`);
    }

    let editorHTML = Blaze.toHTMLWithData(Editor, {
      cdnURL: cdnURL,
      filePath,
      code,
    });

    response.writeHead(200, {'Content-Type': 'text/html'})
    response.end(editorHTML);
  }
  catch(e) {
    console.log(request);
    response.writeHead(500, {'Content-Type': 'text/html'})
    response.end(`Unable to resolve ${request.originalUrl} to a source file.`);
  }
});

Picker.route('/reval/read', function(params, request, response) {

  try {
    let filePath = params.query.filePath || Utils.findFilePath(params.query.templateName, params.query.sourceType);
    filePath = Utils.resolvePath(Utils.normalizePath(filePath));

    let code = Reval.getFile(filePath);
    if (!code) {
      throw new Meteor.Error(`Unable to find ${filePath}`);
    }

    let fileInfo = JSON.stringify({ filePath, code });
    response.writeHead(200, {'Content-Type': 'text/html'})
    response.end(fileInfo);
  }
  catch(e) {
    console.log(e.stack);
    // console.log(request);
    response.writeHead(500, {'Content-Type': 'text/html'})
    response.end(`Unable to resolve ${request.originalUrl} to a source file.`);
  }
});

Picker.route('/reval/files', function(params, request, response) {
  response.writeHead(200, {'Content-Type': 'text/html'})

  try {
    response.end(JSON.stringify(Utils.findAllFiles()));
  }
  catch(e) {
    console.log(request);
    response.end(`Failed to retrieve files.`);
  }
});

Picker.route('/reval/worker-proxy.js', function(params, request, response) {
  response.writeHead(200, {'Content-Type': 'text/javascript'})
  response.end(`
    self.MonacoEnvironment = {
        baseUrl: '${cdnURL}/'
    };
    importScripts('${cdnURL}/vs/base/worker/workerMain.js');
  `);
});


<|start_filename|>packages/qualia_reval/client/autocomplete/revalAutocomplete.css<|end_filename|>
revalAutocomplete {
    position: absolute;
    text-align: center;
    width: 300px;
    top: 50%;
    left: 50%;
    transform: translateX(-50%) translateY(-50%);
}

revalAutocomplete input {
    width: 100%;
    max-width: 500px;
    outline: 0;
    padding: 10px;
    display: inline-block;
    border: 2px solid #ffffff52;
    box-shadow: inset 0 1px 3px #00000066;
    border-radius: 5px;
    vertical-align: middle;
    box-sizing: border-box;
    font-family: "Lato", "Helvetica Neue", Helvetica, Arial, sans-serif;
    background-color: #151c229e;
    color: #FFFFFF;
}
revalAutocomplete input::-webkit-input-placeholder {
  color: #CCCCCC;
}


revalAutocomplete input:focus {
   border-color: #FFFFFF;
}
revalAutocomplete input:focus::-webkit-input-placeholder {
  color: #EEEEEE;
}


<|start_filename|>packages/qualia_reval/client/ui/reval_ui.css<|end_filename|>
revalUI {
    /* all: initial; */
    * {
        all: unset;
    }
    all: unset;
}

revalUI {
    position: absolute;
    width: 100%;
    height: 100%;
    font-family: Lato;
    background-image: linear-gradient(rgba(0, 0, 0, 0), rgba(34, 117, 79, 0.5)), url(/packages/qualia_reval/assets/blue-polygons.jpg);
    font-size: 14px;
}

toggleReval {
    position: fixed;
    bottom: 5px;
    right: 15px;
    z-index: 10000;
    opacity: .2;
    cursor: pointer;
}

toggleReval:hover {
    opacity: .8;
}

revalUI revalCode {
    position: absolute;
    left: 0px;
    top: 0px;
    height: 100%;
    border-style: none;
    opacity: 0.7;
}

revalUI iframe {
    width: 100%;
    height: 100%;
    border-style: none;
}

revalUI editorPlaceholder {
    display: flex;
    height: 100%;
    align-items: center;
    justify-content: center;

    white-space: pre;
    color: white;
    font-size: 2em;
}

revalUI revalPanel {
    position: absolute;
    right: 0px;
    bottom: 0px;
    overflow: auto;
    -webkit-overflow-scrolling: touch;
    border-style: none;
    color: white;
}

revalUI revalPanelHeader {
    display: flex;
    align-items: center;
    height: 2em;
    background-color: rgba(0, 0, 130, 0.1);
}

revalUI revalPanelHeader revalClearAll {
    background-color: #be2c21;
}
revalUI revalPanelHeader revalSaveAll {
    background-color: #2e8631;
}
revalUI revalPanelHeader revalPublish {
    background-color: #1a5fce;
}

revalUI revalPanelHeader revalPanelTitle {
    flex: 1;
    text-align: center;
}

revalUI revalPanelHeader revalPanelSearch {
    text-align: right;
    margin-right: 0.5em;
    opacity: 0.8;
    width: 160px;
}

revalUI revalPanel table {
    border-collapse: collapse;
    width: 100%;
}
revalUI revalPanel table td:last-child {
    white-space: nowrap;
    width: 145px;
}

revalUI revalPanel tbody {
    background: rgba(0, 0, 0, 0);
}

revalUI revalPanel th, revalUI revalPanel td {
    padding: 8px;
    text-align: center;
    border-style: none;
}

revalUI revalPanel tr {
    background-color: rgba(142, 165, 131, 0.16);
}

revalUI revalPanel tr:hover {
    background-color: rgba(142, 165, 131, 0.36);
}

revalUI savePatch, revalUI clearPatch, revalUI revalPublish, revalUI revalSaveAll, revalUI revalClearAll {
    border: none;
    color: white;
    padding: 4px 16px;
    text-align: center;
    text-decoration: none;
    display: inline-block;
    font-size: 12px;
    margin: 2px 1px;
    cursor: pointer;
}

revalUI savePatch {
    background-color: #2e8631;
}

revalUI clearPatch {
    background-color: #be2c21;
}

revalUI revalCodeTopbar {
    display: flex;
}

revalUI codePath {
    text-align: left;
    border: none;
    color: white;
    padding: 4px 16px;
    text-decoration: none;
    display: inline-block;
    font-size: 12px;
    margin: 0px 0px;
}

revalUI codeTabs {
    flex: 1;
    text-align: center;
}

revalUI codeTab {
    border: none;
    color: white;
    padding: 4px 16px;
    text-align: center;
    text-decoration: none;
    display: inline-block;
    font-size: 12px;
    margin: 0px 0px;
    cursor: pointer;
}

revalUI codeTab:hover {
    background-color: rgba(142, 165, 131, 0.36);
}

revalUI toggleLive {
    text-align: right;
    border: none;
    color: white;
    padding: 4px 16px;
    text-decoration: none;
    display: inline-block;
    font-size: 12px;
    margin: 0px 0px;
}

revalUI toggleLive:hover {
    background-color: rgba(142, 165, 131, 0.36);
}
