<commit_before>function getProcessElt(elt)
{
      if (elt != null)
      {
          if (elt.hasAttribute("topic-process"))
          {
              return elt;
          }
      }
      return getProcessElt(elt.parentElement);
}

function doSubscribeToTopic(event) {
    console.log("doSubscribeToTopic:" + event);

    var target = event.target;
    var topicProcessElt = getProcessElt(target);
    var topicProcess = topicProcessElt.getAttribute("topic-process");

    var topicActionsElt = target.parentElement;
    var topicNameElt = topicActionsElt.querySelector(".topicName");
    var topicName = topicNameElt.value;

    var topicItemTemplate = document.getElementById("SubscriptionItem-template");
    var topicItemElt = topicItemTemplate.cloneNode(true);
    topicItemElt.id = "";

//    var subscriptionsListElt = topicProcessElt.querySelector(".subscriptionsList > tbody");
//    var topicItemElt = subscriptionsListElt.rows[1].cloneNode(true);
    topicItemElt.classList.add("subscription-" + topicName);

    var topicNameElt = topicItemElt.querySelector(".topicName");
    topicNameElt.textContent = topicName;

    var SubscriptionsListElt = topicProcessElt.querySelector(".subscriptionsList");
    SubscriptionsListElt.appendChild(topicItemElt);

//    subscriptionsListElt.appendChild(topicItemElt);
    topicItemElt.style.display = "block";

    if (topicProcess == "renderer")
    {
        ipcBus.subscribe(topicName, onIPC_renderer);
    }
    if (topicProcess == "master")
    {
        ipcBus.send("ipc-tests/master-subscribe-topic", topicName);
//        ipcRenderer.send("ipc-tests/ipc-master-subscribe", topicName);
    }
    if (topicProcess == "node")
    {
        ipcBus.send("ipc-tests/node-subscribe-topic", topicName);
//        ipcRenderer.send("ipc-tests/ipc-master-subscribe", topicName);
    }
    console.log(topicProcess + " topicName : " + topicName + " - subscribe");
}

function doSendMessageToTopic(event){
    console.log("doSendMessageToTopic:" + event);

    var target = event.target;
    var topicProcessElt = getProcessElt(target);
    var topicProcess = topicProcessElt.getAttribute("topic-process");

    var topicItemElt = target.parentElement;
    var topicNameElt = topicItemElt.querySelector(".topicName");
    var topicName = topicNameElt.value;

    var topicMsgElt = topicItemElt.querySelector(".topicMsg");
    var topicMsg = topicMsgElt.value;

    if (topicProcess == "renderer")
    {
        ipcBus.send(topicName, topicMsg);
    }
    if (topicProcess == "master")
    {
        ipcBus.send("ipc-tests/master-send-topic", { topic : topicName, msg : topicMsg});
//        ipcRenderer.send("ipc-tests/ipc-master-send", { "topic" : topicName, "msg" : target.value} );
    }
    if (topicProcess == "node")
    {
        ipcBus.send("ipc-tests/node-send-topic", { topic : topicName, msg : topicMsg});
//        ipcRenderer.send("ipc-tests/ipc-master-send", { "topic" : topicName, "msg" : target.value} );
    }
    console.log("topicName : " + topicName + " - send:" + topicMsg);
}

function doUnsubscribeFromTopic(event){
    console.log("doUnsubscribeFromTopic:" + event);

    var target = event.target;
    var topicProcessElt = getProcessElt(target);
    var topicProcess = topicProcessElt.getAttribute("topic-process");

    var topicItemElt = target.parentElement;
    var topicNameElt = topicItemElt.querySelector(".topicName");
    var topicName = topicNameElt.value;

    var SubscriptionsListElt = document.querySelector("fieldset." + topicProcess + " > div");
    SubscriptionsListElt.removeChild(topicItemElt);

    if (processTarget == "renderer")
    {
        ipcBus.unsubscribe(topicName, onIPC_renderer);
    }
    if (processTarget == "master")
    {
        ipcBus.send("ipc-tests/master-unsubscribe-topic", topicName);
//        ipcRenderer.send("ipc-tests/ipc-master-unsubscribe", topicName);
    }
    if (processTarget == "node")
    {
        ipcBus.send("ipc-tests/node-unsubscribe-topic", topicName);
//        ipcRenderer.send("ipc-tests/ipc-master-unsubscribe", topicName);
    }
    console.log(processTarget + " topicName : " + topicName + " - unsubscribe");
}

function doClearTopic(event)
{
    var target = event.target;
    var topicItemElt = target.parentElement;
    var topicReceivedElt = topicItemElt.querySelector(".topicReceived");
    topicReceivedElt.value = "";
}

function onIPC_received(topicProcess, topicName, msgContent)
{
    console.log(topicProcess + " msgTopic:" + topicName + " msgContent:" + msgContent)

    var SubscriptionsListElt = document.querySelector("fieldset." + topicProcess + " > div");
    var topicItemElt = SubscriptionsListElt.querySelector(".subscription-" + topicName);
    if (topicItemElt != null)
    {
        var topicReceivedElt = topicItemElt.querySelector(".topicReceived");
        topicReceivedElt.value += msgContent + "\n";
    }
}

function onIPC_renderer(msgTopic, msgContent) {
    onIPC_received("renderer", msgTopic, msgContent);
}

function onIPC_master(msgTopic, args) {
    onIPC_received("master", args["topic"], args["msg"]);
}

function onIPC_node(msgTopic, args) {
    onIPC_received("node", args["topic"], args["msg"]);
}

function doQueryBrokerState() {
    ipcBus.queryBrokerState();
}

function onIPC_BrokerStatusTopic(msgTopic, msgContent) {
    console.log("queryBrokerState - msgTopic:" + msgTopic + " msgContent:" + msgContent)

    var brokerStatesListElt = document.getElementById("brokerStatesList");
    // Keep the header
    while (brokerStatesListElt.rows.length > 1) {
        brokerStatesListElt.deleteRow(1);
    }
    for(var i = 0; i < msgContent.length; ++i)
    {
        var row = brokerStatesListElt.insertRow(-1);
        var cell = row.insertCell(0);
        cell.innerHTML = msgContent[i]["topic"];

        var cell = row.insertCell(1);
        cell.innerHTML = msgContent[i]["connCount"];

        var cell = row.insertCell(2);
        cell.innerHTML = msgContent[i]["subCount"];
    }
}

//var ipcRenderer = require('electron').ipcRenderer;

ipcBus.subscribe('IPC_BUS_BROKER_STATUS_TOPIC', onIPC_BrokerStatusTopic);

ipcBus.subscribe("ipc-tests/master-received-topic", onIPC_master);
ipcBus.subscribe("ipc-tests/node-received-topic", onIPC_node);

window.onload=function()
{
    var processesList =
    [
        {class:"master",    title:"Master"},
        {class:"renderer",  title:"Renderer"},
        {class:"node",      title:"Node"}
    ];

    var processesListElt = document.getElementById("ProcessesList");
    var processTemplateElt = document.getElementById("ProcessItem-template");

    for (var i = 0; i < processesList.length; ++i)
    {
        var processItemElt = processTemplateElt.cloneNode(true);
        processItemElt.id = "";
        processItemElt.setAttribute("topic-process", processesList[i].class);

        var processLegendElt = processItemElt.querySelector("h3");
        processLegendElt.textContent = processesList[i].title;

        processesListElt.appendChild(processItemElt);
        processItemElt.style.display = "block";
    }
}<commit_msg>rebase<commit_after>function getProcessElt(elt)
{
      if (elt != null)
      {
          if (elt.hasAttribute("topic-process"))
          {
              return elt;
          }
      }
      return getProcessElt(elt.parentElement);
}

function doSubscribeToTopic(event) {
    console.log("doSubscribeToTopic:" + event);

    var target = event.target;
    var topicProcessElt = getProcessElt(target);
    var topicProcess = topicProcessElt.getAttribute("topic-process");

    var topicActionsElt = target.parentElement;
    var topicNameElt = topicActionsElt.querySelector(".topicName");
    var topicName = topicNameElt.value;

    var topicItemTemplate = document.getElementById("SubscriptionItem-template");
    var topicItemElt = topicItemTemplate.cloneNode(true);
    topicItemElt.id = "";

//    var subscriptionsListElt = topicProcessElt.querySelector(".subscriptionsList > tbody");
//    var topicItemElt = subscriptionsListElt.rows[1].cloneNode(true);
    topicItemElt.classList.add("subscription-" + topicName);

    var topicNameElt = topicItemElt.querySelector(".topicName");
    topicNameElt.textContent = topicName;

    var SubscriptionsListElt = topicProcessElt.querySelector(".subscriptionsList");
    SubscriptionsListElt.appendChild(topicItemElt);

//    subscriptionsListElt.appendChild(topicItemElt);
    topicItemElt.style.display = "block";

    if (topicProcess == "renderer")
    {
        ipcBus.subscribe(topicName, onIPC_renderer);
    }
    if (topicProcess == "master")
    {
        ipcBus.send("ipc-tests/master-subscribe-topic", topicName);
//        ipcRenderer.send("ipc-tests/ipc-master-subscribe", topicName);
    }
    if (topicProcess == "node")
    {
        ipcBus.send("ipc-tests/node-subscribe-topic", topicName);
//        ipcRenderer.send("ipc-tests/ipc-master-subscribe", topicName);
    }
    console.log(topicProcess + " topicName : " + topicName + " - subscribe");
}

function doSendMessageToTopic(event){
    console.log("doSendMessageToTopic:" + event);

    var target = event.target;
    var topicProcessElt = getProcessElt(target);
    var topicProcess = topicProcessElt.getAttribute("topic-process");

    var topicItemElt = target.parentElement;
    var topicNameElt = topicItemElt.querySelector(".topicName");
    var topicName = topicNameElt.value;

    var topicMsgElt = topicItemElt.querySelector(".topicMsg");
    var topicMsg = topicMsgElt.value;

    if (topicProcess == "renderer")
    {
        ipcBus.send(topicName, topicMsg);
    }
    if (topicProcess == "master")
    {
        ipcBus.send("ipc-tests/master-send-topic", { topic : topicName, msg : topicMsg});
//        ipcRenderer.send("ipc-tests/ipc-master-send", { "topic" : topicName, "msg" : target.value} );
    }
    if (topicProcess == "node")
    {
        ipcBus.send("ipc-tests/node-send-topic", { topic : topicName, msg : topicMsg});
//        ipcRenderer.send("ipc-tests/ipc-master-send", { "topic" : topicName, "msg" : target.value} );
    }
    console.log("topicName : " + topicName + " - send:" + topicMsg);
}

function doUnsubscribeFromTopic(event){
    console.log("doUnsubscribeFromTopic:" + event);

    var target = event.target;
    var topicProcessElt = getProcessElt(target);
    var topicProcess = topicProcessElt.getAttribute("topic-process");

    var topicItemElt = target.parentElement;
    var topicNameElt = topicItemElt.querySelector(".topicName");
    var topicName = topicNameElt.value;

    var SubscriptionsListElt = document.querySelector("fieldset." + topicProcess + " > div");
    SubscriptionsListElt.removeChild(topicItemElt);

    if (processTarget == "renderer")
    {
        ipcBus.unsubscribe(topicName, onIPC_renderer);
    }
    if (processTarget == "master")
    {
        ipcBus.send("ipc-tests/master-unsubscribe-topic", topicName);
//        ipcRenderer.send("ipc-tests/ipc-master-unsubscribe", topicName);
    }
    if (processTarget == "node")
    {
        ipcBus.send("ipc-tests/node-unsubscribe-topic", topicName);
//        ipcRenderer.send("ipc-tests/ipc-master-unsubscribe", topicName);
    }
    console.log(processTarget + " topicName : " + topicName + " - unsubscribe");
}

