<commit_before>require.config({
  paths: {
    dynatable: ["$!services.webjars.url('jquery-dynatable', 'jquery.dynatable.js')"]
  }
});

require(['jquery'], function($) {
   require(['dynatable'], function(dyna) {
      var loadMNM = function($) {
         new PhenoTips.widgets.MatchingNotificationManager($);
      };

      (XWiki.domIsLoaded && loadMNM($)) || document.observe("xwiki:dom:loaded", loadMNM.bind(this, $));
   });
});

var PhenoTips = (function (PhenoTips) {
  var widgets = PhenoTips.widgets = PhenoTips.widgets || {};
  widgets.MatchingNotificationManager = Class.create({

    initialize : function ($)
    {
      this._ajaxURL = new XWiki.Document('WebHome', 'MatchingNotification').getURL('get') + '?outputSyntax=plain';
      this._$ = $;
      this._tableBuilt = false;

      this._matches = undefined;
      this._notificationResults = undefined;
      this._filteredNotified = false;

      $('#find-matches-button').on('click', this._findMatches.bind(this));
      $('#show-matches-button').on('click', this._showMatches.bind(this));
      $('#send-notifications-button').on('click', this._sendNotification.bind(this));
      $('#checkbox_all').on('click', this._checkboxAllClicked.bind(this));
      $('#checkbox_filter_notified').on('click', this._filterNotifiedClicked.bind(this));
      $('#expand_all').on('click', this._expandAllClicked.bind(this));
    },

    _findMatches : function()
    {
        var _this = this;

        var score = this._checkScore('find-matches-score', 'find-matches-messages');
        if (score == undefined) {
          return;
        }
        new Ajax.Request(_this._ajaxURL,
          {  parameters : {action : 'find-matches',
                           score  : score
             },
             onSuccess : function (response) {
                _this._showSuccess('find-matches-messages');
                console.log("find matches result, score = " + score);
                console.log(response.responseJSON);
             },
             onFailure : function (response) {
                _this._showFailure('find-matches-messages');
             }
          }
        );
        this._showSent('find-matches-messages');

        this._$('#show-matches-score').val(score);
        this._showMatches();
    },

    _showMatches : function()
    {
      var _this = this;
      var score = _this._checkScore('show-matches-score', 'show-matches-messages');
      if (score == undefined) {
        return;
      }
      new Ajax.Request(this._ajaxURL,
        {  parameters : {action   : 'show-matches',
                         score    : score,
                         notified : false
           },
           onSuccess : function (response) {
              _this._showSuccess('show-matches-messages');
              console.log("show matches result, score = " + score);
              console.log(response.responseJSON);
              _this._matches = _this._formatMatches(response.responseJSON.matches);
              _this._buildTable();
           },
           onFailure : function (response) {
              _this._showFailure('show-matches-messages');
           }
        }
      );
      _this._showSent('show-matches-messages');
    },

    _sendNotification : function()
    {
       var _this = this;
       var ids = this._readMatchesToNotify();
       var idsJson = JSON.stringify({ ids: ids});
       console.log("Sending " + idsJson);
       new Ajax.Request(_this._ajaxURL,
          { parameters : {action : 'send-notifications',
                          idsForNotification : idsJson
            },
            onSuccess : function (response) {
               console.log("onSuccess, received:");
               console.log(response.responseText);
               _this._showSuccess('send-notifications-messages');
              _this._notificationResults = response.responseJSON;
              _this._showMatches();
            },
            onFailure : function (response) {
               _this._showFailure('send-notifications-messages');
            }
          }
       );
       this._showSent('send-notifications-messages');
    },

    _checkScore : function(scoreFieldName, messagesFieldName) {
      var score = this._$('#' + scoreFieldName).val();
      if (score == undefined || score == "") {
         this._showHint(messagesFieldName, "$services.localization.render('phenotips.matchingNotifications.emptyScore')");
         return;
      } else if (isNaN(score)) {
         this._showHint(messagesFieldName, "$services.localization.render('phenotips.matchingNotifications.invalidScore')");
         return;
      };
      scoreNumber = Number(score);
      if (scoreNumber < 0 || scoreNumber > 1) {
         this._showHint(messagesFieldName, "$services.localization.render('phenotips.matchingNotifications.invalidScore')");
         return;
      }
      return score;
    },

    _showSent : function(messagesFieldName) {
      this._showHint(messagesFieldName, "$services.localization.render('phenotips.matchingNotifications.requestSent')");
    },

    _showSuccess : function(messagesFieldName, value) {
      this._showHint(messagesFieldName, "$services.localization.render('phenotips.matchingNotifications.success')");
    },

    _showFailure : function(messagesFieldName) {
      this._showHint(messagesFieldName, "$services.localization.render('phenotips.matchingNotifications.failure')");
    },

    _showHint : function(messagesFieldName, message) {
      var messages = this._$('#' + messagesFieldName);
      messages.empty();
      messages.append(new Element('div', {'class' : 'xHint'}).update(message));
    },

    _formatMatches : function(matches)
    {
       for (var i = 0 ; i < matches.length ; i++) {
          // scores
          matches[i].score = this._roundScore(matches[i].score);
          matches[i].phenotypicScore = this._roundScore(matches[i].phenotypicScore);
          matches[i].genotypicScore = this._roundScore(matches[i].genotypicScore);

          // remote id
          var remoteId = matches[i].remoteId;
          if (remoteId == undefined || remoteId == 'undefined') {
             matches[i].remoteId = '-';
          }

          // Phenotypes
          [matches[i].phenotypes, matches[i].matchedPhenotypes] = this._formatPhenotypes(matches[i].phenotypes, matches[i].matchedPhenotypes);
       }
       return matches;
    },

    // Format two JSON objects containing phenotypes for display (compare to formatGenesArrays)
    _formatPhenotypes : function(obj1, obj2)
    {
       // Replace predefined {key,value} phenotypes with value-s.
       for (var i=0; i<obj1.predefined.size(); i++) {
          obj1.predefined[i] = obj1.predefined[i].name;
       }
       for (var i=0; i<obj2.predefined.size(); i++) {
          obj2.predefined[i] = obj2.predefined[i].name;
       }

       // If there are no phenotypes, push emptiness sign to predefined
       obj1.empty = (obj1.predefined.size() + obj1.freeText.size() == 0);
       obj2.empty = (obj2.predefined.size() + obj2.freeText.size() == 0);

       return [obj1, obj2];
    },

    _roundScore : function(score)
    {
        return Math.round(Number(score) * 100) / 100;
    },

    _buildTable : function()
    {
      var _this = this;

      var matchesToUse = this._matches;

      if (this._filteredNotified) {
          var unnotifiedMatches = [];
          for (var i = 0 ; i < this._matches.length ; i++) {
              var current = this._matches[i];
              if (!current.notified) {
                 unnotifiedMatches.push(current);
              }
          }
          matchesToUse = unnotifiedMatches;
      }

      if (this._tableBuilt) {
         var table = this._$('#matchesTable').data('dynatable');
         table.settings.dataset.originalRecords = matchesToUse;
         table.process();
      } else {
         this._$('#matchesTable').dynatable({
            dataset: {
               records: matchesToUse
            },
            writers: {
               _rowWriter: this._rowWriter.bind(this)
            },
            features: {
               pushState : false
            }
         }).bind('dynatable:afterProcess', this._processingComplete.bind(this));
         this._processingComplete();
      }

      // Mark notification results as successful/failed
      var results = this._notificationResults.results;
      if (results != undefined) {
         for (var i = 0 ; i < results.length ; i++) {
            var result = results[i];
            var tr = this._$('#matchesTable').find("#tr_" + result.id);
            tr.attr('class', result.success ? 'successful' : 'failed');
            if (!result.success) {
                tr.find(":checkbox").attr('checked', 'checked');
            }
         }
      }

       this._tableBuilt = true;
    },

    _rowWriter : function(rowIndex, record, columns, cellWriter)
    {
       var tr = '';

       // checkbox - column 0
       var checkbox = '<input type="checkbox" id="checkbox_' + record.id + '" ' + (record.notified ? 'checked disabled ' : '') + '/>	';

       // column 1,2 - reference patient and matched patient, their genes and phenotypes
       patientTd  = this._getPatientDetailsTd(record.patientId, record.genes, record.phenotypes, 'patientTd', record.id);
       matchedPatientTd = this._getPatientDetailsTd(record.matchedPatientId, record.matchedGenes, record.matchedPhenotypes, 'matchedPatientTd', record.id);

       // grab the record's attribute for each column.
       // skip first column (index 0), it's for checkbox
       for (var i = 0, len = columns.length; i < len; i++) {
         var id = columns[i].id;
         if (id == 'checkbox') {
            tr += checkbox;
         } else if (id == 'patient') {
            tr += patientTd;
         } else if (id == 'matchedPatient') {
            tr += matchedPatientTd;
         } else {
             tr += cellWriter(columns[i], record);
         }
       }

       return '
' + tr + '
';
    },

    _getPatientDetailsTd : function(patientId, genes, phenotypes, tdId, matchId)
    {
       var td = '';

       var patientHref = new XWiki.Document(patientId, 'data').getURL();

       // Patient id and collapsible icon
       td += '
';
       td += '' + patientId + '';
       td += '
';

       // Collapsible div
       td += '
';

       // Genes
       td += '
';
       var genesTitle = 'Genes';
       if (genes.size() == 0) {
           genesTitle += ': -';
       }
       td += '
' + genesTitle + '
';
       if (genes.size() != 0) {
          td += '
';
          for (var i = 0 ; i < genes.size() ; i++) {
              td += '
' + genes[i] + '
';
          }
          td += '
';
       }
       td += '
';

       // Phenotypes
       td += '
';
       var phenotypesTitle = 'Phenotypes';
       if (phenotypes.empty) {
           phenotypesTitle += ': -';
       }
       td += '
' + phenotypesTitle + '
';
       if (!phenotypes.empty) {
          td += '
';
          for (var i = 0 ; i < phenotypes.predefined.size() ; i++) {
              td += '
' + phenotypes.predefined[i] + '
';
          }
          for (var i = 0 ; i < phenotypes.freeText.size() ; i++) {
             td += '
'
             td += '
';
             td += ' ';
             td += phenotypes.freeText[i];
             td += '
';
             td += '
';
          }
          td += '
';
       }
       td += '
';

       // End collapsible div
       td += '
';

       td += '	';
       return td;
    },

    _processingComplete : function(specificTd)
    {
       // Makes genes-div the same height in patient and matched patient columns
       this._$('#matchesTable > tbody').find('tr').each(function (index, elm)
       {
          var patientTd = this._$(elm).find('#patientTd');
          var matchedPatientTd = this._$(elm).find('#matchedPatientTd');

          var genesDiv = this._$(patientTd).find('.genes-div');
          var matchedGenesDiv = this._$(matchedPatientTd).find('.genes-div');

          var h = Math.max(genesDiv.height(), matchedGenesDiv.height());
          genesDiv.height(h);
          matchedGenesDiv.height(h);
       }.bind(this));

       // Register listeners to components in table
       this._$('#matchesTable').find('[class*="collapse-gp-tool"]').on('click', function(event) {
          this._expandCollapseGP(event.target);
       }.bind(this));

       this._expandAllClicked();
    },

    _checkboxAllClicked : function(event)
    {
       var checked = event.target.checked;
       this._$('#matchesTable').find(":checkbox").each(function (index, elm) {
          if (!elm.disabled) {
             elm.checked = checked;
          }
       });
    },

    _filterNotifiedClicked : function(event)
    {
       var checked = event.target.checked;
       this._filteredNotified = checked;
       this._buildTable();
    },

    _readMatchesToNotify : function()
    {
       var ids = [];
       this._$('#matchesTable').find(":checkbox").each(function (index, elm) {
          if (elm.checked && !elm.disabled) {
             var id = Number(elm.id.substr(elm.id.indexOf('_')+1));
             ids.push(String(id));
          }
       });
       return ids;
    },

    // target is the component that was clicked to expand/collapse (this +/- sign).
    // expand is boolean, when undefined, the value will be understood from target
    _expandCollapseGP : function(target, expand)
    {
       // find matchId to collapse/expand
       var lookFor = 'collapse-gp-tool-';
       var classes = target.classList;
       var greped = this._$.grep(classes , function(item) {return item.indexOf(lookFor) > -1});
       var matchId = greped[0].substring(lookFor.length);

       // collapse/expand divs
       this._$('#matchesTable').find(".collapse-gp-div-"+matchId).each(function (index, elm) {

          if (expand == undefined) {
             expand = this._$(elm).hasClass('collapsed');
          }

          if (expand) {
             this._$(elm).removeClass("collapsed");
          } else {
             this._$(elm).addClass("collapsed");
          }

       }.bind(this));

       // change display of collapse/display component (+/-)
       this._$('#matchesTable').find(".collapse-gp-tool-"+matchId).each(function (index, elm) {

          if (expand) {
             this._$(elm).removeClass("fa-plus-square-o");
             this._$(elm).addClass("fa-minus-square-o");
          } else {
             this._$(elm).removeClass("fa-minus-square-o");
             this._$(elm).addClass("fa-plus-square-o");
          }

       }.bind(this));

    },

    _expandAllClicked : function()
    {
       var checkbox = this._$('#expand_all');
       var expand = this._$(checkbox).is(':checked');
       this._$('#matchesTable').find('[class*="collapse-gp-tool-"]').each(function (index, elm) {
          this._expandCollapseGP(elm, expand);
       }.bind(this));
    }

  });
  return PhenoTips;
}(PhenoTips || {}));
<commit_msg>Bugfix - reloading matches table.<commit_after>require.config({
  paths: {
    dynatable: ["$!services.webjars.url('jquery-dynatable', 'jquery.dynatable.js')"]
  }
});

