<commit_before>'use strict';

var Contacts = {
  _loaded: false,
  get view() {
    delete this.view;
    return this.view = document.getElementById('contacts-view-scrollable');
  },
  setup: function contactsSetup() {
    document.getElementById('contacts').addEventListener('change',
      (function contactTabChanged(evt) {
        // loading contacts the first time the view appears
        this.load();

        ContactDetails.hide();
      }).bind(this));
  },
  load: function contactsLoad() {
    if (this._loaded) {
      return;
    }

    // Could be much easier to have an argument named 'parameters' pass as
    // a second argument that I can omit
    this.find(['id', 'displayName'], this.show.bind(this));
  },
  find: function contactsFind(fields, callback) {
    // Ideally I would like to choose the ordering
    // It also misses simple constaints like the one you can pass to the
    // webSMS API
    window.navigator.mozContacts.find(fields, function findCallback(contacts) {
      contacts.sort(function contactsSort(a, b) {
        return a.name.familyName[0] > b.name.familyName[0];
      });
      callback(contacts);
    });
  },
  show: function contactsShow(contacts) {
    var content = '';
    var currentLetter = '';

    var count = contacts.length;
    for (var i = 0; i < count; i++) {
      var contact = contacts[i];
      var displayName = contact.displayName;

      var name = contact.name.familyName[0];
      if (currentLetter != name[0]) {
        currentLetter = name[0].toUpperCase();

        content += '
' +
                   '' +
                      currentLetter +
                   '
';
      }

      content += '
';
      for (var key in contact.name) {
        content += '' +  contact.name[key] + ' ';
      }
      content += '
';
    }

    var contactsContainer = document.getElementById('contacts-container');
    contactsContainer.innerHTML = content;
    this.filter();

    this._loaded = true;
  },
  filter: function contactsFilter(value) {
    var container = document.getElementById('contacts-container');
    var contacts = container.children;

    var count = contacts.length;
    for (var i = 0; i < count; i++) {
      var contact = contacts[i];
      if (contact.className == 'contact-header')
        continue;

      var name = contact.querySelector('span').textContent;
      var rule = new RegExp(value, 'gi');
      contact.hidden = (name.search(rule) == -1);
    }

    // If there is 0 childs for a particular letter, hide it.
    var enabledLetters = [];
    for (var i = 0; i < count; i++) {
      var contactHeader = contacts[i];
      if (contactHeader.className != 'contact-header')
        continue;

      var nextSibling = contactHeader.nextSibling;
      while (nextSibling && nextSibling.className == 'contact') {
        if (!nextSibling.hidden) {
          enabledLetters.push(contactHeader.id);
          break;
        }
        nextSibling = nextSibling.nextSibling;
      }
    }

    // Reflect the change in the shortcut letter
    var shortcuts = document.getElementById('contacts-shortcuts').children;
    for (var j = 1; j < shortcuts.length; j++) {
      var shortcut = shortcuts[j];
      var targetId = shortcut.name;
      var header = document.getElementById(targetId);

      var disabled = (enabledLetters.indexOf(targetId) == -1);
      if (header && disabled) {
        shortcut.setAttribute('data-disabled', 'true');
        header.hidden = true;
      } else if (disabled) {
        shortcut.setAttribute('data-disabled', 'true');
      } else {
        shortcut.removeAttribute('data-disabled');
        header.hidden = false;
      }
    }
  },
  anchor: function contactsAnchor(targetId) {
    var target = document.getElementById(targetId);
    if (!target)
      return;

    var top = target.getBoundingClientRect().top;
    var scrollable = document.getElementById('contacts-view-scrollable');
    var scrollableTop = scrollable.getBoundingClientRect().top;
    scrollable.scrollTop = (top - scrollableTop) + scrollable.scrollTop;
  },
  showDetails: function contactsShowDetails(evt) {
    // I'm under the impression that there will be a better way to do this
    // with the final API (ie. getting a contact from an id)
    var contactId = evt.target.id;

    this.find(['id'], function showDetailCallback(contacts) {
      var results = contacts.filter(function finById(contact) {
        return (contact.id == contactId);
      });
      var contact = results[0];
      if (contact) {
        ContactDetails.show(contact);
      }
    });
  },
  create: function contactsCreate() {
    // creating an empty contact
    var contact = {
      name: {
        familyName: [],
        givenName: []
      },
      phones: [],
      emails: []
    };
    ContactDetails.show(contact);
  }
};

var ShortcutsHandler = {
  setup: function sh_setup() {
    ['touchstart', 'touchmove', 'touchend'].forEach((function(evt) {
      this.shortcutsBar.addEventListener(evt, this, true);
    }).bind(this));
  },

  get shortcutsBar() {
    delete this.shortcutsBar;
    return this.shortcutsBar = document.getElementById('contacts-shortcuts');
  },

  get shortcutsBackground() {
    delete this.shortcutsBackground;
    var id = 'contacts-shortcuts-background';
    return this.shortcutsBackground = document.getElementById(id);
  },

  handleEvent: function sh_handleEvent(evt) {
    // preventing the events from bubbling to the contacts list
    evt.preventDefault();

    switch (evt.type) {
      case 'touchstart':
        this.startTracking();
      case 'touchmove': // fall through
        this.anchorForPosition(evt.targetTouches[0].clientY);
        break;

      case 'touchend':
        this.stopTracking();
        break;
    }
  },

  startTracking: function sh_startTracking() {
    this.shortcutsBackground.classList.add('tracking');

    // we keep a reference to the horizontal center of the zone
    // it allows us to keep anchoring correctly if the user gets
    // out of the zone while swiping
    var rect = this.shortcutsBar.getBoundingClientRect();
    this._positionX = rect.left + (rect.width / 2);
  },
  stopTracking: function sh_stopTracking() {
    this.shortcutsBackground.classList.remove('tracking');
    delete this._positionX;
  },
  anchorForPosition: function sh_anchorForPosition(positionY) {
    // only inspecting the vertical point of the touch
    var target = document.elementFromPoint(this._positionX, positionY).name;
    Contacts.anchor(target);
  }
};

var ContactDetails = {
  _editing: false,
  setup: function cd_setup() {
    window.addEventListener('keypress', this, true);
  },
  get overlay() {
    delete this.overlay;
    return this.overlay = document.getElementById('contacts-overlay');
  },
  get container() {
    delete this.container;
    return this.container =
      document.getElementById('contact-details-container');
  },
  get view() {
    delete this.view;
    return this.view = document.getElementById('contact-details-view');
  },
  get editButton() {
    delete this.editButton;
    return this.editButton = document.getElementById('contact-edit-button');
  },
  set contact(contact) {
    delete this._contact;
    this._contact = contact;
    this.render();
  },
  execute: function cd_execute(evt) {
    var action = evt.currentTarget.dataset.action;
    if (!this[action]) {
      this.hide();
      return;
    }

    this[action](evt);
  },
  show: function cd_show(contact) {
    if (typeof contact != 'undefined') {
      this.contact = contact;
    }

    var overlay = this.overlay;
    overlay.classList.add('displayed');

    // directly entering the edit mode if this is a new contact
    if (!this._contact.id) {
      this.edit();
    }
  },
  hide: function cd_hide() {
    if (!this.overlay.classList.contains('displayed')) {
      return false;
    }

    var overlay = this.overlay;
    overlay.classList.add('hidden');
    overlay.addEventListener('transitionend', function fadeWait() {
      overlay.removeEventListener('transitionend', fadeWait);

      overlay.classList.remove('hidden');
      overlay.classList.remove('displayed');
    });

    this.endEditing();
    return true;
  },
  add: function cd_add(evt) {
    var parent = evt.currentTarget.parentNode;
    var type = '';
    switch (parent.id) {
      case 'contact-phones':
        type = 'tel';
        break;
      case 'contact-emails':
        type = 'email';
        break;
      default:
        break;
    }

    var newElement = document.createElement('div');
    newElement.innerHTML = this.inputFragment(type, '', false);
    parent.insertBefore(newElement, evt.currentTarget);

    newElement.querySelector('input').focus();
  },
  remove: function cd_remove(element) {
    element.parentNode.removeChild(element);
  },
  edit: function cd_edit() {
    if (this._editing) {
      return;
    }
    this._editing = true;

    this.smoothTransition((function cd_editTransition() {
      this.editButton.dataset.action = 'save';
      this.view.classList.add('editing');
      // making the fields editable
      var inputs = this.view.getElementsByTagName('input');
      for (var i = 0; i < inputs.length; i++) {
        var input = inputs[i];
        input.disabled = false;
      }
    }).bind(this));
  },
  save: function cd_save() {
    // TODO: actually save the contact

    this.endEditing();
  },
  destroy: function cd_destroy() {
    // TODO: destroy the contact
    this.hide();
  },
  call: function cd_call(evt) {
    if (this._editing) {
      return;
    }

    this.hide();
    var number = evt.target.dataset.number;
    if (number) {
      CallHandler.call(number);
    }
  },

  endEditing: function cd_endEditing() {
    if (!this._editing) {
      return false;
    }
    this._editing = false;

    this.smoothTransition((function cd_endEditingTransition() {
      this.editButton.dataset.action = 'edit';
      this.view.classList.remove('editing');
      // disabling the fields and cleaning up
      var inputs = this.view.getElementsByTagName('input');
      for (var i = 0; i < inputs.length; i++) {
        var input = inputs[i];
        input.disabled = true;

        if (input.value.length == 0) {
          this.remove(input.parentNode);
        }
      }
    }).bind(this));
    return true;
  },
  // scrolling to the right position when one of the fields
  // takes focus
  autoscroll: function cd_autoscroll(event) {
    var element = event.currentTarget;
    var self = this;

    var scrollInPlace = function cd_autoscrollMove() {
      var bounds = element.getBoundingClientRect();
      var bottom = bounds.top + bounds.height;
      var yDelta = bottom - self.container.getBoundingClientRect().height;
      if (yDelta > 0) {
        self.container.scrollTop += yDelta;
      }
    };
    scrollInPlace();

    //also listening to the next resize for keyboard handling
    window.addEventListener('resize', function cd_afterResize() {
      window.removeEventListener('resize', cd_afterResize);

      scrollInPlace();
    });
  },

  // back button handling
  handleEvent: function cd_handleEvent(evt) {
    if (evt.type !== 'keypress' || evt.keyCode != evt.DOM_VK_ESCAPE) {
      return;
    }

    if (this.endEditing() || this.hide()) {
      evt.preventDefault();
    }
  },

  render: function cd_render() {
    var names = '';
    for (var key in this._contact.name) {
      names += '  ' +  this._contact.name[key];
    }
    document.getElementById('contact-name').innerHTML = names;

    var addAttr = 'data-action="add" onclick="ContactDetails.execute(event)"';
    var phones = '';
    this._contact.phones.forEach(function phoneIterator(phone) {
      phones += '
' +
                'phone' +
                '  ' + this.inputFragment('tel', phone) +
                '
';
    }, this);
    phones += '
' +
              '  Add phone' +
              '
';
    document.getElementById('contact-phones').innerHTML = phones;

    var emails = '';
    this._contact.emails.forEach(function emailIterator(email) {
      emails += '
email' + this.inputFragment('email', email) + '
';
    }, this);
    emails += '
' +
              '  Add email' +
              '
';
    document.getElementById('contact-emails').innerHTML = emails;
  },
  inputFragment: function cd_inputFragment(type, value, disabled) {
    disabled = (typeof disabled == 'undefined') ? true : disabled;

    return '<input type="' + type + '" value="' + value +
           '  " data-action="autoscroll"' +
           '  ' + (disabled ? 'disabled="disabled"' : '') +
           '  onfocus="ContactDetails.execute(event)" />';
  },
  smoothTransition: function cd_smoothTransition(callback) {
    var detailsView = this.view;
    detailsView.classList.add('hidden');
    detailsView.addEventListener('transitionend', function cd_smoothFinish() {
      detailsView.removeEventListener('transitionend', cd_smoothFinish);

      callback();

      detailsView.classList.remove('hidden');
    });
  }

};