function doClearTopic(event)
{
    var target = event.target;
    var topicItemElt = target.parentElement;
    var topicReceivedElt = topicItemElt.querySelector(".topicReceived");
    topicReceivedElt.value = "";
}

function onIPC_received(topicProcess, topicName, msgContent)
{
    console.log(topicProcess + " msgTopic:" + topicName + " msgContent:" + msgContent)

    var SubscriptionsListElt = document.querySelector("div[topic-process='" + topicProcess + "'] > div");
    var topicItemElt = SubscriptionsListElt.querySelector(".subscription-" + topicName);
    if (topicItemElt != null)
    {
        var topicReceivedElt = topicItemElt.querySelector(".topicReceived");
        topicReceivedElt.value += msgContent + "\n";
    }
}

function onIPC_renderer(msgTopic, msgContent) {
    onIPC_received("renderer", msgTopic, msgContent);
}

function onIPC_master(msgTopic, args) {
    onIPC_received("master", args["topic"], args["msg"]);
}

function onIPC_node(msgTopic, args) {
    onIPC_received("node", args["topic"], args["msg"]);
}

function doQueryBrokerState() {
    ipcBus.queryBrokerState();
}

function onIPC_BrokerStatusTopic(msgTopic, msgContent) {
    console.log("queryBrokerState - msgTopic:" + msgTopic + " msgContent:" + msgContent)

    var brokerStatesListElt = document.getElementById("brokerStatesList");
    // Keep the header
    while (brokerStatesListElt.rows.length > 1) {
        brokerStatesListElt.deleteRow(1);
    }
    for(var i = 0; i < msgContent.length; ++i)
    {
        var row = brokerStatesListElt.insertRow(-1);
        var cell = row.insertCell(0);
        cell.innerHTML = msgContent[i]["topic"];

        var cell = row.insertCell(1);
        cell.innerHTML = msgContent[i]["connCount"];

        var cell = row.insertCell(2);
        cell.innerHTML = msgContent[i]["subCount"];
    }
}

//var ipcRenderer = require('electron').ipcRenderer;

ipcBus.subscribe('IPC_BUS_BROKER_STATUS_TOPIC', onIPC_BrokerStatusTopic);

ipcBus.subscribe("ipc-tests/master-received-topic", onIPC_master);
ipcBus.subscribe("ipc-tests/node-received-topic", onIPC_node);

window.onload=function()
{
    var processesList =
    [
        {class:"master",    title:"Master"},
        {class:"renderer",  title:"Renderer"},
        {class:"node",      title:"Node"}
    ];

    var processesListElt = document.getElementById("ProcessesList");
    var processTemplateElt = document.getElementById("ProcessItem-template");

    for (var i = 0; i < processesList.length; ++i)
    {
        var processItemElt = processTemplateElt.cloneNode(true);
        processItemElt.id = "";
        processItemElt.setAttribute("topic-process", processesList[i].class);

        var processLegendElt = processItemElt.querySelector("h3");
        processLegendElt.textContent = processesList[i].title;

        processesListElt.appendChild(processItemElt);
        processItemElt.style.display = "block";
    }
}<|endoftext|>


<commit_before>/*
 * typeahead.js
 * https://github.com/twitter/typeahead.js
 * Copyright 2013 Twitter, Inc. and other contributors; Licensed MIT
 */

var Dropdown = (function() {

  // constructor
  // -----------

  function Dropdown(o) {
    var that = this, onSuggestionClick, onSuggestionMouseEnter,
        onSuggestionMouseLeave;

    o = o || {};

    if (!o.menu) {
      $.error('menu is required');
    }

    this.isOpen = false;
    this.isEmpty = true;

    this.datasets = _.map(o.datasets, initializeDataset);

    // bound functions
    onSuggestionClick = _.bind(this._onSuggestionClick, this);
    onSuggestionMouseEnter = _.bind(this._onSuggestionMouseEnter, this);
    onSuggestionMouseLeave = _.bind(this._onSuggestionMouseLeave, this);

    this.$menu = $(o.menu)
    .on('click.tt', '.tt-suggestion', onSuggestionClick)
    .on('mouseenter.tt', '.tt-suggestion', onSuggestionMouseEnter)
    .on('mouseleave.tt', '.tt-suggestion', onSuggestionMouseLeave);

    _.each(this.datasets, function(dataset) {
      that.$menu.append(dataset.getRoot());
      dataset.onSync('rendered', that._onRendered, that);
    });
  }

  // instance methods
  // ----------------

  _.mixin(Dropdown.prototype, EventEmitter, {

    // ### private

    _onSuggestionClick: function onSuggestionClick($e) {
      this.trigger('suggestionClicked', $($e.currentTarget));
    },

    _onSuggestionMouseEnter: function onSuggestionMouseEnter($e) {
      this._removeCursor();
      this._setCursor($($e.currentTarget), true);
    },

    _onSuggestionMouseLeave: function onSuggestionMouseLeave($e) {
      this._removeCursor();
    },

    _onRendered: function onRendered() {
      this.isEmpty = _.every(this.datasets, isDatasetEmpty);

      this.isEmpty ? this._hide() : (this.isOpen && this._show());

      this.trigger('datasetRendered');

      function isDatasetEmpty(dataset) { return dataset.isEmpty(); }
    },

    _hide: function() {
      this.$menu.hide();
    },

    _show: function() {
      // can't use jQuery#show because $menu is a span element we want
      // display: block; not dislay: inline;
      this.$menu.css('display', 'block');
    },

    _getSuggestions: function getSuggestions() {
      return this.$menu.find('.tt-suggestion');
    },

    _getCursor: function getCursor() {
      return this.$menu.find('.tt-cursor').first();
    },

    _setCursor: function setCursor($el, silent) {
      $el.first().addClass('tt-cursor');

      !silent && this.trigger('cursorMoved');
    },

    _removeCursor: function removeCursor() {
      this._getCursor().removeClass('tt-cursor');
    },

    _moveCursor: function moveCursor(increment) {
      var $suggestions, $oldCursor, newCursorIndex, $newCursor;

      if (!this.isOpen) { return; }

      $oldCursor = this._getCursor();
      $suggestions = this._getSuggestions();

      this._removeCursor();

      // shifting before and after modulo to deal with -1 index
      newCursorIndex = $suggestions.index($oldCursor) + increment;
      newCursorIndex = (newCursorIndex + 1) % ($suggestions.length + 1) - 1;

      if (newCursorIndex === -1) {
        this.trigger('cursorRemoved');

        return;
      }

      else if (newCursorIndex < -1) {
        newCursorIndex = $suggestions.length - 1;
      }

      this._setCursor($newCursor = $suggestions.eq(newCursorIndex));

      // in the case of scrollable overflow
      // make sure the cursor is visible in the menu
      this._ensureVisible($newCursor);
    },

    _ensureVisible: function ensureVisible($el) {
      var elTop, elBottom, menuScrollTop, menuHeight;

      elTop = $el.position().top;
      elBottom = elTop + $el.outerHeight(true);
      menuScrollTop = this.$menu.scrollTop();
      menuHeight = this.$menu.height() +
        parseInt(this.$menu.css('paddingTop'), 10) +
        parseInt(this.$menu.css('paddingBottom'), 10);

      if (elTop < 0) {
        this.$menu.scrollTop(menuScrollTop + elTop);
      }

      else if (menuHeight < elBottom) {
        this.$menu.scrollTop(menuScrollTop + (elBottom - menuHeight));
      }
    },

    // ### public

    close: function close() {
      if (this.isOpen) {
        this.isOpen = false;

        this._removeCursor();
        this._hide();

        this.trigger('closed');
      }
    },

    open: function open() {
      if (!this.isOpen) {
        this.isOpen = true;

        !this.isEmpty && this._show();

        this.trigger('opened');
      }
    },

    setLanguageDirection: function setLanguageDirection(dir) {
      this.$menu.css(dir === 'ltr' ? css.ltr : css.rtl);
    },

    moveCursorUp: function moveCursorUp() {
      this._moveCursor(-1);
    },

    moveCursorDown: function moveCursorDown() {
      this._moveCursor(+1);
    },

    getDatumForSuggestion: function getDatumForSuggestion($el) {
      var datum = null;

      if ($el.length) {
        datum = {
          raw: Dataset.extractDatum($el),
          value: Dataset.extractValue($el),
          datasetName: Dataset.extractDatasetName($el)
        };
      }

      return datum;
    },

    getDatumForCursor: function getDatumForCursor() {
      return this.getDatumForSuggestion(this._getCursor().first());
    },

    getDatumForTopSuggestion: function getDatumForTopSuggestion() {
      return this.getDatumForSuggestion(this._getSuggestions().first());
    },

    update: function update(query) {
      _.each(this.datasets, updateDataset);

      function updateDataset(dataset) { dataset.update(query); }
    },

    empty: function empty() {
      _.each(this.datasets, clearDataset);

      function clearDataset(dataset) { dataset.clear(); }
    },

    isVisible: function isVisible() {
      return this.isOpen && !this.isEmpty;
    },

    destroy: function destroy() {
      this.$menu.off('.tt');

      this.$menu = null;

      _.each(this.datasets, destroyDataset);

      function destroyDataset(dataset) { dataset.destroy(); }
    }
  });

  return Dropdown;

  // helper functions
  // ----------------

  function initializeDataset(oDataset) {
    return new Dataset(oDataset);
  }
})();
<commit_msg>Set `this.isEmpty = true` in `Dropdown.empty()`<commit_after>/*
 * typeahead.js
 * https://github.com/twitter/typeahead.js
 * Copyright 2013 Twitter, Inc. and other contributors; Licensed MIT
 */