require(['jquery'], function($) {
   require(['dynatable'], function(dyna) {
      var loadMNM = function($) {
         new PhenoTips.widgets.MatchingNotificationManager($);
      };

      (XWiki.domIsLoaded && loadMNM($)) || document.observe("xwiki:dom:loaded", loadMNM.bind(this, $));
   });
});

var PhenoTips = (function (PhenoTips) {
  var widgets = PhenoTips.widgets = PhenoTips.widgets || {};
  widgets.MatchingNotificationManager = Class.create({

    initialize : function ($)
    {
      this._ajaxURL = new XWiki.Document('WebHome', 'MatchingNotification').getURL('get') + '?outputSyntax=plain';
      this._$ = $;
      this._tableBuilt = false;

      this._matches = undefined;
      this._notificationResults = undefined;
      this._filteredNotified = false;

      $('#find-matches-button').on('click', this._findMatches.bind(this));
      $('#show-matches-button').on('click', this._showMatches.bind(this));
      $('#send-notifications-button').on('click', this._sendNotification.bind(this));
      $('#checkbox_all').on('click', this._checkboxAllClicked.bind(this));
      $('#checkbox_filter_notified').on('click', this._filterNotifiedClicked.bind(this));
      $('#expand_all').on('click', this._expandAllClicked.bind(this));
    },

    _findMatches : function()
    {
        var _this = this;

        var score = this._checkScore('find-matches-score', 'find-matches-messages');
        if (score == undefined) {
          return;
        }
        new Ajax.Request(_this._ajaxURL,
          {  parameters : {action : 'find-matches',
                           score  : score
             },
             onSuccess : function (response) {
                _this._showSuccess('find-matches-messages');
                console.log("find matches result, score = " + score);
                console.log(response.responseJSON);
             },
             onFailure : function (response) {
                _this._showFailure('find-matches-messages');
             }
          }
        );
        this._showSent('find-matches-messages');

        this._$('#show-matches-score').val(score);
        this._showMatches();
    },

    _showMatches : function()
    {
      var _this = this;
      var score = _this._checkScore('show-matches-score', 'show-matches-messages');
      if (score == undefined) {
        return;
      }
      new Ajax.Request(this._ajaxURL,
        {  parameters : {action   : 'show-matches',
                         score    : score,
                         notified : false
           },
           onSuccess : function (response) {
              _this._showSuccess('show-matches-messages');
              console.log("show matches result, score = " + score);
              console.log(response.responseJSON);
              _this._matches = _this._formatMatches(response.responseJSON.matches);
              _this._buildTable();
           },
           onFailure : function (response) {
              _this._showFailure('show-matches-messages');
           }
        }
      );
      _this._showSent('show-matches-messages');
    },

    _sendNotification : function()
    {
       var _this = this;
       var ids = this._readMatchesToNotify();
       var idsJson = JSON.stringify({ ids: ids});
       console.log("Sending " + idsJson);
       new Ajax.Request(_this._ajaxURL,
          { parameters : {action : 'send-notifications',
                          idsForNotification : idsJson
            },
            onSuccess : function (response) {
               console.log("onSuccess, received:");
               console.log(response.responseText);
               _this._showSuccess('send-notifications-messages');
              _this._notificationResults = response.responseJSON;
              _this._showMatches();
            },
            onFailure : function (response) {
               _this._showFailure('send-notifications-messages');
            }
          }
       );
       this._showSent('send-notifications-messages');
    },

    _checkScore : function(scoreFieldName, messagesFieldName) {
      var score = this._$('#' + scoreFieldName).val();
      if (score == undefined || score == "") {
         this._showHint(messagesFieldName, "$services.localization.render('phenotips.matchingNotifications.emptyScore')");
         return;
      } else if (isNaN(score)) {
         this._showHint(messagesFieldName, "$services.localization.render('phenotips.matchingNotifications.invalidScore')");
         return;
      };
      scoreNumber = Number(score);
      if (scoreNumber < 0 || scoreNumber > 1) {
         this._showHint(messagesFieldName, "$services.localization.render('phenotips.matchingNotifications.invalidScore')");
         return;
      }
      return score;
    },

    _showSent : function(messagesFieldName) {
      this._showHint(messagesFieldName, "$services.localization.render('phenotips.matchingNotifications.requestSent')");
    },

    _showSuccess : function(messagesFieldName, value) {
      this._showHint(messagesFieldName, "$services.localization.render('phenotips.matchingNotifications.success')");
    },

    _showFailure : function(messagesFieldName) {
      this._showHint(messagesFieldName, "$services.localization.render('phenotips.matchingNotifications.failure')");
    },

    _showHint : function(messagesFieldName, message) {
      var messages = this._$('#' + messagesFieldName);
      messages.empty();
      messages.append(new Element('div', {'class' : 'xHint'}).update(message));
    },

    _formatMatches : function(matches)
    {
       for (var i = 0 ; i < matches.length ; i++) {
          // scores
          matches[i].score = this._roundScore(matches[i].score);
          matches[i].phenotypicScore = this._roundScore(matches[i].phenotypicScore);
          matches[i].genotypicScore = this._roundScore(matches[i].genotypicScore);

          // remote id
          var remoteId = matches[i].remoteId;
          if (remoteId == undefined || remoteId == 'undefined') {
             matches[i].remoteId = '-';
          }

          // Phenotypes
          [matches[i].phenotypes, matches[i].matchedPhenotypes] = this._formatPhenotypes(matches[i].phenotypes, matches[i].matchedPhenotypes);
       }
       return matches;
    },

    // Format two JSON objects containing phenotypes for display (compare to formatGenesArrays)
    _formatPhenotypes : function(obj1, obj2)
    {
       // Replace predefined {key,value} phenotypes with value-s.
       for (var i=0; i<obj1.predefined.size(); i++) {
          obj1.predefined[i] = obj1.predefined[i].name;
       }
       for (var i=0; i<obj2.predefined.size(); i++) {
          obj2.predefined[i] = obj2.predefined[i].name;
       }

       // If there are no phenotypes, push emptiness sign to predefined
       obj1.empty = (obj1.predefined.size() + obj1.freeText.size() == 0);
       obj2.empty = (obj2.predefined.size() + obj2.freeText.size() == 0);

       return [obj1, obj2];
    },

    _roundScore : function(score)
    {
        return Math.round(Number(score) * 100) / 100;
    },

    _buildTable : function()
    {
      var _this = this;

      var matchesToUse = this._matches;

      if (this._filteredNotified) {
          var unnotifiedMatches = [];
          for (var i = 0 ; i < this._matches.length ; i++) {
              var current = this._matches[i];
              if (!current.notified) {
                 unnotifiedMatches.push(current);
              }
          }
          matchesToUse = unnotifiedMatches;
      }

      if (this._tableBuilt) {
         var table = this._$('#matchesTable').data('dynatable');
         table.settings.dataset.originalRecords = matchesToUse;
         table.process();
      } else {
         this._$('#matchesTable').dynatable({
            dataset: {
               records: matchesToUse
            },
            writers: {
               _rowWriter: this._rowWriter.bind(this)
            },
            features: {
               pushState : false
            }
         }).bind('dynatable:afterProcess', this._processingComplete.bind(this));
         this._processingComplete();
      }

      // Mark notification results as successful/failed
      if (this._notificationResults != undefined) {
         var results = this._notificationResults.results;
         if (results != undefined) {
            for (var i = 0 ; i < results.length ; i++) {
               var result = results[i];
               var tr = this._$('#matchesTable').find("#tr_" + result.id);
               tr.attr('class', result.success ? 'successful' : 'failed');
               if (!result.success) {
                   tr.find(":checkbox").attr('checked', 'checked');
               }
            }
         }
      }

       this._tableBuilt = true;
    },

    _rowWriter : function(rowIndex, record, columns, cellWriter)
    {
       var tr = '';

       // checkbox - column 0
       var checkbox = '<input type="checkbox" id="checkbox_' + record.id + '" ' + (record.notified ? 'checked disabled ' : '') + '/>	';

       // column 1,2 - reference patient and matched patient, their genes and phenotypes
       patientTd  = this._getPatientDetailsTd(record.patientId, record.genes, record.phenotypes, 'patientTd', record.id);
       matchedPatientTd = this._getPatientDetailsTd(record.matchedPatientId, record.matchedGenes, record.matchedPhenotypes, 'matchedPatientTd', record.id);

       // grab the record's attribute for each column.
       // skip first column (index 0), it's for checkbox
       for (var i = 0, len = columns.length; i < len; i++) {
         var id = columns[i].id;
         if (id == 'checkbox') {
            tr += checkbox;
         } else if (id == 'patient') {
            tr += patientTd;
         } else if (id == 'matchedPatient') {
            tr += matchedPatientTd;
         } else {
             tr += cellWriter(columns[i], record);
         }
       }

       return '
' + tr + '
';
    },

    _getPatientDetailsTd : function(patientId, genes, phenotypes, tdId, matchId)
    {
       var td = '';

       var patientHref = new XWiki.Document(patientId, 'data').getURL();

       // Patient id and collapsible icon
       td += '
';
       td += '' + patientId + '';
       td += '
';

       // Collapsible div
       td += '
';

       // Genes
       td += '
';
       var genesTitle = 'Genes';
       if (genes.size() == 0) {
           genesTitle += ': -';
       }
       td += '
' + genesTitle + '
';
       if (genes.size() != 0) {
          td += '
';
          for (var i = 0 ; i < genes.size() ; i++) {
              td += '
' + genes[i] + '
';
          }
          td += '
';
       }
       td += '
';

       // Phenotypes
       td += '
';
       var phenotypesTitle = 'Phenotypes';
       if (phenotypes.empty) {
           phenotypesTitle += ': -';
       }
       td += '
' + phenotypesTitle + '
';
       if (!phenotypes.empty) {
          td += '
';
          for (var i = 0 ; i < phenotypes.predefined.size() ; i++) {
              td += '
' + phenotypes.predefined[i] + '
';
          }
          for (var i = 0 ; i < phenotypes.freeText.size() ; i++) {
             td += '
'
             td += '
';
             td += ' ';
             td += phenotypes.freeText[i];
             td += '
';
             td += '
';
          }
          td += '
';
       }
       td += '
';

       // End collapsible div
       td += '
';

       td += '	';
       return td;
    },

    _processingComplete : function(specificTd)
    {
       // Makes genes-div the same height in patient and matched patient columns
       this._$('#matchesTable > tbody').find('tr').each(function (index, elm)
       {
          var patientTd = this._$(elm).find('#patientTd');
          var matchedPatientTd = this._$(elm).find('#matchedPatientTd');

          var genesDiv = this._$(patientTd).find('.genes-div');
          var matchedGenesDiv = this._$(matchedPatientTd).find('.genes-div');

          var h = Math.max(genesDiv.height(), matchedGenesDiv.height());
          genesDiv.height(h);
          matchedGenesDiv.height(h);
       }.bind(this));

       // Register listeners to components in table
       this._$('#matchesTable').find('[class*="collapse-gp-tool"]').on('click', function(event) {
          this._expandCollapseGP(event.target);
       }.bind(this));

       this._expandAllClicked();
    },

    _checkboxAllClicked : function(event)
    {
       var checked = event.target.checked;
       this._$('#matchesTable').find(":checkbox").each(function (index, elm) {
          if (!elm.disabled) {
             elm.checked = checked;
          }
       });
    },

    _filterNotifiedClicked : function(event)
    {
       var checked = event.target.checked;
       this._filteredNotified = checked;
       this._buildTable();
    },

    _readMatchesToNotify : function()
    {
       var ids = [];
       this._$('#matchesTable').find(":checkbox").each(function (index, elm) {
          if (elm.checked && !elm.disabled) {
             var id = Number(elm.id.substr(elm.id.indexOf('_')+1));
             ids.push(String(id));
          }
       });
       return ids;
    },

    // target is the component that was clicked to expand/collapse (this +/- sign).
    // expand is boolean, when undefined, the value will be understood from target
    _expandCollapseGP : function(target, expand)
    {
       // find matchId to collapse/expand
       var lookFor = 'collapse-gp-tool-';
       var classes = target.classList;
       var greped = this._$.grep(classes , function(item) {return item.indexOf(lookFor) > -1});
       var matchId = greped[0].substring(lookFor.length);

       // collapse/expand divs
       this._$('#matchesTable').find(".collapse-gp-div-"+matchId).each(function (index, elm) {

          if (expand == undefined) {
             expand = this._$(elm).hasClass('collapsed');
          }

          if (expand) {
             this._$(elm).removeClass("collapsed");
          } else {
             this._$(elm).addClass("collapsed");
          }

       }.bind(this));

       // change display of collapse/display component (+/-)
       this._$('#matchesTable').find(".collapse-gp-tool-"+matchId).each(function (index, elm) {

          if (expand) {
             this._$(elm).removeClass("fa-plus-square-o");
             this._$(elm).addClass("fa-minus-square-o");
          } else {
             this._$(elm).removeClass("fa-minus-square-o");
             this._$(elm).addClass("fa-plus-square-o");
          }

       }.bind(this));

    },

    _expandAllClicked : function()
    {
       var checkbox = this._$('#expand_all');
       var expand = this._$(checkbox).is(':checked');
       this._$('#matchesTable').find('[class*="collapse-gp-tool-"]').each(function (index, elm) {
          this._expandCollapseGP(elm, expand);
       }.bind(this));
    }

  });
  return PhenoTips;
}(PhenoTips || {}));
<|endoftext|>