window.addEventListener('load', function contactSetup(evt) {
  window.removeEventListener('load', contactSetup);
  Contacts.setup();
  ShortcutsHandler.setup();
  ContactDetails.setup();
});
<commit_msg>Fix a small bug with the contacts that always let the 'A' letter visible<commit_after>'use strict';

var Contacts = {
  _loaded: false,
  get view() {
    delete this.view;
    return this.view = document.getElementById('contacts-view-scrollable');
  },
  setup: function contactsSetup() {
    document.getElementById('contacts').addEventListener('change',
      (function contactTabChanged(evt) {
        // loading contacts the first time the view appears
        this.load();

        ContactDetails.hide();
      }).bind(this));
  },
  load: function contactsLoad() {
    if (this._loaded) {
      return;
    }

    // Could be much easier to have an argument named 'parameters' pass as
    // a second argument that I can omit
    this.find(['id', 'displayName'], this.show.bind(this));
  },
  find: function contactsFind(fields, callback) {
    // Ideally I would like to choose the ordering
    // It also misses simple constaints like the one you can pass to the
    // webSMS API
    window.navigator.mozContacts.find(fields, function findCallback(contacts) {
      contacts.sort(function contactsSort(a, b) {
        return a.name.familyName[0] > b.name.familyName[0];
      });
      callback(contacts);
    });
  },
  show: function contactsShow(contacts) {
    var content = '';
    var currentLetter = '';

    var count = contacts.length;
    for (var i = 0; i < count; i++) {
      var contact = contacts[i];
      var displayName = contact.displayName;

      var name = contact.name.familyName[0];
      if (currentLetter != name[0]) {
        currentLetter = name[0].toUpperCase();

        content += '
' +
                   '' +
                      currentLetter +
                   '
';
      }

      content += '
';
      for (var key in contact.name) {
        content += '' +  contact.name[key] + ' ';
      }
      content += '
';
    }

    var contactsContainer = document.getElementById('contacts-container');
    contactsContainer.innerHTML = content;
    this.filter();

    this._loaded = true;
  },
  filter: function contactsFilter(value) {
    var container = document.getElementById('contacts-container');
    var contacts = container.children;

    var count = contacts.length;
    for (var i = 0; i < count; i++) {
      var contact = contacts[i];
      if (contact.className == 'contact-header')
        continue;

      var name = contact.querySelector('span').textContent;
      var rule = new RegExp(value, 'gi');
      contact.hidden = (name.search(rule) == -1);
    }

    // If there is 0 childs for a particular letter, hide it.
    var enabledLetters = [];
    for (var i = 0; i < count; i++) {
      var contactHeader = contacts[i];
      if (contactHeader.className != 'contact-header')
        continue;

      var nextSibling = contactHeader.nextSibling;
      while (nextSibling && nextSibling.className == 'contact') {
        if (!nextSibling.hidden) {
          enabledLetters.push(contactHeader.id);
          break;
        }
        nextSibling = nextSibling.nextSibling;
      }
    }

    // Reflect the change in the shortcut letter
    var shortcuts = document.getElementById('contacts-shortcuts').children;
    for (var j = 0; j < shortcuts.length; j++) {
      var shortcut = shortcuts[j];
      var targetId = shortcut.name;
      var header = document.getElementById(targetId);

      var disabled = (enabledLetters.indexOf(targetId) == -1);
      if (header && disabled) {
        shortcut.setAttribute('data-disabled', 'true');
        header.hidden = true;
      } else if (disabled) {
        shortcut.setAttribute('data-disabled', 'true');
      } else {
        shortcut.removeAttribute('data-disabled');
        header.hidden = false;
      }
    }
  },
  anchor: function contactsAnchor(targetId) {
    var target = document.getElementById(targetId);
    if (!target)
      return;

    var top = target.getBoundingClientRect().top;
    var scrollable = document.getElementById('contacts-view-scrollable');
    var scrollableTop = scrollable.getBoundingClientRect().top;
    scrollable.scrollTop = (top - scrollableTop) + scrollable.scrollTop;
  },
  showDetails: function contactsShowDetails(evt) {
    // I'm under the impression that there will be a better way to do this
    // with the final API (ie. getting a contact from an id)
    var contactId = evt.target.id;

    this.find(['id'], function showDetailCallback(contacts) {
      var results = contacts.filter(function finById(contact) {
        return (contact.id == contactId);
      });
      var contact = results[0];
      if (contact) {
        ContactDetails.show(contact);
      }
    });
  },
  create: function contactsCreate() {
    // creating an empty contact
    var contact = {
      name: {
        familyName: [],
        givenName: []
      },
      phones: [],
      emails: []
    };
    ContactDetails.show(contact);
  }
};

var ShortcutsHandler = {
  setup: function sh_setup() {
    ['touchstart', 'touchmove', 'touchend'].forEach((function(evt) {
      this.shortcutsBar.addEventListener(evt, this, true);
    }).bind(this));
  },

  get shortcutsBar() {
    delete this.shortcutsBar;
    return this.shortcutsBar = document.getElementById('contacts-shortcuts');
  },

  get shortcutsBackground() {
    delete this.shortcutsBackground;
    var id = 'contacts-shortcuts-background';
    return this.shortcutsBackground = document.getElementById(id);
  },

  handleEvent: function sh_handleEvent(evt) {
    // preventing the events from bubbling to the contacts list
    evt.preventDefault();

    switch (evt.type) {
      case 'touchstart':
        this.startTracking();
      case 'touchmove': // fall through
        this.anchorForPosition(evt.targetTouches[0].clientY);
        break;

      case 'touchend':
        this.stopTracking();
        break;
    }
  },

  startTracking: function sh_startTracking() {
    this.shortcutsBackground.classList.add('tracking');

    // we keep a reference to the horizontal center of the zone
    // it allows us to keep anchoring correctly if the user gets
    // out of the zone while swiping
    var rect = this.shortcutsBar.getBoundingClientRect();
    this._positionX = rect.left + (rect.width / 2);
  },
  stopTracking: function sh_stopTracking() {
    this.shortcutsBackground.classList.remove('tracking');
    delete this._positionX;
  },
  anchorForPosition: function sh_anchorForPosition(positionY) {
    // only inspecting the vertical point of the touch
    var target = document.elementFromPoint(this._positionX, positionY).name;
    Contacts.anchor(target);
  }
};

var ContactDetails = {
  _editing: false,
  setup: function cd_setup() {
    window.addEventListener('keypress', this, true);
  },
  get overlay() {
    delete this.overlay;
    return this.overlay = document.getElementById('contacts-overlay');
  },
  get container() {
    delete this.container;
    return this.container =
      document.getElementById('contact-details-container');
  },
  get view() {
    delete this.view;
    return this.view = document.getElementById('contact-details-view');
  },
  get editButton() {
    delete this.editButton;
    return this.editButton = document.getElementById('contact-edit-button');
  },
  set contact(contact) {
    delete this._contact;
    this._contact = contact;
    this.render();
  },
  execute: function cd_execute(evt) {
    var action = evt.currentTarget.dataset.action;
    if (!this[action]) {
      this.hide();
      return;
    }

    this[action](evt);
  },
  show: function cd_show(contact) {
    if (typeof contact != 'undefined') {
      this.contact = contact;
    }

    var overlay = this.overlay;
    overlay.classList.add('displayed');

    // directly entering the edit mode if this is a new contact
    if (!this._contact.id) {
      this.edit();
    }
  },
  hide: function cd_hide() {
    if (!this.overlay.classList.contains('displayed')) {
      return false;
    }

    var overlay = this.overlay;
    overlay.classList.add('hidden');
    overlay.addEventListener('transitionend', function fadeWait() {
      overlay.removeEventListener('transitionend', fadeWait);

      overlay.classList.remove('hidden');
      overlay.classList.remove('displayed');
    });

    this.endEditing();
    return true;
  },
  add: function cd_add(evt) {
    var parent = evt.currentTarget.parentNode;
    var type = '';
    switch (parent.id) {
      case 'contact-phones':
        type = 'tel';
        break;
      case 'contact-emails':
        type = 'email';
        break;
      default:
        break;
    }

    var newElement = document.createElement('div');
    newElement.innerHTML = this.inputFragment(type, '', false);
    parent.insertBefore(newElement, evt.currentTarget);

    newElement.querySelector('input').focus();
  },
  remove: function cd_remove(element) {
    element.parentNode.removeChild(element);
  },
  edit: function cd_edit() {
    if (this._editing) {
      return;
    }
    this._editing = true;

    this.smoothTransition((function cd_editTransition() {
      this.editButton.dataset.action = 'save';
      this.view.classList.add('editing');
      // making the fields editable
      var inputs = this.view.getElementsByTagName('input');
      for (var i = 0; i < inputs.length; i++) {
        var input = inputs[i];
        input.disabled = false;
      }
    }).bind(this));
  },
  save: function cd_save() {
    // TODO: actually save the contact

    this.endEditing();
  },
  destroy: function cd_destroy() {
    // TODO: destroy the contact
    this.hide();
  },
  call: function cd_call(evt) {
    if (this._editing) {
      return;
    }

    this.hide();
    var number = evt.target.dataset.number;
    if (number) {
      CallHandler.call(number);
    }
  },

  endEditing: function cd_endEditing() {
    if (!this._editing) {
      return false;
    }
    this._editing = false;

    this.smoothTransition((function cd_endEditingTransition() {
      this.editButton.dataset.action = 'edit';
      this.view.classList.remove('editing');
      // disabling the fields and cleaning up
      var inputs = this.view.getElementsByTagName('input');
      for (var i = 0; i < inputs.length; i++) {
        var input = inputs[i];
        input.disabled = true;

        if (input.value.length == 0) {
          this.remove(input.parentNode);
        }
      }
    }).bind(this));
    return true;
  },
  // scrolling to the right position when one of the fields
  // takes focus
  autoscroll: function cd_autoscroll(event) {
    var element = event.currentTarget;
    var self = this;

    var scrollInPlace = function cd_autoscrollMove() {
      var bounds = element.getBoundingClientRect();
      var bottom = bounds.top + bounds.height;
      var yDelta = bottom - self.container.getBoundingClientRect().height;
      if (yDelta > 0) {
        self.container.scrollTop += yDelta;
      }
    };
    scrollInPlace();

    //also listening to the next resize for keyboard handling
    window.addEventListener('resize', function cd_afterResize() {
      window.removeEventListener('resize', cd_afterResize);

      scrollInPlace();
    });
  },

  // back button handling
  handleEvent: function cd_handleEvent(evt) {
    if (evt.type !== 'keypress' || evt.keyCode != evt.DOM_VK_ESCAPE) {
      return;
    }

    if (this.endEditing() || this.hide()) {
      evt.preventDefault();
    }
  },

  render: function cd_render() {
    var names = '';
    for (var key in this._contact.name) {
      names += '  ' +  this._contact.name[key];
    }
    document.getElementById('contact-name').innerHTML = names;

    var addAttr = 'data-action="add" onclick="ContactDetails.execute(event)"';
    var phones = '';
    this._contact.phones.forEach(function phoneIterator(phone) {
      phones += '
' +
                'phone' +
                '  ' + this.inputFragment('tel', phone) +
                '
';
    }, this);
    phones += '
' +
              '  Add phone' +
              '
';
    document.getElementById('contact-phones').innerHTML = phones;

    var emails = '';
    this._contact.emails.forEach(function emailIterator(email) {
      emails += '
email' + this.inputFragment('email', email) + '
';
    }, this);
    emails += '
' +
              '  Add email' +
              '
';
    document.getElementById('contact-emails').innerHTML = emails;
  },
  inputFragment: function cd_inputFragment(type, value, disabled) {
    disabled = (typeof disabled == 'undefined') ? true : disabled;

    return '<input type="' + type + '" value="' + value +
           '  " data-action="autoscroll"' +
           '  ' + (disabled ? 'disabled="disabled"' : '') +
           '  onfocus="ContactDetails.execute(event)" />';
  },
  smoothTransition: function cd_smoothTransition(callback) {
    var detailsView = this.view;
    detailsView.classList.add('hidden');
    detailsView.addEventListener('transitionend', function cd_smoothFinish() {
      detailsView.removeEventListener('transitionend', cd_smoothFinish);

      callback();

      detailsView.classList.remove('hidden');
    });
  }

};