var Dropdown = (function() {

  // constructor
  // -----------

  function Dropdown(o) {
    var that = this, onSuggestionClick, onSuggestionMouseEnter,
        onSuggestionMouseLeave;

    o = o || {};

    if (!o.menu) {
      $.error('menu is required');
    }

    this.isOpen = false;
    this.isEmpty = true;

    this.datasets = _.map(o.datasets, initializeDataset);

    // bound functions
    onSuggestionClick = _.bind(this._onSuggestionClick, this);
    onSuggestionMouseEnter = _.bind(this._onSuggestionMouseEnter, this);
    onSuggestionMouseLeave = _.bind(this._onSuggestionMouseLeave, this);

    this.$menu = $(o.menu)
    .on('click.tt', '.tt-suggestion', onSuggestionClick)
    .on('mouseenter.tt', '.tt-suggestion', onSuggestionMouseEnter)
    .on('mouseleave.tt', '.tt-suggestion', onSuggestionMouseLeave);

    _.each(this.datasets, function(dataset) {
      that.$menu.append(dataset.getRoot());
      dataset.onSync('rendered', that._onRendered, that);
    });
  }

  // instance methods
  // ----------------

  _.mixin(Dropdown.prototype, EventEmitter, {

    // ### private

    _onSuggestionClick: function onSuggestionClick($e) {
      this.trigger('suggestionClicked', $($e.currentTarget));
    },

    _onSuggestionMouseEnter: function onSuggestionMouseEnter($e) {
      this._removeCursor();
      this._setCursor($($e.currentTarget), true);
    },

    _onSuggestionMouseLeave: function onSuggestionMouseLeave($e) {
      this._removeCursor();
    },

    _onRendered: function onRendered() {
      this.isEmpty = _.every(this.datasets, isDatasetEmpty);

      this.isEmpty ? this._hide() : (this.isOpen && this._show());

      this.trigger('datasetRendered');

      function isDatasetEmpty(dataset) { return dataset.isEmpty(); }
    },

    _hide: function() {
      this.$menu.hide();
    },

    _show: function() {
      // can't use jQuery#show because $menu is a span element we want
      // display: block; not dislay: inline;
      this.$menu.css('display', 'block');
    },

    _getSuggestions: function getSuggestions() {
      return this.$menu.find('.tt-suggestion');
    },

    _getCursor: function getCursor() {
      return this.$menu.find('.tt-cursor').first();
    },

    _setCursor: function setCursor($el, silent) {
      $el.first().addClass('tt-cursor');

      !silent && this.trigger('cursorMoved');
    },

    _removeCursor: function removeCursor() {
      this._getCursor().removeClass('tt-cursor');
    },

    _moveCursor: function moveCursor(increment) {
      var $suggestions, $oldCursor, newCursorIndex, $newCursor;

      if (!this.isOpen) { return; }

      $oldCursor = this._getCursor();
      $suggestions = this._getSuggestions();

      this._removeCursor();

      // shifting before and after modulo to deal with -1 index
      newCursorIndex = $suggestions.index($oldCursor) + increment;
      newCursorIndex = (newCursorIndex + 1) % ($suggestions.length + 1) - 1;

      if (newCursorIndex === -1) {
        this.trigger('cursorRemoved');

        return;
      }

      else if (newCursorIndex < -1) {
        newCursorIndex = $suggestions.length - 1;
      }

      this._setCursor($newCursor = $suggestions.eq(newCursorIndex));

      // in the case of scrollable overflow
      // make sure the cursor is visible in the menu
      this._ensureVisible($newCursor);
    },

    _ensureVisible: function ensureVisible($el) {
      var elTop, elBottom, menuScrollTop, menuHeight;

      elTop = $el.position().top;
      elBottom = elTop + $el.outerHeight(true);
      menuScrollTop = this.$menu.scrollTop();
      menuHeight = this.$menu.height() +
        parseInt(this.$menu.css('paddingTop'), 10) +
        parseInt(this.$menu.css('paddingBottom'), 10);

      if (elTop < 0) {
        this.$menu.scrollTop(menuScrollTop + elTop);
      }

      else if (menuHeight < elBottom) {
        this.$menu.scrollTop(menuScrollTop + (elBottom - menuHeight));
      }
    },

    // ### public

    close: function close() {
      if (this.isOpen) {
        this.isOpen = false;

        this._removeCursor();
        this._hide();

        this.trigger('closed');
      }
    },

    open: function open() {
      if (!this.isOpen) {
        this.isOpen = true;

        !this.isEmpty && this._show();

        this.trigger('opened');
      }
    },

    setLanguageDirection: function setLanguageDirection(dir) {
      this.$menu.css(dir === 'ltr' ? css.ltr : css.rtl);
    },

    moveCursorUp: function moveCursorUp() {
      this._moveCursor(-1);
    },

    moveCursorDown: function moveCursorDown() {
      this._moveCursor(+1);
    },

    getDatumForSuggestion: function getDatumForSuggestion($el) {
      var datum = null;

      if ($el.length) {
        datum = {
          raw: Dataset.extractDatum($el),
          value: Dataset.extractValue($el),
          datasetName: Dataset.extractDatasetName($el)
        };
      }

      return datum;
    },

    getDatumForCursor: function getDatumForCursor() {
      return this.getDatumForSuggestion(this._getCursor().first());
    },

    getDatumForTopSuggestion: function getDatumForTopSuggestion() {
      return this.getDatumForSuggestion(this._getSuggestions().first());
    },

    update: function update(query) {
      _.each(this.datasets, updateDataset);

      function updateDataset(dataset) { dataset.update(query); }
    },

    empty: function empty() {
      _.each(this.datasets, clearDataset);
      this.isEmpty = true;

      function clearDataset(dataset) { dataset.clear(); }
    },

    isVisible: function isVisible() {
      return this.isOpen && !this.isEmpty;
    },

    destroy: function destroy() {
      this.$menu.off('.tt');

      this.$menu = null;

      _.each(this.datasets, destroyDataset);

      function destroyDataset(dataset) { dataset.destroy(); }
    }
  });

  return Dropdown;

  // helper functions
  // ----------------

  function initializeDataset(oDataset) {
    return new Dataset(oDataset);
  }
})();
<|endoftext|>


<commit_before>var _actions = {};

var action = function(description, callback) {
    if (callback === undefined) {
        //TODO: error logging like applebot
        _actions[description]();
    }
    else {
        _actions[description] = callback;
    }
};

var step = function(description, waitFunction, callback) {
    waitFunction();
    callback();
};

var runScript = function(client, appSettings) {
    action("Do something", function() {
        client.click("#")
    });

    step("Wait for something", function() {
        client.waitFor("div[data-stickyscrolling-placeholder]", TIMEOUT);
    }, function() {
        // when done
        action("Do something");
    });
}

var ScriptRunner = {
    run: function(namespace, action, userOptions) {
        console.log("running " + namespace + ":" + action + " with " + JSON.stringify(userOptions));

        startSelenium(function(client) {

            var selenium = require('selenium-standalone');
            var seleniumServer = selenium({stdio: 'pipe'});

            seleniumServer.stdout.on('data', function(output) {
                var val = output.toString().trim();
                console.log(val);
                if(val.indexOf('jetty.jetty.Server')>-1){
                    client = client.init();
                    start();
                }
            });

            seleniumServer.stderr.on('data', function (data) {
              console.log('stderr: ' + data);
            });

            seleniumServer.on('close', function (code) {
              console.log('child process exited with code ' + code);
            });

            function start() {
                runScript(client, userOptions);
            }

        });
    },
    startSelenium: function(callback) {

        var webdriverjs = require('webdriverjs');
        var driverOptions = {
            desiredCapabilities: {
                browserName: 'chrome'
            }
        };
        var client = webdriverjs.remote(driverOptions);

        callback(client);
    }
};

exports = module.exports = ScriptRunner;
<commit_msg>uploading app <commit_after>var _actions = {};

var action = function(description, callback) {
    if (callback === undefined) {
        //TODO: error logging like applebot
        _actions[description]();
    }
    else {
        _actions[description] = callback;
    }
};

var step = function(description, waitFunction, callback) {
    waitFunction();
    callback();
};

