Angelmmiguel/opalbox-jquery

View on GitHub
src/js/opalbox.jquery.js

Summary

Maintainability
A
1 hr
Test Coverage
/*
 * Generate an HTML box to write ruby code an execute it on output.
 * This plugin uses http://opalrb.org/ to compile the Ruby code into
 * javascript.
 *
 * @author Angel M (@laux_es)
 * @version 1.0.0
 * @license MIT License
 */
;(function ($, window, document, undefined) {
  // Plugin name
  var pluginName = 'opalBox';

  function OpalBox( element, options ){
    // Store base
    this.element = element;
    this._name = pluginName;
    this.requirements = false;
    this._defaults = $.fn.opalBox.defaults;

    // Get options
    this.options = $.extend( {}, this._defaults, options );

    // Init the plugin
    this.init();
  }

  // Avoid Plugin.prototype conflicts
  $.extend(OpalBox.prototype, {
    // Initialization
    init: function () {
      this._initOpal();
      this._createBox();
      this._buildCache();
      this._bindEvents();
    },
    // Remove plugin instance.
    destroy: function() {
      this._unbindEvents();
      this.$element.removeData();
    },
    // Load compile OpalBox library
    _initOpal: function() {
      if (typeof Opal !== 'object' ||
          typeof Opal.modules["opal-parser"] !== 'function'){
        this._notLoadedError();
      } else {
        this.requirements = true;
        Opal.load('opal-parser');
      }
    },
    // Create the HTML elements
    _createBox: function() {
      // Base element
      this.$element = $(this.element);
      this.code = this.$element.text();
      // Show selected theme
      this.$element.addClass(this.options.theme + ' opbox');
      // Base structure
      this.$element.html("");
      this.$element.append(this._baseHTML());
    },
    // Base HTML of opalBox
    _baseHTML: function() {
      return '' +
        '<div class="opbox-header">' + this.options.title + '</div>' +
        '<div class="opbox-code-wrap">' +
          '<textarea class="opbox-code">' + this.code + '</textarea>' +
        '</div>' +
        '<div class="opbox-output">' +
          '<div class="opbox-execute">' +
            '<button class="opbox-run">Run</button>' +
            '<div class="opbox-loader"></div>' +
            '<div class="opbox-loader delayed"></div>' +
          '</div>' +
          '<div class="opbox-result">' +
            '<p class="empty">' + this.options.result + '</p>' +
          '</div>' +
        '</div>';
    },
    // Cache DOM nodes for performance
    _buildCache: function () {
      // Execute button
      this.$execute = this.$element.find('.opbox-run');
      // Loader
      this.$loader = this.$element.find('.opbox-loader');
      // Code area
      this.$code = this.$element.find('.opbox-code');
      // Result
      this.$result = this.$element.find('.opbox-result');
      // Standard console log
      this.standardLog = window.console.log;
    },
    // Bind events that trigger methods
    _bindEvents: function() {
      var self = this;
      // Execute the code
      self.$execute.on('click' + '.' + self._name, function() {
        self.execute.call(self);
      });
      // Adjust size of the box
      if (self.options.autoadjust) {
        self.$code.on('input', function() {
          // Force the height of the box
          $(this).height( this.scrollHeight - 25 );
        });
        // Call event to Adjust
        self.$code.trigger('input');
      }
    },
    // Show an error because Opal libraries are not loaded.
    _notLoadedError: function() {
      console.warn("Required Opal libraries are not loaded");
    },
    // Unbind events that trigger methods
    unbindEvents: function() {
      // Unbind click execute event.
      this.$execute.off('.' + this._name);
    },
    // Compile the code using Opal
    compile: function() {
      return Opal.compile(this.$code.val());
    },
    // Execute the code and show the output
    execute: function() {
      if (!this.requirements) {
        this._notLoadedError();
        this.$result.html("<p class='empty'>Libraries are not loaded.<p>");
        return;
      }

      // Show button as "Running"
      this._setButtonLoading();

      // Initialize
      var compiled = this.compile(),
          error,
          res;

      // Show a loading text
      this.$result.html("");

      // Modify the log to capture "puts" sentences
      try {
        var self = this;
        // Capture puts sentences
        window.console.log = function(msg){
          self._showResult(msg);
        };
        res = eval(compiled);
      } catch(compiled_error) {
        error = compiled_error.message;
      }
      // Return to normal console
      window.console.log = this.standardLog;

      // Show result
      if (error === undefined){
        this._showResult(res);
      } else {
        this._showResult(error, true);
      }

      // On complete
      this._setButtonRun();
      this.callback();
    },
    // Set the button as "Running"
    _setButtonLoading: function() {
      this.$execute.html('&nbsp;');
      this.$loader.show();
    },
    // Set the button as "Normal"
    _setButtonRun: function() {
      this.$loader.hide();
      this.$execute.html('Run');
    },
    // Show errors and results
    _showResult: function(msg, err){
      if (err === true){
        this.$result.append("<p class='opbox-error'>" + msg + "</p>");
      } else {
        this.$result.append("<p>" + msg + "</p>");
      }
    },
    callback: function() {
      // Cache onComplete option
      var onComplete = this.options.onComplete;
      if ( typeof onComplete === 'function' ) {
        // Callback when run the code
        onComplete.call(this.element);
      }
    }
  });

  $.fn.opalBox = function (options) {
    this.each(function() {
      if (!$.data(this, "plugin_" + pluginName)) {
        $.data(this, "plugin_" + pluginName, new OpalBox(this, options));
      }
    });
    // Return self Jquery object
    return this;
  };

  // Default options!
  $.fn.opalBox.defaults = {
    // Theme of the opalBox
    theme: 'light',
    // Listener when compile and show the data
    onComplete: null,
    // Title of the block
    title: 'Ruby code',
    // Placeholder when there aren't any result
    result: 'Result will appear here',
    // Autoadjust height of the textarea to the code
    autoadjust: false
  };

})( jQuery, window, document );