window.addEventListener('load', function contactSetup(evt) {
  window.removeEventListener('load', contactSetup);
  Contacts.setup();
  ShortcutsHandler.setup();
  ContactDetails.setup();
});
<|endoftext|>


<commit_before>/**
 * Created by martin on 2017-03-26.
 */
app.factory('CardService', function ($cookies, $resource, $firebaseArray) {

  var useCardStats = true;
  var loggedIn = false;
  var boardsLoaded = false;
  var boards = [];
  var loadingCounter = 0;
  var listTypes = ['To Do','In Progress','Verifying','Done'];
  var userId;

  //Authorize to the trello api
  this.authorize = function(cb, shouldLoadBoards) {
    Trello.authorize({
      type: 'popup',
      name: 'Getting Started Application',
      scope: {
        read: 'true',
        write: 'true' },
      expiration: 'never',
      success: function() {
        loggedIn = true;
        if(shouldLoadBoards){
          loadBoards(cb);
        }else{
          cb();
        }
        //cb();
      },
      error: function() { console.log('Failed authentication'); }
    });
  };

  this.logout = function () {
    loggedIn = false;
  }

  var loadBoards = function (cb) {
    // Get all of the information about the boards you have access to
    var success = function(data) {
      boards = data;
      for(var i = 0; i < boards.length; i++){
        loadLists(i, cb);
      }
    };
    var error = function(errorMsg) {
      console.log(errorMsg);
    };
    //First load the user Id
    Trello.get('/member/me', function (data) {
      userId = data.id;
      Trello.get('/member/me/boards', success, error);
    });
  };

  var loadLists = function (boardIndex, cb) {
    var boardId = boards[boardIndex].id;
    var success = function (data) {
      boards[boardIndex].lists = data;
      loadCards(boardIndex, cb);
    };
    var error = function(errorMsg) {
      console.log(errorMsg);
    };
    Trello.get('/boards/' + boardId + '/lists', success, error);
  };

  var loadCards = function (boardIndex, cb) {
    // Get all of the information about the boards you have access to
    var boardId = boards[boardIndex].id;
    var success = function(data) {
      boards[boardIndex].cards = data;
      //Call the callback when all the boards has got their cards
      if(++loadingCounter >= boards.length){
        loadCardLabels(boardIndex, cb);
      }
      else{
        loadCardLabels(boardIndex, cb);
      }
    };
    var error = function(errorMsg) {
      console.log(errorMsg);
    };
    Trello.get('/boards/' + boardId + '/cards', success, error);
  };

  // Load labels from firebase
  var loadCardLabels = function(boardIndex, cb) {
    var boardId = boards[boardIndex].id;
    var ref = firebase.database().ref().child(boardId);
    var obj = $firebaseArray(ref);

    obj.$loaded().then(function(){
      for(var i = 0; i < obj.length; i++) {
        var cardId = obj[i].$id;
        var cardIndex = findIndexOfCard(boardIndex, cardId);
        boards[boardIndex].cards[cardIndex].label = obj[i].label;
      }
      if(useCardStats){
        cardStats([], cb);
      }
      else {
        cb();
      }
    });
  }

  var myCards = function(boardIndex){
    boards[boardIndex].myCards = [];
    if(boards[boardIndex].cards == undefined){
      return;
    }
    for(var i = 0; i < boards[boardIndex].cards.length; i++) {
      var currentCard = boards[boardIndex].cards[i];
      var memberIds = currentCard.idMembers;
      for(var j = 0; j < memberIds.length; j++){
        if(memberIds[j] == userId){
          boards[boardIndex].myCards.push(currentCard);
        }
      }
    }
  };

  var cardStatsOneBoard = function (boardIndex) {
    //Add my cards to the board
    myCards(boardIndex);
    //Create array for holding the stats
    boards[boardIndex].cardStats = {
      highPriority : 0,
      mediumPriority : 0,
      lowPriority : 0
    };
    //Get the id of the list named "done"
    var doneListIdArray = boards[boardIndex].lists.filter(function (obj) {
      return obj.name.toLowerCase() == "done";
    });
    if(doneListIdArray.length != 0){
      var doneListId = doneListIdArray[0].id;
    }
    if(boards[boardIndex].cards == undefined){
      return;
    }
    //Loop through all the cards and add statistics for each
    for(var j = 0; j < boards[boardIndex].cards.length; j++) {

      var card = boards[boardIndex].cards[j];
      //Check the label of the card
      var label = card.label;

      // If there is no label
      if(label == undefined){
        boards[boardIndex].cardStats.lowPriority++;
        continue;
      }
      //Remove all the cards that are in the "done" list
      if(doneListId != undefined){
        if(card.idList == doneListId){
          continue;
        }
      }

      if(label.toLowerCase() == "high priority"){
        boards[boardIndex].cardStats.highPriority++;
      }
      else if(label.toLowerCase() == "medium priority"){
        boards[boardIndex].cardStats.mediumPriority++;
      }
      else if(label.toLowerCase() == "low priority"){
        boards[boardIndex].cardStats.lowPriority++;
      }
      else {
        //If it is the last label and it is none of the above then give it low priority
        boards[boardIndex].cardStats.lowPriority++;
      }
    }
  }

  var cardStats = function (card, cb) {
    for(var i = 0; i < boards.length; i++){
      cardStatsOneBoard(i);
    }
    boardsLoaded = true;
    cb(card);
  };

  this.loadData = function (cb) {
    if(loggedIn){
      //Check if the boards are already loaded
      if(boardsLoaded){cb();}
      else{loadBoards(cb);}
    }else{
      //Authorize the user if not logged in
      this.authorize(cb, true);
    }
  };

  this.getBoards = function () {
    //Check if boards are loaded
    if(boardsLoaded){
      return boards;
    }
  };

  // Get board with specific id from the ones loaded.
  this.getBoard = function(id) {
    for(var i = 0; i < boards.length; i++) {
      if(boards[i].id == id)
        return boards[i];
    }
    return false; // If non existant
  }

  // Give the board with the id a new name
  this.changeBoardName = function(id, newName) {
    Trello.put('boards/'+id+'/name?value='+newName);
  }

  // Add lables to specific board
  var addLables = function(boardId, cb) {
    for(var i = 0; i < lables.length; i++){
        var l = lables[i];
        Trello.post('/boards/'+boardId+'/labels?name='+l.name+'&color='+l.color);
    }
    cb();
  };

  this.createNewBoard = function(cb) {
    Trello.post('/boards?name=New Project&defaultLists=false', function(board) {
      for(var i = 0; i < listTypes.length; i++) {
        console.log(listTypes[i]);
        //Make sure that all the API calls has been done before adding the board
        if(i == listTypes.length-1) {
          Trello.post('/lists?idBoard='+board.id+'&name='+listTypes[i], function () {
            boards.push(board); // Add new board to array
            loadLists(boards.length-1, function(){
              cb(board.id);
            });
          });
        }else {
          //Adds lists to board
          Trello.post('/lists?idBoard='+board.id+'&name='+listTypes[i])
        }
      }
    });
  };

  this.addBoard = function(board) {
    boards.push(board);
  };

  // Adds a new card to the api
  this.addNewCard = function(boardId, listName, cardName, cb) {
    // Go throught all boards
    for(var i = 0; i < boards.length; i++){
      // Find board with the correct id
      if(boards[i].id == boardId) {
        // Check if the list exist (If board was created on trello)
        var list = getList(boards[i].lists, listName);
        // Create list if it didn't exist
        if(list == null) {
          Trello.post('/lists?idBoard='+boardId+'&name='+listName, function(newList){
            boards[findBoardIndex(boardId)].lists.push(newList);
            addCardToList(newList.id, boardId, cardName, cb);
          });
        } else {
          addCardToList(list.id, boardId, cardName, cb);
        }
      }
    }
  };

  // Adds a card to the given list
  function addCardToList(listId, boardId, cardName, cb){
    // Add new card to API
    Trello.post('cards?idList='+listId+"&name="+cardName+'&idMembers='+userId, function (card) {
      //When the card is added to the API, add the card to our model
      boards[findBoardIndex(boardId)].cards.push(card);
      cardStats(card, cb);
      //cb();
    }, function (errorMsg) {
      console.log(errorMsg)
    });
  }

  // Return the lists from the board
  // Return null if it don't exist
  var getList = function(lists, listName) {
    for(var i = 0; i < lists.length; i++) {
      if(lists[i].name == listName){
        return lists[i];
      }
    }
    return null;
  }

  var findBoardIndex = function(boardId) {
    for(var i = 0; i < boards.length; i++){
      if(boards[i].id == boardId){
        return i;
      }
    }
  }

  this.isLoggedIn = function () {
    return loggedIn;
  };

  this.boardsLoaded = function () {
    return boardsLoaded;
  };

  // Returns the list with the name from the board with the id
  this.getListId = function(boardId, listName) {
    for(var i = 0; i < boards.length; i++) {
      if(boards[i].id == boardId) {
        for(var j = 0; j < boards[i].lists.length; j++) {
          if(boards[i].lists[j].name == listName) {
            return boards[i].lists[j].id;
          }
        }
      }
    }
    return listTypes;
  };

  this.getListsToShow = function() {
    return listTypes;
  };

  //Adds description to the card
  this.addDescriptionToCard = function(card){
    Trello.put("cards/"+card.id+"/desc?value="+card.desc);
  };

  this.changeNameOfCard = function (card) {
    Trello.put("cards/"+card.id +"/name?value="+card.name);
  };

  var getLabelId = function (boardId, labelName) {
    var board = boards[findBoardIndex(boardId)];
    var labels = board.labels;
    var correctLabel = labels.filter(function (label) {
      return label.name.toLowerCase() == labelName.toLowerCase();
    })[0];
    return correctLabel.id;
  };

  var findIndexOfCard = function (boardIndex, cardId) {
    for(var i = 0; i < boards[boardIndex].cards.length; i++){
      if(boards[boardIndex].cards[i].id == cardId){
        return i;
      }
    }
  };

  var findLabelColor = function (labelName) {
    for(var i = 0; i < lables.length; i++){
      if(lables[i].name.toLowerCase() == labelName.toLowerCase()){
        return lables[i].color;
      }
    }
  };

  this.changeLabelOfCard = function (boardId, cardId, label) {
    var boardIndex = findBoardIndex(boardId);
    var cardIndex = findIndexOfCard(boardIndex, cardId);
    boards[boardIndex].cards[cardIndex].label = label;
    cardStatsOneBoard(boardIndex); // Update cardstats
  };

  this.deleteCard = function (boardId, cardId) {
    Trello.delete("cards/"+cardId);
    //Also delete from the model
    for(var i = 0; i < boards.length; i++){
      if(boards[i].id == boardId){
        boards[i].cards = boards[i].cards.filter(function (card) {
          return card.id != cardId;
        });
        boards[i].myCards = boards[i].myCards.filter(function (card){
          return card.id != cardId;
        });
      }
    }
  };

  this.deleteBoard = function(boardId) {
    Trello.delete("boards/"+boardId);
    for(var i = 0; i < boards.length; i++) {
      if(boards[i].id == boardId){
        boards.splice(i, 1);
      }
    }
  };

  this.moveCard =function(card, listName, cb){
		var idList = this.getListId(card.idBoard, listName);

    // If list not found create it first
    if(idList == listTypes) {
      Trello.post('/lists?idBoard='+card.idBoard+'&name='+listName, function(newList){
        boards[findBoardIndex(card.idBoard)].lists.push(newList);
        moveToDone(card, newList.id, cb);
      });
    }else {
      moveToDone(card, idList, cb);
    }
	}

  var moveToDone = function(card, idList, cb) {
    Trello.put("cards/"+card.id+"/idList?value="+idList);
    for(var i=0; i< boards.length; i++){
      for(var j=0; j< boards[i].cards.length; j++){
        if(boards[i].cards[j].id == card.id)
            boards[i].cards[j].idList= idList;
            cb();
      }
    }
  }

  // The different lables that we use
  var lables =
  [{
    name: 'Low Priority',
    color: 'green'
  },
  {
    name: 'Medium Priority',
    color: 'yellow'
  },
  {
    name: 'High Priority',
    color: 'red'
  }];
  return this;
});
<commit_msg>Removed unused stuff<commit_after>/**
 * Created by martin on 2017-03-26.
 */