var runScript = function(client, appSettings) {

    action("Login", function(){
        client.url('https://play.google.com/apps/publish');
        client.setValue('#Email', appSettings.email, function(err, res) {

        });
        client.setValue('#Passwd', appSettings.password, function(err, res) {

        });
        client.submitForm('form#gaia_loginform', function(err, res) {

        });
    });

    action("Click on Add new app", function() {
        client.execute(function() {
            var spans = document.getElementsByTagName('span');
            correct_span = null;
            for(var i = 0; i < spans.length; i++) {
                var span = spans[i];
                if (span.innerHTML.trim() === "Add new application") {
                    correct_span = span;
                }
            }
        var buttonId = correct_span.parentElement.parentElement.id;
        document.querySelector("button#" + buttonId).click();
        }, [], function(err, res) {
            var value = res.value;
        });
    });

    step("Wait for first page", function() {
        client.waitFor('table tr a', TIMEOUT, function(err, res) {

        });
    }, function() {
            action("Click on Add new app");
    });

    action("Fill in and submit initial app information", function() {
        client.setValue(".popupContent .gwt-TextBox", appSettings.name, function(err, res) {

        });
        client.execute(function() {
            var divs = document.getElementsByTagName('div');
            var correct_div = null;
            for (var i = 0; i <divs.length; i++){
                var div = divs[i];
                if (div.innerHTML.trim() === "Upload APK"){
                    correct_div = div;
                }
            }
            var buttonId = correct_div.parentElement.id;
            document.querySelector("button#" + buttonId).click();
        }, [], function(err, res) {
            var value = res.value;
        });
    });

    step("Wait for app dialogue", function() {
        client.waitFor(".popupContent fieldset", TIMEOUT, function(err, res) {

        });
    }, function() {
        action("Fill in and submit initial app information");
    });

    action("Go to APK upload", function() {
        client.execute(function() {
            var divs = document.getElementsByTagName('div');
            matching_divs = [];

            for (var i = 0; i <divs.length; i++){
                var div = divs[i];
                if (div.innerHTML.trim() === "Upload your first APK to Production"){
                    matching_divs[matching_divs.length] = div;
                }
            }

            var correct_div = null;
            for (var i = 0; i <matching_divs.length ; i++){
                var div = matching_divs[i];
                if (div.parentElement.parentElement.children.length===3){
                    if (div.parentElement.parentElement.tagName === "DIV"){
                        correct_div = div;
                    }
                }
            }

            var buttonId = correct_div.parentElement.id;
            document.querySelector("button#" + buttonId).click();
        }, [], function(err, res) {
            var value = res.value;
        });

        client.execute(function(specialId) {
            var interval = window.setInterval(function() {
                var divs = document.getElementsByTagName('div');
                var correct_div = null;
                for (var i = 0; i <divs.length; i++){
                    var div = divs[i];
                    if (div.innerHTML.trim() === "Browse files"){
                        console.log("HIT");
                        correct_div = div;
                    }
                }

                var anyHit = false;
                if (correct_div) {
                    var nodes = correct_div.parentElement.parentElement.children;
                    for (var i =0; i <nodes.length; i++){
                        var node = nodes[i];
                        if (node.tagName==="INPUT"){
                            console.log("hit");
                            node.id= specialId;
                            node.addEventListener("change", function(e) {
                                console.log("NODE CHANGE");
                            });
                            anyHit = true;
                        }
                    }
                }

                if (anyHit) {
                    correct_div.innerHTML = "shit";
                    correct_div.id = "correct_div";
                    window.clearInterval(interval);
                }
            }, 1000);

        }, [specialId], function(err, res) {

        });
    });

    step("Wait for APK page", function() {
        client.waitFor("div[data-stickyscrolling-placeholder]", TIMEOUT, function(err, res) {

        });
    }, function() {
        action("Go to APK upload");
    });

    var upload_id = 'apk_uploading_id';

    action("Upload APK", function() {
        client.chooseFile("#" + specialId, appSettings.apk_path, function(err, res) {

        });

        client.execute(function(upload_id){
            var p = document.getElementsByTagName('p');
            for (i in p) {
                if (p[i].innerText.trim()==="Supported devices") {
                    p[i].parentElement.id = upload_id;
                    break;
                }
            }

        }, [upload_id]);
    });

    step("Wait for APK box", function() {
        client.waitFor("#correct_div", TIMEOUT, function(err, res) {

        });
    }, function() {
        action("Upload APK");
    });

    action("Go to Store Listing page", function() {
        client.execute(function(){
            var links =  document.getElementsByTagName('a');
            for (i in links){
                var link = links[i];
                if (link.innerText === "Store Listing"){
                    link.click();
                    break;
                }
            }
        });
    });

    step("Wait for APK upload", function() {
        client.waitForVisible('#' + upload_id, TIMEOUT * 10, function(err, res){

        });
    }, function() {
        action("Go to Store Listing page");
    });

    action("Fill in Store Listing information", function() {
        function searchForChild(parentTag, attribute, match, nested, childTag, childID, client){
            client.execute(function(parentTag, attribute, match, nested, childTag, childID) {
                var elements = document.querySelectorAll(parentTag);
                var correct_element = null;
                for(var i = 0; i < elements.length; i++) {
                    var element = elements[i];
                    if (element[attribute] === null) {}
                    else if (element[attribute].trim() === match) {
                        correct_element = element;
                    }
                }
                var i = 0;
                while (i < nested){
                    correct_element = correct_element.parentElement;
                    i++;
                }

                var child = correct_element.querySelector(childTag);
                child.id = childID;

            }, [parentTag, attribute, match, nested, childTag, childID]);

            return childID;
        }

        client.execute(function(){
            var textAreas = document.getElementsByTagName('textArea');
            textAreas[0].id = "textArea0Id";
            textAreas[1].id = "textArea1Id";

            var selectAreas = document.querySelectorAll('select');
            selectAreas[0].id = "selectArea0Id";
            selectAreas[1].id = "selectArea1Id";
            selectAreas[2].id = "selectArea2Id";
        });

        client.setValue("textarea#textArea0Id", appSettings.subtext, function(err, res){

        });

        client.setValue("textarea#textArea1Id", appSettings.promo, function(err,res){

        });

        client.click('#selectArea0Id option[value = ' + appSettings.category + ']');
        client.click('#selectArea1Id option[value = ' + appSettings.subcategory + ']');
        client.click('#selectArea2Id option[value = ' + appSettings.rating + ']');

        var promo_video_id = searchForChild('p', 'innerText', 'Promo Video', 2, 'input', 'promo_vid_child_id', client);
        var website_id = searchForChild('div', 'innerText', 'Website', 1, 'input', 'website_text_id', client);
        var email_id = searchForChild('div', 'innerText', 'Email', 1, 'input', 'email_text_id', client);
        var phone_id = searchForChild('div', 'innerText', 'Phone', 1, 'input', 'phone_text_id', client);
        var privacy_id = searchForChild('div', 'innerText' ,'Privacy Policy', 1, 'input', 'privacy_policy_id', client);

        client.setValue("#"+promo_video_id, appSettings.promo_vid, function(err, res){});
        client.setValue("#"+website_id, appSettings.website, function(err, res){});
        client.setValue("#"+email_id, appSettings.public_email, function(err, res){});
        client.setValue("#"+phone_id, appSettings.phone, function(err, res){});

        if (appSettings.privacy === null || appSettings.privacy === ""){
            client.execute(function(privacy_id){
                var elem = document.getElementById(privacy_id);
                var checkbox = elem.parentElement.querySelector('input[type="checkbox"]');
                checkbox.click();
            }, [privacy_id]);
        }
        else{
            client.setValue("#"+privacy_id, appSettings.privacy, function(err, res){});
        }
    });

    //not sure how to do this part
    action("Upload screenshots and graphics", function() {

    });

    action("Click save button", function() {
        this.execute(function(){
            var divs = document.querySelectorAll('div');
            correct_div = null;
            for (var i = 0; i <divs.length; i++){
                var div = divs[i];

                if (div.innerHTML.trim() === "Save"){
                    correct_div = div;
                }
            }
            correct_div.parentElement.click()
            correct_div.id = "documentSaved";

        });

        this.waitFor("#documentSaved", TIMEOUT, function(err,res){

        });

        this.execute(function(){
            var spans = document.querySelectorAll('span');

            for (var i in spans){
                var span = spans[i];

                if (span.innerText === "Your application has been saved."){
                    span.id = "documentCompletelySaved";
                }
            }
        });
    });

    step("Wait for Store Listing page", function() {
        client.waitFor('select', TIMEOUT, function(err, res){

            });
    }, function() {
        action("Fill in Store Listing information");
        action("Upload screenshots and graphics");
        action("Click save button");
    });

    action("Go to Pricing & Distribution page", function() {
        client.execute(function(){
            var links =  document.getElementsByTagName('a');
            for (i in links){
                var link = links[i];
                if (link.innerText === "Pricing & Distribution"){
                    link.click();
                    break;
                }
            }
        });
    });

    step("Wait for completely saved document", function() {
        this.waitFor("#documentCompletelySaved", TIMEOUT, function(err,res) {
        });
    }, function() {
        action("Go to Pricing & Distribution page");
    });

    action("Fill in Pricing & Distribution information - locations", function() {
        client.execute(function(listOfCountries){
            var toReturn = []

            var labels = document.querySelector('h4').parentElement.getElementsByTagName('label');
            for (var i in labels) {
                var label = labels[i];

                if (listOfCountries.indexOf(label.innerText) !== -1 ){
                    var input = label.querySelector('input');
                    if (input.id=== ""){
                        input.id = "languageToClickID" + i;
                    }
                    toReturn[toReturn.length] = input.id;
                }
            }

            for (language in toReturn){
                var elem = document.getElementById(toReturn[language]);
                elem.click();
            }
        }, [appSettings.locations]);
    });

    action("Fill in Pricing & Distribution information - education", function() {

        if (appSettings.education){
            client.execute(function(){
                var field = document.getElementsByTagName('fieldset')[1];
                field.querySelector('input').click();
            });

            client.waitFor('div.popupContent', TIMEOUT, function(err,res){

            });

            client.execute(function(ads, purchases){
                var divs = document.getElementsByTagName('div');
                for (i in divs){
                    var div = divs[i];
                    if (div.innerText === 'Continue'){
                        correct_div.parentElement.click();
                    }
                }

                var popUp = document.querySelector('div.popupContent');

                var inputs = popUp.getElementsByTagName('input');

                if (ads){
                    inputs[0].click();
                }
                else{
                    inputs[1].click();
                }
                if (purchases){
                    inputs[2].click();
                }
                else{
                    inputs[3].click();
                }

                var buttons = popUp.getElementsByTagName('button');
                for (i in buttons){
                    var button = buttons[i];
                    if (button.innerText.trim() === 'Save'){
                        button.click();
                        break;
                    }
                }

            }, [appSettings.education_ads, appSettings.education_purchases]);
        }
    });

    action("Fill in Pricing & Distribution information - opt in", function() {
        client.execute(function(optInValues){
            toReturn = [];
            var field = document.querySelectorAll('fieldset')[2];
            for (i in field.children){
                var child = field.children[i];
                if (child.tagName === "LABEL"){
                    if (optInValues[i]){
                        var input = child.querySelector('input');
                        if (input.id === ""){
                            input.id = "optInButtonToClickID" + child;
                        }
                        toReturn[toReturn.length] = input.id;
                    }
                }
            }

            for (opt in toReturn){
                var elem = document.getElementById(toReturn[opt]);
                elem.click();
            }

        }, [ [appSettings.marketing_opt_out, appSettings.content_guidelines, appSettings.us_export_laws] ]);
    });

    step("Wait for Pricing & Distribution page", function() {
        client.waitFor('colgroup', TIMEOUT, function(err, res){

        });
    }, function() {
        action("Fill in Pricing & Distribution information - locations");
        action("Fill in Pricing & Distribution information - education");
        action("Fill in Pricing & Distribution information - opt in");
        action("Click save button");
    });

    step("Wait for completely saved document", function() {
        this.waitFor("#documentCompletelySaved", TIMEOUT, function(err,res) {

        });
    }, function() {

    });

}

var ScriptRunner = {
    run: function(namespace, action, userOptions) {
        console.log("running " + namespace + ":" + action + " with " + JSON.stringify(userOptions));

        startSelenium(function(client) {

            var selenium = require('selenium-standalone');
            var seleniumServer = selenium({stdio: 'pipe'});

            seleniumServer.stdout.on('data', function(output) {
                var val = output.toString().trim();
                console.log(val);
                if(val.indexOf('jetty.jetty.Server')>-1){
                    client = client.init();
                    start();
                }
            });

            seleniumServer.stderr.on('data', function (data) {
              console.log('stderr: ' + data);
            });

            seleniumServer.on('close', function (code) {
              console.log('child process exited with code ' + code);
            });

            function start() {
                runScript(client, userOptions);
            }

        });
    },
    startSelenium: function(callback) {

        var webdriverjs = require('webdriverjs');
        var driverOptions = {
            desiredCapabilities: {
                browserName: 'chrome'
            }
        };
        var client = webdriverjs.remote(driverOptions);

        callback(client);
    }
};

exports = module.exports = ScriptRunner;
<|endoftext|>