<commit_before>
const React = require('react');

const CompLibrary = require('../../core/CompLibrary');
const Container = CompLibrary.Container;
const GridBlock = CompLibrary.GridBlock;

const CWD = process.cwd();

const translate = require('../../server/translate.js').translate;

const siteConfig = require(`${CWD}/siteConfig.js`);
// versions post docusaurus
const versions = require(`${CWD}/versions.json`);
// versions pre docusaurus
const oldversions = require(`${CWD}/oldversions.json`);

function Versions(props) {
    const latestStableVersion = versions[0];
    const repoUrl = `https://github.com/${siteConfig.organizationName}/${
      siteConfig.projectName
    }`;
    return (

      <Container className="mainContainer documentContainer postContainer">

{siteConfig.title} <translate>Versions</translate>

<translate>Latest Stable Version</translate>

<translate>Latest stable release of Apache Pulsar.</translate>

                {latestStableVersion}

                  <a
                    href={`${siteConfig.baseUrl}docs/${props.language}/standalone`}>
                    <translate>
                    Documentation
                    </translate>

                  <a href={`${siteConfig.baseUrl}release-notes#${latestStableVersion}`}>
                    <translate>
                    Release Notes
                    </translate>

<translate>Latest Version</translate>

          <translate>
          Here you can find the latest documentation and unreleased code.
          </translate>

                master

                  <a
                    href={`${siteConfig.baseUrl}docs/${props.language}/next/standalone`}>
                    <translate>Documentation</translate>

                  <translate>Source Code</translate>

<translate>Past Versions</translate>

          <translate>
            Here you can find documentation for previous versions of Apache Pulsar.
          </translate>

              {versions.map(
                version =>
                  version !== latestStableVersion && (

                      {version}

                        <a
                          href={`${siteConfig.baseUrl}docs/${props.language}/${version}/standalone`}>
                          <translate>Documentation</translate>

                        <a href={`${siteConfig.baseUrl}/release-notes#${version}`}>
                          <translate>Release Notes</translate>

                  )
              )}
              {oldversions.map(
                version =>
                  version !== latestStableVersion && (

                      {version}

                        <a
                          href={`${siteConfig.url}/docs/v${version}/getting-started/LocalCluster/`}>
                          <translate>Documentation</translate>

                        <a href={`${siteConfig.baseUrl}/release-notes#${version}`}>
                          <translate>Release Notes</translate>

                  )
              )}

          <translate>
            You can find past versions of this project on{' '}
            <a href={`${repoUrl}/releases`}>GitHub or download from{' '}
            <a href={`${siteConfig.baseUrl}download`}>Apache.
          </translate>

      </Container>

  );
}

Versions.title = 'Versions';