app.factory('CardService', function ($cookies, $resource, $firebaseArray) {

  var useCardStats = true;
  var loggedIn = false;
  var boardsLoaded = false;
  var boards = [];
  var loadingCounter = 0;
  var listTypes = ['To Do','In Progress','Verifying','Done'];
  var userId;

  //Authorize to the trello api
  this.authorize = function(cb, shouldLoadBoards) {
    Trello.authorize({
      type: 'popup',
      name: 'Getting Started Application',
      scope: {
        read: 'true',
        write: 'true' },
      expiration: 'never',
      success: function() {
        loggedIn = true;
        if(shouldLoadBoards){
          loadBoards(cb);
        }else{
          cb();
        }
      },
      error: function() { console.log('Failed authentication'); }
    });
  };

  this.logout = function () {
    loggedIn = false;
  }

  var loadBoards = function (cb) {
    // Get all of the information about the boards you have access to
    var success = function(data) {
      boards = data;
      for(var i = 0; i < boards.length; i++){
        loadLists(i, cb);
      }
    };
    var error = function(errorMsg) {
      console.log(errorMsg);
    };
    //First load the user Id
    Trello.get('/member/me', function (data) {
      userId = data.id;
      Trello.get('/member/me/boards', success, error);
    });
  };

  var loadLists = function (boardIndex, cb) {
    var boardId = boards[boardIndex].id;
    var success = function (data) {
      boards[boardIndex].lists = data;
      loadCards(boardIndex, cb);
    };
    var error = function(errorMsg) {
      console.log(errorMsg);
    };
    Trello.get('/boards/' + boardId + '/lists', success, error);
  };

  var loadCards = function (boardIndex, cb) {
    // Get all of the information about the boards you have access to
    var boardId = boards[boardIndex].id;
    var success = function(data) {
      boards[boardIndex].cards = data;
      //Call the callback when all the boards has got their cards
      if(++loadingCounter >= boards.length){
        loadCardLabels(boardIndex, cb);
      }
      else{
        loadCardLabels(boardIndex, cb);
      }
    };
    var error = function(errorMsg) {
      console.log(errorMsg);
    };
    Trello.get('/boards/' + boardId + '/cards', success, error);
  };

  // Load labels from firebase
  var loadCardLabels = function(boardIndex, cb) {
    var boardId = boards[boardIndex].id;
    var ref = firebase.database().ref().child(boardId);
    var obj = $firebaseArray(ref);

    obj.$loaded().then(function(){
      for(var i = 0; i < obj.length; i++) {
        var cardId = obj[i].$id;
        var cardIndex = findIndexOfCard(boardIndex, cardId);
        boards[boardIndex].cards[cardIndex].label = obj[i].label;
      }
      if(useCardStats){
        cardStats([], cb);
      }
      else {
        cb();
      }
    });
  }

  var myCards = function(boardIndex){
    boards[boardIndex].myCards = [];
    if(boards[boardIndex].cards == undefined){
      return;
    }
    for(var i = 0; i < boards[boardIndex].cards.length; i++) {
      var currentCard = boards[boardIndex].cards[i];
      var memberIds = currentCard.idMembers;
      for(var j = 0; j < memberIds.length; j++){
        if(memberIds[j] == userId){
          boards[boardIndex].myCards.push(currentCard);
        }
      }
    }
  };

  var cardStatsOneBoard = function (boardIndex) {
    //Add my cards to the board
    myCards(boardIndex);
    //Create array for holding the stats
    boards[boardIndex].cardStats = {
      highPriority : 0,
      mediumPriority : 0,
      lowPriority : 0
    };
    //Get the id of the list named "done"
    var doneListIdArray = boards[boardIndex].lists.filter(function (obj) {
      return obj.name.toLowerCase() == "done";
    });
    if(doneListIdArray.length != 0){
      var doneListId = doneListIdArray[0].id;
    }
    if(boards[boardIndex].cards == undefined){
      return;
    }
    //Loop through all the cards and add statistics for each
    for(var j = 0; j < boards[boardIndex].cards.length; j++) {

      var card = boards[boardIndex].cards[j];
      //Check the label of the card
      var label = card.label;

      // If there is no label
      if(label == undefined){
        boards[boardIndex].cardStats.lowPriority++;
        continue;
      }
      //Remove all the cards that are in the "done" list
      if(doneListId != undefined){
        if(card.idList == doneListId){
          continue;
        }
      }

      if(label.toLowerCase() == "high priority"){
        boards[boardIndex].cardStats.highPriority++;
      }
      else if(label.toLowerCase() == "medium priority"){
        boards[boardIndex].cardStats.mediumPriority++;
      }
      else if(label.toLowerCase() == "low priority"){
        boards[boardIndex].cardStats.lowPriority++;
      }
      else {
        //If it is the last label and it is none of the above then give it low priority
        boards[boardIndex].cardStats.lowPriority++;
      }
    }
  }

  var cardStats = function (card, cb) {
    for(var i = 0; i < boards.length; i++){
      cardStatsOneBoard(i);
    }
    boardsLoaded = true;
    cb(card);
  };

  this.loadData = function (cb) {
    if(loggedIn){
      //Check if the boards are already loaded
      if(boardsLoaded){cb();}
      else{loadBoards(cb);}
    }else{
      //Authorize the user if not logged in
      this.authorize(cb, true);
    }
  };

  this.getBoards = function () {
    //Check if boards are loaded
    if(boardsLoaded){
      return boards;
    }
  };

  // Get board with specific id from the ones loaded.
  this.getBoard = function(id) {
    for(var i = 0; i < boards.length; i++) {
      if(boards[i].id == id)
        return boards[i];
    }
    return false; // If non existant
  }

  // Give the board with the id a new name
  this.changeBoardName = function(id, newName) {
    Trello.put('boards/'+id+'/name?value='+newName);
  }

  // // Add lables to specific board
  // var addLables = function(boardId, cb) {
  //   for(var i = 0; i < lables.length; i++){
  //       var l = lables[i];
  //       Trello.post('/boards/'+boardId+'/labels?name='+l.name+'&color='+l.color);
  //   }
  //   cb();
  // };

  this.createNewBoard = function(cb) {
    Trello.post('/boards?name=New Project&defaultLists=false', function(board) {
      for(var i = 0; i < listTypes.length; i++) {
        console.log(listTypes[i]);
        //Make sure that all the API calls has been done before adding the board
        if(i == listTypes.length-1) {
          Trello.post('/lists?idBoard='+board.id+'&name='+listTypes[i], function () {
            boards.push(board); // Add new board to array
            loadLists(boards.length-1, function(){
              cb(board.id);
            });
          });
        }else {
          //Adds lists to board
          Trello.post('/lists?idBoard='+board.id+'&name='+listTypes[i])
        }
      }
    });
  };

  this.addBoard = function(board) {
    boards.push(board);
  };

  // Adds a new card to the api
  this.addNewCard = function(boardId, listName, cardName, cb) {
    // Go throught all boards
    for(var i = 0; i < boards.length; i++){
      // Find board with the correct id
      if(boards[i].id == boardId) {
        // Check if the list exist (If board was created on trello)
        var list = getList(boards[i].lists, listName);
        // Create list if it didn't exist
        if(list == null) {
          Trello.post('/lists?idBoard='+boardId+'&name='+listName, function(newList){
            boards[findBoardIndex(boardId)].lists.push(newList);
            addCardToList(newList.id, boardId, cardName, cb);
          });
        } else {
          addCardToList(list.id, boardId, cardName, cb);
        }
      }
    }
  };

  // Adds a card to the given list
  function addCardToList(listId, boardId, cardName, cb){
    // Add new card to API
    Trello.post('cards?idList='+listId+"&name="+cardName+'&idMembers='+userId, function (card) {
      //When the card is added to the API, add the card to our model
      boards[findBoardIndex(boardId)].cards.push(card);
      cardStats(card, cb);
      //cb();
    }, function (errorMsg) {
      console.log(errorMsg)
    });
  }

  // Return the lists from the board
  // Return null if it don't exist
  var getList = function(lists, listName) {
    for(var i = 0; i < lists.length; i++) {
      if(lists[i].name == listName){
        return lists[i];
      }
    }
    return null;
  }

  var findBoardIndex = function(boardId) {
    for(var i = 0; i < boards.length; i++){
      if(boards[i].id == boardId){
        return i;
      }
    }
  }

  this.isLoggedIn = function () {
    return loggedIn;
  };

  this.boardsLoaded = function () {
    return boardsLoaded;
  };

  // Returns the list with the name from the board with the id
  this.getListId = function(boardId, listName) {
    for(var i = 0; i < boards.length; i++) {
      if(boards[i].id == boardId) {
        for(var j = 0; j < boards[i].lists.length; j++) {
          if(boards[i].lists[j].name == listName) {
            return boards[i].lists[j].id;
          }
        }
      }
    }
    return listTypes;
  };

  this.getListsToShow = function() {
    return listTypes;
  };

  //Adds description to the card
  this.addDescriptionToCard = function(card){
    Trello.put("cards/"+card.id+"/desc?value="+card.desc);
  };

  this.changeNameOfCard = function (card) {
    Trello.put("cards/"+card.id +"/name?value="+card.name);
  };

  // var getLabelId = function (boardId, labelName) {
  //   var board = boards[findBoardIndex(boardId)];
  //   var labels = board.labels;
  //   var correctLabel = labels.filter(function (label) {
  //     return label.name.toLowerCase() == labelName.toLowerCase();
  //   })[0];
  //   return correctLabel.id;
  // };

  var findIndexOfCard = function (boardIndex, cardId) {
    for(var i = 0; i < boards[boardIndex].cards.length; i++){
      if(boards[boardIndex].cards[i].id == cardId){
        return i;
      }
    }
  };

  // var findLabelColor = function (labelName) {
  //   for(var i = 0; i < lables.length; i++){
  //     if(lables[i].name.toLowerCase() == labelName.toLowerCase()){
  //       return lables[i].color;
  //     }
  //   }
  // };

  this.changeLabelOfCard = function (boardId, cardId, label) {
    var boardIndex = findBoardIndex(boardId);
    var cardIndex = findIndexOfCard(boardIndex, cardId);
    boards[boardIndex].cards[cardIndex].label = label;
    cardStatsOneBoard(boardIndex); // Update cardstats
  };

  this.deleteCard = function (boardId, cardId) {
    Trello.delete("cards/"+cardId);
    //Also delete from the model
    for(var i = 0; i < boards.length; i++){
      if(boards[i].id == boardId){
        boards[i].cards = boards[i].cards.filter(function (card) {
          return card.id != cardId;
        });
        boards[i].myCards = boards[i].myCards.filter(function (card){
          return card.id != cardId;
        });
      }
    }
  };

  this.deleteBoard = function(boardId) {
    Trello.delete("boards/"+boardId);
    for(var i = 0; i < boards.length; i++) {
      if(boards[i].id == boardId){
        boards.splice(i, 1);
      }
    }
  };

  this.moveCard =function(card, listName, cb){
		var idList = this.getListId(card.idBoard, listName);

    // If list not found create it first
    if(idList == listTypes) {
      Trello.post('/lists?idBoard='+card.idBoard+'&name='+listName, function(newList){
        boards[findBoardIndex(card.idBoard)].lists.push(newList);
        moveToDone(card, newList.id, cb);
      });
    }else {
      moveToDone(card, idList, cb);
    }
	}

  var moveToDone = function(card, idList, cb) {
    Trello.put("cards/"+card.id+"/idList?value="+idList);
    for(var i=0; i< boards.length; i++){
      for(var j=0; j< boards[i].cards.length; j++){
        if(boards[i].cards[j].id == card.id)
            boards[i].cards[j].idList= idList;
            cb();
      }
    }
  }

  // // The different lables that we use
  // var lables =
  // [{
  //   name: 'Low Priority',
  //   color: 'green'
  // },
  // {
  //   name: 'Medium Priority',
  //   color: 'yellow'
  // },
  // {
  //   name: 'High Priority',
  //   color: 'red'
  // }];
  return this;
});
<|endoftext|>