<commit_before>module.exports = function(grunt) {
    require('load-grunt-tasks')(grunt);
    require('phplint').gruntPlugin(grunt);

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json')

        // Helper variables
        , dirs: {
            bower: 'bower_components'
            , css: 'assets/css'
            , js: 'assets/js'
            , images: 'assets/images'
            , fonts: 'assets/fonts'
            , svg: 'assets/svg'
        }

        /*
         * SASS
         * - Compiles all *.scss files into one style.css
         * - In dev mode there is no minifying
         */

        , sass: {
            dev: {
                options: {
                    outputStyle: 'nested'
                    , sourceMap: true
                }
                , files: {
                    '<%= dirs.css %>/style.css': '<%= dirs.css %>/style.scss'
                }
            }
            , build: {
                options: {
                    outputStyle: 'compressed'
                    , sourceMap: false
                }
                , files: {
                    '<%= dirs.css %>/style.css': '<%= dirs.css %>/style.scss'
                }
            }
        }

        /*
         * Autoprefixer
         * - Makes sure you use the proper vendor-prefixed CSS properties
         */

        , autoprefixer: {
            options: {
                browsers: [ 'last 2 versions' ]
                , map: true
            }
            , dist: {
                files: {
                    '<%= dirs.css %>/style.css': '<%= dirs.css %>/style.css'
                }
            }
        }

        /*
         * Connect
         * - Inbuilt web server
         */

        , connect: {
            server: {
                options: {
                    port: 9001
                    , base: ''
                }
            }
        }

        /*
         * Concat task
         * - Merges all the devided JavaScript into one big one for production use
         * - Ignores "modernizr.js", since it has to be seperate
         */

        , concat: {
            options: {
                separator: ';'
            }
            , dist: {
                src: [
                    '<%= dirs.bower %>/moment/moment.js'
                    , '<%= dirs.bower %>/ExplorerCanvas/excanvas.js'
                    , '<%= dirs.bower %>/jquery/dist/jquery.js'
                    , '<%= dirs.bower %>/bootstrap-sass-official/assets/javascripts/bootstrap.js'
                    , '<%= dirs.bower %>/jquery.validate/dist/jquery.validate.js'
                    , '<%= dirs.bower %>/CanvasLoader/js/heartcode-canvasloader.js'
                    , '<%= dirs.bower %>/jquery.countdown/dist/jquery.countdown.js'
                    , '<%= dirs.bower %>/jquery.tablesorter/js/jquery.tablesorter.js'
                    , '<%= dirs.bower %>/jquery.tablesorter/js/jquery.tablesorter.widgets.js'
                    , '<%= dirs.bower %>/jquery.tablesorter/js/jquery.tablesorter.widgets-dilter-formatter.js'
                    , '<%= dirs.bower %>/jquery.tablesorter/addons/pager/jquery.tablesorter.pager.js'
                    , '<%= dirs.bower %>/jQuery-Visualize/js/visualize.jQuery.js'
                    , '<%= dirs.bower %>/jquery-mousewheel/jquery.mousewheel.js'
                    , '<%= dirs.bower %>/eonasdan-bootstrap-datetimepicker/src/js/bootstrap-datetimepicker.js'
                    , '<%= dirs.js %>/*.js'
                    , '!<%= dirs.js %>/modernizr.js'
                    , '!<%= dirs.js %>/_admin.js'
                    , '!<%= dirs.js %>/build.js'
                    , '!<%= dirs.js %>/build.admin.js'
                ]
                , dest: '<%= dirs.js %>/build.js'
            }
            , admin: {
                src: [
                    '<%= dirs.js %>/build.js'
                    , '<%= dirs.js %>/_admin.js'
                ]
                , dest: '<%= dirs.js %>/build.admin.js'
            }
        }

        /*
         * JShint
         * - Validate JavaScript
         */

        , jshint: {
            options: {
                jshintrc: true
            }
            , all: [
                'Gruntfile.js'
                , '<%= dirs.js %>/*.js'
                , '!<%= dirs.js %>/modernizr.js'
                , '!<%= dirs.js %>/build.js'
                , '!<%= dirs.js %>/build.admin.js'
            ]
        }

        /*
         * HTMLhint
         * - Validate HTML
         */

        , htmlhint: {
            html: {
                options: {
                    'tag-pair': true
                }
                , src: [ '*.html' ]
            }
        }

        /*
         * Uglify
         * - Minify JavaScript
         */

        , uglify: {
            core: {
                files: [
                  {
                      '<%= dirs.js %>/build.js': [ '<%= dirs.js %>/build.js' ]
                      , '<%= dirs.js %>/modernizr.js': [ '<%= dirs.bower %>/modernizr/modernizr.js' ]
                  }
                ]
            },
            admin: {
                files: [
                  {
                      '<%= dirs.js %>/build.admin.js': [ '<%= dirs.js %>/build.admin.js' ]
                  }
                ]
            }
        }

        /*
         * Imagemin
         * - Minify images
         */

        , imagemin: {
            dynamic: {
                files: [ {
                    expand: true
                    , cwd: '<%= dirs.images %>'
                    , src: [ '**/*.{png,jpg,gif}' ]
                    , dest: '<%= dirs.images %>'
                } ]
            }
        }

        /*
         * Copy
         * - Copy files via Grunt from A to B
         * - Used for Bootstraps fonts which are included in the bower component folder
         */

        , copy: {
          main: {
            files: [
              { expand: true, cwd: '<%= dirs.bower %>/bootstrap-sass-official/assets/fonts/bootstrap/', src: [ '**' ], dest: '<%= dirs.fonts %>' }
              , { expand: true, cwd: '<%= dirs.bower %>/eonasdan-bootstrap-datetimepicker/build/css/', src: [ 'bootstrap-datetimepicker.css' ], dest: '<%= dirs.css %>/libs', rename: function(dest) { return dest + '/bootstrap-datetimepicker.scss'; } }
              , { expand: true, cwd: '<%= dirs.bower %>/jquery.tablesorter/css/', src: [ 'theme.bootstrap.css' ], dest: '<%= dirs.css %>/libs', rename: function(dest) { return dest + '/tablesorter-theme.bootstrap.scss'; } }
              , { expand: true, cwd: '<%= dirs.bower %>/jQuery-Visualize/css/', src: [ 'visualize.css' ], dest: '<%= dirs.css %>/libs', rename: function(dest) { return dest + '/visualize.scss'; } }
              , { expand: true, cwd: '<%= dirs.bower %>/jQuery-Visualize/css/', src: [ 'visualize-light.css' ], dest: '<%= dirs.css %>/libs', rename: function(dest) { return dest + '/visualize-light.scss'; } }
            ]
          }
        }

        /*
         * JSCS (JavaScriptCodeStyle)
         * - Check JS markup based on policies defined in .jscsrc
         */

        , jscs: {
          src: 'assets/js/_*.js'
          , options: {
            config: '.jscsrc'
          }
        }

        /*
         * phplint
         * - Check for PHP syntax errors
         */

        , phplint: {
          options: {
            stdout: true
            , stderr: true
          }
          , files: [ 'actions/*.php', 'ajaxactions/*.php' ]
        }

        /*
         * Watch
         * - Watches files for changes and recompiles if neccesary
         */

        , watch: {
            options: {
                livereload: true
            }
            , sass: {
                files: [ '<%= dirs.css %>/*.scss' ]
                , tasks: [ 'sass:dev', 'autoprefixer' ]
            }
            , images: {
                files: [ '<%= dirs.images %>/*.{png,jpg,gif}' ]
                , tasks: [ 'imagemin' ]
            }
            , html: {
                files: [ '*.html' ]
                , tasks: [ 'htmlhint' ]
            }
            , php: {
                files: [ '*.php' ]
                , tasks: [ 'phplint' ]
            }
            , scripts: {
                files: [ 'Gruntfile.js', '<%= dirs.js %>/*.js' ]
                , tasks: [ 'jshint', 'jscs', 'concat' ]
                , options: {
                    spawn: false
                }
            }
        }
    });

    grunt.registerTask('default', [ 'copy', 'sass:build', 'autoprefixer', 'concat', 'uglify', 'imagemin', 'jscs', 'phplint' ]);
    grunt.registerTask('dev', [ 'copy', 'connect', 'watch' ]);
};
<commit_msg>Lint php files while working in dev mode<commit_after>module.exports = function(grunt) {
    require('load-grunt-tasks')(grunt);
    require('phplint').gruntPlugin(grunt);

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json')

        // Helper variables
        , dirs: {
            bower: 'bower_components'
            , css: 'assets/css'
            , js: 'assets/js'
            , images: 'assets/images'
            , fonts: 'assets/fonts'
            , svg: 'assets/svg'
        }

        /*
         * SASS
         * - Compiles all *.scss files into one style.css
         * - In dev mode there is no minifying
         */

        , sass: {
            dev: {
                options: {
                    outputStyle: 'nested'
                    , sourceMap: true
                }
                , files: {
                    '<%= dirs.css %>/style.css': '<%= dirs.css %>/style.scss'
                }
            }
            , build: {
                options: {
                    outputStyle: 'compressed'
                    , sourceMap: false
                }
                , files: {
                    '<%= dirs.css %>/style.css': '<%= dirs.css %>/style.scss'
                }
            }
        }

        /*
         * Autoprefixer
         * - Makes sure you use the proper vendor-prefixed CSS properties
         */

        , autoprefixer: {
            options: {
                browsers: [ 'last 2 versions' ]
                , map: true
            }
            , dist: {
                files: {
                    '<%= dirs.css %>/style.css': '<%= dirs.css %>/style.css'
                }
            }
        }

        /*
         * Connect
         * - Inbuilt web server
         */

        , connect: {
            server: {
                options: {
                    port: 9001
                    , base: ''
                }
            }
        }

        /*
         * Concat task
         * - Merges all the devided JavaScript into one big one for production use
         * - Ignores "modernizr.js", since it has to be seperate
         */

        , concat: {
            options: {
                separator: ';'
            }
            , dist: {
                src: [
                    '<%= dirs.bower %>/moment/moment.js'
                    , '<%= dirs.bower %>/ExplorerCanvas/excanvas.js'
                    , '<%= dirs.bower %>/jquery/dist/jquery.js'
                    , '<%= dirs.bower %>/bootstrap-sass-official/assets/javascripts/bootstrap.js'
                    , '<%= dirs.bower %>/jquery.validate/dist/jquery.validate.js'
                    , '<%= dirs.bower %>/CanvasLoader/js/heartcode-canvasloader.js'
                    , '<%= dirs.bower %>/jquery.countdown/dist/jquery.countdown.js'
                    , '<%= dirs.bower %>/jquery.tablesorter/js/jquery.tablesorter.js'
                    , '<%= dirs.bower %>/jquery.tablesorter/js/jquery.tablesorter.widgets.js'
                    , '<%= dirs.bower %>/jquery.tablesorter/js/jquery.tablesorter.widgets-dilter-formatter.js'
                    , '<%= dirs.bower %>/jquery.tablesorter/addons/pager/jquery.tablesorter.pager.js'
                    , '<%= dirs.bower %>/jQuery-Visualize/js/visualize.jQuery.js'
                    , '<%= dirs.bower %>/jquery-mousewheel/jquery.mousewheel.js'
                    , '<%= dirs.bower %>/eonasdan-bootstrap-datetimepicker/src/js/bootstrap-datetimepicker.js'
                    , '<%= dirs.js %>/*.js'
                    , '!<%= dirs.js %>/modernizr.js'
                    , '!<%= dirs.js %>/_admin.js'
                    , '!<%= dirs.js %>/build.js'
                    , '!<%= dirs.js %>/build.admin.js'
                ]
                , dest: '<%= dirs.js %>/build.js'
            }
            , admin: {
                src: [
                    '<%= dirs.js %>/build.js'
                    , '<%= dirs.js %>/_admin.js'
                ]
                , dest: '<%= dirs.js %>/build.admin.js'
            }
        }

        /*
         * JShint
         * - Validate JavaScript
         */

        , jshint: {
            options: {
                jshintrc: true
            }
            , all: [
                'Gruntfile.js'
                , '<%= dirs.js %>/*.js'
                , '!<%= dirs.js %>/modernizr.js'
                , '!<%= dirs.js %>/build.js'
                , '!<%= dirs.js %>/build.admin.js'
            ]
        }

        /*
         * HTMLhint
         * - Validate HTML
         */

        , htmlhint: {
            html: {
                options: {
                    'tag-pair': true
                }
                , src: [ '*.html' ]
            }
        }

        /*
         * Uglify
         * - Minify JavaScript
         */

        , uglify: {
            core: {
                files: [
                  {
                      '<%= dirs.js %>/build.js': [ '<%= dirs.js %>/build.js' ]
                      , '<%= dirs.js %>/modernizr.js': [ '<%= dirs.bower %>/modernizr/modernizr.js' ]
                  }
                ]
            },
            admin: {
                files: [
                  {
                      '<%= dirs.js %>/build.admin.js': [ '<%= dirs.js %>/build.admin.js' ]
                  }
                ]
            }
        }

        /*
         * Imagemin
         * - Minify images
         */

        , imagemin: {
            dynamic: {
                files: [ {
                    expand: true
                    , cwd: '<%= dirs.images %>'
                    , src: [ '**/*.{png,jpg,gif}' ]
                    , dest: '<%= dirs.images %>'
                } ]
            }
        }

        /*
         * Copy
         * - Copy files via Grunt from A to B
         * - Used for Bootstraps fonts which are included in the bower component folder
         */

        , copy: {
          main: {
            files: [
              { expand: true, cwd: '<%= dirs.bower %>/bootstrap-sass-official/assets/fonts/bootstrap/', src: [ '**' ], dest: '<%= dirs.fonts %>' }
              , { expand: true, cwd: '<%= dirs.bower %>/eonasdan-bootstrap-datetimepicker/build/css/', src: [ 'bootstrap-datetimepicker.css' ], dest: '<%= dirs.css %>/libs', rename: function(dest) { return dest + '/bootstrap-datetimepicker.scss'; } }
              , { expand: true, cwd: '<%= dirs.bower %>/jquery.tablesorter/css/', src: [ 'theme.bootstrap.css' ], dest: '<%= dirs.css %>/libs', rename: function(dest) { return dest + '/tablesorter-theme.bootstrap.scss'; } }
              , { expand: true, cwd: '<%= dirs.bower %>/jQuery-Visualize/css/', src: [ 'visualize.css' ], dest: '<%= dirs.css %>/libs', rename: function(dest) { return dest + '/visualize.scss'; } }
              , { expand: true, cwd: '<%= dirs.bower %>/jQuery-Visualize/css/', src: [ 'visualize-light.css' ], dest: '<%= dirs.css %>/libs', rename: function(dest) { return dest + '/visualize-light.scss'; } }
            ]
          }
        }

        /*
         * JSCS (JavaScriptCodeStyle)
         * - Check JS markup based on policies defined in .jscsrc
         */

        , jscs: {
          src: 'assets/js/_*.js'
          , options: {
            config: '.jscsrc'
          }
        }

        /*
         * phplint
         * - Check for PHP syntax errors
         */

        , phplint: {
          options: {
            stdout: true
            , stderr: true
          }
          , files: ['*.php', 'actions/*.php', 'ajaxactions/*.php' ]
        }

        /*
         * Watch
         * - Watches files for changes and recompiles if neccesary
         */

        , watch: {
            options: {
                livereload: true
            }
            , sass: {
                files: [ '<%= dirs.css %>/*.scss' ]
                , tasks: [ 'sass:dev', 'autoprefixer' ]
            }
            , images: {
                files: [ '<%= dirs.images %>/*.{png,jpg,gif}' ]
                , tasks: [ 'imagemin' ]
            }
            , html: {
                files: [ '*.html' ]
                , tasks: [ 'htmlhint' ]
            }
            , php: {
                files: [ '*.php', 'actions/*.php', 'ajaxactions/*.php' ]
                , tasks: [ 'phplint' ]
            }
            , scripts: {
                files: [ 'Gruntfile.js', '<%= dirs.js %>/*.js' ]
                , tasks: [ 'jshint', 'jscs', 'concat' ]
                , options: {
                    spawn: false
                }
            }
        }
    });

    grunt.registerTask('default', [ 'copy', 'sass:build', 'autoprefixer', 'concat', 'uglify', 'imagemin', 'jscs', 'phplint' ]);
    grunt.registerTask('dev', [ 'copy', 'connect', 'watch' ]);
};
<|endoftext|>