module.exports = Versions;
<commit_msg>[documentation] Issue 2386: release notes link of old versions broken (#2388)<commit_after>
const React = require('react');

const CompLibrary = require('../../core/CompLibrary');
const Container = CompLibrary.Container;
const GridBlock = CompLibrary.GridBlock;

const CWD = process.cwd();

const translate = require('../../server/translate.js').translate;

const siteConfig = require(`${CWD}/siteConfig.js`);
// versions post docusaurus
const versions = require(`${CWD}/versions.json`);
// versions pre docusaurus
const oldversions = require(`${CWD}/oldversions.json`);

function Versions(props) {
    const latestStableVersion = versions[0];
    const repoUrl = `https://github.com/${siteConfig.organizationName}/${
      siteConfig.projectName
    }`;
    return (

      <Container className="mainContainer documentContainer postContainer">

{siteConfig.title} <translate>Versions</translate>

<translate>Latest Stable Version</translate>

<translate>Latest stable release of Apache Pulsar.</translate>

                {latestStableVersion}

                  <a
                    href={`${siteConfig.baseUrl}docs/${props.language}/standalone`}>
                    <translate>
                    Documentation
                    </translate>

                  <a href={`${siteConfig.baseUrl}release-notes#${latestStableVersion}`}>
                    <translate>
                    Release Notes
                    </translate>

<translate>Latest Version</translate>

          <translate>
          Here you can find the latest documentation and unreleased code.
          </translate>

                master

                  <a
                    href={`${siteConfig.baseUrl}docs/${props.language}/next/standalone`}>
                    <translate>Documentation</translate>

                  <translate>Source Code</translate>

<translate>Past Versions</translate>

          <translate>
            Here you can find documentation for previous versions of Apache Pulsar.
          </translate>

              {versions.map(
                version =>
                  version !== latestStableVersion && (

                      {version}

                        <a
                          href={`${siteConfig.baseUrl}docs/${props.language}/${version}/standalone`}>
                          <translate>Documentation</translate>

                        <a href={`${siteConfig.baseUrl}release-notes#${version}`}>
                          <translate>Release Notes</translate>

                  )
              )}
              {oldversions.map(
                version =>
                  version !== latestStableVersion && (

                      {version}

                        <a
                          href={`${siteConfig.baseUrl}docs/v${version}/getting-started/LocalCluster/`}>
                          <translate>Documentation</translate>

                        <a href={`${siteConfig.baseUrl}release-notes#${version}`}>
                          <translate>Release Notes</translate>

                  )
              )}

          <translate>
            You can find past versions of this project on{' '}
            <a href={`${repoUrl}/releases`}>GitHub or download from{' '}
            <a href={`${siteConfig.baseUrl}download`}>Apache.
          </translate>

      </Container>

  );
}

Versions.title = 'Versions';

module.exports = Versions;
<|endoftext|>


<commit_before>(function ($) {
  $.extend(true, window, {
    Slick: {
      Data: {
        GroupItemMetadataProvider: GroupItemMetadataProvider
      }
    }
  });

  /***
   * Provides item metadata for group (Slick.Group) and totals (Slick.Totals) rows produced by the DataView.
   * This metadata overrides the default behavior and formatting of those rows so that they appear and function
   * correctly when processed by the grid.
   *
   * This class also acts as a grid plugin providing event handlers to expand & collapse groups.
   * If "grid.registerPlugin(...)" is not called, expand & collapse will not work.
   *
   * @class GroupItemMetadataProvider
   * @module Data
   * @namespace Slick.Data
   * @constructor
   * @param options
   */
  function GroupItemMetadataProvider(options) {
    var _grid;
    var _defaults = {
      groupCssClass: "slick-group",
      groupTitleCssClass: "slick-group-title",
      totalsCssClass: "slick-group-totals",
      groupSelectable: false,
      groupFocusable: true,
      totalsFocusable: false,
      toggleCssClass: "slick-group-toggle",
      toggleExpandedCssClass: "expanded",
      toggleCollapsedCssClass: "collapsed",
      enableExpandCollapse: true,
      groupFormatter: defaultGroupCellFormatter,
      totalsFormatter: defaultTotalsCellFormatter
    };

    options = $.extend(true, {}, _defaults, options);

    function defaultGroupCellFormatter(row, cell, value, columnDef, item) {
      if (!options.enableExpandCollapse) {
        return item.title;
      }

      var indentation = item.level * 15 + "px";

      return "" +
          "" +
          "" +
            item.title +
          "";
    }

    function defaultTotalsCellFormatter(row, cell, value, columnDef, item) {
      return (columnDef.groupTotalsFormatter && columnDef.groupTotalsFormatter(item, columnDef)) || "";
    }

    function init(grid) {
      _grid = grid;
      _grid.onClick.subscribe(handleGridClick);
      _grid.onKeyDown.subscribe(handleGridKeyDown);

    }

    function destroy() {
      if (_grid) {
        _grid.onClick.unsubscribe(handleGridClick);
        _grid.onKeyDown.unsubscribe(handleGridKeyDown);
      }
    }

    function handleGridClick(e, args) {
      var item = this.getDataItem(args.row);
      if (item && item instanceof Slick.Group && $(e.target).hasClass(options.toggleCssClass)) {
        if (item.collapsed) {
          this.getData().expandGroup(item.groupingKey);
        } else {
          this.getData().collapseGroup(item.groupingKey);
        }

        e.stopImmediatePropagation();
        e.preventDefault();
      }
    }

    // TODO:  add -/+ handling
    function handleGridKeyDown(e, args) {
      if (options.enableExpandCollapse && (e.which == $.ui.keyCode.SPACE)) {
        var activeCell = this.getActiveCell();
        if (activeCell) {
          var item = this.getDataItem(activeCell.row);
          if (item && item instanceof Slick.Group) {
            if (item.collapsed) {
              this.getData().expandGroup(item.groupingKey);
            } else {
              this.getData().collapseGroup(item.groupingKey);
            }

            e.stopImmediatePropagation();
            e.preventDefault();
          }
        }
      }
    }

    function getGroupRowMetadata(item, row, cell, rows) {
      return {
        selectable: options.groupSelectable,
        focusable: options.groupFocusable,
        cssClasses: options.groupCssClass,
        columns: {
          0: {
            colspan: "*",
            formatter: options.groupFormatter,
            editor: null
          }
        }
      };
    }

    function getTotalsRowMetadata(item, row, cell, rows) {
      return {
        selectable: false,
        focusable: options.totalsFocusable,
        cssClasses: options.totalsCssClass,
        formatter: options.totalsFormatter,
        editor: null
      };
    }

    function getRowMetadata(item) {
      if (options.getRowMetadata) {
        return options.getRowMetadata(item);
      }

      return null;
    }

    return {
      "init": init,
      "destroy": destroy,
      "getRowMetadata": getRowMetadata,
      "getGroupRowMetadata": getGroupRowMetadata,
      "getTotalsRowMetadata": getTotalsRowMetadata
    };
  }
})(jQuery);
<commit_msg>- groupItemMetadataProvider options: treat the totals row just like the group row: have options for both 'selectable' and 'focusable' for each<commit_after>(function ($) {
  $.extend(true, window, {
    Slick: {
      Data: {
        GroupItemMetadataProvider: GroupItemMetadataProvider
      }
    }
  });

  /***
   * Provides item metadata for group (Slick.Group) and totals (Slick.Totals) rows produced by the DataView.
   * This metadata overrides the default behavior and formatting of those rows so that they appear and function
   * correctly when processed by the grid.
   *
   * This class also acts as a grid plugin providing event handlers to expand & collapse groups.
   * If "grid.registerPlugin(...)" is not called, expand & collapse will not work.
   *
   * @class GroupItemMetadataProvider
   * @module Data
   * @namespace Slick.Data
   * @constructor
   * @param options
   */
  function GroupItemMetadataProvider(options) {
    var _grid;
    var _defaults = {
      groupCssClass: "slick-group",
      groupTitleCssClass: "slick-group-title",
      totalsCssClass: "slick-group-totals",
      groupSelectable: false,
      groupFocusable: true,
      totalsSelectable: false,
      totalsFocusable: false,
      toggleCssClass: "slick-group-toggle",
      toggleExpandedCssClass: "expanded",
      toggleCollapsedCssClass: "collapsed",
      enableExpandCollapse: true,
      groupFormatter: defaultGroupCellFormatter,
      totalsFormatter: defaultTotalsCellFormatter,
      getRowMetadata: null // function (item, row, cell, rows)
    };

    options = $.extend(true, {}, _defaults, options);

    function defaultGroupCellFormatter(row, cell, value, columnDef, item) {
      if (!options.enableExpandCollapse) {
        return item.title;
      }

      var indentation = item.level * 15 + "px";

      return "" +
          "" +
          "" +
            item.title +
          "";
    }

    function defaultTotalsCellFormatter(row, cell, value, columnDef, item) {
      return (columnDef.groupTotalsFormatter && columnDef.groupTotalsFormatter(item, columnDef)) || "";
    }

    function init(grid) {
      _grid = grid;
      _grid.onClick.subscribe(handleGridClick);
      _grid.onKeyDown.subscribe(handleGridKeyDown);

    }

    function destroy() {
      if (_grid) {
        _grid.onClick.unsubscribe(handleGridClick);
        _grid.onKeyDown.unsubscribe(handleGridKeyDown);
      }
    }

    function handleGridClick(e, args) {
      var item = this.getDataItem(args.row);
      if (item && item instanceof Slick.Group && $(e.target).hasClass(options.toggleCssClass)) {
        if (item.collapsed) {
          this.getData().expandGroup(item.groupingKey);
        } else {
          this.getData().collapseGroup(item.groupingKey);
        }

        e.stopImmediatePropagation();
        e.preventDefault();
      }
    }

    // TODO:  add -/+ handling
    function handleGridKeyDown(e, args) {
      if (options.enableExpandCollapse && (e.which == $.ui.keyCode.SPACE)) {
        var activeCell = this.getActiveCell();
        if (activeCell) {
          var item = this.getDataItem(activeCell.row);
          if (item && item instanceof Slick.Group) {
            if (item.collapsed) {
              this.getData().expandGroup(item.groupingKey);
            } else {
              this.getData().collapseGroup(item.groupingKey);
            }

            e.stopImmediatePropagation();
            e.preventDefault();
          }
        }
      }
    }

    function getGroupRowMetadata(item, row, cell, rows) {
      return {
        selectable: options.groupSelectable,
        focusable: options.groupFocusable,
        cssClasses: options.groupCssClass,
        columns: {
          0: {
            colspan: "*",
            formatter: options.groupFormatter,
            editor: null
          }
        }
      };
    }

    function getTotalsRowMetadata(item, row, cell, rows) {
      return {
        selectable: options.totalsSelectable,
        focusable: options.totalsFocusable,
        cssClasses: options.totalsCssClass,
        formatter: options.totalsFormatter,
        editor: null
      };
    }

    function getRowMetadata(item, row, cell, rows) {
      if (options.getRowMetadata) {
        return options.getRowMetadata(item, row, cell, rows);
      }

      return null;
    }

    return {
      "init": init,
      "destroy": destroy,
      "getRowMetadata": getRowMetadata,
      "getGroupRowMetadata": getGroupRowMetadata,
      "getTotalsRowMetadata": getTotalsRowMetadata
    };
  }
})(jQuery);
<|endoftext|>


<commit_before>/**
 * This file is part of the NovaeCalc project.
 *
 * It is permitted to use, redistribute and/or modify this software
 * under the terms of the MIT License
 *
 * @author Felix Maier <maier.felix96@gmail.com>
 * @copyright (c) 2015 Felix Maier, @felixmaier
 *
 * You may not change or remove these lines
 *
 */

"use strict";

  /**
   * Update the grid height
   *
   * @method updateHeight
   * @static
   */
  CORE.Grid.prototype.updateHeight = function(dir, scrollAmount) {

    var width = this.Settings.x,
        height = this.Settings.y,
        br = this.Settings.y,
        helper = 0,
        calculation = 0,
        /** Cell name attributes */
        Letter = null,
        Number = 0;

    /** Speed optimization, avoid using regular expressions */
    if (this.Settings.scrolledX <= 0) Letter = CORE.DOM.Output.children[0].getAttribute("name").match(CORE.REGEX.numbers).join("");
    else Letter = CORE.$.numberToAlpha(this.Settings.scrolledX + 1);

    /** Check if safe integer, also don't go below zero */
    this.Settings.scrolledY = this.Settings.scrolledY < 0 ? 0 : CORE.$.isSafeInteger(this.Settings.scrolledY);

    for (var ii = 0; ii < width * height; ++ii) {

      /** Scroll Down */
      if (dir === "down") {
        Number = (this.Settings.scrolledY - scrollAmount) + 1;
        calculation = ( ( ii + scrollAmount ) - helper + Number);
      /** Scroll Up */
      } else if (dir === "up") {
        Number = (this.Settings.scrolledY + scrollAmount) + 1;
        calculation = ( ( ii - scrollAmount ) - helper + Number);
      /** Scroll to Default */
      } else {
        Number = 1;
        calculation = ( ( ii + this.Settings.scrolledY ) - helper + Number);
      }

      CORE.DOM.Output.children[ii].setAttribute("name", Letter + calculation);

      /** Remove style of cell */
      this.removeCellStyling(ii);

      /** Master selection column */
      if (CORE.Selector.masterSelected.Columns[Letter]) {
        this.updateCellMasterStyling(Letter, ii);
      /** Master selection row */
      } else if (CORE.Selector.masterSelected.Rows[calculation]) {
        this.updateCellMasterStyling(calculation, ii);
      }

      /** Check if cell is registered, if yes update its styling */
      if (CORE.Cells.Used[Letter] && CORE.Cells.Used[Letter][Letter + calculation]) {
        /** Higher priority than master styling */
        this.removeCellStyling(ii);
        this.updateCellStyling(Letter, Letter + calculation, ii);
      }

      /** Only resize grid if necessary */
      if (!CORE.Event.lastAction.scrollY) {
        /** Horizontal cell row got resized */
        this.resizeHorizontal(Letter, ii);
      }

      /** Vertical cell row got resized */
      this.resizeVertical(calculation, ii);

      if ( (ii + 1) % br === 0) {
        Letter = CORE.$.numberToAlpha(CORE.$.alphaToNumber(Letter) + 1);
        helper = ii + 1;
      }

    }

  };

  /**
   * Update the grid width
   *
   * @method updateWidth
   * @static
   */
  CORE.Grid.prototype.updateWidth = function(dir) {

    var lastX = 0,
        lastY = 0;

    var width = this.Settings.x,
        height = this.Settings.y,
        br = this.Settings.y,
        ii = 0;

    var calculation = 0;

    var Letter = null,
        Number = 0;

    /** View fix for the width */
    width += 1;

    /** View fix for the height */
    height += 1;

    for (var xx = 0; xx < width; ++xx) {

      for (var yy = 0; yy < height; ++yy) {

        if (xx > 0) {
          if (xx !== lastX) {
            lastX = xx;
            for (var kk = 0; kk < height; ++kk) {
              /** Calculate cell node position */
              calculation = (kk + ii - (lastX) - height);
              if (CORE.DOM.Output.children[calculation]) {

                /** Calculate cell number */
                if (kk === 0) Number = (height - 1) + this.Settings.scrolledY;
                else Number = kk + this.Settings.scrolledY;

                CORE.DOM.Output.children[calculation].setAttribute("name", Letter + calculation);

                /** Check if cell is registered, if yes update its styling */
                /* Seems like not necessary */
                /*if (CORE.Cells.Used[Letter] && [Letter + calculation]) {
                  this.updateCellStyling(Letter, Letter + calculation, calculation);
                }*/

              }
            }
          }
        }

        ii += 1;

        if (dir === "right" || dir === "left") {
          if (lastX === 0) Letter = CORE.$.numberToAlpha(this.Settings.scrolledX + 1);
          else Letter = CORE.$.numberToAlpha(lastX + (this.Settings.scrolledX + 1));
        }
        else {
          if (this.Settings.scrolledX === 0) {
            Letter = CORE.$.numberToAlpha(this.Settings.scrolledX + 1);
          } else if (this.Settings.scrolledX === 1) {
            /** Default 0 position */
            Letter = CORE.$.numberToAlpha(this.Settings.scrolledX);
          }
          else Letter = CORE.$.numberToAlpha(this.Settings.scrolledX + 1);
        }

        /** Check if safe integer */
        this.Settings.scrolledX = CORE.$.isSafeInteger(this.Settings.scrolledX)

      }

    }

    this.updateHeight("default", CORE.Settings.Scroll.Vertical);

  };

  /**
   * Update specific cell with a custom styling
   *
   * @method updateCellStyling
   * @static
   */
  CORE.Grid.prototype.updateCellStyling = function(name, cell, ii) {

    if (!CORE.Cells.Used[name]) return void 0;

    if (!CORE.Cells.Used[name][cell]) return void 0;

    /** Check if cell has custom content */
    if (CORE.Cells.Used[name][cell].Content !== undefined && CORE.Cells.Used[name][cell].Content !== null) {
      CORE.DOM.Cache[ii].innerHTML = CORE.Cells.Used[name][cell].Content;
    }

    /** Check if cell has a custom font */
    if (CORE.Cells.Used[name][cell].Font) {
      CORE.DOM.Cache[ii].style.fontFamily = CORE.Cells.Used[name][cell].Font;
    }

    /** Check if cell has a custom font size */
    if (CORE.Cells.Used[name][cell].FontSize) {
      CORE.DOM.Cache[ii].style.fontSize = CORE.Cells.Used[name][cell].FontSize + "px";
    }

    /** Check if cell has a custom font color */
    if (CORE.Cells.Used[name][cell].Color) {
      CORE.DOM.Cache[ii].style.color = CORE.Cells.Used[name][cell].Color;
    }

    /** Check if cell has a custom font bold property */
    if (CORE.Cells.Used[name][cell].FontBold) {
      CORE.DOM.Cache[ii].style.fontWeight = "bold";
    }

    /** Check if cell has a custom font italic property */
    if (CORE.Cells.Used[name][cell].FontItalic) {
      CORE.DOM.Cache[ii].style.fontStyle = "italic";
    }

    /** Check if cells font is underlined */
    if (CORE.Cells.Used[name][cell].FontUnderlined) {
      CORE.DOM.Cache[ii].style.textDecoration = "underline";
    }

    /** Check if cell has a custom background color */
    if (CORE.Cells.Used[name][cell].BackgroundColor) {
      CORE.DOM.Cache[ii].style.background = CORE.Cells.Used[name][cell].BackgroundColor;
    }

    /** Check if cell has custom border settings */
    if (CORE.Cells.Used[name][cell].Border.used) {
      /** Left border */
      if (CORE.Cells.Used[name][cell].Border.left) {
        CORE.DOM.Cache[ii].style.borderLeft = "2px solid black";
      }
      /** Right border */
      if (CORE.Cells.Used[name][cell].Border.right) {
        CORE.DOM.Cache[ii].style.borderRight = "2px solid black";
      }
      /** Top border */
      if (CORE.Cells.Used[name][cell].Border.top) {
        CORE.DOM.Cache[ii].style.borderTop = "2px solid black";
      }
      /** Bottom border */
      if (CORE.Cells.Used[name][cell].Border.bottom) {
        CORE.DOM.Cache[ii].style.borderBottom = "2px solid black";
      }
      /** Full border */
      if (CORE.Cells.Used[name][cell].Border.full) {
        CORE.DOM.Cache[ii].style.border = "2px solid black";
      }
    }

  };

  /**
   * Update specific cell with a master selection styling
   *
   * @method updateCellMasterStyling
   * @static
   */
  CORE.Grid.prototype.updateCellMasterStyling = function(name, ii) {

    /** Switch between columns and rows */
    var data = CORE.Selector.masterSelected.Columns[name] || CORE.Selector.masterSelected.Rows[name];

    /** Check if cell has a custom font */
    if (data.Font) {
      CORE.DOM.Cache[ii].style.fontFamily = data.Font;
    }

    /** Check if cell has a custom font size */
    if (data.FontSize) {
      CORE.DOM.Cache[ii].style.fontSize = data.FontSize + "px";
    }

    /** Check if cell has a custom font color */
    if (data.Color) {
      CORE.DOM.Cache[ii].style.color = data.Color;
    }

    /** Check if cell has a custom font bold property */
    if (data.FontBold) {
      CORE.DOM.Cache[ii].style.fontWeight = "bold";
    }

    /** Check if cell has a custom font italic property */
    if (data.FontItalic) {
      CORE.DOM.Cache[ii].style.fontStyle = "italic";
    }

    /** Check if cells font is underlined */
    if (data.FontUnderlined) {
      CORE.DOM.Cache[ii].style.textDecoration = "underline";
    }

    /** Check if cell has a custom background color */
    if (data.BackgroundColor) {
      CORE.DOM.Cache[ii].style.background = data.BackgroundColor;
    }

    /** Check if cell has custom border settings */
    if (data.Border.used) {
      /** Left border */
      if (data.Border.left) {
        CORE.DOM.Cache[ii].style.borderLeft = "2px solid black";
      }
      /** Right border */
      if (data.Border.right) {
        CORE.DOM.Cache[ii].style.borderRight = "2px solid black";
      }
      /** Top border */
      if (data.Border.top) {
        CORE.DOM.Cache[ii].style.borderTop = "2px solid black";
      }
      /** Bottom border */
      if (data.Border.bottom) {
        CORE.DOM.Cache[ii].style.borderBottom = "2px solid black";
      }
      /** Full border */
      if (data.Border.full) {
        CORE.DOM.Cache[ii].style.border = "2px solid black";
      }
    }

  };

  /**
   * Remove specific cell styling
   *
   * @method removeCellStyling
   * @static
   */
  CORE.Grid.prototype.removeCellStyling = function(ii) {

    CORE.DOM.Cache[ii].style.borderLeft = "";
    CORE.DOM.Cache[ii].style.borderRight = "";
    CORE.DOM.Cache[ii].style.borderTop = "";
    CORE.DOM.Cache[ii].style.borderBottom = "";
    CORE.DOM.Cache[ii].style.border = "";
    CORE.DOM.Cache[ii].style.fontFamily = "";
    CORE.DOM.Cache[ii].style.fontSize = 12 + "px";
    CORE.DOM.Cache[ii].style.fontStyle = "normal";
    CORE.DOM.Cache[ii].style.fontWeight = "normal";
    CORE.DOM.Cache[ii].style.textDecoration = "none";
    CORE.DOM.Cache[ii].style.background = "#fff";
    CORE.DOM.Cache[ii].style.color = "#000";
    CORE.DOM.Cache[ii].innerHTML = "";

  };<commit_msg>Bug fix<commit_after>/**
 * This file is part of the NovaeCalc project.
 *
 * It is permitted to use, redistribute and/or modify this software
 * under the terms of the MIT License
 *
 * @author Felix Maier <maier.felix96@gmail.com>
 * @copyright (c) 2015 Felix Maier, @felixmaier
 *
 * You may not change or remove these lines
 *
 */

"use strict";

  /**
   * Update the grid height
   *
   * @method updateHeight
   * @static
   */
  CORE.Grid.prototype.updateHeight = function(dir, scrollAmount) {

    var width = this.Settings.x,
        height = this.Settings.y,
        br = this.Settings.y,
        helper = 0,
        calculation = 0,
        /** Cell name attributes */
        Letter = null,
        Number = 0;

    /** Speed optimization, avoid using regular expressions */
    if (this.Settings.scrolledX <= 0) Letter = CORE.DOM.Output.children[0].getAttribute("name").match(CORE.REGEX.numbers).join("");
    else Letter = CORE.$.numberToAlpha(this.Settings.scrolledX + 1);

    /** Check if safe integer, also don't go below zero */
    this.Settings.scrolledY = this.Settings.scrolledY < 0 ? 0 : CORE.$.isSafeInteger(this.Settings.scrolledY);

    for (var ii = 0; ii < width * height; ++ii) {

      /** Scroll Down */
      if (dir === "down") {
        Number = (this.Settings.scrolledY - scrollAmount) + 1;
        calculation = ( ( ii + scrollAmount ) - helper + Number);
      /** Scroll Up */
      } else if (dir === "up") {
        Number = (this.Settings.scrolledY + scrollAmount) + 1;
        calculation = ( ( ii - scrollAmount ) - helper + Number);
      /** Scroll to Default */
      } else {
        Number = 1;
        calculation = ( ( ii + this.Settings.scrolledY ) - helper + Number);
      }

      CORE.DOM.Output.children[ii].setAttribute("name", Letter + calculation);

      /** Remove style of cell */
      this.removeCellStyling(ii);

      /** Master selection column */
      if (CORE.Selector.masterSelected.Columns[Letter]) {
        this.updateCellMasterStyling(Letter, ii);
      /** Master selection row */
      } else if (CORE.Selector.masterSelected.Rows[calculation]) {
        this.updateCellMasterStyling(calculation, ii);
      }

      /** Check if cell is registered, if yes update its styling */
      if (CORE.Cells.Used[Letter] && CORE.Cells.Used[Letter][Letter + calculation]) {
        /** Higher priority than master styling */
        this.updateCellStyling(Letter, Letter + calculation, ii);
      }

      /** Only resize grid if necessary */
      if (!CORE.Event.lastAction.scrollY) {
        /** Horizontal cell row got resized */
        this.resizeHorizontal(Letter, ii);
      }

      /** Vertical cell row got resized */
      this.resizeVertical(calculation, ii);

      if ( (ii + 1) % br === 0) {
        Letter = CORE.$.numberToAlpha(CORE.$.alphaToNumber(Letter) + 1);
        helper = ii + 1;
      }

    }

  };

  /**
   * Update the grid width
   *
   * @method updateWidth
   * @static
   */
  CORE.Grid.prototype.updateWidth = function(dir) {

    var lastX = 0,
        lastY = 0;

    var width = this.Settings.x,
        height = this.Settings.y,
        br = this.Settings.y,
        ii = 0;

    var calculation = 0;

    var Letter = null,
        Number = 0;

    /** View fix for the width */
    width += 1;

    /** View fix for the height */
    height += 1;

    for (var xx = 0; xx < width; ++xx) {

      for (var yy = 0; yy < height; ++yy) {

        if (xx > 0) {
          if (xx !== lastX) {
            lastX = xx;
            for (var kk = 0; kk < height; ++kk) {
              /** Calculate cell node position */
              calculation = (kk + ii - (lastX) - height);
              if (CORE.DOM.Output.children[calculation]) {

                /** Calculate cell number */
                if (kk === 0) Number = (height - 1) + this.Settings.scrolledY;
                else Number = kk + this.Settings.scrolledY;

                CORE.DOM.Output.children[calculation].setAttribute("name", Letter + calculation);

                /** Check if cell is registered, if yes update its styling */
                /* Seems like not necessary */
                /*if (CORE.Cells.Used[Letter] && [Letter + calculation]) {
                  this.updateCellStyling(Letter, Letter + calculation, calculation);
                }*/

              }
            }
          }
        }

        ii += 1;

        if (dir === "right" || dir === "left") {
          if (lastX === 0) Letter = CORE.$.numberToAlpha(this.Settings.scrolledX + 1);
          else Letter = CORE.$.numberToAlpha(lastX + (this.Settings.scrolledX + 1));
        }
        else {
          if (this.Settings.scrolledX === 0) {
            Letter = CORE.$.numberToAlpha(this.Settings.scrolledX + 1);
          } else if (this.Settings.scrolledX === 1) {
            /** Default 0 position */
            Letter = CORE.$.numberToAlpha(this.Settings.scrolledX);
          }
          else Letter = CORE.$.numberToAlpha(this.Settings.scrolledX + 1);
        }

        /** Check if safe integer */
        this.Settings.scrolledX = CORE.$.isSafeInteger(this.Settings.scrolledX)

      }

    }

    this.updateHeight("default", CORE.Settings.Scroll.Vertical);

  };

  /**
   * Update specific cell with a custom styling
   *
   * @method updateCellStyling
   * @static
   */
  CORE.Grid.prototype.updateCellStyling = function(name, cell, ii) {

    if (!CORE.Cells.Used[name]) return void 0;

    if (!CORE.Cells.Used[name][cell]) return void 0;

    /** Check if cell has custom content */
    if (CORE.Cells.Used[name][cell].Content !== undefined && CORE.Cells.Used[name][cell].Content !== null) {
      CORE.DOM.Cache[ii].innerHTML = CORE.Cells.Used[name][cell].Content;
    }

    /** Check if cell has a custom font */
    if (CORE.Cells.Used[name][cell].Font) {
      CORE.DOM.Cache[ii].style.fontFamily = CORE.Cells.Used[name][cell].Font;
    }

    /** Check if cell has a custom font size */
    if (CORE.Cells.Used[name][cell].FontSize) {
      CORE.DOM.Cache[ii].style.fontSize = CORE.Cells.Used[name][cell].FontSize + "px";
    }

    /** Check if cell has a custom font color */
    if (CORE.Cells.Used[name][cell].Color) {
      CORE.DOM.Cache[ii].style.color = CORE.Cells.Used[name][cell].Color;
    }

    /** Check if cell has a custom font bold property */
    if (CORE.Cells.Used[name][cell].FontBold) {
      CORE.DOM.Cache[ii].style.fontWeight = "bold";
    }

    /** Check if cell has a custom font italic property */
    if (CORE.Cells.Used[name][cell].FontItalic) {
      CORE.DOM.Cache[ii].style.fontStyle = "italic";
    }

    /** Check if cells font is underlined */
    if (CORE.Cells.Used[name][cell].FontUnderlined) {
      CORE.DOM.Cache[ii].style.textDecoration = "underline";
    }

    /** Check if cell has a custom background color */
    if (CORE.Cells.Used[name][cell].BackgroundColor) {
      CORE.DOM.Cache[ii].style.background = CORE.Cells.Used[name][cell].BackgroundColor;
    }

    /** Check if cell has custom border settings */
    if (CORE.Cells.Used[name][cell].Border.used) {
      /** Left border */
      if (CORE.Cells.Used[name][cell].Border.left) {
        CORE.DOM.Cache[ii].style.borderLeft = "2px solid black";
      }
      /** Right border */
      if (CORE.Cells.Used[name][cell].Border.right) {
        CORE.DOM.Cache[ii].style.borderRight = "2px solid black";
      }
      /** Top border */
      if (CORE.Cells.Used[name][cell].Border.top) {
        CORE.DOM.Cache[ii].style.borderTop = "2px solid black";
      }
      /** Bottom border */
      if (CORE.Cells.Used[name][cell].Border.bottom) {
        CORE.DOM.Cache[ii].style.borderBottom = "2px solid black";
      }
      /** Full border */
      if (CORE.Cells.Used[name][cell].Border.full) {
        CORE.DOM.Cache[ii].style.border = "2px solid black";
      }
    }

  };

  /**
   * Update specific cell with a master selection styling
   *
   * @method updateCellMasterStyling
   * @static
   */
  CORE.Grid.prototype.updateCellMasterStyling = function(name, ii) {

    /** Switch between columns and rows */
    var data = CORE.Selector.masterSelected.Columns[name] || CORE.Selector.masterSelected.Rows[name];

    /** Check if cell has a custom font */
    if (data.Font) {
      CORE.DOM.Cache[ii].style.fontFamily = data.Font;
    }

    /** Check if cell has a custom font size */
    if (data.FontSize) {
      CORE.DOM.Cache[ii].style.fontSize = data.FontSize + "px";
    }

    /** Check if cell has a custom font color */
    if (data.Color) {
      CORE.DOM.Cache[ii].style.color = data.Color;
    }

    /** Check if cell has a custom font bold property */
    if (data.FontBold) {
      CORE.DOM.Cache[ii].style.fontWeight = "bold";
    }

    /** Check if cell has a custom font italic property */
    if (data.FontItalic) {
      CORE.DOM.Cache[ii].style.fontStyle = "italic";
    }

    /** Check if cells font is underlined */
    if (data.FontUnderlined) {
      CORE.DOM.Cache[ii].style.textDecoration = "underline";
    }

    /** Check if cell has a custom background color */
    if (data.BackgroundColor) {
      CORE.DOM.Cache[ii].style.background = data.BackgroundColor;
    }

    /** Check if cell has custom border settings */
    if (data.Border.used) {
      /** Left border */
      if (data.Border.left) {
        CORE.DOM.Cache[ii].style.borderLeft = "2px solid black";
      }
      /** Right border */
      if (data.Border.right) {
        CORE.DOM.Cache[ii].style.borderRight = "2px solid black";
      }
      /** Top border */
      if (data.Border.top) {
        CORE.DOM.Cache[ii].style.borderTop = "2px solid black";
      }
      /** Bottom border */
      if (data.Border.bottom) {
        CORE.DOM.Cache[ii].style.borderBottom = "2px solid black";
      }
      /** Full border */
      if (data.Border.full) {
        CORE.DOM.Cache[ii].style.border = "2px solid black";
      }
    }

  };

  /**
   * Remove specific cell styling
   *
   * @method removeCellStyling
   * @static
   */
  CORE.Grid.prototype.removeCellStyling = function(ii) {

    CORE.DOM.Cache[ii].style.borderLeft = "";
    CORE.DOM.Cache[ii].style.borderRight = "";
    CORE.DOM.Cache[ii].style.borderTop = "";
    CORE.DOM.Cache[ii].style.borderBottom = "";
    CORE.DOM.Cache[ii].style.border = "";
    CORE.DOM.Cache[ii].style.fontFamily = "";
    CORE.DOM.Cache[ii].style.fontSize = 12 + "px";
    CORE.DOM.Cache[ii].style.fontStyle = "normal";
    CORE.DOM.Cache[ii].style.fontWeight = "normal";
    CORE.DOM.Cache[ii].style.textDecoration = "none";
    CORE.DOM.Cache[ii].style.background = "#fff";
    CORE.DOM.Cache[ii].style.color = "#000";
    CORE.DOM.Cache[ii].innerHTML = "";

  };<|endoftext|>


<commit_before>/**
* @fileoverview Interprets a given command and calls workspace controller_.functions.
* @author pandori@wisc.edu (Sahib Pandori)
* @author david.liang@wisc.edu (David Liang)
*/

'use strict';

goog.provide('SpeechBlocks.Interpreter');

goog.require('SpeechBlocks.Predecessor');
goog.require('SpeechBlocks.StatementInput')
goog.require('SpeechBlocks.Successor');
goog.require('SpeechBlocks.Translation');
goog.require('SpeechBlocks.ValueInput');
goog.require('goog.structs.Map');

/**
* Constructs an interpreter that takes actions as input and controls the Blockly Workspace.
* @param {!SpeechBlocks.Controller} controller_.The workspace controller_.
*/
SpeechBlocks.Interpreter = function(controller) {
  /** @private @const */
  this.controller_ = controller;
  /** @private */
  this.id_ = 1;
  /** @private */
  //this.blockTypeMap_ = new goog.structs.Map();
  /**
  this.blockTypeMap_.set('if','controls_if');
  this.blockTypeMap_.set('comparison','logic_compare');
  this.blockTypeMap_.set('repeat','controls_repeat_ext');
  this.blockTypeMap_.set('number','math_number');
  this.blockTypeMap_.set('arithmetic','math_arithmetic');
  this.blockTypeMap_.set('text','text');
  this.blockTypeMap_.set('print','text_print');
  this.blockTypeMap_.set('set','variables_set');
  this.blockTypeMap_.set('variable','variables_get');
  */
  // temporary
  var inputReceived = false;
  var inputText;
  var rawFile;
  try {
    console.log('a');
    rawFile = new XMLHttpRequest();
    rawFile.open("GET", 'https://aravart.github.io/speech-blocks/grammar/blockTypeMap.txt', true);
    rawFile.onreadystatechange = function () {
      console.log(rawFile.readyState);
      if(rawFile.readyState == 4) {
        console.log('b')
        if(rawFile.status == 200 || rawFile.status == 0) {
          console.log('c');
          if (!inputReceived) {
            console.log('INPUT BEING SET');
            inputText = rawFile.responseText;
            inputReceived = true;
            console.log('INPUT SET');
            console.log(inputText);
            inputText = inputText.split(/\r\n|\r|\n/g);
            this.blockTypeMap_ = new goog.structs.Map();
            for (var i = 0; i < inputText.length; i++) {
              var keyValuePair = inputText[i].split(":");
              console.log(keyValuePair[0]);
              console.log(keyValuePair[1]);
              if (keyValuePair[0] != null && keyValuePair[1] != null) {
                this.blockTypeMap_.set(keyValuePair[0], keyValuePair[1]);
              }
            }
          }
          else {console.log('input already set');}
        }
      }
      else {console.log('--b');}
      if (rawFile.status == 200 || rawFile.status == 0) {rawFile.send();}
    }
  } catch(err) { console.log(err.stack) }

  try {
    setTimeout(function() {rawFile.onreadystatechange();},2000);
  }
  catch(err) {console.log(err.stack) }
  if (this.blockTypeMap_ != null) {
    console.log(this.blockTypeMap_.isEmpty());
    console.log(this.blockTypeMap_.getValues())
    console.log(this.blockTypeMap_.get('if'));
  }
  else {console.log("BLOCKTYPEMAP IS NULL")}
}

/**
* Interprets a given command by calling the corresponding action function.
* @param {Object=} command Command object from parser.
*/
SpeechBlocks.Interpreter.prototype.interpret = function(command) {
  switch (command.action) {
    case 'run': this.run(command); break;
    case 'add': this.addBlock(command); break;
    case 'move': this.moveBlock(command); break;
    case 'modify': this.modifyBlock(command); break;
    case 'delete': this.deleteBlock(command); break;
    default: console.log('Unsupported command');
  }
};

/**
* Compiles the Blockly code into JavaScript and runs it.
* @param {Object=} command Object with command specifics from the parser.
*/
SpeechBlocks.Interpreter.prototype.run = function(command) {
  Blockly.JavaScript.addReservedWords('code');
  var code = Blockly.JavaScript.workspaceToCode(this.controller_.workspace);
  eval(code);
};

/**
* Adds a specified block.
* @param {Object=} command Command object from parser.
*/
SpeechBlocks.Interpreter.prototype.addBlock = function(command) {
  var type = this.blockTypeMap_.get(command.type);
  if (type != null) { this.controller_.addBlock(type, (this.id_++).toString(), new SpeechBlocks.Translation(0,0)); }
};

/**
* @TODO Implement error checking
* Moves a specified block.
* @param {Object=} command Command object from parser.
*/
SpeechBlocks.Interpreter.prototype.moveBlock = function(command) {
  // ensure these fields are strings, so validity checking works properly
  command.block = command.block.toString();
  command.where.block = command.where.block.toString();
  if (this.isBlockIdValid(command.block)) {
    if (command.where == 'trash') {
      this.deleteBlock(command.block);
    } else {
      if (this.isBlockIdValid(command.where.block)) {
        switch (command.where.position) {
          // modify these so the 'where' field is right
          case 'after': this.controller_.moveBlock(command.block, new SpeechBlocks.Successor(command.where.block)); break;
          case 'before': this.controller_.moveBlock(command.block, new SpeechBlocks.Predecessor(command.where.block)); break;
          case 'lhs': case 'rhs': case 'to the right of':
          var inputList_ = this.controller_.getBlockValueInputs(command.where.block);
          if (inputList_.length < 1) { console.log('NO VALUE INPUTS FOR SPECIFIED BLOCK'); }
          else if (command.where.position == 'rhs' || command.where.position == 'to the right of') { this.controller_.moveBlock(command.block, new SpeechBlocks.ValueInput(command.where.block, inputList_[inputList_.length-1]));	}
          else { this.controller_.moveBlock(command.block, new SpeechBlocks.ValueInput(command.where.block, inputList_[0])); }
          case 'inside':
          var statementList_ = this.controller_.getBlockStatementInputs(command.where.block);
          if (statementList_.length < 1) { console.log('NO STATEMENT INPUTS FOR SPECIFIED BLOCK'); }
          else { this.controller_.moveBlock(command.block, new SpeechBlocks.StatementInput(command.where.block, statementList_[statementList_.length-1])); }
          break;
          default:
          console.log('Unsupported position');
        }
      }
    }
  }
  else {
    console.log('Invalid block Id');
  }
};

/**
* @TODO Implement each case of property
* Modifies a specified block.
* @param {Object=} command Command object from parser.
*/
SpeechBlocks.Interpreter.prototype.modifyBlock = function(command) {
  if (this.isBlockIdValid(command.block)) {
    switch(command.property) {
      case 'number':
      command.value = Number(command.value);
      this.controller.setBlockField(command.block, fieldName, command.value);
      break;
      case 'text':

      this.controller.setBlockField(command.block, fieldName, command.value);
      break;
      case 'comparison':

      this.controller.setBlockField(command.block, fieldName, command.value);
      break;
      case 'operation':

      this.controller.setBlockField(command.block, fieldName, command.value);
      break;
      case 'name':

      this.controller.setBlockField(command.block, fieldName, command.value);
      break;
      default:
      console.log('Unsupported property');
    }
    console.log('MODIFY NOT YET SUPPORTED');
  }
  else {
    console.log('Invalid block Id');
  }
};

/**
* Delete a specified block.
* @param {Object=} command Command object from parser.
*/
SpeechBlocks.Interpreter.prototype.deleteBlock = function(blockId) {
  if (this.isBlockIdValid(blockId)) { this.controller_.removeBlock(blockId); }
  else { console.log('Invalid block Id'); }
};

/**
* Checks to see if a block Id is valid.
* @param {!String=} blockId as string.
*/
SpeechBlocks.Interpreter.prototype.isBlockIdValid = function(blockRequestId) {
  return this.controller_.getAllBlockIds().contains(blockRequestId);
}
<commit_msg>File IO testing<commit_after>/**
* @fileoverview Interprets a given command and calls workspace controller_.functions.
* @author pandori@wisc.edu (Sahib Pandori)
* @author david.liang@wisc.edu (David Liang)
*/

'use strict';

goog.provide('SpeechBlocks.Interpreter');

goog.require('SpeechBlocks.Predecessor');
goog.require('SpeechBlocks.StatementInput')
goog.require('SpeechBlocks.Successor');
goog.require('SpeechBlocks.Translation');
goog.require('SpeechBlocks.ValueInput');
goog.require('goog.structs.Map');

/**
* Constructs an interpreter that takes actions as input and controls the Blockly Workspace.
* @param {!SpeechBlocks.Controller} controller_.The workspace controller_.
*/
SpeechBlocks.Interpreter = function(controller) {
  /** @private @const */
  this.controller_ = controller;
  /** @private */
  this.id_ = 1;
  /** @private */
  //this.blockTypeMap_ = new goog.structs.Map();
  /**
  this.blockTypeMap_.set('if','controls_if');
  this.blockTypeMap_.set('comparison','logic_compare');
  this.blockTypeMap_.set('repeat','controls_repeat_ext');
  this.blockTypeMap_.set('number','math_number');
  this.blockTypeMap_.set('arithmetic','math_arithmetic');
  this.blockTypeMap_.set('text','text');
  this.blockTypeMap_.set('print','text_print');
  this.blockTypeMap_.set('set','variables_set');
  this.blockTypeMap_.set('variable','variables_get');
  */
  // temporary
  var inputReceived = false;
  var inputText;
  var rawFile;
  try {
    console.log('a');
    rawFile = new XMLHttpRequest();
    rawFile.open("GET", 'https://aravart.github.io/speech-blocks/grammar/blockTypeMap.txt', true);
    rawFile.onreadystatechange = function () {
      console.log(rawFile.readyState);
      if(rawFile.readyState == 4) {
        console.log('b')
        if(rawFile.status == 200 || rawFile.status == 0) {
          console.log('c');
          if (!inputReceived) {
            console.log('INPUT BEING SET');
            inputText = rawFile.responseText;
            inputReceived = true;
            console.log('INPUT SET');
            console.log(inputText);
            inputText = inputText.split(/\r\n|\r|\n/g);
            this.blockTypeMap_ = new goog.structs.Map();
            for (var i = 0; i < inputText.length; i++) {
              var keyValuePair = inputText[i].split(":");
              console.log(keyValuePair[0]);
              console.log(keyValuePair[1]);
              if (keyValuePair[0] != null && keyValuePair[1] != null) {
                this.blockTypeMap_.set(keyValuePair[0], keyValuePair[1]);
              }
            }
          }
          else {console.log('input already set');}
        }
      }
      else {console.log('--b');}
      if (rawFile.status == 200 || rawFile.status == 0) {rawFile.send();}
    }
  } catch(err) { console.log(err.stack) }

  try {
    setTimeout(function() {rawFile.onreadystatechange();},2000);
  }
  catch(err) {console.log(err.stack) }
  setTimeout(function() {
    if (this.blockTypeMap_ != null) {
      console.log(this.blockTypeMap_.isEmpty());
      console.log(this.blockTypeMap_.getValues())
      console.log(this.blockTypeMap_.get('if'));
    }
    else {console.log("BLOCKTYPEMAP IS NULL")}
  },3000);
}

/**
* Interprets a given command by calling the corresponding action function.
* @param {Object=} command Command object from parser.
*/
SpeechBlocks.Interpreter.prototype.interpret = function(command) {
  switch (command.action) {
    case 'run': this.run(command); break;
    case 'add': this.addBlock(command); break;
    case 'move': this.moveBlock(command); break;
    case 'modify': this.modifyBlock(command); break;
    case 'delete': this.deleteBlock(command); break;
    default: console.log('Unsupported command');
  }
};

/**
* Compiles the Blockly code into JavaScript and runs it.
* @param {Object=} command Object with command specifics from the parser.
*/
SpeechBlocks.Interpreter.prototype.run = function(command) {
  Blockly.JavaScript.addReservedWords('code');
  var code = Blockly.JavaScript.workspaceToCode(this.controller_.workspace);
  eval(code);
};

/**
* Adds a specified block.
* @param {Object=} command Command object from parser.
*/
SpeechBlocks.Interpreter.prototype.addBlock = function(command) {
  var type = this.blockTypeMap_.get(command.type);
  if (type != null) { this.controller_.addBlock(type, (this.id_++).toString(), new SpeechBlocks.Translation(0,0)); }
};

/**
* @TODO Implement error checking
* Moves a specified block.
* @param {Object=} command Command object from parser.
*/
SpeechBlocks.Interpreter.prototype.moveBlock = function(command) {
  // ensure these fields are strings, so validity checking works properly
  command.block = command.block.toString();
  command.where.block = command.where.block.toString();
  if (this.isBlockIdValid(command.block)) {
    if (command.where == 'trash') {
      this.deleteBlock(command.block);
    } else {
      if (this.isBlockIdValid(command.where.block)) {
        switch (command.where.position) {
          // modify these so the 'where' field is right
          case 'after': this.controller_.moveBlock(command.block, new SpeechBlocks.Successor(command.where.block)); break;
          case 'before': this.controller_.moveBlock(command.block, new SpeechBlocks.Predecessor(command.where.block)); break;
          case 'lhs': case 'rhs': case 'to the right of':
          var inputList_ = this.controller_.getBlockValueInputs(command.where.block);
          if (inputList_.length < 1) { console.log('NO VALUE INPUTS FOR SPECIFIED BLOCK'); }
          else if (command.where.position == 'rhs' || command.where.position == 'to the right of') { this.controller_.moveBlock(command.block, new SpeechBlocks.ValueInput(command.where.block, inputList_[inputList_.length-1]));	}
          else { this.controller_.moveBlock(command.block, new SpeechBlocks.ValueInput(command.where.block, inputList_[0])); }
          case 'inside':
          var statementList_ = this.controller_.getBlockStatementInputs(command.where.block);
          if (statementList_.length < 1) { console.log('NO STATEMENT INPUTS FOR SPECIFIED BLOCK'); }
          else { this.controller_.moveBlock(command.block, new SpeechBlocks.StatementInput(command.where.block, statementList_[statementList_.length-1])); }
          break;
          default:
          console.log('Unsupported position');
        }
      }
    }
  }
  else {
    console.log('Invalid block Id');
  }
};

/**
* @TODO Implement each case of property
* Modifies a specified block.
* @param {Object=} command Command object from parser.
*/
SpeechBlocks.Interpreter.prototype.modifyBlock = function(command) {
  if (this.isBlockIdValid(command.block)) {
    switch(command.property) {
      case 'number':
      command.value = Number(command.value);
      this.controller.setBlockField(command.block, fieldName, command.value);
      break;
      case 'text':

      this.controller.setBlockField(command.block, fieldName, command.value);
      break;
      case 'comparison':

      this.controller.setBlockField(command.block, fieldName, command.value);
      break;
      case 'operation':

      this.controller.setBlockField(command.block, fieldName, command.value);
      break;
      case 'name':

      this.controller.setBlockField(command.block, fieldName, command.value);
      break;
      default:
      console.log('Unsupported property');
    }
    console.log('MODIFY NOT YET SUPPORTED');
  }
  else {
    console.log('Invalid block Id');
  }
};

/**
* Delete a specified block.
* @param {Object=} command Command object from parser.
*/
SpeechBlocks.Interpreter.prototype.deleteBlock = function(blockId) {
  if (this.isBlockIdValid(blockId)) { this.controller_.removeBlock(blockId); }
  else { console.log('Invalid block Id'); }
};

/**
* Checks to see if a block Id is valid.
* @param {!String=} blockId as string.
*/
SpeechBlocks.Interpreter.prototype.isBlockIdValid = function(blockRequestId) {
  return this.controller_.getAllBlockIds().contains(blockRequestId);
}
<|endoftext|>


<commit_before>const jdb = require('node-json-db');
const db = new jdb("otc", true, true);
var orderid=0;
var sort_by = function(field, reverse, primer){

   var key = primer ?
       function(x) {return primer(x[field])} :
       function(x) {return x[field]};

   reverse = !reverse ? 1 : -1;

   return function (a, b) {
       return a = key(a), b = key(b), reverse * ((a > b) - (b > a));
     }
}

module.exports = {
	wts:function(message, command) {
		var btc = command[1];
		var qrl = command[2];

		// Check if there's characters...
		if(!btc || !qrl) {
			message.channel.send("otc wts [btc/qrl] [qrl]")
			return;
		}
	    if(!/^[0-9\.]+$/.test(btc) || !/^[0-9\.]+$/.test(qrl)) {
	        message.channel.send("That's not a number I understand");
	        return;
	    }
		btc = parseFloat(btc);
		qrl = parseFloat(qrl);

		if(btc>1 || btc <= 0.000000001) {
			message.channel.send("BTC price per QRL is either too high or too low. Range 0.00000001 - 1");
			return;
		}
		if(qrl<=1) {
			message.channel.send("Can't sell less than 1 QRL at a time");
			return;
		}
		var total = btc*qrl;

		// Check if WTB is lower than WTS
		try {
			var wtb = db.getData("/wtb");
			var wtb_sorted = wtb.sort(sort_by('btc',false, parseFloat));

			if(wtb_sorted[0]['btc'] > btc - 0.00000001) {
				message.channel.send("Sorry, the WTS order must be higher than the *highest* WTB order at this time");
				return;
			}
		} catch(error) {

		};

		message.channel.send("Market WTS order at "+btc+" for "+qrl+" QRL for a total of ("+total+" BTC)");

		// Get orderid
		db.push('/orderid[]',{});
		orderid=db.getData("/orderid").length;

		db.push("/wts[]", {
			"orderid":orderid,
			"btc":btc,
			"qrl":qrl,
			"username":message.author.username,
			"userid":message.author.id
		}, true);

		this.market(message);
	},
	wtb:function(message, command) {
		var btc = command[1];
		var qrl = command[2];

		if(!btc || !qrl) {
			message.channel.send("otc wtb [btc/qrl] [qrl]")
			return;
		}
	    if(!/^[0-9\.]+$/.test(btc) || !/^[0-9\.]+$/.test(qrl)) {
	        message.channel.send("That's not a number I understand");
	        return;
	    }

		btc = parseFloat(btc);
		qrl = parseFloat(qrl);

		// check if there's characters...
		if(btc>1 || btc <= 0.000000001) {
			message.channel.send("BTC price per QRL is either too high or too low. Range 0.00000001 - 1");
			return;
		}
		if(qrl<=1) {
			message.channel.send("Can't buy less than 1 QRL at a time");
			return;
		}
		var total = btc*qrl;

		// Check if WTB is lower than WTS
		try {
			var wts = db.getData("/wts");
			var wts_sorted = wts.sort(sort_by('btc',false, parseFloat));

			if(wts_sorted[0]['btc'] < btc + 0.00000001) {
				message.channel.send("Sorry, the WTB order must be lower than the *lowest* WTS order at this time");
				return;
			}
		} catch(error) {

		};

		message.channel.send("Market WTB order at "+btc+" for "+qrl+" QRL for a total of ("+total+" BTC)");

			// Get orderid
		db.push('/orderid[]',{});
		orderid=db.getData("/orderid").length;

		db.push("/wtb[]", {
			"orderid":orderid,
			"btc":btc,
			"qrl":qrl,
			"username":message.author.username,
			"userid":message.author.id
		}, true);

		this.market(message);
	},
	clear:function(message, command) {

		try {
			var wtb = db.getData("/wtb");
			var wtb_arr = [];

			wtb.forEach(function(arr) {
				var orderid = parseInt(arr['orderid']);

				if(command[1]==orderid || command[1]==arr['btc']) {
					if(message.author.id==arr['userid']) {
						return;
					} else {
						message.channel.send("Please sir, deleting another person's order isn't nice.");
					}
				}

				wtb_arr.push(arr);
			});
			db.push("/wtb", wtb_arr);
		} catch(error) {

		}

		try {
			var wts = db.getData("/wts");
			var wts_arr = [];

			wts.forEach(function(arr) {
				var orderid = parseInt(arr['orderid']);

				if(command[1]==orderid || command[1]==arr['btc']) {
					if(message.author.id==arr['userid']) {
						return;
					} else {
						message.channel.send("Please sir, deleting another person's order isn't nice.");
					}
				}

				wts_arr.push(arr);
			});
			db.push("/wts", wts_arr);
		} catch(error) {

		}

		this.market(message,command);

	},
	clearall:function(message,command) {
		if(message.author.id == '356943957588049920') {
			db.delete("/");
			message.channel.send("Market deleted");
		} else {
			message.channel.send("Market not deleted");
		}

	},
	market:function(message, command) {
		var output = "";
		var total = 0;

		try {
			var wts = db.getData("/wts");
			var wts_sorted = wts.sort(sort_by('btc',true, parseFloat));

			output += "Market WTS```";
			wts_sorted.forEach(function(arr) {
				qrl = parseFloat(arr['qrl']);
				btc = parseFloat(arr['btc']);
				order = String(arr['orderid']);
				total= btc * qrl;
				output += order.padStart(3," ")+" ";
				output += btc.toFixed(8)+' btc/qrl';
				output += String(arr.qrl).padStart(6," ")+" qrl ";
				output += String(total.toFixed(8)).padStart(12," ")+" BTC "+arr.username+"\n";
			});
			output += "```";
		} catch(error) {

		}

		try {
			var wtb = db.getData("/wtb");
			var wtb_sorted = wtb.sort(sort_by('btc',true, parseFloat));
			console.log(wtb);
			console.log(wtb_sorted);

			output += "Market WTB```";
			wtb_sorted.forEach(function(arr) {
				qrl = parseFloat(arr.qrl);
				btc = parseFloat(arr.btc);
				order = String(arr.orderid);
				total= btc * qrl;
				output += order.padStart(3," ")+" ";
				output += btc.toFixed(8)+' btc/qrl';
				output += String(arr.qrl).padStart(6," ")+" qrl ";
				output += String(total.toFixed(8)).padStart(12," ")+" BTC "+arr.username+"\n";
			});
			output += "```";
		} catch(error) {

		}

		if(output=="") {
			output = "No markets available";
		}
		message.channel.send(output);
	},
	otc:async function(message, subcommand, config) {
		var	command = subcommand.toLowerCase().split(' ');

		switch(command[0]) {
			case 'wts':
				this.wts(message, command);
			break;
			case 'wtb':
				this.wtb(message, command);
			break;
			case 'market':
				this.market(message, command);
			break;
			case 'clear':
				this.clear(message, command);
			break;
			case 'clearall':
				this.clearall(message, command);
			break;
			default:
				message.channel.send("otc [wts/wtb] [btc] [quantity]");
		}

	}
}<commit_msg>Prevent empty markets<commit_after>const jdb = require('node-json-db');
const db = new jdb("otc", true, true);
var orderid=0;
var sort_by = function(field, reverse, primer){

   var key = primer ?
       function(x) {return primer(x[field])} :
       function(x) {return x[field]};

   reverse = !reverse ? 1 : -1;

   return function (a, b) {
       return a = key(a), b = key(b), reverse * ((a > b) - (b > a));
     }
}

module.exports = {
	wts:function(message, command) {
		var btc = command[1];
		var qrl = command[2];

		// Check if there's characters...
		if(!btc || !qrl) {
			message.channel.send("otc wts [btc/qrl] [qrl]")
			return;
		}
	    if(!/^[0-9\.]+$/.test(btc) || !/^[0-9\.]+$/.test(qrl)) {
	        message.channel.send("That's not a number I understand");
	        return;
	    }
		btc = parseFloat(btc);
		qrl = parseFloat(qrl);

		if(btc>1 || btc <= 0.000000001) {
			message.channel.send("BTC price per QRL is either too high or too low. Range 0.00000001 - 1");
			return;
		}
		if(qrl<=1) {
			message.channel.send("Can't sell less than 1 QRL at a time");
			return;
		}
		var total = btc*qrl;

		// Check if WTB is lower than WTS
		try {
			var wtb = db.getData("/wtb");
			var wtb_sorted = wtb.sort(sort_by('btc',false, parseFloat));

			if(wtb_sorted[0]['btc'] > btc - 0.00000001) {
				message.channel.send("Sorry, the WTS order must be higher than the *highest* WTB order at this time");
				return;
			}
		} catch(error) {

		};

		message.channel.send("Market WTS order at "+btc+" for "+qrl+" QRL for a total of ("+total+" BTC)");

		// Get orderid
		db.push('/orderid[]',{});
		orderid=db.getData("/orderid").length;

		db.push("/wts[]", {
			"orderid":orderid,
			"btc":btc,
			"qrl":qrl,
			"username":message.author.username,
			"userid":message.author.id
		}, true);

		this.market(message);
	},
	wtb:function(message, command) {
		var btc = command[1];
		var qrl = command[2];

		if(!btc || !qrl) {
			message.channel.send("otc wtb [btc/qrl] [qrl]")
			return;
		}
	    if(!/^[0-9\.]+$/.test(btc) || !/^[0-9\.]+$/.test(qrl)) {
	        message.channel.send("That's not a number I understand");
	        return;
	    }

		btc = parseFloat(btc);
		qrl = parseFloat(qrl);

		// check if there's characters...
		if(btc>1 || btc <= 0.000000001) {
			message.channel.send("BTC price per QRL is either too high or too low. Range 0.00000001 - 1");
			return;
		}
		if(qrl<=1) {
			message.channel.send("Can't buy less than 1 QRL at a time");
			return;
		}
		var total = btc*qrl;

		// Check if WTB is lower than WTS
		try {
			var wts = db.getData("/wts");
			var wts_sorted = wts.sort(sort_by('btc',false, parseFloat));

			if(wts_sorted[0]['btc'] < btc + 0.00000001) {
				message.channel.send("Sorry, the WTB order must be lower than the *lowest* WTS order at this time");
				return;
			}
		} catch(error) {

		};

		message.channel.send("Market WTB order at "+btc+" for "+qrl+" QRL for a total of ("+total+" BTC)");

			// Get orderid
		db.push('/orderid[]',{});
		orderid=db.getData("/orderid").length;

		db.push("/wtb[]", {
			"orderid":orderid,
			"btc":btc,
			"qrl":qrl,
			"username":message.author.username,
			"userid":message.author.id
		}, true);

		this.market(message);
	},
	clear:function(message, command) {

		try {
			var wtb = db.getData("/wtb");
			var wtb_arr = [];

			wtb.forEach(function(arr) {
				var orderid = parseInt(arr['orderid']);

				if(command[1]==orderid || command[1]==arr['btc']) {
					if(message.author.id==arr['userid']) {
						return;
					} else {
						message.channel.send("Please sir, deleting another person's order isn't nice.");
					}
				}

				wtb_arr.push(arr);
			});
			db.push("/wtb", wtb_arr);
		} catch(error) {

		}

		try {
			var wts = db.getData("/wts");
			var wts_arr = [];

			wts.forEach(function(arr) {
				var orderid = parseInt(arr['orderid']);

				if(command[1]==orderid || command[1]==arr['btc']) {
					if(message.author.id==arr['userid']) {
						return;
					} else {
						message.channel.send("Please sir, deleting another person's order isn't nice.");
					}
				}

				wts_arr.push(arr);
			});
			db.push("/wts", wts_arr);
		} catch(error) {

		}

		this.market(message,command);

	},
	clearall:function(message,command) {
		if(message.author.id == '356943957588049920') {
			db.delete("/");
			message.channel.send("Market deleted");
		} else {
			message.channel.send("Market not deleted");
		}

	},
	market:function(message, command) {
		var output = "";
		var total = 0;

		try {
			var wts = db.getData("/wts");
			var wts_sorted = wts.sort(sort_by('btc',true, parseFloat));

			if(wts.length>0) {

				output += "Market WTS (want to sell)```";
				wts_sorted.forEach(function(arr) {
					qrl = parseFloat(arr['qrl']);
					btc = parseFloat(arr['btc']);
					order = String(arr['orderid']);
					total= btc * qrl;
					output += order.padStart(3," ")+" ";
					output += btc.toFixed(8)+' btc/qrl';
					output += String(arr.qrl).padStart(6," ")+" qrl ";
					output += String(total.toFixed(8)).padStart(12," ")+" BTC "+arr.username+"\n";
				});
				output += "```";
			}
		} catch(error) {

		}

		try {
			var wtb = db.getData("/wtb");
			var wtb_sorted = wtb.sort(sort_by('btc',true, parseFloat));

			if(wtb.length>0) {
				output += "Market WTB (want to buy)```";
				wtb_sorted.forEach(function(arr) {
					qrl = parseFloat(arr.qrl);
					btc = parseFloat(arr.btc);
					order = String(arr.orderid);
					total= btc * qrl;
					output += order.padStart(3," ")+" ";
					output += btc.toFixed(8)+' btc/qrl';
					output += String(arr.qrl).padStart(6," ")+" qrl ";
					output += String(total.toFixed(8)).padStart(12," ")+" BTC "+arr.username+"\n";
				});
				output += "```";
			}
		} catch(error) {

		}

		if(output=="") {
			output = "No markets available";
		}
		message.channel.send(output);
	},
	otc:async function(message, subcommand, config) {
		var	command = subcommand.toLowerCase().split(' ');

		switch(command[0]) {
			case 'wts':
				this.wts(message, command);
			break;
			case 'wtb':
				this.wtb(message, command);
			break;
			case 'market':
				this.market(message, command);
			break;
			case 'clear':
				this.clear(message, command);
			break;
			case 'clearall':
				this.clearall(message, command);
			break;
			default:
				message.channel.send("otc [wts/wtb] [btc] [quantity]");
		}

	}
}<|endoftext|>