<commit_before>'use strict'

import { Component }   from 'react'
import DocumentTitle   from 'react-document-title'
//import { UAParser }    from 'ua-parser-js'

import {installationLinks} from '../config'

class DownloadsPage extends Component {

  constructor(props) {
    super(props)
  }

  render() {
    /*const parser = new UAParser()
    const result = parser.getResult()
    const browserName = result.browser.name
    const osName = result.os.name*/

    return (
      <DocumentTitle title="Install Blockstack">

                      Install Blockstack

Browser Portal

                            Developer alpha

                                Releases on GitHub

                            macOS

                                Coming Soon

                            Linux

                                Coming Soon

                            Windows

                                Coming Soon

Core Server + CLI

                          Instructions on GitHub

      </DocumentTitle>
    )
  }

}

export default DownloadsPage
<commit_msg>Server + CLI -> Run a Full Node<commit_after>'use strict'

import { Component }   from 'react'
import DocumentTitle   from 'react-document-title'
//import { UAParser }    from 'ua-parser-js'

import {installationLinks} from '../config'

class DownloadsPage extends Component {

  constructor(props) {
    super(props)
  }

  render() {
    /*const parser = new UAParser()
    const result = parser.getResult()
    const browserName = result.browser.name
    const osName = result.os.name*/

    return (
      <DocumentTitle title="Install Blockstack">

                      Install Blockstack

Browser Portal

                            Developer alpha

                                Releases on GitHub

                            macOS

                                Coming Soon

                            Linux

                                Coming Soon

                            Windows

                                Coming Soon

Run a Full Node

                          Instructions on GitHub

      </DocumentTitle>
    )
  }

}

export default DownloadsPage
<|endoftext|>