<commit_before>// Generated on 2015-01-29 using generator-angular 0.10.0
'use strict';

// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// use this if you want to recursively match all subfolders:
// 'test/spec/**/*.js'

module.exports = function (grunt) {

  // Load grunt tasks automatically
  require('load-grunt-tasks')(grunt);

  // Time how long tasks take. Can help when optimizing build times
  require('time-grunt')(grunt);
  grunt.loadNpmTasks('grunt-angular-architecture-graph');
  grunt.loadNpmTasks('grunt-bump');
  grunt.loadNpmTasks('grunt-jsinspect');

  grunt.loadNpmTasks('grunt-istanbul-coverage');
  // Configurable paths for the application
  var appConfig = {
    app: require('./bower.json').appPath || 'app',
    dist: 'dist'
  };

  // Define the configuration for all the tasks
  grunt.initConfig({

    // Project settings
    yeoman: appConfig,
    angular_architecture_graph: {
      diagram: {
        files: {
          '<%= yeoman.app %>/Architecture': ['<%= yeoman.app %>/scripts/{,*/}*.js']
        }
      }
    },
    coverage: {
      default: {
        options: {
          thresholds: {
            'statements': 90,
            'branches': 90,
            'lines': 90,
            'functions': 90
          },
          dir: 'json',
          root: 'coverage'
        }
      }
    },
    bump: {
      options: {
        files: ['bower.json'],
        updateConfigs: [],
        commit: true,
        commitMessage: 'Release v%VERSION%',
        commitFiles: ['-a'],
        createTag: true,
        tagName: 'v%VERSION%',
        tagMessage: 'Version %VERSION%',
        push: true,
        pushTo: 'upstream',
        gitDescribeOptions: '--tags --always --abbrev=1 --dirty=-d',
        globalReplace: false,
        prereleaseName: false,
        regExp: false
      }
    },
    jsinspect: {
      inspect: {
        options: {
          threshold:   30,
          diff:        true,
          identifiers: false,
          failOnMatch: true,
          suppress:    100,
          reporter:    'default'
        },
        src: [
          '<%= yeoman.app %>/**/*.js'
        ]
      }
    },
    // Watches files for changes and runs tasks based on the changed files
    watch: {
      bower: {
        files: ['bower.json'],
        tasks: ['wiredep']
      },
      js: {
        files: ['<%= yeoman.app %>/scripts/{,*/}*.js'],
        tasks: ['newer:jshint:all'],
        options: {
          livereload: '<%= connect.options.livereload %>'
        }
      },
      jsTest: {
        files: ['test/spec/{,*/}*.js'],
        tasks: ['newer:jshint:test', 'karma']
      },
      styles: {
        files: ['<%= yeoman.app %>/styles/{,*/}*.css'],
        tasks: ['newer:copy:styles', 'autoprefixer']
      },
      gruntfile: {
        files: ['Gruntfile.js']
      },
      livereload: {
        options: {
          livereload: '<%= connect.options.livereload %>'
        },
        files: [
          '<%= yeoman.app %>/{,*/}*.html',
          '.tmp/styles/{,*/}*.css',
          '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
        ]
      }
    },

    // The actual grunt server settings
    connect: {
      options: {
        port: 9000,
        // Change this to '0.0.0.0' to access the server from outside.
        hostname: 'localhost',
        livereload: 35729
      },
      livereload: {
        options: {
          open: true,
          middleware: function (connect) {
            return [
              connect.static('.tmp'),
              connect().use(
                '/bower_components',
                connect.static('./bower_components')
              ),
              connect.static(appConfig.app)
            ];
          }
        }
      },
      test: {
        options: {
          port: 9001,
          middleware: function (connect) {
            return [
              connect.static('.tmp'),
              connect.static('test'),
              connect().use(
                '/bower_components',
                connect.static('./bower_components')
              ),
              connect.static(appConfig.app)
            ];
          }
        }
      },
      dist: {
        options: {
          open: true,
          base: '<%= yeoman.dist %>'
        }
      }
    },

    // Make sure code styles are up to par and there are no obvious mistakes
    jshint: {
      options: {
        jshintrc: '.jshintrc',
        reporter: require('jshint-stylish')
      },
      all: {
        src: [
          'Gruntfile.js',
          '<%= yeoman.app %>/scripts/{,*/}*.js'
        ]
      },
      test: {
        options: {
          jshintrc: 'test/.jshintrc'
        },
        src: ['test/spec/{,*/}*.js']
      }
    },

    // Empties folders to start fresh
    clean: {
      dist: {
        files: [{
          dot: true,
          src: [
            '.tmp',
            '<%= yeoman.dist %>/{,*/}*',
            '!<%= yeoman.dist %>/.git{,*/}*'
          ]
        }]
      },
      server: '.tmp',
      coverage:'coverage'
    },

    // Add vendor prefixed styles
    autoprefixer: {
      options: {
        browsers: ['last 1 version']
      },
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/styles/',
          src: '{,*/}*.css',
          dest: '.tmp/styles/'
        }]
      }
    },

    // Automatically inject Bower components into the app
    wiredep: {
      app: {
        src: ['<%= yeoman.app %>/index.html'],
        ignorePath:  /\.\.\//
      }
    },

    // Renames files for browser caching purposes
    filerev: {
      dist: {
        src: [
          '<%= yeoman.dist %>/scripts/{,*/}*.js',
          '<%= yeoman.dist %>/styles/{,*/}*.css',
          '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
          '<%= yeoman.dist %>/styles/fonts/*'
        ]
      }
    },

    // Reads HTML for usemin blocks to enable smart builds that automatically
    // concat, minify and revision files. Creates configurations in memory so
    // additional tasks can operate on them
    useminPrepare: {
      html: '<%= yeoman.app %>/index.html',
      options: {
        dest: '<%= yeoman.dist %>',
        flow: {
          html: {
            steps: {
              //js: ['concat', 'uglifyjs'],
              js: ['concat'],
              css: ['cssmin']
            },
            post: {}
          }
        }
      }
    },

    // Performs rewrites based on filerev and the useminPrepare configuration
    usemin: {
      html: ['<%= yeoman.dist %>/{,*/}*.html'],
      css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
      options: {
        assetsDirs: ['<%= yeoman.dist %>','<%= yeoman.dist %>/images']
      }
    },

    // The following *-min tasks will produce minified files in the dist folder
    // By default, your `index.html`'s <!-- Usemin block --> will take care of
    // minification. These next options are pre-configured if you do not wish
    // to use the Usemin blocks.
    // cssmin: {
    //   dist: {
    //     files: {
    //       '<%= yeoman.dist %>/styles/main.css': [
    //         '.tmp/styles/{,*/}*.css'
    //       ]
    //     }
    //   }
    // },
    uglify: {
      dist: {
        files: {
          '<%= yeoman.dist %>/scripts/ui-accordion.min.js': [
            '<%= yeoman.dist %>/scripts/ui-accordion.js'
          ],
          '<%= yeoman.dist %>/scripts/vendor.min.js': [
            '<%= yeoman.dist %>/scripts/vendor.js'
          ]
        }
      },
      options : {
        beautify : {
          ascii_only : true
        }
      }
    },
    // imagemin: {
    //   dist: {
    //     files: [{
    //       expand: true,
    //       cwd: '<%= yeoman.app %>/images',
    //       src: '{,*/}*.{png,jpg,jpeg,gif}',
    //       dest: '<%= yeoman.dist %>/images'
    //     }]
    //   }
    // },

    svgmin: {
      dist: {
        files: [{
          expand: true,
          cwd: '<%= yeoman.app %>/images',
          src: '{,*/}*.svg',
          dest: '<%= yeoman.dist %>/images'
        }]
      }
    },

    htmlmin: {
      dist: {
        options: {
          collapseWhitespace: true,
          conservativeCollapse: true,
          collapseBooleanAttributes: true,
          removeCommentsFromCDATA: true,
          removeOptionalTags: true
        },
        files: [{
          expand: true,
          cwd: '<%= yeoman.dist %>',
          src: ['*.html', 'views/{,*/}*.html'],
          dest: '<%= yeoman.dist %>'
        }]
      }
    },

    // ng-annotate tries to make the code safe for minification automatically
    // by using the Angular long form for dependency injection.
    ngAnnotate: {
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/concat/scripts',
          src: ['*.js', '!oldieshim.js'],
          dest: '.tmp/concat/scripts'
        }]
      }
    },

    // Replace Google CDN references
    cdnify: {
      dist: {
        html: ['<%= yeoman.dist %>/*.html']
      }
    },

    // Copies remaining files to places other tasks can use
    copy: {
      dist: {
        files: [{
          expand: true,
          dot: true,
          cwd: '<%= yeoman.app %>',
          dest: '<%= yeoman.dist %>',
          src: [
            '*.{ico,png,txt}',
            '.htaccess',
            '*.html',
            'views/{,*/}*.html',
            'images/{,*/}*.{webp}',
            'fonts/{,*/}*.*'
          ]
        }, {
          expand: true,
          cwd: '.tmp/images',
          dest: '<%= yeoman.dist %>/images',
          src: ['generated/*']
        }, {
          expand: true,
          cwd: 'bower_components/bootstrap/dist',
          src: 'fonts/*',
          dest: '<%= yeoman.dist %>'
        }]
      },
      styles: {
        expand: true,
        cwd: '<%= yeoman.app %>/styles',
        dest: '.tmp/styles/',
        src: '{,*/}*.css'
      }
    },

    // Run some tasks in parallel to speed up the build process
    concurrent: {
      server: [
        'copy:styles'
      ],
      test: [
        'copy:styles'
      ],
      dist: [
        'copy:styles',
        //'imagemin',
        'svgmin'
      ]
    },

    // Test settings
    karma: {
      unit: {
        configFile: 'test/karma.conf.js',
        singleRun: true
      }
    }
  });

  grunt.registerTask('serve', 'Compile then start a connect web server', function (target) {
    if (target === 'dist') {
      return grunt.task.run(['build', 'connect:dist:keepalive']);
    }

    grunt.task.run([
      'clean:server',
      'wiredep',
      'concurrent:server',
      'autoprefixer',
      'connect:livereload',
      'watch'
    ]);
  });

  grunt.registerTask('server', 'DEPRECATED TASK. Use the "serve" task instead', function (target) {
    grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
    grunt.task.run(['serve:' + target]);
  });

  grunt.registerTask('test', [
    'clean:server','clean:coverage',
    'concurrent:test',
    'autoprefixer',
    'connect:test',
    'karma'
  ]);

  grunt.registerTask('build', [
    'newer:jshint',
    'jsinspect','coverage',
    'clean:dist',
    'wiredep',
    'useminPrepare',
    'concurrent:dist',
    'autoprefixer',
    'concat',
    'ngAnnotate',
    'copy:dist',
    'cdnify',
    'cssmin',
    'uglify',
    //'filerev',
    'usemin',
    'htmlmin',
    'angular_architecture_graph'
  ]);

  grunt.registerTask('architecture-graph', [
    'angular_architecture_graph'
  ]);

  grunt.registerTask('default', [

    'test',
    'build'
  ]);
  grunt.registerTask('bump', [
    'bump'
  ]);
};
<commit_msg>commented angular_architecture_graph<commit_after>// Generated on 2015-01-29 using generator-angular 0.10.0
'use strict';

// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// use this if you want to recursively match all subfolders:
// 'test/spec/**/*.js'

module.exports = function (grunt) {

  // Load grunt tasks automatically
  require('load-grunt-tasks')(grunt);

  // Time how long tasks take. Can help when optimizing build times
  require('time-grunt')(grunt);
  grunt.loadNpmTasks('grunt-angular-architecture-graph');
  grunt.loadNpmTasks('grunt-bump');
  grunt.loadNpmTasks('grunt-jsinspect');

  grunt.loadNpmTasks('grunt-istanbul-coverage');
  // Configurable paths for the application
  var appConfig = {
    app: require('./bower.json').appPath || 'app',
    dist: 'dist'
  };

  // Define the configuration for all the tasks
  grunt.initConfig({

    // Project settings
    yeoman: appConfig,
    angular_architecture_graph: {
      diagram: {
        files: {
          '<%= yeoman.app %>/Architecture': ['<%= yeoman.app %>/scripts/{,*/}*.js']
        }
      }
    },
    coverage: {
      default: {
        options: {
          thresholds: {
            'statements': 90,
            'branches': 90,
            'lines': 90,
            'functions': 90
          },
          dir: 'json',
          root: 'coverage'
        }
      }
    },
    bump: {
      options: {
        files: ['bower.json'],
        updateConfigs: [],
        commit: true,
        commitMessage: 'Release v%VERSION%',
        commitFiles: ['-a'],
        createTag: true,
        tagName: 'v%VERSION%',
        tagMessage: 'Version %VERSION%',
        push: true,
        pushTo: 'upstream',
        gitDescribeOptions: '--tags --always --abbrev=1 --dirty=-d',
        globalReplace: false,
        prereleaseName: false,
        regExp: false
      }
    },
    jsinspect: {
      inspect: {
        options: {
          threshold:   30,
          diff:        true,
          identifiers: false,
          failOnMatch: true,
          suppress:    100,
          reporter:    'default'
        },
        src: [
          '<%= yeoman.app %>/**/*.js'
        ]
      }
    },
    // Watches files for changes and runs tasks based on the changed files
    watch: {
      bower: {
        files: ['bower.json'],
        tasks: ['wiredep']
      },
      js: {
        files: ['<%= yeoman.app %>/scripts/{,*/}*.js'],
        tasks: ['newer:jshint:all'],
        options: {
          livereload: '<%= connect.options.livereload %>'
        }
      },
      jsTest: {
        files: ['test/spec/{,*/}*.js'],
        tasks: ['newer:jshint:test', 'karma']
      },
      styles: {
        files: ['<%= yeoman.app %>/styles/{,*/}*.css'],
        tasks: ['newer:copy:styles', 'autoprefixer']
      },
      gruntfile: {
        files: ['Gruntfile.js']
      },
      livereload: {
        options: {
          livereload: '<%= connect.options.livereload %>'
        },
        files: [
          '<%= yeoman.app %>/{,*/}*.html',
          '.tmp/styles/{,*/}*.css',
          '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
        ]
      }
    },

    // The actual grunt server settings
    connect: {
      options: {
        port: 9000,
        // Change this to '0.0.0.0' to access the server from outside.
        hostname: 'localhost',
        livereload: 35729
      },
      livereload: {
        options: {
          open: true,
          middleware: function (connect) {
            return [
              connect.static('.tmp'),
              connect().use(
                '/bower_components',
                connect.static('./bower_components')
              ),
              connect.static(appConfig.app)
            ];
          }
        }
      },
      test: {
        options: {
          port: 9001,
          middleware: function (connect) {
            return [
              connect.static('.tmp'),
              connect.static('test'),
              connect().use(
                '/bower_components',
                connect.static('./bower_components')
              ),
              connect.static(appConfig.app)
            ];
          }
        }
      },
      dist: {
        options: {
          open: true,
          base: '<%= yeoman.dist %>'
        }
      }
    },

    // Make sure code styles are up to par and there are no obvious mistakes
    jshint: {
      options: {
        jshintrc: '.jshintrc',
        reporter: require('jshint-stylish')
      },
      all: {
        src: [
          'Gruntfile.js',
          '<%= yeoman.app %>/scripts/{,*/}*.js'
        ]
      },
      test: {
        options: {
          jshintrc: 'test/.jshintrc'
        },
        src: ['test/spec/{,*/}*.js']
      }
    },

    // Empties folders to start fresh
    clean: {
      dist: {
        files: [{
          dot: true,
          src: [
            '.tmp',
            '<%= yeoman.dist %>/{,*/}*',
            '!<%= yeoman.dist %>/.git{,*/}*'
          ]
        }]
      },
      server: '.tmp',
      coverage:'coverage'
    },

    // Add vendor prefixed styles
    autoprefixer: {
      options: {
        browsers: ['last 1 version']
      },
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/styles/',
          src: '{,*/}*.css',
          dest: '.tmp/styles/'
        }]
      }
    },

    // Automatically inject Bower components into the app
    wiredep: {
      app: {
        src: ['<%= yeoman.app %>/index.html'],
        ignorePath:  /\.\.\//
      }
    },

    // Renames files for browser caching purposes
    filerev: {
      dist: {
        src: [
          '<%= yeoman.dist %>/scripts/{,*/}*.js',
          '<%= yeoman.dist %>/styles/{,*/}*.css',
          '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
          '<%= yeoman.dist %>/styles/fonts/*'
        ]
      }
    },

    // Reads HTML for usemin blocks to enable smart builds that automatically
    // concat, minify and revision files. Creates configurations in memory so
    // additional tasks can operate on them
    useminPrepare: {
      html: '<%= yeoman.app %>/index.html',
      options: {
        dest: '<%= yeoman.dist %>',
        flow: {
          html: {
            steps: {
              //js: ['concat', 'uglifyjs'],
              js: ['concat'],
              css: ['cssmin']
            },
            post: {}
          }
        }
      }
    },

    // Performs rewrites based on filerev and the useminPrepare configuration
    usemin: {
      html: ['<%= yeoman.dist %>/{,*/}*.html'],
      css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
      options: {
        assetsDirs: ['<%= yeoman.dist %>','<%= yeoman.dist %>/images']
      }
    },

    // The following *-min tasks will produce minified files in the dist folder
    // By default, your `index.html`'s <!-- Usemin block --> will take care of
    // minification. These next options are pre-configured if you do not wish
    // to use the Usemin blocks.
    // cssmin: {
    //   dist: {
    //     files: {
    //       '<%= yeoman.dist %>/styles/main.css': [
    //         '.tmp/styles/{,*/}*.css'
    //       ]
    //     }
    //   }
    // },
    uglify: {
      dist: {
        files: {
          '<%= yeoman.dist %>/scripts/ui-accordion.min.js': [
            '<%= yeoman.dist %>/scripts/ui-accordion.js'
          ],
          '<%= yeoman.dist %>/scripts/vendor.min.js': [
            '<%= yeoman.dist %>/scripts/vendor.js'
          ]
        }
      },
      options : {
        beautify : {
          ascii_only : true
        }
      }
    },
    // imagemin: {
    //   dist: {
    //     files: [{
    //       expand: true,
    //       cwd: '<%= yeoman.app %>/images',
    //       src: '{,*/}*.{png,jpg,jpeg,gif}',
    //       dest: '<%= yeoman.dist %>/images'
    //     }]
    //   }
    // },

    svgmin: {
      dist: {
        files: [{
          expand: true,
          cwd: '<%= yeoman.app %>/images',
          src: '{,*/}*.svg',
          dest: '<%= yeoman.dist %>/images'
        }]
      }
    },

    htmlmin: {
      dist: {
        options: {
          collapseWhitespace: true,
          conservativeCollapse: true,
          collapseBooleanAttributes: true,
          removeCommentsFromCDATA: true,
          removeOptionalTags: true
        },
        files: [{
          expand: true,
          cwd: '<%= yeoman.dist %>',
          src: ['*.html', 'views/{,*/}*.html'],
          dest: '<%= yeoman.dist %>'
        }]
      }
    },

    // ng-annotate tries to make the code safe for minification automatically
    // by using the Angular long form for dependency injection.
    ngAnnotate: {
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/concat/scripts',
          src: ['*.js', '!oldieshim.js'],
          dest: '.tmp/concat/scripts'
        }]
      }
    },

    // Replace Google CDN references
    cdnify: {
      dist: {
        html: ['<%= yeoman.dist %>/*.html']
      }
    },

    // Copies remaining files to places other tasks can use
    copy: {
      dist: {
        files: [{
          expand: true,
          dot: true,
          cwd: '<%= yeoman.app %>',
          dest: '<%= yeoman.dist %>',
          src: [
            '*.{ico,png,txt}',
            '.htaccess',
            '*.html',
            'views/{,*/}*.html',
            'images/{,*/}*.{webp}',
            'fonts/{,*/}*.*'
          ]
        }, {
          expand: true,
          cwd: '.tmp/images',
          dest: '<%= yeoman.dist %>/images',
          src: ['generated/*']
        }, {
          expand: true,
          cwd: 'bower_components/bootstrap/dist',
          src: 'fonts/*',
          dest: '<%= yeoman.dist %>'
        }]
      },
      styles: {
        expand: true,
        cwd: '<%= yeoman.app %>/styles',
        dest: '.tmp/styles/',
        src: '{,*/}*.css'
      }
    },

    // Run some tasks in parallel to speed up the build process
    concurrent: {
      server: [
        'copy:styles'
      ],
      test: [
        'copy:styles'
      ],
      dist: [
        'copy:styles',
        //'imagemin',
        'svgmin'
      ]
    },

    // Test settings
    karma: {
      unit: {
        configFile: 'test/karma.conf.js',
        singleRun: true
      }
    }
  });

  grunt.registerTask('serve', 'Compile then start a connect web server', function (target) {
    if (target === 'dist') {
      return grunt.task.run(['build', 'connect:dist:keepalive']);
    }

    grunt.task.run([
      'clean:server',
      'wiredep',
      'concurrent:server',
      'autoprefixer',
      'connect:livereload',
      'watch'
    ]);
  });

  grunt.registerTask('server', 'DEPRECATED TASK. Use the "serve" task instead', function (target) {
    grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
    grunt.task.run(['serve:' + target]);
  });

  grunt.registerTask('test', [
    'clean:server','clean:coverage',
    'concurrent:test',
    'autoprefixer',
    'connect:test',
    'karma'
  ]);

  grunt.registerTask('build', [
    'newer:jshint',
    'jsinspect','coverage',
    'clean:dist',
    'wiredep',
    'useminPrepare',
    'concurrent:dist',
    'autoprefixer',
    'concat',
    'ngAnnotate',
    'copy:dist',
    'cdnify',
    'cssmin',
    'uglify',
    //'filerev',
    'usemin',
    'htmlmin'//,
    //'angular_architecture_graph'
  ]);

  grunt.registerTask('architecture-graph', [
    'angular_architecture_graph'
  ]);

  grunt.registerTask('default', [

    'test',
    'build'
  ]);
  grunt.registerTask('bump', [
    'bump'
  ]);
};
<|endoftext|>