<commit_before>'use strict';
/*
 * Copyright 2015 Next Century Corporation
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

angular.module("neonDemo.services")
.factory("TranslationService", ["config", "$http", "$q", "ConnectionService", function(config, $http, $q, connectionService) {
    var service = {};

    var apis = {
        google: {
            base: "https://www.googleapis.com/language/translate/v2",
            key: (config.translationKeys) ? config.translationKeys.google : undefined,
            methods: {
                translate: "",
                detect: "/detect",
                languages: "/languages"
            },
            params: {
                key: "key",
                from: "source",
                to: "target",
                text: "q",
                other: ["format=text"]
            },
            languages: {}
        }
    };

    var chosenApi;
    var translationCache = {};

    /**
     * Sets the default translation service.
     * @param {String} serviceName Name of the service to set as default.
     * @param {Function} successCallback
     * @param {Function} failureCallback
     * @method setService
     */
    service.setService = function(serviceName, successCallback, failureCallback) {
        chosenApi = serviceName;

        if(!apis[chosenApi].key) {
            if(failureCallback) {
                failureCallback({
                    message: "No key available",
                    reason: "No key set for " + chosenApi
                });
            }
        } else if(!apis[chosenApi].languages || _.keys(apis[chosenApi].languages).length === 0) {
            setSupportedLanguages().then(successCallback, failureCallback);
        } else {
            if(successCallback) {
                successCallback();
            }
        }
    };

    /**
     * Returns all the available translation services.
     * @method getAllServices
     * @return {Array} List of all the translation services.
     */
    service.getAllServices = function() {
        return _.keys(apis);
    };

    /**
     * If the service being used has an API key.
     * @method hasKey
     * @return {Boolean} True if there is an API key being used, false otherwise.
     */
    service.hasKey = function() {
        return apis[chosenApi].key ? true : false;
    };

    /**
     * Translates all strings in text with language code specified in 'from' to the language
     * code specified in 'to'. If no 'from' is provided, it will be automatically detected.
     * @param {Array} text List of strings to translate
     * @param {String} to Language code to translate all text to.
     * @param {Function} successCallback
     * @param {Function} failureCallback
     * @param {String} [from] Optional language code that all the text are in. If none is
     * provided then it will be detected for each string individually in the text array.
     * @method translate
     */
    service.translate = function(text, to, successCallback, failureCallback, from) {
        if(!apis[chosenApi].key) {
            failureCallback({
                message: "Key not provided",
                reason: "Key not provided"
            });
        } else if(!text.length) {
            failureCallback({
                message: "No text provided",
                reason: "No text provided"
            });
        } else {
            translationCache[to] = translationCache[to] || {};

            var translateCallback = function() {
                var deferred = $q.defer();

                var params = apis[chosenApi].params.key + "=" + apis[chosenApi].key;

                apis[chosenApi].params.other.forEach(function(param) {
                    params += "&" + param;
                });

                var cached = [];

                text.forEach(function(elem) {
                    if(translationCache[to][elem]) {
                        // Add a blank parameter so their indices match the indices of the list of cached translations.
                        params += "&" + apis[chosenApi].params.text + "=";
                        cached.push(translationCache[to][elem]);
                    } else {
                        params += "&" + apis[chosenApi].params.text + "=" + encodeURIComponent(elem);
                        // Add a blank element to the list of cached translations so its indices match the indices of the parameters.
                        cached.push("");
                    }
                });

                if(!to || !apis[chosenApi].languages[to]) {
                    deferred.reject({
                        message: "Unknown target language",
                        reason: "Unknown target language"
                    });
                    return deferred.promise;
                }

                params += "&" + apis[chosenApi].params.to + "=" + to;

                // If no 'from' (source) language is given, each text is auto-detected individually.
                // If it does exist, check that the language code is in the set of supported languages
                if(from && !apis[chosenApi].languages[from]) {
                    deferred.reject({
                        message: "Unknown source language",
                        reason: "Unknown source language"
                    });
                    return deferred.promise;
                } else if(from) {
                    params += "&" + apis[chosenApi].params.from + "=" + from;
                }

                $http.get(apis[chosenApi].base + apis[chosenApi].methods.translate + "?" + params)
                    .then(function(response) {
                        // Cache the translations for later use.
                        response.data.data.translations.forEach(function(item, index) {
                            translationCache[to][text[index]] = item.translatedText;
                        });
                        // Add the cached translations in the response data for the callback.
                        cached.forEach(function(item, index) {
                            if(item) {
                                response.data.data.translations[index].translatedText = item;
                            }
                        });
                        deferred.resolve(response);
                    }, function(response) {
                        deferred.reject({
                            message: response.data.error.message,
                            reason: concatErrorResponses(response.data.error.errors)
                        });
                    });

                return deferred.promise;
            };

            if(!apis[chosenApi].languages || _.keys(apis[chosenApi].languages).length === 0) {
                setSupportedLanguages().then(translateCallback().then(successCallback, failureCallback), failureCallback);
            } else {
                translateCallback().then(successCallback, failureCallback);
            }
        }
    };

    /**
     * Retrieves all languages supported by the default translation service.
     * @param {Function} successCallback
     * @param {Function} failureCallback
     * @method getSupportedLanguages
     */
    service.getSupportedLanguages = function(successCallback, failureCallback) {
        if(!apis[chosenApi].languages || _.keys(apis[chosenApi].languages).length === 0) {
            setSupportedLanguages().then(successCallback, failureCallback);
        } else {
            successCallback(apis[chosenApi].languages);
        }
    };

    /**
     * Retrieves and sets all languages supported by the default translation service.
     * @method setSupportedLanguages
     * @return {Promise}
     * @private
     */
    var setSupportedLanguages = function() {
        var deferred = $q.defer();

        var params = apis[chosenApi].params.key + "=" + apis[chosenApi].key +
            "&" + apis[chosenApi].params.to + "=en";

        $http.get(apis[chosenApi].base + apis[chosenApi].methods.languages + "?" + params)
            .then(function(response) {
                _.forEach(response.data.data.languages, function(elem) {
                    apis[chosenApi].languages[elem.language] = elem.name;
                });
                deferred.resolve(apis[chosenApi].languages);
            }, function(response) {
                deferred.reject({
                    message: response.data.error.message,
                    reason: concatErrorResponses(response.data.error.errors)
                });
            });

        return deferred.promise;
    };

    /**
     * Helper method to combine a list of errors and their reasons into one string.
     * @param {Array} errors Array of errors containing reasons for the error.
     * @param {String} errors[].reason Reason for a particular error.
     * @method concatErrorResponses
     * @return {String} All the error reasons in one string.
     * @private
     */
    var concatErrorResponses = function(errors) {
        var reasons = "Reasons:\n";
        _.forEach(errors, function(error) {
            reasons += error.reason + "\n";
        });
        return reasons;
    };

    /**
     * Loads the translation cache by asking the Neon server.
     * @method loadTranslationCache
     * @private
     */
    var loadTranslationCache = function() {
        var connection = connectionService.getActiveConnection();
        if(connection) {
            connection.getTranslationCache(function(response) {
                translationCache = JSON.parse(response);
            });
        }
    };

    /**
     * Saves the translation cache by sending it to the Neon server.
     * @method saveTranslationCache
     */
    service.saveTranslationCache = function() {
        var connection = connectionService.getActiveConnection();
        if(connection) {
            connection.setTranslationCache(translationCache);
        }
    };

    service.setService("google");

    loadTranslationCache();

    return service;
}]);
<commit_msg>NEON-1841 Fixed a minor bug in the translation service found during the code review.<commit_after>'use strict';
/*
 * Copyright 2015 Next Century Corporation
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

angular.module("neonDemo.services")
.factory("TranslationService", ["config", "$http", "$q", "ConnectionService", function(config, $http, $q, connectionService) {
    var service = {};

    var apis = {
        google: {
            base: "https://www.googleapis.com/language/translate/v2",
            key: (config.translationKeys) ? config.translationKeys.google : undefined,
            methods: {
                translate: "",
                detect: "/detect",
                languages: "/languages"
            },
            params: {
                key: "key",
                from: "source",
                to: "target",
                text: "q",
                other: ["format=text"]
            },
            languages: {}
        }
    };

    var chosenApi;
    var translationCache = {};

    /**
     * Sets the default translation service.
     * @param {String} serviceName Name of the service to set as default.
     * @param {Function} successCallback
     * @param {Function} failureCallback
     * @method setService
     */
    service.setService = function(serviceName, successCallback, failureCallback) {
        chosenApi = serviceName;

        if(!apis[chosenApi].key) {
            if(failureCallback) {
                failureCallback({
                    message: "No key available",
                    reason: "No key set for " + chosenApi
                });
            }
        } else if(!apis[chosenApi].languages || _.keys(apis[chosenApi].languages).length === 0) {
            setSupportedLanguages().then(successCallback, failureCallback);
        } else {
            if(successCallback) {
                successCallback();
            }
        }
    };

    /**
     * Returns all the available translation services.
     * @method getAllServices
     * @return {Array} List of all the translation services.
     */
    service.getAllServices = function() {
        return _.keys(apis);
    };

    /**
     * If the service being used has an API key.
     * @method hasKey
     * @return {Boolean} True if there is an API key being used, false otherwise.
     */
    service.hasKey = function() {
        return apis[chosenApi].key ? true : false;
    };

    /**
     * Translates all strings in text with language code specified in 'from' to the language
     * code specified in 'to'. If no 'from' is provided, it will be automatically detected.
     * @param {Array} text List of strings to translate
     * @param {String} to Language code to translate all text to.
     * @param {Function} successCallback
     * @param {Function} failureCallback
     * @param {String} [from] Optional language code that all the text are in. If none is
     * provided then it will be detected for each string individually in the text array.
     * @method translate
     */
    service.translate = function(text, to, successCallback, failureCallback, from) {
        if(!apis[chosenApi].key) {
            failureCallback({
                message: "Key not provided",
                reason: "Key not provided"
            });
        } else if(!text.length) {
            failureCallback({
                message: "No text provided",
                reason: "No text provided"
            });
        } else {
            translationCache[to] = translationCache[to] || {};

            var translateCallback = function() {
                var deferred = $q.defer();

                var params = apis[chosenApi].params.key + "=" + apis[chosenApi].key;

                apis[chosenApi].params.other.forEach(function(param) {
                    params += "&" + param;
                });

                var cached = [];

                text.forEach(function(elem) {
                    if(translationCache[to][elem]) {
                        // Add a blank parameter so their indices match the indices of the list of cached translations.
                        params += "&" + apis[chosenApi].params.text + "=";
                        cached.push(translationCache[to][elem]);
                    } else {
                        params += "&" + apis[chosenApi].params.text + "=" + encodeURIComponent(elem);
                        // Add a blank element to the list of cached translations so its indices match the indices of the parameters.
                        cached.push("");
                    }
                });

                if(!to || !apis[chosenApi].languages[to]) {
                    deferred.reject({
                        message: "Unknown target language",
                        reason: "Unknown target language"
                    });
                    return deferred.promise;
                }

                params += "&" + apis[chosenApi].params.to + "=" + to;

                // If no 'from' (source) language is given, each text is auto-detected individually.
                // If it does exist, check that the language code is in the set of supported languages
                if(from && !apis[chosenApi].languages[from]) {
                    deferred.reject({
                        message: "Unknown source language",
                        reason: "Unknown source language"
                    });
                    return deferred.promise;
                } else if(from) {
                    params += "&" + apis[chosenApi].params.from + "=" + from;
                }

                $http.get(apis[chosenApi].base + apis[chosenApi].methods.translate + "?" + params)
                    .then(function(response) {
                        // Cache the translations for later use.
                        response.data.data.translations.forEach(function(item, index) {
                            if(!cached[index]) {
                                translationCache[to][text[index]] = item.translatedText;
                            }
                        });
                        // Add the cached translations in the response data for the callback.
                        cached.forEach(function(item, index) {
                            if(item) {
                                response.data.data.translations[index].translatedText = item;
                            }
                        });
                        deferred.resolve(response);
                    }, function(response) {
                        deferred.reject({
                            message: response.data.error.message,
                            reason: concatErrorResponses(response.data.error.errors)
                        });
                    });

                return deferred.promise;
            };

            if(!apis[chosenApi].languages || _.keys(apis[chosenApi].languages).length === 0) {
                setSupportedLanguages().then(translateCallback().then(successCallback, failureCallback), failureCallback);
            } else {
                translateCallback().then(successCallback, failureCallback);
            }
        }
    };

    /**
     * Retrieves all languages supported by the default translation service.
     * @param {Function} successCallback
     * @param {Function} failureCallback
     * @method getSupportedLanguages
     */
    service.getSupportedLanguages = function(successCallback, failureCallback) {
        if(!apis[chosenApi].languages || _.keys(apis[chosenApi].languages).length === 0) {
            setSupportedLanguages().then(successCallback, failureCallback);
        } else {
            successCallback(apis[chosenApi].languages);
        }
    };

    /**
     * Retrieves and sets all languages supported by the default translation service.
     * @method setSupportedLanguages
     * @return {Promise}
     * @private
     */
    var setSupportedLanguages = function() {
        var deferred = $q.defer();

        var params = apis[chosenApi].params.key + "=" + apis[chosenApi].key +
            "&" + apis[chosenApi].params.to + "=en";

        $http.get(apis[chosenApi].base + apis[chosenApi].methods.languages + "?" + params)
            .then(function(response) {
                _.forEach(response.data.data.languages, function(elem) {
                    apis[chosenApi].languages[elem.language] = elem.name;
                });
                deferred.resolve(apis[chosenApi].languages);
            }, function(response) {
                deferred.reject({
                    message: response.data.error.message,
                    reason: concatErrorResponses(response.data.error.errors)
                });
            });

        return deferred.promise;
    };

    /**
     * Helper method to combine a list of errors and their reasons into one string.
     * @param {Array} errors Array of errors containing reasons for the error.
     * @param {String} errors[].reason Reason for a particular error.
     * @method concatErrorResponses
     * @return {String} All the error reasons in one string.
     * @private
     */
    var concatErrorResponses = function(errors) {
        var reasons = "Reasons:\n";
        _.forEach(errors, function(error) {
            reasons += error.reason + "\n";
        });
        return reasons;
    };

    /**
     * Loads the translation cache by asking the Neon server.
     * @method loadTranslationCache
     * @private
     */
    var loadTranslationCache = function() {
        var connection = connectionService.getActiveConnection();
        if(connection) {
            connection.getTranslationCache(function(response) {
                translationCache = JSON.parse(response);
            });
        }
    };

    /**
     * Saves the translation cache by sending it to the Neon server.
     * @method saveTranslationCache
     */
    service.saveTranslationCache = function() {
        var connection = connectionService.getActiveConnection();
        if(connection) {
            connection.setTranslationCache(translationCache);
        }
    };

    service.setService("google");

    loadTranslationCache();

    return service;
}]);
<|endoftext|>


<commit_before>import { isArray } from '@ember/array';
import { get, computed } from '@ember/object';
import Resource from 'ember-api-store/models/resource';
import { inject as service } from '@ember/service';

function portMatch(ports, equals, endsWith) {
  if (!isArray(ports)) {
    ports = [ports];
  }

  if (!isArray(equals)) {
    equals = [equals];
  }

  if (!isArray(endsWith)) {
    endsWith = [endsWith];
  }

  for (let i = 0; i < ports.length; i++) {
    let port = ports[i];
    if (equals.includes(port)) {
      return true;
    }

    for (let j = 0; j < endsWith.length; j++) {
      let suffix = endsWith[j] + '';
      let portStr = port + '';
      if (portStr !== suffix && portStr.endsWith(suffix)) {
        return true;
      }
    }
  }

  return false;
}

var PublicEndpoint = Resource.extend({
  globalStore: service(),
  scope: service(),

  portProto: computed('port', 'protocol', function () {
    let out = get(this,'port') + '/' + get(this,'protocol').toLowerCase();
    return out;
  }),

  // ip:port
  endpoint: computed('port', 'address', function() {
    const address = get(this, 'address');

    let out = '';
    if ( address && address !== 'NodePort' ) {
      out = address;
    } else {
      const globalStore = get(this, 'globalStore');
      const node = globalStore.all('node').findBy('clusterId', get(this,'scope.currentCluster.id'));
      if ( node ) {
        out = get(node, 'ipAddress');
      }
    }

    if (out) {
      out += ':' + get(this,'port');
    }

    return out;
  }),

  // [ip:]port[/udp]
  displayEndpoint: computed('port','protocol', function() {
    let out = '';
    out += get(this,'port');
    let proto = get(this,'protocol').toLowerCase();
    if (proto !== 'tcp') {
      out += '/' + proto;
    }
    return out;
  }),

  linkEndpoint: computed('isTcp', 'isMaybeSecure', 'displayEndpoint', function() {
    if (get(this,'isTcp')) {
      let out = get(this,'endpoint');

      if (get(this,'isMaybeSecure')) {
        out = 'https://' + out.replace(/:443$/, '');
      } else {
        out = 'http://' + out.replace(/:80$/, '');
      }

      return out;
    }
  }),

  isTcp: computed('protocol', function() {
    return get(this,'protocol').toLowerCase() === 'tcp';
  }),

  isMaybeSecure: computed('port', function() {
    return portMatch([get(this,'port')], [443, 8443], '443');
  }),
});

export default PublicEndpoint;
<commit_msg>New endpoint API<commit_after>import { isArray } from '@ember/array';
import { get, computed } from '@ember/object';
import Resource from 'ember-api-store/models/resource';
import { inject as service } from '@ember/service';

function portMatch(ports, equals, endsWith) {
  if (!isArray(ports)) {
    ports = [ports];
  }

  if (!isArray(equals)) {
    equals = [equals];
  }

  if (!isArray(endsWith)) {
    endsWith = [endsWith];
  }

  for (let i = 0; i < ports.length; i++) {
    let port = ports[i];
    if (equals.includes(port)) {
      return true;
    }

    for (let j = 0; j < endsWith.length; j++) {
      let suffix = endsWith[j] + '';
      let portStr = port + '';
      if (portStr !== suffix && portStr.endsWith(suffix)) {
        return true;
      }
    }
  }

  return false;
}

var PublicEndpoint = Resource.extend({
  globalStore: service(),
  scope: service(),

  portProto: computed('port', 'protocol', function () {
    let out = get(this,'port') + '/' + get(this,'protocol').toLowerCase();
    return out;
  }),

  // ip:port
  endpoint: computed('port', 'addresses', 'allNodes', function() {
    const addresses = get(this, 'addresses');
    const allNodes = get(this, 'allNodes');

    let out = '';
    if ( allNodes ) {
      const globalStore = get(this, 'globalStore');
      const node = globalStore.all('node').findBy('clusterId', get(this,'scope.currentCluster.id'));
      if ( node ) {
        out = get(node, 'ipAddress');
      }
    } else if ( addresses && addresses.length ) {
      out = addresses[0];
    }

    if (out) {
      out += ':' + get(this,'port');
    }

    return out;
  }),

  // port[/udp]
  displayEndpoint: computed('port','protocol', function() {
    let out = '';
    out += get(this,'port');
    let proto = get(this,'protocol').toLowerCase();
    if (proto !== 'tcp') {
      out += '/' + proto;
    }
    return out;
  }),

  linkEndpoint: computed('isTcp', 'isMaybeSecure', 'displayEndpoint', function() {
    if (get(this,'isTcp')) {
      let out = get(this,'endpoint');

      if (get(this,'isMaybeSecure')) {
        out = 'https://' + out.replace(/:443$/, '');
      } else {
        out = 'http://' + out.replace(/:80$/, '');
      }

      return out;
    }
  }),

  isTcp: computed('protocol', function() {
    return get(this,'protocol').toLowerCase() === 'tcp';
  }),

  isMaybeSecure: computed('port', function() {
    return portMatch([get(this,'port')], [443, 8443], '443');
  }),
});

export default PublicEndpoint;
<|endoftext|>


<commit_before>/// <binding AfterBuild='sass' />

var gulp = require('gulp');
var concat = require("gulp-concat");
var uglify = require('gulp-uglify');
var sass = require('gulp-sass');
var Builder = require('systemjs-builder');
var inlineNg2Template = require("gulp-inline-ng2-template");
var ts = require("gulp-typescript");
var tsProject = ts.createProject("./wwwroot/tsconfig.json");
var runSequence = require('run-sequence');
var tslint = require('gulp-tslint');
var ngc = require('gulp-ngc');
var rollup = require('rollup-stream');
var source = require('vinyl-source-stream');

//production publish task
gulp.task('prod', function (done) {
    runSequence('ngc', 'bundle-shims', 'bundle-app', 'bundle-setup-app', 'bundle-conduct-app', 'sass', 'bundle-css', function () {
        done();
    });
});

gulp.task('lint', function (done) {
    gulp.src("./wwwroot/app/**/*.ts")
        .pipe(tslint({formatter:"stylish"}))
        .pipe(tslint.report());
});

//sass to css
gulp.task("sass", function () {
    return gulp.src('./wwwroot/css/*.scss')
        .pipe(sass().on('error', sass.logError))
        .pipe(gulp.dest('./wwwroot/css'));
});

//sass to css continuous watch
gulp.task('watch', ['sass'], function () {
    gulp.watch('./wwwroot/css/*.scss', ['sass']);
});

//Bundle all css into bundle.css
//TODO: also minify it, and maybe solve relative import errors if any
gulp.task("bundle-css", function () {
    return gulp.src([
        './node_modules/bootstrap/dist/css/bootstrap.css',
        './node_modules/@angular/material/prebuilt-themes/indigo-pink.css',
        './wwwroot/css/style.css'
    ])
    .pipe(concat('bundle.css'))
    .pipe(gulp.dest('./wwwroot/dist'));
});

//Converts ts files to js with html template inline
gulp.task("tstojs", function () {
    return tsProject.src()
        .pipe(inlineNg2Template({ base: './wwwroot/', useRelativePaths: true }))
        .pipe(tsProject())
        .js.pipe(gulp.dest("./wwwroot/app"));
});

//bundle shim files
gulp.task('bundle-shims', function () {
    return gulp.src(['./node_modules/core-js/client/shim.js',
        './node_modules/zone.js/dist/zone.js',
        './node_modules/clipboard/dist/clipboard.js',
        './node_modules/jspdf/dist/jspdf.debug.js',
        './node_modules/jspdf-autotable/dist/jspdf.plugin.autotable.js',
        './node_modules/filesaver.js/FileSaver.js',
        './node_modules/exceljs/dist/exceljs.js',
        './node_modules/screenfull/dist/screenfull.js'
    ])
    .pipe(concat('shims.js'))
    .pipe(uglify())
    .pipe(gulp.dest('./wwwroot/dist'));
});

//bundle main dashboard app
gulp.task('ngc', function (done) {
    return ngc('./wwwroot/tsconfig.aot.json');
});

gulp.task('bundle-app', () => {
    return rollup('wwwroot/rollup-config.js')
        .pipe(source('app.bundle.js'))
        .pipe(gulp.dest('./wwwroot/dist'));
});

//bundle setup app
gulp.task('bundle-setup-app', function (done) {
    return rollup('wwwroot/rollup-config.setup.js')
        .pipe(source('app.setup.bundle.js'))
        .pipe(gulp.dest('./wwwroot/dist'));
});

//bundle conduct app
gulp.task('bundle-conduct-app', function (done) {
    return rollup('wwwroot/rollup-config.conduct.js')
        .pipe(source('app.conduct.bundle.js'))
        .pipe(gulp.dest('./wwwroot/dist'));
});<commit_msg>changed file saver path in gulpfile<commit_after>/// <binding AfterBuild='sass' />

var gulp = require('gulp');
var concat = require("gulp-concat");
var uglify = require('gulp-uglify');
var sass = require('gulp-sass');
var Builder = require('systemjs-builder');
var inlineNg2Template = require("gulp-inline-ng2-template");
var ts = require("gulp-typescript");
var tsProject = ts.createProject("./wwwroot/tsconfig.json");
var runSequence = require('run-sequence');
var tslint = require('gulp-tslint');
var ngc = require('gulp-ngc');
var rollup = require('rollup-stream');
var source = require('vinyl-source-stream');

//production publish task
gulp.task('prod', function (done) {
    runSequence('ngc', 'bundle-shims', 'bundle-app', 'bundle-setup-app', 'bundle-conduct-app', 'sass', 'bundle-css', function () {
        done();
    });
});

gulp.task('lint', function (done) {
    gulp.src("./wwwroot/app/**/*.ts")
        .pipe(tslint({formatter:"stylish"}))
        .pipe(tslint.report());
});

//sass to css
gulp.task("sass", function () {
    return gulp.src('./wwwroot/css/*.scss')
        .pipe(sass().on('error', sass.logError))
        .pipe(gulp.dest('./wwwroot/css'));
});

//sass to css continuous watch
gulp.task('watch', ['sass'], function () {
    gulp.watch('./wwwroot/css/*.scss', ['sass']);
});

//Bundle all css into bundle.css
//TODO: also minify it, and maybe solve relative import errors if any
gulp.task("bundle-css", function () {
    return gulp.src([
        './node_modules/bootstrap/dist/css/bootstrap.css',
        './node_modules/@angular/material/prebuilt-themes/indigo-pink.css',
        './wwwroot/css/style.css'
    ])
    .pipe(concat('bundle.css'))
    .pipe(gulp.dest('./wwwroot/dist'));
});

//Converts ts files to js with html template inline
gulp.task("tstojs", function () {
    return tsProject.src()
        .pipe(inlineNg2Template({ base: './wwwroot/', useRelativePaths: true }))
        .pipe(tsProject())
        .js.pipe(gulp.dest("./wwwroot/app"));
});

//bundle shim files
gulp.task('bundle-shims', function () {
    return gulp.src(['./node_modules/core-js/client/shim.js',
        './node_modules/zone.js/dist/zone.js',
        './node_modules/clipboard/dist/clipboard.js',
        './node_modules/jspdf/dist/jspdf.debug.js',
        './node_modules/jspdf-autotable/dist/jspdf.plugin.autotable.js',
        './node_modules/file-saver/FileSaver.js',
        './node_modules/exceljs/dist/exceljs.js',
        './node_modules/screenfull/dist/screenfull.js'
    ])
    .pipe(concat('shims.js'))
    .pipe(uglify())
    .pipe(gulp.dest('./wwwroot/dist'));
});

//bundle main dashboard app
gulp.task('ngc', function (done) {
    return ngc('./wwwroot/tsconfig.aot.json');
});

gulp.task('bundle-app', () => {
    return rollup('wwwroot/rollup-config.js')
        .pipe(source('app.bundle.js'))
        .pipe(gulp.dest('./wwwroot/dist'));
});

//bundle setup app
gulp.task('bundle-setup-app', function (done) {
    return rollup('wwwroot/rollup-config.setup.js')
        .pipe(source('app.setup.bundle.js'))
        .pipe(gulp.dest('./wwwroot/dist'));
});

//bundle conduct app
gulp.task('bundle-conduct-app', function (done) {
    return rollup('wwwroot/rollup-config.conduct.js')
        .pipe(source('app.conduct.bundle.js'))
        .pipe(gulp.dest('./wwwroot/dist'));
});<|endoftext|>


<commit_before>/* 提供建立 apidoc 文件自動化 */
class ApiDoc {
  constructor (docDir, defineDoc) {
    const fs = require(`fs`)

    if (!fs.existsSync(docDir)) throw Error(`docDir 資料夾不存在`)
    const stats = fs.statSync(docDir)
    if (!stats.isDirectory()) throw Error(`docDir 必須為資料夾`)

    this._docDir = docDir
    this._defineFile = `${this._docDir}/${defineDoc}`
    this._define = {}
    this._path = require(`path`)
    this._fs = require(`fs`)
  }

  _regDefine (name) {
    if (!name) throw Error(`請輸入 apiDefine 名稱`)
    if (this._define[name]) throw Error(`apiDefine ${name} 已定義`)
    this._define[name] = true
    return name
  }

  _toString (data) {
    if (_.isString(data)) return data
    if (_.isError(data)) return data.toString()
    return JSON.stringify(data, null, 2)
  }

  _ensureDirectoryExistence (filePath) {
    const dirPath = this._path.dirname(filePath)
    if (this._fs.existsSync(dirPath)) {
      return true
    }
    this._ensureDirectoryExistence(dirPath)
    this._fs.mkdirSync(dirPath)
  }

  _unlinkDirRecursively (filePath) {
    if (!this._fs.existsSync(filePath)) return

  }

  _write (filePath, data) {
    this._ensureDirectoryExistence(filePath)
    this._fs.appendFileSync(filePath, `${data}\n\n`, `utf8`)
  }

  _writeApiFile (group = ``, data) {
    this._write(`${this._docDir}/${group}Doc.js`, data)
  }

  _writeDefineFile (data) {
    this._write(this._defineFile, data)
  }

  genVersion (version) {
    return `@apiVersion ${version}`
  }