<commit_before>const Joi = require('@hapi/joi')
const decodeValidator = require('./validation/decode.js')
const timezoneValidator = require('./validation/timezone.js')
const localeValidator = require('./validation/locale.js')

const logSchema = Joi.object({
  level: Joi.string().strict().valid('silent', 'trace', 'debug', 'info', 'owner', 'warn', 'error', 'fatal').default('info'),
  destination: Joi.string().allow('').default(''),
  linkErrs: Joi.bool().strict().default(true),
  unfiltered: Joi.bool().strict().default(true),
  failedFeeds: Joi.bool().strict().default(true)
})

const botSchema = Joi.object({
  token: Joi.string().strict().default(''),
  locale: localeValidator.config().locale(),
  enableCommands: Joi.bool().strict().default(true),
  prefix: Joi.string().strict().default('rss.'),
  status: Joi.string().valid('online', 'dnd', 'invisible', 'idle').default('online'),
  activityType: Joi.string().valid('', 'PLAYING', 'STREAMING', 'LISTENING', 'WATCHING').default(''),
  activityName: Joi.string().strict().allow('').default(''),
  streamActivityURL: Joi.string().strict().allow('').default(''),
  ownerIDs: Joi.array().items(Joi.string().strict()).default([]),
  menuColor: Joi.number().strict().greater(0).default(5285609),
  deleteMenus: Joi.bool().strict().default(true),
  runSchedulesOnStart: Joi.bool().strict().default(true),
  exitOnSocketIssues: Joi.bool().strict().default(true)
})

const databaseSchema = Joi.object({
  uri: Joi.string().strict().default('mongodb://localhost:27017/rss'),
  redis: Joi.string().strict().allow('').default(''),
  connection: Joi.object().default({}),
  articlesExpire: Joi.number().strict().greater(-1).default(14),
  deliveryRecordsExpire: Joi.number().strict().greater(-1).default(5)
})

const feedsSchema = Joi.object({
  refreshRateMinutes: Joi.number().strict().greater(0).default(10),
  articleRateLimit: Joi.number().strict().greater(-1).default(0),
  timezone: timezoneValidator.config().timezone(),
  dateFormat: Joi.string().strict().default('ddd, D MMMM YYYY, h:mm A z'),
  dateLanguage: Joi.string().strict().default('en'),
  dateLanguageList: Joi.array().items(Joi.string().strict()).min(1).default(['en']),
  dateFallback: Joi.bool().strict().default(false),
  timeFallback: Joi.bool().strict().default(false),
  max: Joi.number().strict().greater(-1).default(0),
  hoursUntilFail: Joi.number().strict().default(0),
  notifyFail: Joi.bool().strict().default(true),
  sendFirstCycle: Joi.bool().strict().default(true),
  cycleMaxAge: Joi.number().strict().default(1),
  defaultText: Joi.string().default(':newspaper:  |  **{title}**\n\n{link}\n\n{subscribers}'),
  imgPreviews: Joi.bool().strict().default(true),
  imgLinksExistence: Joi.bool().strict().default(true),
  checkDates: Joi.bool().strict().default(true),
  formatTables: Joi.bool().strict().default(false),
  directSubscribers: Joi.bool().strict().default(false),
  decode: decodeValidator.config().encoding()
})

const advancedSchema = Joi.object({
  shards: Joi.number().greater(-1).strict().default(0),
  batchSize: Joi.number().greater(0).strict().default(400),
  parallelBatches: Joi.number().greater(0).strict().default(1),
  parallelRuns: Joi.number().greater(0).strict().default(1)
})

const schema = Joi.object({
  dev: Joi.number().strict().greater(-1),
  _vip: Joi.bool().strict(),
  _vipRefreshRateMinutes: Joi.number().strict(),
  log: logSchema.default(logSchema.validate({}).value),
  bot: botSchema.default(botSchema.validate({}).value),
  database: databaseSchema.default(databaseSchema.validate({}).value),
  feeds: feedsSchema.default(feedsSchema.validate({}).value),
  advanced: advancedSchema.default(advancedSchema.validate({}).value),
  webURL: Joi.string().strict().allow('').default('')
})

module.exports = {
  schemas: {
    log: logSchema,
    bot: botSchema,
    database: databaseSchema,
    feeds: feedsSchema,
    advanced: advancedSchema,
    config: schema
  },
  defaults: schema.validate({}).value,
  validate: config => {
    const results = schema.validate(config, {
      abortEarly: false
    })
    if (results.error) {
      const str = results.error.details
        .map(d => d.message)
        .join('\n')

      throw new TypeError(`Bot config validation failed\n\n${str}\n`)
    }
  }
}
<commit_msg>Change config.database.deliveryRecordsExpire to 2<commit_after>const Joi = require('@hapi/joi')
const decodeValidator = require('./validation/decode.js')
const timezoneValidator = require('./validation/timezone.js')
const localeValidator = require('./validation/locale.js')

const logSchema = Joi.object({
  level: Joi.string().strict().valid('silent', 'trace', 'debug', 'info', 'owner', 'warn', 'error', 'fatal').default('info'),
  destination: Joi.string().allow('').default(''),
  linkErrs: Joi.bool().strict().default(true),
  unfiltered: Joi.bool().strict().default(true),
  failedFeeds: Joi.bool().strict().default(true)
})

const botSchema = Joi.object({
  token: Joi.string().strict().default(''),
  locale: localeValidator.config().locale(),
  enableCommands: Joi.bool().strict().default(true),
  prefix: Joi.string().strict().default('rss.'),
  status: Joi.string().valid('online', 'dnd', 'invisible', 'idle').default('online'),
  activityType: Joi.string().valid('', 'PLAYING', 'STREAMING', 'LISTENING', 'WATCHING').default(''),
  activityName: Joi.string().strict().allow('').default(''),
  streamActivityURL: Joi.string().strict().allow('').default(''),
  ownerIDs: Joi.array().items(Joi.string().strict()).default([]),
  menuColor: Joi.number().strict().greater(0).default(5285609),
  deleteMenus: Joi.bool().strict().default(true),
  runSchedulesOnStart: Joi.bool().strict().default(true),
  exitOnSocketIssues: Joi.bool().strict().default(true)
})

const databaseSchema = Joi.object({
  uri: Joi.string().strict().default('mongodb://localhost:27017/rss'),
  redis: Joi.string().strict().allow('').default(''),
  connection: Joi.object().default({}),
  articlesExpire: Joi.number().strict().greater(-1).default(14),
  deliveryRecordsExpire: Joi.number().strict().greater(-1).default(2)
})

const feedsSchema = Joi.object({
  refreshRateMinutes: Joi.number().strict().greater(0).default(10),
  articleRateLimit: Joi.number().strict().greater(-1).default(0),
  timezone: timezoneValidator.config().timezone(),
  dateFormat: Joi.string().strict().default('ddd, D MMMM YYYY, h:mm A z'),
  dateLanguage: Joi.string().strict().default('en'),
  dateLanguageList: Joi.array().items(Joi.string().strict()).min(1).default(['en']),
  dateFallback: Joi.bool().strict().default(false),
  timeFallback: Joi.bool().strict().default(false),
  max: Joi.number().strict().greater(-1).default(0),
  hoursUntilFail: Joi.number().strict().default(0),
  notifyFail: Joi.bool().strict().default(true),
  sendFirstCycle: Joi.bool().strict().default(true),
  cycleMaxAge: Joi.number().strict().default(1),
  defaultText: Joi.string().default(':newspaper:  |  **{title}**\n\n{link}\n\n{subscribers}'),
  imgPreviews: Joi.bool().strict().default(true),
  imgLinksExistence: Joi.bool().strict().default(true),
  checkDates: Joi.bool().strict().default(true),
  formatTables: Joi.bool().strict().default(false),
  directSubscribers: Joi.bool().strict().default(false),
  decode: decodeValidator.config().encoding()
})

const advancedSchema = Joi.object({
  shards: Joi.number().greater(-1).strict().default(0),
  batchSize: Joi.number().greater(0).strict().default(400),
  parallelBatches: Joi.number().greater(0).strict().default(1),
  parallelRuns: Joi.number().greater(0).strict().default(1)
})

const schema = Joi.object({
  dev: Joi.number().strict().greater(-1),
  _vip: Joi.bool().strict(),
  _vipRefreshRateMinutes: Joi.number().strict(),
  log: logSchema.default(logSchema.validate({}).value),
  bot: botSchema.default(botSchema.validate({}).value),
  database: databaseSchema.default(databaseSchema.validate({}).value),
  feeds: feedsSchema.default(feedsSchema.validate({}).value),
  advanced: advancedSchema.default(advancedSchema.validate({}).value),
  webURL: Joi.string().strict().allow('').default('')
})

module.exports = {
  schemas: {
    log: logSchema,
    bot: botSchema,
    database: databaseSchema,
    feeds: feedsSchema,
    advanced: advancedSchema,
    config: schema
  },
  defaults: schema.validate({}).value,
  validate: config => {
    const results = schema.validate(config, {
      abortEarly: false
    })
    if (results.error) {
      const str = results.error.details
        .map(d => d.message)
        .join('\n')

      throw new TypeError(`Bot config validation failed\n\n${str}\n`)
    }
  }
}
<|endoftext|>