  genApi (opt) {
    if (!opt) return
    const path = opt.path || `-`
    let method = opt.method || ``
    let title = opt.title || ``
    method = method.toUpperCase()
    title = _.upperFirst(title)
    return `@api {${method}} ${path} ${title}`
  }

  genDescription (str) {
    if (!str) return
    str = _.upperFirst(str)
    return `@apiDescription ${str}`
  }

  genName (str) {
    if (!str) return
    str = _.upperFirst(str)
    return `@apiName ${str}`
  }

  genGroup (str) {
    if (!str) return
    str = _.upperFirst(str)
    return `@apiGroup ${str}`
  }

  genHeader (opt) {
    const field = opt.field
    let type = opt.type
    let desc = opt.desc
    type = _.upperFirst(type)
    desc = _.upperFirst(desc)
    return `@apiHeader (Header) {${type}} ${field} ${desc}`
  }

  genParam (opt) {
    const field = opt.field
    let group = `${opt.group ? _.upperFirst(opt.group) + ` ` : ``}Param`
    let type = opt.type
    let desc = opt.desc
    type = _.upperFirst(type)
    desc = _.upperFirst(desc)
    return `@apiParam (${group}) {${type}} ${field} ${desc}`
  }

  genSuccess (opt) {
    const data = opt.data
    const str = this._toString(data)
    let name = opt.name
    name = _.upperFirst(name)
    return `@apiSuccessExample ${name}\n${str}`
  }

  genError (opt) {
    const data = opt.data
    const str = this._toString(data)
    let name = opt.name
    name = _.upperFirst(name)
    return `@apiErrorExample ${name}\n${str}`
  }

  genDefine (opt) {
    const name = opt.name
    const data = opt.data
    this._regDefine(name)
    const str = this._toString(data)
    return `@apiDefine ${name}\n${str}`
  }

  genUse (name) {
    if (!name) return
    return `@apiUse ${name}`
  }

  outputApi (settingMap = {}) {
    const apiSampleRequestStr = `@apiSampleRequest off`
    const fnMap = {
      version: (setting) => {
        return this.genVersion(setting) + `\n`
      },
      api: (setting) => {
        return this.genApi(setting) + `\n`
      },
      description: (setting) => {
        return this.genDescription(setting) + `\n`
      },
      name: (setting) => {
        return this.genName(setting) + `\n`
      },
      group: (setting) => {
        return this.genGroup(setting) + `\n`
      },
      header: (settings) => {
        let str = ``
        for (const setting of settings) {
          str += this.genHeader(setting) + `\n`
        }
        return str
      },
      param: (settings) => {
        let str = ``
        for (const setting of settings) {
          str += this.genParam(setting) + `\n`
        }
        return str
      },
      success: (settings) => {
        let str = ``
        for (const setting of settings) {
          str += this.genSuccess(setting) + `\n`
        }
        return str
      },
      error: (settings) => {
        let str = ``
        for (const setting of settings) {
          str += this.genError(setting) + `\n`
        }
        return str
      },
      use: (settings) => {
        let str = ``
        for (const setting of settings) {
          str += this.genUse(setting) + `\n`
        }
        return str
      },
    }

    let str = `/**\n${apiSampleRequestStr}\n`
    for (const key in settingMap) {
      const fn = fnMap[key]
      if (!fn) continue
      const setting = settingMap[key]
      str += fn(setting)
    }
    str += `*/`
    this._writeApiFile(settingMap.group, str)
  }

  outputDefine (name, data) {
    let str = `/**\n`
    str += this.genDefine({name, data}) + `\n`
    str += `*/`
    this._writeDefineFile(str)
  }

  unlinkDocFiles () {
    const files = this._fs.readdirSync(this._docDir)
    for (const file of files) {
      const subPath = `${this._docDir}/${file}`
      this._unlinkDirRecursively(subPath)

      const stats = this._fs.statSync(subPath)
      if (!stats.isDirectory()) {
        this._fs.unlinkSync(subPath)
      }
    }
  }
}

module.exports = ApiDoc
<commit_msg>優化 ck.ApiDoc.outputApi 新增參數 paramExample<commit_after>/* 提供建立 apidoc 文件自動化 */
class ApiDoc {
  constructor (docDir, defineDoc) {
    const fs = require(`fs`)

    if (!fs.existsSync(docDir)) throw Error(`docDir 資料夾不存在`)
    const stats = fs.statSync(docDir)
    if (!stats.isDirectory()) throw Error(`docDir 必須為資料夾`)

    this._docDir = docDir
    this._defineFile = `${this._docDir}/${defineDoc}`
    this._define = {}
    this._path = require(`path`)
    this._fs = require(`fs`)
  }

  _regDefine (name) {
    if (!name) throw Error(`請輸入 apiDefine 名稱`)
    if (this._define[name]) throw Error(`apiDefine ${name} 已定義`)
    this._define[name] = true
    return name
  }

  _toString (data) {
    if (_.isString(data)) return data
    if (_.isError(data)) return data.toString()
    return JSON.stringify(data, null, 2)
  }

  _ensureDirectoryExistence (filePath) {
    const dirPath = this._path.dirname(filePath)
    if (this._fs.existsSync(dirPath)) {
      return true
    }
    this._ensureDirectoryExistence(dirPath)
    this._fs.mkdirSync(dirPath)
  }

  _unlinkDirRecursively (filePath) {
    if (!this._fs.existsSync(filePath)) return

  }

  _write (filePath, data) {
    this._ensureDirectoryExistence(filePath)
    this._fs.appendFileSync(filePath, `${data}\n\n`, `utf8`)
  }

  _writeApiFile (group = ``, data) {
    this._write(`${this._docDir}/${group}Doc.js`, data)
  }

  _writeDefineFile (data) {
    this._write(this._defineFile, data)
  }

  genVersion (version) {
    return `@apiVersion ${version}`
  }

  genApi (opt) {
    if (!opt) return
    const path = opt.path || `-`
    let method = opt.method || ``
    let title = opt.title || ``
    method = method.toUpperCase()
    title = _.upperFirst(title)
    return `@api {${method}} ${path} ${title}`
  }

  genDescription (str) {
    if (!str) return
    str = _.upperFirst(str)
    return `@apiDescription ${str}`
  }

  genName (str) {
    if (!str) return
    str = _.upperFirst(str)
    return `@apiName ${str}`
  }

  genGroup (str) {
    if (!str) return
    str = _.upperFirst(str)
    return `@apiGroup ${str}`
  }

  genHeader (opt) {
    const field = opt.field
    let type = opt.type
    let desc = opt.desc
    type = _.upperFirst(type)
    desc = _.upperFirst(desc)
    return `@apiHeader (Header) {${type}} ${field} ${desc}`
  }

  genParam (opt) {
    const field = opt.field
    let group = `${opt.group ? _.upperFirst(opt.group) + ` ` : ``}Param`
    let type = opt.type
    let desc = opt.desc
    type = _.upperFirst(type)
    desc = _.upperFirst(desc)
    return `@apiParam (${group}) {${type}} ${field} ${desc}`
  }

  genParamExample (opt) {
    const data = opt.data
    const str = this._toString(data)
    let name = opt.name
    name = _.upperFirst(name)
    return `@apiParamExample ${name}\n${str}`
  }

  genSuccess (opt) {
    const data = opt.data
    const str = this._toString(data)
    let name = opt.name
    name = _.upperFirst(name)
    return `@apiSuccessExample ${name}\n${str}`
  }

  genError (opt) {
    const data = opt.data
    const str = this._toString(data)
    let name = opt.name
    name = _.upperFirst(name)
    return `@apiErrorExample ${name}\n${str}`
  }

  genDefine (opt) {
    const name = opt.name
    const data = opt.data
    this._regDefine(name)
    const str = this._toString(data)
    return `@apiDefine ${name}\n${str}`
  }

  genUse (name) {
    if (!name) return
    return `@apiUse ${name}`
  }

  outputApi (settingMap = {}) {
    const apiSampleRequestStr = `@apiSampleRequest off`
    const fnMap = {
      version: (setting) => {
        return this.genVersion(setting) + `\n`
      },
      api: (setting) => {
        return this.genApi(setting) + `\n`
      },
      description: (setting) => {
        return this.genDescription(setting) + `\n`
      },
      name: (setting) => {
        return this.genName(setting) + `\n`
      },
      group: (setting) => {
        return this.genGroup(setting) + `\n`
      },
      header: (settings) => {
        let str = ``
        for (const setting of settings) {
          str += this.genHeader(setting) + `\n`
        }
        return str
      },
      param: (settings) => {
        let str = ``
        for (const setting of settings) {
          str += this.genParam(setting) + `\n`
        }
        return str
      },
      paramExample: (settings) => {
        let str = ``
        for (const setting of settings) {
          str += this.genParamExample(setting) + `\n`
        }
        return str
      },
      success: (settings) => {
        let str = ``
        for (const setting of settings) {
          str += this.genSuccess(setting) + `\n`
        }
        return str
      },
      error: (settings) => {
        let str = ``
        for (const setting of settings) {
          str += this.genError(setting) + `\n`
        }
        return str
      },
      use: (settings) => {
        let str = ``
        for (const setting of settings) {
          str += this.genUse(setting) + `\n`
        }
        return str
      },
    }

    let str = `/**\n${apiSampleRequestStr}\n`
    for (const key in settingMap) {
      const fn = fnMap[key]
      if (!fn) continue
      const setting = settingMap[key]
      str += fn(setting)
    }
    str += `*/`
    this._writeApiFile(settingMap.group, str)
  }

  outputDefine (name, data) {
    let str = `/**\n`
    str += this.genDefine({name, data}) + `\n`
    str += `*/`
    this._writeDefineFile(str)
  }

  unlinkDocFiles () {
    const files = this._fs.readdirSync(this._docDir)
    for (const file of files) {
      const subPath = `${this._docDir}/${file}`
      this._unlinkDirRecursively(subPath)

      const stats = this._fs.statSync(subPath)
      if (!stats.isDirectory()) {
        this._fs.unlinkSync(subPath)
      }
    }
  }
}

module.exports = ApiDoc
<|endoftext|>


<commit_before>var events = angular.module('pcApp.events', [
    'pcApp.events.controllers.event',
    'pcApp.metrics.directives.pcDatagrid',
    'pcApp.references.directives.forms'
]);

events.config(function($routeProvider) {
    $routeProvider
        .when('/events', {
            controller: 'EventsController',
            templateUrl: 'modules/events/partials/list.html'
        })
        .when('/events/create', {
            controller: 'EventCreateController',
            templateUrl: 'modules/events/partials/create.html'
        })
        .when('/events/:eventId', {
            controller: 'EventDetailController',
            templateUrl: 'modules/events/partials/detail.html'
        })
        .otherwise({ redirectTo: '/' });
});<commit_msg>fixed events.js<commit_after>var events = angular.module('pcApp.events', [
    'pcApp.events.controllers.event',
    //'pcApp.metrics.directives.pcDatagrid',
    'pcApp.references.directives.forms'
]);

events.config(function($routeProvider) {
    $routeProvider
        .when('/events', {
            controller: 'EventsController',
            templateUrl: 'modules/events/partials/list.html'
        })
        .when('/events/create', {
            controller: 'EventCreateController',
            templateUrl: 'modules/events/partials/create.html'
        })
        .when('/events/:eventId', {
            controller: 'EventDetailController',
            templateUrl: 'modules/events/partials/detail.html'
        })
        .otherwise({ redirectTo: '/' });
});<|endoftext|>