<commit_before>(function (angular) {
  'use strict';
  var Module = angular.module('datePicker', []);

  Module.directive('datePicker', function () {
    var viewOptions = ['month', 'date', 'year', 'month', 'hours', 'minutes'];

    function isValidDate(date) {
      return date instanceof Date && !isNaN(date.getTime());
    }

    function getVisibleMinutes(date) {
      date = new Date(date || new Date());
      date = new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours());
      var minutes = [];
      var step = 5;
      var stop = date.getTime() + 60 * 60 * 1000;
      while (date.getTime() < stop) {
        minutes.push(date);
        date = new Date(date.getTime() + step * 60 * 1000);
      }
      return minutes;
    }

    function getVisibleWeeks(date) {
      date = new Date(date || new Date());
      date.setDate(1);
      date.setHours(0);
      date.setMinutes(0);
      date.setSeconds(0);
      date.setMilliseconds(0);

      if (date.getDay() === 0) {
        date.setDate(-5);
      } else {
        date.setDate(date.getDate() - (date.getDay() - 1));
      }
      if (date.getDate() === 1) {
        date.setDate(-6);
      }

      var weeks = [];
      while (weeks.length < 6) {
        var week = [];
        for (var i = 0; i < 7; i++) {
          week.push(new Date(date));
          date.setDate(date.getDate() + 1);
        }
        weeks.push(week);
      }
      return weeks;
    }

    function getVisibleYears(date) {
      var years = [];
      date = new Date(date || new Date());
      date.setFullYear(date.getFullYear() - (date.getFullYear() % 10));
      for (var i = 0; i < 12; i++) {
        years.push(new Date(date.getFullYear() + (i - 1), 0, 1));
      }
      return years;
    }

    function getDaysOfWeek(date) {
      date = new Date(date || new Date());
      date = new Date(date.getFullYear(), date.getMonth(), date.getDate());
      date.setDate(date.getDate() - (date.getDay() - 1));
      var days = [];
      for (var i = 0; i < 7; i++) {
        days.push(new Date(date));
        date.setDate(date.getDate() + 1);
      }
      return days;
    }

    function getVisibleMonths(date) {
      date = new Date(date || new Date());
      var year = date.getFullYear();
      var months = [];
      for (var month = 0; month < 12; month++) {
        months.push(new Date(year, month, 1));
      }
      return months;
    }

    function getVisibleHours(date) {
      date = new Date(date || new Date());
      date.setHours(0);
      date.setMinutes(0);
      date.setSeconds(0);
      date.setMilliseconds(0);
      var hours = [];
      for (var i = 0; i < 24; i++) {
        hours.push(date);
        date = new Date(date.getTime() + 60 * 60 * 1000);
      }
      return hours;
    }

    return {
      scope      : {
        date  : '=datePicker',
        after : '=?',
        before: '=?'
      },
      link       : function (scope, element, attrs) {

        scope.views = [];
        for (var attr in attrs) { //noinspection JSUnfilteredForInLoop
          if (viewOptions.indexOf(attr) !== -1) { //noinspection JSUnfilteredForInLoop
            scope.views.push(attr);
          }
        }
        if (!scope.views.length) {
          scope.views = ['date', 'month', 'year', 'hours', 'minutes'];
        }
        scope.view = scope.views[0];

        function hasView(view) {
          return scope.views.indexOf(view) !== -1;
        }

        function ensureDate() {
          // we need to return new instance as ngModel $watch watches only for identity - not for equality
          if (!(isValidDate(scope.date))) {
            scope.date = new Date(2000, 1, 1);
          }
          scope.date = new Date(scope.date);
        }

        function setYear(date) {
          ensureDate();
          scope.date.setFullYear(date.getFullYear());
        }

        function setMonth(date) {
          setYear(date);
          scope.date.setMonth(date.getMonth());
        }

        function setDate(date) {
          setMonth(date);
          scope.date.setDate(date.getDate());
        }

        function setHours(date) {
          setDate(date);
          scope.date.setHours(date.getHours());
        }

        function setMinutes(date) {
          setHours(date);
          scope.date.setMinutes(date.getMinutes());
        }

        scope.setYear = function (date) {
          setYear(date);
          scope.$emit('setYear', date);
        };

        scope.setMonth = function (date) {
          setMonth(date);
          scope.$emit('setMonth', date);
        };

        scope.setDate = function (date) {
          setDate(date);
          scope.$emit('setDate', date);
        };

        scope.setHours = function (date) {
          setHours(date);
          scope.$emit('setHours', date);
        };

        scope.setMinutes = function (date) {
          setMinutes(date);
          scope.$emit('setMinutes', date);
        };

        scope.setView = function setView(view) {
          if (hasView(view)) {
            scope.view = view;
            switch (view) {
            case 'minutes':
              scope.minutes = getVisibleMinutes(scope.visibleDate);
              break;
            case 'hours'  :
              scope.hours = getVisibleHours(scope.visibleDate);
              break;
            case 'date'   :
              scope.weeks = getVisibleWeeks(scope.visibleDate);
              break;
            case 'month'  :
              scope.months = getVisibleMonths(scope.visibleDate);
              break;
            case 'year'   :
              scope.years = getVisibleYears(scope.visibleDate);
              break;
            }
          }
        };

        scope.nextMonth = function (delta) {
          scope.visibleDate.setMonth(scope.visibleDate.getMonth() + (delta || 1));
        };

        scope.prevMonth = function (delta) {
          scope.nextMonth(-delta || -1);
        };

        scope.nextDay = function (delta) {
          scope.visibleDate.setDate(scope.visibleDate.getDate() + (delta || 1));
        };

        scope.prevDay = function (delta) {
          scope.nextDay(-delta || -1);
        };

        scope.nextHour = function (delta) {
          scope.visibleDate.setHours(scope.visibleDate.getHours() + (delta || 1));
        };

        scope.prevHour = function (delta) {
          scope.nextHour(-delta || -1);
        };

        scope.nextYear = function (delta) {
          scope.visibleDate.setFullYear(scope.visibleDate.getFullYear() + (delta || 1));
        };
        scope.prevYear = function (delta) {
          scope.nextYear(-delta || -1);
        };

        scope.visibleDate = new Date();

        scope.$watch('date', function (date) {
          if (date) {
            scope.visibleDate = new Date(date);
          }
        });

        scope.isAfter = function (date) {
          return date >= scope.after;
        };

        scope.isBefore = function (date) {
          return date <= scope.before;
        };

        function validDate() {
          return scope.date instanceof Date;
        }

        scope.isSameMinutes = function (date) {
          if (!validDate()){
            return false;
          }
          var b = scope.date;
          return (date.getTime() - date.getSeconds() * 1000 - date.getMilliseconds()) === (b.getTime() - b.getSeconds() * 1000 - b.getMilliseconds());
        };

        scope.isSameMonth = function (date) {
          if (!validDate()){
            return false;
          }
          return date.getFullYear() === scope.date.getFullYear() && date.getMonth() === scope.date.getMonth();
        };

        scope.isSameYear = function (date) {
          if (!validDate()){
            return false;
          }
          return date.getFullYear() === scope.date.getFullYear();
        };

        scope.isSameDate = function (date) {
          if (!validDate()){
            return false;
          }
          return scope.date.getDate() === date.getDate() && scope.isSameMonth(date);
        };

        scope.isSameHour = function (date) {
          if (!validDate()){
            return false;
          }
          return scope.date.getHours() === date.getHours() && scope.isSameDate(date);
        };

        scope.isOldMonth = function (date) {
          return date
            .getTime() < scope.visibleDate.getTime() && date.getMonth() !== scope.visibleDate.getMonth();
        };

        scope.isNewHour = function (date) {
          return date.getTime() > scope.visibleDate.getTime() && date.getHours() !== scope.visibleDate.getHours();
        };

        scope.isOldHour = function (date) {
          return date.getTime() < scope.visibleDate.getTime() && date.getHours() !== scope.visibleDate.getHours();
        };

        scope.isNewMonth = function (date) {
          return date.getTime() > scope.visibleDate.getTime() && date.getMonth() !== scope.visibleDate.getMonth();
        };

        scope.$on('setDate', scope.setView.bind(null, 'hours'));
        scope.$on('setMonth', scope.setView.bind(null, 'date'));
        scope.$on('setHours', scope.setView.bind(null, 'minutes'));
        scope.$on('setYear', scope.setView.bind(null, 'month'));

        scope.$watch(function () {
          return isValidDate(scope.visibleDate);
        }, function (valid) {
          if (!valid) {
            scope.visibleDate = new Date();
          }
        });

        //hours
        scope.$watch('[visibleDate.getDate(),visibleDate.getHours()].join()', function () {
          if (scope.view === 'hours') {
            scope.hours = getVisibleHours(scope.visibleDate);
          }
        });
        //date
        scope.$watch('[visibleDate.getFullYear(),visibleDate.getMonth(),visibleDate.getDate()].join()', function () {
          if (scope.view === 'date') {
            scope.weeks = getVisibleWeeks(scope.visibleDate);
            scope.weekdays = getDaysOfWeek(scope.visibleDate);
          }
        });

        scope.$watch('[visibleDate.getFullYear(),visibleDate.getMonth()].join()', function () {
          if (scope.view === 'month') {
            scope.months = getVisibleMonths(scope.visibleDate);
          }
        });

        scope.$watch('visibleDate.getYear()', function () {
          if (scope.view === 'year') {
            scope.years = getVisibleYears(scope.visibleDate);
          }
        });

        scope.$watch('visibleDate.getTime()', function () {
          if (scope.view === 'minutes') {
            scope.minutes = getVisibleMinutes(scope.visibleDate);
          }
        });

      },
      replace:true,
      templateUrl: 'templates/datepicker.html'
    };
  });

  Module.directive('dateTime', function ($compile, $document, $filter) {
    var body = $document.find('body');
    var dateFilter = $filter('date');
    return {
      require: 'ngModel',
      link   : function (scope, element, attrs, ngModel) {
        var format = attrs.format || 'yyyy-MM-dd HH:mm';

        var viewsOptions = ['date', 'year', 'month', 'hours', 'minutes', 'month'];
        var views = [];
        for (var attr in attrs) {
          //noinspection JSUnfilteredForInLoop
          if (viewsOptions.indexOf(attr) !== -1) { //noinspection JSUnfilteredForInLoop
            views.push(attr);
          }
        }

        scope.$watch(''+attrs.ngModel+'.getTime()',function(a,b){
          if(a!==b){
            ngModel.$setViewValue(a);
          }
        });

        function formatter(value) {
          return dateFilter(value, format);
        }

        ngModel.$formatters.push(formatter);

        var picker = null;
        var clear = angular.noop;

        element.bind('focus', function () {
          if (!picker) {
            picker = $compile('

')(scope);
            body.append(picker);
            scope.$digest();
            var pos = angular.extend(element.offset(), { height: element[0].offsetHeight });
            picker.css({ top: pos.top + pos.height, left: pos.left, display: 'block', position: 'absolute'});
            picker.bind('mousedown', function () {
              return false;
            });
          }
          return false;
        });
        element.bind('blur', function () {
          clear();
          clear = angular.noop;
          if (picker){
            picker.remove();
          }
          picker = null;
        });
      }
    };
  });

  Module.directive('dateRange', function () {
    return {
      templateUrl: 'templates/daterange.html',
      scope   : {
        start: '=',
        end  : '='
      },
      link    : function (scope) {
        scope.$watch('start.getTime()', function (value) {
          if (value && scope.end && value > scope.end.getTime()) {
            scope.end = new Date(value);
          }
        });
        scope.$watch('end.getTime()', function (value) {
          if (value && scope.start && value < scope.start.getTime()) {
            scope.start = new Date(value);
          }
        });
      }
    };
  });
})(angular);
<commit_msg>stage 1<commit_after>(function (angular) {
  'use strict';
  var Module = angular.module('datePicker', []);

  Module.directive('datePicker', function () {
    var viewOptions = ['month', 'date', 'year', 'month', 'hours', 'minutes'];

    function isValidDate(date) {
      return date instanceof Date && !isNaN(date.getTime());
    }

    function getVisibleMinutes(date) {
      date = new Date(date || new Date());
      date = new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours());
      var minutes = [];
      var step = 5;
      var stop = date.getTime() + 60 * 60 * 1000;
      while (date.getTime() < stop) {
        minutes.push(date);
        date = new Date(date.getTime() + step * 60 * 1000);
      }
      return minutes;
    }

    function getVisibleWeeks(date) {
      date = new Date(date || new Date());
      date.setDate(1);
      date.setHours(0);
      date.setMinutes(0);
      date.setSeconds(0);
      date.setMilliseconds(0);

      if (date.getDay() === 0) {
        date.setDate(-5);
      } else {
        date.setDate(date.getDate() - (date.getDay() - 1));
      }
      if (date.getDate() === 1) {
        date.setDate(-6);
      }

      var weeks = [];
      while (weeks.length < 6) {
        var week = [];
        for (var i = 0; i < 7; i++) {
          week.push(new Date(date));
          date.setDate(date.getDate() + 1);
        }
        weeks.push(week);
      }
      return weeks;
    }

    function getVisibleYears(date) {
      var years = [];
      date = new Date(date || new Date());
      date.setFullYear(date.getFullYear() - (date.getFullYear() % 10));
      for (var i = 0; i < 12; i++) {
        years.push(new Date(date.getFullYear() + (i - 1), 0, 1));
      }
      return years;
    }

    function getDaysOfWeek(date) {
      date = new Date(date || new Date());
      date = new Date(date.getFullYear(), date.getMonth(), date.getDate());
      date.setDate(date.getDate() - (date.getDay() - 1));
      var days = [];
      for (var i = 0; i < 7; i++) {
        days.push(new Date(date));
        date.setDate(date.getDate() + 1);
      }
      return days;
    }

    function getVisibleMonths(date) {
      date = new Date(date || new Date());
      var year = date.getFullYear();
      var months = [];
      for (var month = 0; month < 12; month++) {
        months.push(new Date(year, month, 1));
      }
      return months;
    }

    function getVisibleHours(date) {
      date = new Date(date || new Date());
      date.setHours(0);
      date.setMinutes(0);
      date.setSeconds(0);
      date.setMilliseconds(0);
      var hours = [];
      for (var i = 0; i < 24; i++) {
        hours.push(date);
        date = new Date(date.getTime() + 60 * 60 * 1000);
      }
      return hours;
    }

    return {
      scope      : {
        date  : '=datePicker',
        after : '=?',
        before: '=?'
      },
      link       : function (scope, element, attrs) {

        scope.views = [];

        for (var attr in attrs) { //noinspection JSUnfilteredForInLoop
          if (viewOptions.indexOf(attr) !== -1) { //noinspection JSUnfilteredForInLoop
            scope.views.push(attr);
          }
        }

        if (!scope.views.length) {
          scope.views = ['date', 'month', 'year', 'hours', 'minutes'];
        }
        scope.view = scope.views[0];

        function hasView(view) {
          return scope.views.indexOf(view) !== -1;
        }

        function ensureDate() {
          // we need to return new instance as ngModel $watch watches only for identity - not for equality
          if (!(isValidDate(scope.date))) {
            scope.date = new Date(2000, 1, 1);
          }
          scope.date = new Date(scope.date);
        }

        function setYear(date) {
          ensureDate();
          scope.date.setFullYear(date.getFullYear());
        }

        function setMonth(date) {
          setYear(date);
          scope.date.setMonth(date.getMonth());
        }

        function setDate(date) {
          setMonth(date);
          scope.date.setDate(date.getDate());
        }

        function setHours(date) {
          setDate(date);
          scope.date.setHours(date.getHours());
        }

        function setMinutes(date) {
          setHours(date);
          scope.date.setMinutes(date.getMinutes());
        }

        scope.setYear = function (date) {
          setYear(date);
          scope.$emit('setYear', date);
        };

        scope.setMonth = function (date) {
          setMonth(date);
          scope.$emit('setMonth', date);
        };

        scope.setDate = function (date) {
          setDate(date);
          scope.$emit('setDate', date);
        };

        scope.setHours = function (date) {
          setHours(date);
          scope.$emit('setHours', date);
        };

        scope.setMinutes = function (date) {
          setMinutes(date);
          scope.$emit('setMinutes', date);
        };

        scope.setView = function setView(view) {
          if (hasView(view)) {
            scope.view = view;
            switch (view) {
            case 'minutes':
              scope.minutes = getVisibleMinutes(scope.visibleDate);
              break;
            case 'hours'  :
              scope.hours = getVisibleHours(scope.visibleDate);
              break;
            case 'date'   :
              scope.weeks = getVisibleWeeks(scope.visibleDate);
              break;
            case 'month'  :
              scope.months = getVisibleMonths(scope.visibleDate);
              break;
            case 'year'   :
              scope.years = getVisibleYears(scope.visibleDate);
              break;
            }
          }
        };

        scope.nextMonth = function (delta) {
          scope.visibleDate.setMonth(scope.visibleDate.getMonth() + (delta || 1));
        };

        scope.prevMonth = function (delta) {
          scope.nextMonth(-delta || -1);
        };

        scope.nextDay = function (delta) {
          scope.visibleDate.setDate(scope.visibleDate.getDate() + (delta || 1));
        };

        scope.prevDay = function (delta) {
          scope.nextDay(-delta || -1);
        };

        scope.nextHour = function (delta) {
          scope.visibleDate.setHours(scope.visibleDate.getHours() + (delta || 1));
        };

        scope.prevHour = function (delta) {
          scope.nextHour(-delta || -1);
        };

        scope.nextYear = function (delta) {
          scope.visibleDate.setFullYear(scope.visibleDate.getFullYear() + (delta || 1));
        };
        scope.prevYear = function (delta) {
          scope.nextYear(-delta || -1);
        };

        scope.visibleDate = new Date();

        scope.$watch('date', function (date) {
          if (date) {
            scope.visibleDate = new Date(date);
          }
        });

        scope.isAfter = function (date) {
          return date >= scope.after;
        };

        scope.isBefore = function (date) {
          return date <= scope.before;
        };

        function validDate() {
          return scope.date instanceof Date;
        }

        scope.isSameMinutes = function (date) {
          if (!validDate()){
            return false;
          }
          var b = scope.date;
          return (date.getTime() - date.getSeconds() * 1000 - date.getMilliseconds()) === (b.getTime() - b.getSeconds() * 1000 - b.getMilliseconds());
        };

        scope.isSameMonth = function (date) {
          if (!validDate()){
            return false;
          }
          return date.getFullYear() === scope.date.getFullYear() && date.getMonth() === scope.date.getMonth();
        };

        scope.isSameYear = function (date) {
          if (!validDate()){
            return false;
          }
          return date.getFullYear() === scope.date.getFullYear();
        };

        scope.isSameDate = function (date) {
          if (!validDate()){
            return false;
          }
          return scope.date.getDate() === date.getDate() && scope.isSameMonth(date);
        };

        scope.isSameHour = function (date) {
          if (!validDate()){
            return false;
          }
          return scope.date.getHours() === date.getHours() && scope.isSameDate(date);
        };

        scope.isOldMonth = function (date) {
          return date
            .getTime() < scope.visibleDate.getTime() && date.getMonth() !== scope.visibleDate.getMonth();
        };

        scope.isNewHour = function (date) {
          return date.getTime() > scope.visibleDate.getTime() && date.getHours() !== scope.visibleDate.getHours();
        };

        scope.isOldHour = function (date) {
          return date.getTime() < scope.visibleDate.getTime() && date.getHours() !== scope.visibleDate.getHours();
        };

        scope.isNewMonth = function (date) {
          return date.getTime() > scope.visibleDate.getTime() && date.getMonth() !== scope.visibleDate.getMonth();
        };

        scope.$on('setDate', scope.setView.bind(null, 'hours'));
        scope.$on('setMonth', scope.setView.bind(null, 'date'));
        scope.$on('setHours', scope.setView.bind(null, 'minutes'));
        scope.$on('setYear', scope.setView.bind(null, 'month'));

        scope.$watch(function () {
          return isValidDate(scope.visibleDate);
        }, function (valid) {
          if (!valid) {
            scope.visibleDate = new Date();
          }
        });

        //hours
        scope.$watch('[visibleDate.getDate(),visibleDate.getHours()].join()', function () {
          if (scope.view === 'hours') {
            scope.hours = getVisibleHours(scope.visibleDate);
          }
        });

        //date
        scope.$watch('[visibleDate.getFullYear(),visibleDate.getMonth(),visibleDate.getDate()].join()', function () {
          if (scope.view === 'date') {
            scope.weeks = getVisibleWeeks(scope.visibleDate);
            scope.weekdays = getDaysOfWeek(scope.visibleDate);
          }
        });

        scope.$watch('[visibleDate.getFullYear(),visibleDate.getMonth()].join()', function () {
          if (scope.view === 'month') {
            scope.months = getVisibleMonths(scope.visibleDate);
          }
        });

        scope.$watch('visibleDate.getYear()', function () {
          if (scope.view === 'year') {
            scope.years = getVisibleYears(scope.visibleDate);
          }
        });

        scope.$watch('visibleDate.getTime()', function () {
          if (scope.view === 'minutes') {
            scope.minutes = getVisibleMinutes(scope.visibleDate);
          }
        });

      },
      replace:true,
      templateUrl: 'templates/datepicker.html'
    };
  });

  Module.directive('dateTime', function ($compile, $document, $filter) {
    var body = $document.find('body');
    var dateFilter = $filter('date');
    return {
      require: 'ngModel',
      link   : function (scope, element, attrs, ngModel) {
        var format = attrs.format || 'yyyy-MM-dd HH:mm';

        var viewsOptions = ['date', 'year', 'month', 'hours', 'minutes', 'month'];
        var views = [];
        for (var attr in attrs) {
          //noinspection JSUnfilteredForInLoop
          if (viewsOptions.indexOf(attr) !== -1) { //noinspection JSUnfilteredForInLoop
            views.push(attr);
          }
        }

        function formatter(value) {
          return dateFilter(value, format);
        }

        ngModel.$formatters.push(formatter);

        var picker = null;
        var clear = function(){
          if(picker){
            picker.remove();
            picker = null;
          }
        };
        var template = '

';

        function update(obj,date){
          ngModel.$setViewValue(date);
        }

        element.bind('focus', function () {
          if (!picker) {
            // create picker element
            picker = $compile(template)(scope);
            body.append(picker);
            scope.$digest();

            scope.$on('setDate', update);
            scope.$on('setMonth',update);
            scope.$on('setHours',update);
            scope.$on('setYear', update);

            var pos = angular.extend(element.offset(), { height: element[0].offsetHeight });
            picker.css({ top: pos.top + pos.height, left: pos.left, display: 'block', position: 'absolute'});
            picker.bind('mousedown', function () {
              return false;
            });
          }
          return false;
        });
        element.bind('blur', clear);
      }
    };
  });

  Module.directive('dateRange', function () {
    return {
      templateUrl: 'templates/daterange.html',
      scope   : {
        start: '=',
        end  : '='
      },
      link    : function (scope) {
        scope.$watch('start.getTime()', function (value) {
          if (value && scope.end && value > scope.end.getTime()) {
            scope.end = new Date(value);
          }
        });
        scope.$watch('end.getTime()', function (value) {
          if (value && scope.start && value < scope.start.getTime()) {
            scope.start = new Date(value);
          }
        });
      }
    };
  });
})(angular);
<|endoftext|>


<commit_before>define(['d3', 'lib/knockout', 'scripts/Utils', 'dagre-d3', 'jquery', 'lettuce', 'jquery.tipsy'],
  function (d3, ko, Utils, dagreD3, $, Lettuce) {
    'use strict';
    function renderPage(skills_data) {
      function setSkillNode() {
        ko.utils.arrayForEach(skills_data.skills, function (skill) {
          var value = skill;
          value.label = skill.name;
          value.height = value.width = 60;
          value.rx = value.ry = 15;
          g.setNode(skill.name, value);
        });
      }

      function setSkillEdge() {
        ko.utils.arrayForEach(skills_data.skills, function (skill) {
          var skill_id = skill.id;
          if (skill.depends) {
            ko.utils.arrayForEach(skill.depends, function (id) {
              var dependents_name = Utils.getSkillById(skills_data.skills, id).name;
              var skill_name = Utils.getSkillById(skills_data.skills, skill_id).name;
              g.setEdge(dependents_name, skill_name, {label: '', lineInterpolate: 'basis'});
            });
          }
        });
      }

      var lettuce = new Lettuce();
      var g = new dagreD3.graphlib.Graph().setGraph({});
      setSkillNode();
      setSkillEdge();
      g.nodes().forEach(function (v) {
        var node = g.node(v);
        console.log(node);
      });

      var render = new dagreD3.render();
      var svg = d3.select('svg');

      g.nodes().forEach(function (v) {
        var node = g.node(v);
        if( node.logo){
          svg.append("defs")
            .append("pattern")
            .attr("id", node.id)
            .attr("width", 80)
            .attr("height", 80)
            .append("svg:image")
            .attr("xlink:href", "./app/logo/" + node.logo)
            .attr("width", 80)
            .attr("height", 80)
            .attr("x", 0)
            .attr("y", 0);
        }
      });

      var inner = svg.append('g');

      render(inner, g);
      console.log(inner.selectAll('g.node'));

      inner.selectAll('rect')
        .attr('class', 'inner');

      inner.selectAll('g.node rect')
        .style("fill", "url(#2)");

      inner.selectAll('g.node')
        .on("click", function (d, i) {
          d3.select(this).style('opacity', '0.5');
        })
        .on('mouseover', function () {
          d3.select(this).style('fill', 'red');
        })
        .on('mouseout', function () {
          d3.select(this).style('fill', 'black');
        });

      inner.selectAll('g.node')
        .attr('title', function (v) {
          var data = {
            name: v,
            description: g.node(v).description
          };
          var results = lettuce.Template.tmpl('
{%=o.name%}

{%=o.description%}
', data);
          return results;
        })
        .each(function (v) {
          $(this).tipsy({gravity: 's', opacity: 1, html: true});
        });

      svg.attr('height', g.graph().height + 120);
    }

    return {
      renderPage: renderPage
    };
  });
<commit_msg>Add render images<commit_after>define(['d3', 'lib/knockout', 'scripts/Utils', 'dagre-d3', 'jquery', 'lettuce', 'jquery.tipsy'],
  function (d3, ko, Utils, dagreD3, $, Lettuce) {
    'use strict';
    function renderPage(skills_data) {
      function setSkillNode() {
        ko.utils.arrayForEach(skills_data.skills, function (skill) {
          var value = skill;
          value.label = skill.name;
          value.height = value.width = 60;
          value.rx = value.ry = 15;
          g.setNode(skill.name, value);
        });
      }

      function setSkillEdge() {
        ko.utils.arrayForEach(skills_data.skills, function (skill) {
          var skill_id = skill.id;
          if (skill.depends) {
            ko.utils.arrayForEach(skill.depends, function (id) {
              var dependents_name = Utils.getSkillById(skills_data.skills, id).name;
              var skill_name = Utils.getSkillById(skills_data.skills, skill_id).name;
              g.setEdge(dependents_name, skill_name, {label: '', lineInterpolate: 'basis'});
            });
          }
        });
      }

      var lettuce = new Lettuce();
      var g = new dagreD3.graphlib.Graph().setGraph({});
      setSkillNode();
      setSkillEdge();
      g.nodes().forEach(function (v) {
        var node = g.node(v);
        //console.log(node);
      });

      var render = new dagreD3.render();
      var svg = d3.select('svg');

      g.nodes().forEach(function (v) {
        var node = g.node(v);
        if( node.logo){
          svg.append("defs")
            .append("pattern")
            .attr("id", node.id)
            .attr("width", 80)
            .attr("height", 80)
            .append("svg:image")
            .attr("xlink:href", "./app/logo/" + node.logo)
            .attr("width", 80)
            .attr("height", 80)
            .attr("x", 0)
            .attr("y", 0);
        }
      });

      var inner = svg.append('g');

      render(inner, g);

      inner.selectAll('rect')
        .attr('class', 'inner');

      g.nodes().forEach(function (v) {
        var node = g.node(v);
        if( node.logo) {
          inner.selectAll('g.node rect')
            .style("fill", "url(#" + node.id + ")");

          //inner.select("rect[id='" + node.id + "']")
          //  .attr("fill", "url(#" + node.id + ")");
        }
      });

      //inner.selectAll('g.node rect')
      //  .style("fill", "url(#2)");

      inner.selectAll('g.node')
        .on("click", function (d, i) {
          d3.select(this).style('opacity', '0.5');
        })
        .on('mouseover', function () {
          d3.select(this).style('fill', 'red');
        })
        .on('mouseout', function () {
          d3.select(this).style('fill', 'black');
        });

      inner.selectAll('g.node')
        .attr('title', function (v) {
          var data = {
            name: v,
            description: g.node(v).description
          };
          var results = lettuce.Template.tmpl('
{%=o.name%}

{%=o.description%}
', data);
          return results;
        })
        .each(function (v) {
          $(this).tipsy({gravity: 's', opacity: 1, html: true});
        });

      svg.attr('height', g.graph().height + 120);
    }

    return {
      renderPage: renderPage
    };
  });
<|endoftext|>


<commit_before>(() => {
  const req = new XMLHttpRequest();
  const _responseHandler = (responseText) => {
    let latestTag;
    try {
      latestTag = JSON.parse(responseText)[0];
    } catch (err) {
      throw new Error('Ha-ha, GitHub! Bad JSON.');
    }

    console.log(`The latest version of Yellfy — ${latestTag.name}!`);
  };

  req.open('GET', 'https://api.github.com/repos/mrmlnc/yellfy/tags', true);
  req.onreadystatechange = () => {
    if (req.readyState === 4) {
      if (req.status === 200) {
        _responseHandler(req.responseText);
      } else {
        throw new Error('Error loading page\n');
      }
    }
  };

  req.send(null);
})();
<commit_msg>Simplify condition<commit_after>(() => {
  const req = new XMLHttpRequest();
  const _responseHandler = (responseText) => {
    let latestTag;
    try {
      latestTag = JSON.parse(responseText)[0];
    } catch (err) {
      throw new Error('Ha-ha, GitHub! Bad JSON.');
    }

    console.log(`The latest version of Yellfy — ${latestTag.name}!`);
  };

  req.open('GET', 'https://api.github.com/repos/mrmlnc/yellfy/tags', true);
  req.onreadystatechange = () => {
    if (req.readyState === 4) {
      if (req.status === 200) {
        _responseHandler(req.responseText);
        return;
      }

      throw new Error('Error loading page\n');
    }
  };

  req.send(null);
})();
<|endoftext|>


<commit_before>
const exec = require('child_process').exec;

var moment = require("moment");
var gpio = require("gpio");
var gpio4 = gpio.export(4, {
   direction: "in",
   ready: function() {
   }
});

var proximity = require('./proximity');
var timestamp = moment().add(1, 'm');

// bind to the "change" event
gpio4.on("change", function(val) {

    // value will report either 1 or 0 (number) when the value changes
    if (val == 1) {

      console.log("checking if should turn on");
      let now = moment();
      if (now.isAfter(timestamp)) {
        console.log("turning on screen");
        const child = exec('xset s reset && xset dpms force on',
          (error, stdout, stderr) => {
            if (error !== null) {
              console.log(`exec error: ${error}`);
            }
        });

        console.log("checking proximity");
        proximity.getDistance();
      }

    } else {
      timestamp = moment().add(1, 'm');
    }
});
<commit_msg>Removing Proximity calls - retesting sensors<commit_after>
const exec = require('child_process').exec;

var moment = require("moment");
var gpio = require("gpio");
var gpio4 = gpio.export(4, {
   direction: "in",
   ready: function() {
   }
});

// var proximity = require('./proximity');

var timestamp = moment().add(1, 'm');

// bind to the "change" event
gpio4.on("change", function(val) {

    console.log("gpio on change");

    // value will report either 1 or 0 (number) when the value changes
    if (val == 1) {

      console.log("checking if should turn on");
      let now = moment();
      if (now.isAfter(timestamp)) {
        console.log("turning on screen");
        const child = exec('xset s reset && xset dpms force on',
          (error, stdout, stderr) => {
            if (error !== null) {
              console.log(`exec error: ${error}`);
            }
        });

        // console.log("checking proximity");
        // proximity.getDistance();
      }

    } else {
      timestamp = moment().add(1, 'm');
    }
});
<|endoftext|>


<commit_before>'use strict'
let mandrill = require('mandrill-api/mandrill')
let logger = require('js-logger-aknudsen').get('emailer')

let {getEnvParam,} = require('./environment')

module.exports = {
  sendEmail: ({subject, html, emailAddress, name,}) => {
    let mandrillClient = new mandrill.Mandrill(getEnvParam('MANDRILL_SECRET'))
    return new Promise((resolve, reject) => {
      let message = {
        html,
        subject,
        from_email: `contact@muzhack.com`,
        from_name: `MuzHack`,
        to: [{
          email: emailAddress,
          name,
          type: 'to',
        },],
        headers: {
          'Reply-To': `no-reply@muzhack.com`,
        },
      }
      logger.debug(`Sending email to '${emailAddress}', secret: '${getEnvParam('MANDRILL_SECRET')}'...`)
      mandrillClient.messages.send({
        message: message,
        async: true,
      }, () => {
        logger.debug(`Sent email to '${emailAddress}' successfully`)
        resolve()
      }, (error) => {
        logger.warn(
            `Unable to send email to '${emailAddress}', Mandrill error: '${error.message}'`)
        reject(`A Mandrill error occurred sending email to '${emailAddress}': '${error.message}'`)
      })
    })
  },
}
<commit_msg>Remove debug logging<commit_after>'use strict'
let mandrill = require('mandrill-api/mandrill')
let logger = require('js-logger-aknudsen').get('emailer')

let {getEnvParam,} = require('./environment')

module.exports = {
  sendEmail: ({subject, html, emailAddress, name,}) => {
    let mandrillClient = new mandrill.Mandrill(getEnvParam('MANDRILL_SECRET'))
    return new Promise((resolve, reject) => {
      let message = {
        html,
        subject,
        from_email: `contact@muzhack.com`,
        from_name: `MuzHack`,
        to: [{
          email: emailAddress,
          name,
          type: 'to',
        },],
        headers: {
          'Reply-To': `no-reply@muzhack.com`,
        },
      }
      logger.debug(`Sending email to '${emailAddress}'...`)
      mandrillClient.messages.send({
        message: message,
        async: true,
      }, () => {
        logger.debug(`Sent email to '${emailAddress}' successfully`)
        resolve()
      }, (error) => {
        logger.warn(
            `Unable to send email to '${emailAddress}', Mandrill error: '${error.message}'`)
        reject(`A Mandrill error occurred sending email to '${emailAddress}': '${error.message}'`)
      })
    })
  },
}
<|endoftext|>


<commit_before>import Ember from 'ember';
import DS from 'ember-data';
import ProjectionQuery from '../utils/projection-query';

export default DS.Store.reopen({
  findAll: function(modelName, options) {
    if (options && options.projection) {
      return this.query(modelName, {
        projection: options.projection
      });
    }

    return this._super.apply(this, arguments);
  },

  findRecord: function(modelName, id, options) {
    if (options && options.projection) {
      // TODO: case of options.reload === false.
      return this.queryRecord(modelName, {
        id: id,
        projection: options.projection
      });
    }

    return this._super.apply(this, arguments);
  },

  query: function(modelName, query) {
    query = this._normalizeQuery(modelName, query);
    return this._super(modelName, query).then(function(recordArray) {
      if (query && query.projection) {
        recordArray.forEach(function(record) {
          // TODO: merge projection if it already defined.
          record.set('projection', query.projection);
        }, this);
      }

      return recordArray;
    });
  },

  queryRecord: function(modelName, query) {
    query = this._normalizeQuery(modelName, query);
    return this._super(modelName, query).then(function(record) {
      if (query && query.projection) {
        // TODO: merge projection if it already defined.
        record.set('projection', query.projection);
      }

      return record;
    });
  },

  _normalizeQuery: function(modelName, query) {
    if (query && query.projection) {
      let projName = query.projection;
      let typeClass = this.modelFor(modelName);
      let proj = typeClass.projections.get(projName);
      let serializer = this.serializerFor(modelName);
      let projQuery = ProjectionQuery.get(proj, serializer);

      delete query.projection;
      query = Ember.merge(query, projQuery);
    }

    return query;
  }
});
<commit_msg>Remove projection writing into record in store<commit_after>import Ember from 'ember';
import DS from 'ember-data';
import ProjectionQuery from '../utils/projection-query';

export default DS.Store.reopen({
  findAll: function(modelName, options) {
    if (options && options.projection) {
      return this.query(modelName, {
        projection: options.projection
      });
    }

    return this._super.apply(this, arguments);
  },

  findRecord: function(modelName, id, options) {
    if (options && options.projection) {
      // TODO: case of options.reload === false.
      return this.queryRecord(modelName, {
        id: id,
        projection: options.projection
      });
    }

    return this._super.apply(this, arguments);
  },

  query: function(modelName, query) {
    query = this._normalizeQuery(modelName, query);
    return this._super(modelName, query).then(function(recordArray) {
      return recordArray;
    });
  },

  queryRecord: function(modelName, query) {
    query = this._normalizeQuery(modelName, query);
    return this._super(modelName, query).then(function(record) {
      return record;
    });
  },

  _normalizeQuery: function(modelName, query) {
    if (query && query.projection) {
      let projName = query.projection;
      let typeClass = this.modelFor(modelName);
      let proj = typeClass.projections.get(projName);
      let serializer = this.serializerFor(modelName);
      let projQuery = ProjectionQuery.get(proj, serializer);

      delete query.projection;
      query = Ember.merge(query, projQuery);
    }

    return query;
  }
});
<|endoftext|>


<commit_before>const botkit = require('botkit');
const { promisify } = require('util');
const { composeReviewMessage } = require('./lib/review-message');
const reviewReply = require('./review-reply');
const feedSettings = require('./feed-settings');

const setupAppHandlers = (slackapp, trustpilotApi) => {

  const slashCommandType = (text) => {
    if (/^[1-5] stars?$/i.test(text) || /^la(te)?st$/i.test(text)) {
      return 'review_query';
    } else if (text === 'settings' || text === 'feed') {
      return 'feed_settings';
    } else {
      return null;
    }
  };

  const handleReviewQuery = async (bot, sourceMessage) => {
    let stars = Number(sourceMessage.text.split(' ')[0]);
    stars = isNaN(stars) ? null : stars;
    const team = bot.team_info;
    const businessUnitId = team.businessUnitId;
    const { canReply } = feedSettings.getChannelFeedSettingsOrDefault(team, sourceMessage.channel);

    const lastReview = await trustpilotApi.getLastUnansweredReview({
      stars,
      businessUnitId,
    });

    bot.replyPrivateDelayedAsync = bot.replyPrivateDelayedAsync || promisify(bot.replyPrivateDelayed);
    if (lastReview) {
      await bot.replyPrivateDelayedAsync(
        sourceMessage,
        composeReviewMessage(lastReview, {
          canReply,
        })
      );
    } else {
      await bot.replyPrivateDelayedAsync(sourceMessage, 'Sorry, I could not find a matching review.');
    }
    return true;
  };

  const handleReplyButton = async (bot, message) => {
    const { canReply } = feedSettings.getChannelFeedSettingsOrDefault(bot.team_info, message.channel);
    if (!canReply) {
      bot.replyPublicDelayedAsync = bot.replyPublicDelayedAsync || promisify(bot.replyPublicDelayed);
      await bot.replyPublicDelayedAsync(message, 'Sorry, it’s no longer possible to reply to reviews'
        + ' from this channel.');
    } else {
      reviewReply.showReplyDialog(bot, message);
    }
  };

  slackapp.on('tick', () => { }); // Avoid filling the logs on each tick

  slackapp.on('create_bot', async (bot, botConfig) => {
    // We're not using the RTM API so we need to tell Botkit to start processing conversations
    slackapp.startTicking();
    bot.startPrivateConversationAsync = promisify(bot.startPrivateConversation);

    const convo = await bot.startPrivateConversationAsync({
      user: botConfig.createdBy,
    });
    convo.say('I am a bot that has just joined your team');
    convo.say('You must now /invite me to a channel so that I can be of use!');
  });

  /*
    Entry points : Slash command, button clicks, dialog submissions
  */

  slackapp.on('slash_command', async (bot, message) => {
    bot.replyAcknowledge();
    const type = slashCommandType(message.text);
    const commandHandlers = {
      'review_query': handleReviewQuery,
      'feed_settings': feedSettings.handleSettingsCommand,
    };
    await commandHandlers[type](bot, message);
    return true;
  });

  slackapp.on('interactive_message_callback', async (bot, message) => {
    bot.replyAcknowledge();
    const action = message.actions[0].value;
    const actionHandlers = {
      'step_1_write_reply': handleReplyButton,
      'open_feed_settings': feedSettings.showFeedSettings,
      'delete_feed_settings': feedSettings.deleteFeedSettings(slackapp),
    };
    await actionHandlers[action](bot, message);
    return true;
  });

  slackapp.on('dialog_submission', async (bot, message) => {
    bot.dialogOk();
    const { dialogType } = JSON.parse(message.callback_id);
    const dialogHandlers = {
      'review_reply': reviewReply.handleReply(trustpilotApi),
      'feed_settings': feedSettings.handleDialogSubmission(slackapp),
    };
    await dialogHandlers[dialogType](bot, message);
    return true;
  });

  /*
    Custom handler : incoming review
  */

  slackapp.on('trustpilot_review_received', async (review, teamId) => {
    slackapp.findTeamByIdAsync = slackapp.findTeamByIdAsync || promisify(slackapp.findTeamById);
    const team = await slackapp.findTeamByIdAsync(teamId);
    const bot = slackapp.spawn(team);
    bot.team_info = team; // eslint-disable-line camelcase
    bot.sendAsync = bot.sendAsync || promisify(bot.send);
    const feeds = feedSettings.getTeamFeeds(team);

    feeds.forEach(async ({ channelId, canReply }) => {
      const message = composeReviewMessage(review, { canReply });
      message.username = bot.config.bot.name; // Confusing, but such is life
      message.channel = channelId;
      try {
        const { ok: sentOk, ts, channel } = await bot.sendAsync(message);
        if (sentOk) {
          slackapp.trigger('trustpilot_review_posted', [bot, { ts, channel, reviewId: review.id }]);
        }
      } catch (e) {
        if (e.message === 'account_inactive') {
          // Integration removed, just do a bit of clean up.
          team.feeds = [];
          slackapp.storage.teams.save(team);
        }
        slackapp.log.error(e);
      }
    });
  });
};

module.exports = (config, trustpilotApi, storage) => {
  // Fallback to jfs when no storage middleware provided
  const slackapp = botkit.slackbot({
    debug: false,
    storage: storage,
    json_file_store: './storage/', // eslint-disable-line camelcase
    retry: 2,
  });
  slackapp.configureSlackApp({
    clientId: config.SLACK_CLIENT_ID,
    clientSecret: config.SLACK_SECRET,
    rtm_receive_messages: false, // eslint-disable-line camelcase
    scopes: ['bot', 'incoming-webhook', 'commands'],
  });
  setupAppHandlers(slackapp, trustpilotApi);
  return slackapp;
};
<commit_msg>Introduce test slash command<commit_after>const botkit = require('botkit');
const { promisify } = require('util');
const { composeReviewMessage } = require('./lib/review-message');
const reviewReply = require('./review-reply');
const feedSettings = require('./feed-settings');

const setupAppHandlers = (slackapp, trustpilotApi) => {

  const slashCommandType = (text) => {
    if (/^[1-5] stars?$/i.test(text) || /^la(te)?st$/i.test(text)) {
      return 'review_query';
    } else if (text === 'settings' || text === 'feed') {
      return 'feed_settings';
    } else if (text === 'test') {
      return 'test_feeds';
    } else {
      return null;
    }
  };

  const handleReviewQuery = async (bot, sourceMessage) => {
    let stars = Number(sourceMessage.text.split(' ')[0]);
    stars = isNaN(stars) ? null : stars;
    const team = bot.team_info;
    const businessUnitId = team.businessUnitId;
    const { canReply } = feedSettings.getChannelFeedSettingsOrDefault(team, sourceMessage.channel);

    const lastReview = await trustpilotApi.getLastUnansweredReview({
      stars,
      businessUnitId,
    });

    bot.replyPrivateDelayedAsync = bot.replyPrivateDelayedAsync || promisify(bot.replyPrivateDelayed);
    if (lastReview) {
      await bot.replyPrivateDelayedAsync(
        sourceMessage,
        composeReviewMessage(lastReview, {
          canReply,
        })
      );
    } else {
      await bot.replyPrivateDelayedAsync(sourceMessage, 'Sorry, I could not find a matching review.');
    }
    return true;
  };

  const testFeeds = async (bot) => {
    const testReview = {
      stars: 5,
      createdAt: new Date(),
      title: 'Test Review',
      text: 'This is just a test to verify the settings on your Slack channels.',
      consumer: {
        displayName: 'The Trustpilot Slack App',
      },
    };
    slackapp.trigger('trustpilot_review_received', [testReview, bot.team_info.id]);
  };

  const handleReplyButton = async (bot, message) => {
    const { canReply } = feedSettings.getChannelFeedSettingsOrDefault(bot.team_info, message.channel);
    if (!canReply) {
      bot.replyPublicDelayedAsync = bot.replyPublicDelayedAsync || promisify(bot.replyPublicDelayed);
      await bot.replyPublicDelayedAsync(message, 'Sorry, it’s no longer possible to reply to reviews'
        + ' from this channel.');
    } else {
      reviewReply.showReplyDialog(bot, message);
    }
  };

  slackapp.on('tick', () => { }); // Avoid filling the logs on each tick

  slackapp.on('create_bot', async (bot, botConfig) => {
    // We're not using the RTM API so we need to tell Botkit to start processing conversations
    slackapp.startTicking();
    bot.startPrivateConversationAsync = promisify(bot.startPrivateConversation);

    const convo = await bot.startPrivateConversationAsync({
      user: botConfig.createdBy,
    });
    convo.say('I am a bot that has just joined your team');
    convo.say('You must now /invite me to a channel so that I can be of use!');
  });

  /*
    Entry points : Slash command, button clicks, dialog submissions
  */

  slackapp.on('slash_command', async (bot, message) => {
    bot.replyAcknowledge();
    const type = slashCommandType(message.text);
    const commandHandlers = {
      'review_query': handleReviewQuery,
      'feed_settings': feedSettings.handleSettingsCommand,
      'test_feeds': testFeeds,
    };
    await commandHandlers[type](bot, message);
    return true;
  });

  slackapp.on('interactive_message_callback', async (bot, message) => {
    bot.replyAcknowledge();
    const action = message.actions[0].value;
    const actionHandlers = {
      'step_1_write_reply': handleReplyButton,
      'open_feed_settings': feedSettings.showFeedSettings,
      'delete_feed_settings': feedSettings.deleteFeedSettings(slackapp),
    };
    await actionHandlers[action](bot, message);
    return true;
  });

  slackapp.on('dialog_submission', async (bot, message) => {
    bot.dialogOk();
    const { dialogType } = JSON.parse(message.callback_id);
    const dialogHandlers = {
      'review_reply': reviewReply.handleReply(trustpilotApi),
      'feed_settings': feedSettings.handleDialogSubmission(slackapp),
    };
    await dialogHandlers[dialogType](bot, message);
    return true;
  });

  /*
    Custom handler : incoming review
  */

  slackapp.on('trustpilot_review_received', async (review, teamId) => {
    slackapp.findTeamByIdAsync = slackapp.findTeamByIdAsync || promisify(slackapp.findTeamById);
    const team = await slackapp.findTeamByIdAsync(teamId);
    const bot = slackapp.spawn(team);
    bot.team_info = team; // eslint-disable-line camelcase
    bot.sendAsync = bot.sendAsync || promisify(bot.send);
    const feeds = feedSettings.getTeamFeeds(team);

    feeds.forEach(async ({ channelId, canReply }) => {
      const message = composeReviewMessage(review, { canReply });
      message.username = bot.config.bot.name; // Confusing, but such is life
      message.channel = channelId;
      try {
        const { ok: sentOk, ts, channel } = await bot.sendAsync(message);
        if (sentOk) {
          slackapp.trigger('trustpilot_review_posted', [bot, { ts, channel, reviewId: review.id }]);
        }
      } catch (e) {
        if (e.message === 'account_inactive') {
          // Integration removed, just do a bit of clean up.
          team.feeds = [];
          slackapp.storage.teams.save(team);
        }
        slackapp.log.error(e);
      }
    });
  });
};

module.exports = (config, trustpilotApi, storage) => {
  // Fallback to jfs when no storage middleware provided
  const slackapp = botkit.slackbot({
    debug: false,
    storage: storage,
    json_file_store: './storage/', // eslint-disable-line camelcase
    retry: 2,
  });
  slackapp.configureSlackApp({
    clientId: config.SLACK_CLIENT_ID,
    clientSecret: config.SLACK_SECRET,
    rtm_receive_messages: false, // eslint-disable-line camelcase
    scopes: ['bot', 'incoming-webhook', 'commands'],
  });
  setupAppHandlers(slackapp, trustpilotApi);
  return slackapp;
};
<|endoftext|>


<commit_before>import axios from 'axios';

let ApiRequest = {
    methods: {
        apiRequest: function (url, callback) {

            EventBus.$emit('start-loading');

            axios.get(url).then(response => {
                EventBus.$emit('stop-loading', { success: true });
                callback(response.data);
            })
            .catch (error => {
                EventBus.$emit('error', error.response.data);
                EventBus.$emit('stop-loading', { success: false });
            });
        }
    }
};

export default ApiRequest;
<commit_msg>Improved error handling<commit_after>import axios from 'axios';

let ApiRequest = {
    methods: {
        apiRequest: function (url, callback) {

            EventBus.$emit('start-loading');

            axios.get(url).then(response => {
                if (response.headers['content-type'] === 'application/json') {
                    EventBus.$emit('stop-loading', { success: true });
                    callback(response.data);
                }
                else {
                    EventBus.$emit('error', '');
                    EventBus.$emit('stop-loading', { success: false });
                }
            })
            .catch (error => {
                EventBus.$emit('error', error.response.data);
                EventBus.$emit('stop-loading', { success: false });
            });
        }
    }
};

export default ApiRequest;
<|endoftext|>


<commit_before>/*
 * COPY: Copy files and folders
 */
module.exports = {
    /*
     * TARGET:  Copies fonts
     */
    installFonts: {
        files: [{
            expand: true,
            flatten: true,
            src: [
                '<%= path.src.npm %>/font-awesome/fonts/*'
            ],
            filter: 'isFile',
            dest: '<%= path.dest.fonts %>/'
        }, {
            expand: true,
            flatten: true,
            cwd: '<%= path.src.npm %>/source-sans-pro',
            src: 'WOFF?(2)/OTF/SourceSansPro-@(It|Regular|Semibold).otf.woff?(2)',
            dest: '<%= path.dest.fonts %>',
            rename: function(dest, src) {
                return dest + '/' + src.replace(/\.otf(\.woff2?)$/, '$1');
            }
        }]
    },

    /*
     * TARGET:  Copies CKEditor files
     */
    installCkeditor1: {
        files: [
            {
                // Copy all CKEditor files
                expand: true,
                cwd: '<%= path.src.lib %>/ckeditor',
                src: [
                    'plugins/**',
                    'skins/**',
                    'styles.js'
                ],
                dest: '<%= path.dest.js %>/ckeditor'
            }, {
                // Copy our empty config file
                src: '<%= path.src.js %>/ckeditor-config.js',
                dest: '<%= path.dest.js %>/ckeditor/config.js'
            }, {
                // Copy style to css folder
                src: '<%= path.src.lib %>/ckeditor/contents.css',
                dest: '<%= path.dest.css %>/ckeditor-contents.css'
            }, {
                // Copy CKEditor locale
                expand: true,
                flatten: true,
                src: '<%= path.src.lib %>/ckeditor/lang/*.js',
                dest: '<%= path.dest.js %>/locale/ckeditor'
            }, {
                // Copy skin
                expand: true,
                cwd: '<%= path.src.lib %>/ckeditor-skin',
                src: 'boltcke/**',
                dest: '<%= path.dest.js %>/ckeditor/skins'
            }
        ]
    },

    /*
     * TARGET:  Copies modified ckeditor.js
     */
    installCkeditor2: {
        // process doesn't work on file level, so we need a new target
        src: '<%= path.src.lib %>/ckeditor/ckeditor.js',
        dest: '<%= path.dest.js %>/ckeditor/ckeditor.js',
        options: {
            process: function (cont) {
                'use strict';

                return cont.replace(/(CKEDITOR\.getUrl\()"lang\/"(\+a\+"\.js"\))/, '$1"../locale/ckeditor/"$2');
            }
        }
    },
};
<commit_msg>Fix missing "use strict"<commit_after>/*
 * COPY: Copy files and folders
 */
module.exports = {
    /*
     * TARGET:  Copies fonts
     */
    installFonts: {
        files: [{
            expand: true,
            flatten: true,
            src: [
                '<%= path.src.npm %>/font-awesome/fonts/*'
            ],
            filter: 'isFile',
            dest: '<%= path.dest.fonts %>/'
        }, {
            expand: true,
            flatten: true,
            cwd: '<%= path.src.npm %>/source-sans-pro',
            src: 'WOFF?(2)/OTF/SourceSansPro-@(It|Regular|Semibold).otf.woff?(2)',
            dest: '<%= path.dest.fonts %>',
            rename: function(dest, src) {
                "use strict";

                return dest + '/' + src.replace(/\.otf(\.woff2?)$/, '$1');
            }
        }]
    },

    /*
     * TARGET:  Copies CKEditor files
     */
    installCkeditor1: {
        files: [
            {
                // Copy all CKEditor files
                expand: true,
                cwd: '<%= path.src.lib %>/ckeditor',
                src: [
                    'plugins/**',
                    'skins/**',
                    'styles.js'
                ],
                dest: '<%= path.dest.js %>/ckeditor'
            }, {
                // Copy our empty config file
                src: '<%= path.src.js %>/ckeditor-config.js',
                dest: '<%= path.dest.js %>/ckeditor/config.js'
            }, {
                // Copy style to css folder
                src: '<%= path.src.lib %>/ckeditor/contents.css',
                dest: '<%= path.dest.css %>/ckeditor-contents.css'
            }, {
                // Copy CKEditor locale
                expand: true,
                flatten: true,
                src: '<%= path.src.lib %>/ckeditor/lang/*.js',
                dest: '<%= path.dest.js %>/locale/ckeditor'
            }, {
                // Copy skin
                expand: true,
                cwd: '<%= path.src.lib %>/ckeditor-skin',
                src: 'boltcke/**',
                dest: '<%= path.dest.js %>/ckeditor/skins'
            }
        ]
    },

    /*
     * TARGET:  Copies modified ckeditor.js
     */
    installCkeditor2: {
        // process doesn't work on file level, so we need a new target
        src: '<%= path.src.lib %>/ckeditor/ckeditor.js',
        dest: '<%= path.dest.js %>/ckeditor/ckeditor.js',
        options: {
            process: function (cont) {
                'use strict';

                return cont.replace(/(CKEDITOR\.getUrl\()"lang\/"(\+a\+"\.js"\))/, '$1"../locale/ckeditor/"$2');
            }
        }
    },
};
<|endoftext|>


<commit_before>// initializes the application and links it with the GUI

// require core modules
const printTotalNodes = require('./core/printTotalNodes.js')
const hoverNodeInfo = require('./core/hoverNodeInfo.js')
const editMenu = require('./core/editMenu.js')
const editEdge = require('./core/editEdge.js')

// require helper functions
const rmElement = require('./helpers/rmElement.js')
// require buttons and keybindings
const buttons = require('./buttons.js')
const keybindings = require('./keybindings.js')

module.exports = function initialize (cy, phase) {
  // initial label render
  cy.nodes().addClass('label-nodes')
  cy.edges().addClass('label-edges')

  // global variables, used in cy.on
  let selectedNode = {}
  let oldSelectedNode = {}
  let selectedEdge = {}
  let srcNode = {}
  let trgNode = {}
  // initialize export variables to prevent undefined errors
  selectedNode.out = {}
  oldSelectedNode.out = {}
  selectedEdge.out = {}
  srcNode.out = {}
  trgNode.out = {}

  // stores the initial state of the nodes in the graph
  const graphNodes = cy.nodes()
  // counter variable to create unique sequential node ids in addComponents.js
  let nodeCounter = ''
  cy.nodes().map(node => {
    nodeCounter = node.data().id
  })

  // cy.on performs actions on intrection with the graph
  // actions when tapping on node
  cy.on('tap', 'node', selection => {
    // removes previous selections classes
    cy.elements().removeClass('selection')
    cy.elements().removeClass('attention')
    cy.elements().removeClass('protect')
    cy.nodes().removeClass('old-selection')

    oldSelectedNode.out = selectedNode.out
    selectedNode.out = selection.target[0]
    selectedNode.out.addClass('selection')

    // adds the old-selection class to the previous selection
    if (Object.keys(oldSelectedNode.out).length !== 0) {
      oldSelectedNode.out.addClass('old-selection')
    }

    srcNode.out = trgNode.out // second selection
    trgNode.out = selectedNode.out.data() // first selection

    selectedEdge.out = {} // clear token

    printTotalNodes(cy) // show number of nodes

    rmElement('message-area-id', 'form-id') // remove the edit node element
    rmElement('window-id', 'nodeMenu-id') // remove node menu element
    rmElement('window-id', 'stageMenu-id') // remove stage menu element
  })
  // actions when tapping on an edge
  cy.on('tap', 'edge', selection => {
    // removes previous selections
    cy.elements().removeClass('selection')
    cy.elements().removeClass('attention')
    cy.elements().removeClass('protect')
    cy.nodes().removeClass('old-selection')
    selection.target.addClass('selection')

    // clear tokens
    selectedNode.out = {}
    oldSelectedNode.out = {}

    selectedEdge.out = selection.target[0]

    printTotalNodes(cy) // show number of nodes

    rmElement('message-area-id', 'form-id') // remove the edit node element
    rmElement('window-id', 'nodeMenu-id') // remove node menu element
    rmElement('window-id', 'stageMenu-id') // remove stage menu element
  })
  // actions when tapping the stage
  cy.on('tap', selection => {
    // checks if only the stage was clicked
    if (selection.target === cy) {
      // removes previous selections
      cy.elements().removeClass('faded')
      cy.elements().removeClass('selection')
      cy.elements().removeClass('attention')
      cy.elements().removeClass('protect')
      cy.nodes().removeClass('old-selection')

      // clear tokens
      selectedNode.out = {}
      oldSelectedNode.out = {}
      selectedEdge.out = {}

      printTotalNodes(cy) // show nubmer of nodes

      rmElement('message-area-id', 'form-id') // remove the edit node element
      rmElement('window-id', 'nodeMenu-id') // remove node menu element
      rmElement('window-id', 'stageMenu-id') // remove stage menu element
    }
  })
  // right clicking on node
  cy.on('cxttap', 'node', selection => {
    selectedNode.out = selection.target[0]
    editMenu.nodeMenu(cy, selection, selectedNode.out)

    // clear tokens
    selectedNode.out = {}
    oldSelectedNode.out = {}
    selectedEdge.out = {}

    rmElement('window-id', 'stageMenu-id') // remove stage menu element
  })
  // right click on edge to edit
  // only available in state diagrams
  if (phase === 'state') {
    cy.on('cxttap', 'edge', selection => {
      editEdge.formEdge(selection.target[0])
    })
  }
  // right clicking on stage
  cy.on('cxttap', selection => {
    // checks if only the stage was clicked
    if (selection.target === cy) {
      // removes the stage menu if it exists
      rmElement('window-id', 'stageMenu-id')
      editMenu.stageMenu(cy, selection)
    }
  })
  // actions when hovering over a node
  cy.on('mouseover', 'node', event => {
    hoverNodeInfo(event.target[0]) // global module
  })
  // actions when hovering out of a node
  cy.on('mouseout', 'node', event => {
    // hides the hover container
    document.getElementById('container-node-id').style.display = 'none'
  })

  // declaration of global buttons
  buttons(
    cy,
    selectedNode,
    selectedEdge,
    srcNode,
    trgNode,
    nodeCounter,
    phase,
    graphNodes
  )
  // enable keybindings
  keybindings(
    cy,
    selectedNode,
    selectedEdge,
    srcNode,
    trgNode,
    phase,
    graphNodes
  )

  // initial node count
  printTotalNodes(cy)
}
<commit_msg>:bug: nodecounter was a string instead of a number<commit_after>// initializes the application and links it with the GUI

// require core modules
const printTotalNodes = require('./core/printTotalNodes.js')
const hoverNodeInfo = require('./core/hoverNodeInfo.js')
const editMenu = require('./core/editMenu.js')
const editEdge = require('./core/editEdge.js')

// require helper functions
const rmElement = require('./helpers/rmElement.js')
// require buttons and keybindings
const buttons = require('./buttons.js')
const keybindings = require('./keybindings.js')

module.exports = function initialize (cy, phase) {
  // initial label render
  cy.nodes().addClass('label-nodes')
  cy.edges().addClass('label-edges')

  // global variables, used in cy.on
  let selectedNode = {}
  let oldSelectedNode = {}
  let selectedEdge = {}
  let srcNode = {}
  let trgNode = {}
  // initialize export variables to prevent undefined errors
  selectedNode.out = {}
  oldSelectedNode.out = {}
  selectedEdge.out = {}
  srcNode.out = {}
  trgNode.out = {}

  // stores the initial state of the nodes in the graph
  const graphNodes = cy.nodes()
  // counter variable to create unique sequential node ids in addComponents.js
  let nodeCounterID = ''
  cy.nodes().map(node => {
    nodeCounterID = node.data().id
  })
  // turn the counter into an number and remove the 'n'
  const nodeCounter = Number(nodeCounterID.replace('n', ''))

  // cy.on performs actions on intrection with the graph
  // actions when tapping on node
  cy.on('tap', 'node', selection => {
    // removes previous selections classes
    cy.elements().removeClass('selection')
    cy.elements().removeClass('attention')
    cy.elements().removeClass('protect')
    cy.nodes().removeClass('old-selection')

    oldSelectedNode.out = selectedNode.out
    selectedNode.out = selection.target[0]
    selectedNode.out.addClass('selection')

    // adds the old-selection class to the previous selection
    if (Object.keys(oldSelectedNode.out).length !== 0) {
      oldSelectedNode.out.addClass('old-selection')
    }

    srcNode.out = trgNode.out // second selection
    trgNode.out = selectedNode.out.data() // first selection

    selectedEdge.out = {} // clear token

    printTotalNodes(cy) // show number of nodes

    rmElement('message-area-id', 'form-id') // remove the edit node element
    rmElement('window-id', 'nodeMenu-id') // remove node menu element
    rmElement('window-id', 'stageMenu-id') // remove stage menu element
  })
  // actions when tapping on an edge
  cy.on('tap', 'edge', selection => {
    // removes previous selections
    cy.elements().removeClass('selection')
    cy.elements().removeClass('attention')
    cy.elements().removeClass('protect')
    cy.nodes().removeClass('old-selection')
    selection.target.addClass('selection')

    // clear tokens
    selectedNode.out = {}
    oldSelectedNode.out = {}

    selectedEdge.out = selection.target[0]

    printTotalNodes(cy) // show number of nodes

    rmElement('message-area-id', 'form-id') // remove the edit node element
    rmElement('window-id', 'nodeMenu-id') // remove node menu element
    rmElement('window-id', 'stageMenu-id') // remove stage menu element
  })
  // actions when tapping the stage
  cy.on('tap', selection => {
    // checks if only the stage was clicked
    if (selection.target === cy) {
      // removes previous selections
      cy.elements().removeClass('faded')
      cy.elements().removeClass('selection')
      cy.elements().removeClass('attention')
      cy.elements().removeClass('protect')
      cy.nodes().removeClass('old-selection')

      // clear tokens
      selectedNode.out = {}
      oldSelectedNode.out = {}
      selectedEdge.out = {}

      printTotalNodes(cy) // show nubmer of nodes

      rmElement('message-area-id', 'form-id') // remove the edit node element
      rmElement('window-id', 'nodeMenu-id') // remove node menu element
      rmElement('window-id', 'stageMenu-id') // remove stage menu element
    }
  })
  // right clicking on node
  cy.on('cxttap', 'node', selection => {
    selectedNode.out = selection.target[0]
    editMenu.nodeMenu(cy, selection, selectedNode.out)

    // clear tokens
    selectedNode.out = {}
    oldSelectedNode.out = {}
    selectedEdge.out = {}

    rmElement('window-id', 'stageMenu-id') // remove stage menu element
  })
  // right click on edge to edit
  // only available in state diagrams
  if (phase === 'state') {
    cy.on('cxttap', 'edge', selection => {
      editEdge.formEdge(selection.target[0])
    })
  }
  // right clicking on stage
  cy.on('cxttap', selection => {
    // checks if only the stage was clicked
    if (selection.target === cy) {
      // removes the stage menu if it exists
      rmElement('window-id', 'stageMenu-id')
      editMenu.stageMenu(cy, selection)
    }
  })
  // actions when hovering over a node
  cy.on('mouseover', 'node', event => {
    hoverNodeInfo(event.target[0]) // global module
  })
  // actions when hovering out of a node
  cy.on('mouseout', 'node', event => {
    // hides the hover container
    document.getElementById('container-node-id').style.display = 'none'
  })

  // declaration of global buttons
  buttons(
    cy,
    selectedNode,
    selectedEdge,
    srcNode,
    trgNode,
    nodeCounter,
    phase,
    graphNodes
  )
  // enable keybindings
  keybindings(
    cy,
    selectedNode,
    selectedEdge,
    srcNode,
    trgNode,
    phase,
    graphNodes
  )

  // initial node count
  printTotalNodes(cy)
}
<|endoftext|>


<commit_before>/**
	_moon.Button_ is an enyo.Button with Moonstone
	styling applied. The color of the button may be customized by specifying a
	background color.

	For more information, see the documentation on
	Buttons in the
	Enyo Developer Guide.
*/

enyo.kind({
	name: 'moon.Button',
	kind: 'enyo.Button',
	mixins: ["moon.MarqueeSupport"],
	published: {
		/**
			A parameter indicating the size of the button.
			If true, the diameter of this button is 60px.
			However, the button's tap target still has a diameter of 78px, so there is
			invisible DOM that wraps the small button to provide the larger tap zone.
		*/
		small: false,
		marquee: true
	},
	classes: 'moon-button moon-header-font enyo-unselectable',
	spotlight: true,
	handlers: {
		//* _onSpotlightSelect_ simulates _mousedown_.
		onSpotlightSelect	: 'depress',
		//* _onSpotlightKeyUp_ simulates _mouseup_.
		onSpotlightKeyUp	: 'undepress',
		//* _onSpotlightFocus_ bubble _requestScrollIntoView_ event
		onSpotlightFocused	: "spotFocused",
		ontap				: "decorateTapEvent"
	},
	//* On creation, updates based on value of _this.small_.
	initComponents: function() {
		this.updateSmall();
		this.inherited(arguments);
	},
	//* Adds _pressed_ CSS class.
	depress: function() {
		this.addClass('pressed');
	},
	//* Bubble _requestScrollIntoView_ event
	spotFocused: function() {
		this.bubble("onRequestScrollIntoView", {side: "top"});
		return true;
	},
	//* Removes _pressed_ CSS class.
	undepress: function() {
		this.removeClass('pressed');
	},
	//* If _this.small_ is true, adds a child that increases the tap area.
	updateSmall: function() {
		if (this.$.tapArea) {
			this.$.tapArea.destroy();
			this.$.client.destroy();
		}

		if (this.small) {
			this.addClass('small');
			this.createComponent({name: "tapArea", classes: "small-button-tap-area", isChrome: true});
			if (this.marquee && !(this.components && this.components.length > 0)) {
				this.createComponent({name: "client", classes: "button-client",
					kind:"moon.MarqueeText", isChrome: true
				});
			} else {
				this.createComponent({name: "client", classes: "small-button-client"});
			}
		} else {
			this.removeClass('small');
			if (this.marquee && !(this.components && this.components.length > 0)) {
				this.createComponent({name: "client", classes: "button-client",
					kind:"moon.MarqueeText", isChrome: true
				});
			}
		}

		this.contentChanged();
	},
	//* When _this.small_ changes, updates and rerenders.
	smallChanged: function() {
		this.updateSmall();
		this.render();
	},
	//* Override to handle potential child components.
	contentChanged: function() {
		if (this.$.client) {
			this.$.client.setContent(this.getContent());
		} else {
			this.inherited(arguments);
		}
	},
	decorateTapEvent: function(inSender, inEvent) {
		inEvent.originator = this;
	}
});<commit_msg>Remove tap handler override, which snuffed out enyo.Button's handler. Enyo-DCO-1.1-Signed-Off-By: Kevin Schaaf (kevin.schaaf@lge.com)<commit_after>/**
	_moon.Button_ is an enyo.Button with Moonstone
	styling applied. The color of the button may be customized by specifying a
	background color.

	For more information, see the documentation on
	Buttons in the
	Enyo Developer Guide.
*/

enyo.kind({
	name: 'moon.Button',
	kind: 'enyo.Button',
	mixins: ["moon.MarqueeSupport"],
	published: {
		/**
			A parameter indicating the size of the button.
			If true, the diameter of this button is 60px.
			However, the button's tap target still has a diameter of 78px, so there is
			invisible DOM that wraps the small button to provide the larger tap zone.
		*/
		small: false,
		marquee: true
	},
	classes: 'moon-button moon-header-font enyo-unselectable',
	spotlight: true,
	handlers: {
		//* _onSpotlightSelect_ simulates _mousedown_.
		onSpotlightSelect	: 'depress',
		//* _onSpotlightKeyUp_ simulates _mouseup_.
		onSpotlightKeyUp	: 'undepress',
		//* _onSpotlightFocus_ bubble _requestScrollIntoView_ event
		onSpotlightFocused	: "spotFocused"
	},
	//* On creation, updates based on value of _this.small_.
	initComponents: function() {
		this.updateSmall();
		this.inherited(arguments);
	},
	//* Adds _pressed_ CSS class.
	depress: function() {
		this.addClass('pressed');
	},
	//* Bubble _requestScrollIntoView_ event
	spotFocused: function() {
		this.bubble("onRequestScrollIntoView", {side: "top"});
		return true;
	},
	//* Removes _pressed_ CSS class.
	undepress: function() {
		this.removeClass('pressed');
	},
	//* If _this.small_ is true, adds a child that increases the tap area.
	updateSmall: function() {
		if (this.$.tapArea) {
			this.$.tapArea.destroy();
			this.$.client.destroy();
		}

		if (this.small) {
			this.addClass('small');
			this.createComponent({name: "tapArea", classes: "small-button-tap-area", isChrome: true});
			if (this.marquee && !(this.components && this.components.length > 0)) {
				this.createComponent({name: "client", classes: "button-client",
					kind:"moon.MarqueeText", isChrome: true
				});
			} else {
				this.createComponent({name: "client", classes: "small-button-client"});
			}
		} else {
			this.removeClass('small');
			if (this.marquee && !(this.components && this.components.length > 0)) {
				this.createComponent({name: "client", classes: "button-client",
					kind:"moon.MarqueeText", isChrome: true
				});
			}
		}

		this.contentChanged();
	},
	//* When _this.small_ changes, updates and rerenders.
	smallChanged: function() {
		this.updateSmall();
		this.render();
	},
	//* Override to handle potential child components.
	contentChanged: function() {
		if (this.$.client) {
			this.$.client.setContent(this.getContent());
		} else {
			this.inherited(arguments);
		}
	}
});<|endoftext|>


<commit_before>
var _     = require( 'lodash' ),
    async = require( 'async' );

/*

   /. collection class vs. record class

   /. server inherits client objects

   /. validation (server vs. client)

   /. authorized methods + filtering attributes to the client

   /. offline support

   /. store cache on client

   /. tids ?

      f23<mongo id (24 chars) >

      <letter + 2 alphanum><mongo id>

   ?. how do we share code between server and client from the same source .js file ?

  Collection Schema BNF:

  <field def>: {
    type:  <string, a field type>,
    as:    <string, a label>,
    desc:  <string, a description>,
    link:  <string, a collection name ... type is implied for this>
    ...
  }

  <field>:
      <field def>
    | [ <field> ]
    | <field object>

  <field object>:
    { ( <field name>: <field> )* }

  <schema>: {
    name:  <string>,
    db:    <mongodb database>, // optional, if not present will default to config.db
    fields: <field object>
  }

*/

var collections = [],
    typesByName = {},
    config      = {};

// Schema
// ======

function pathAdd(path, add) {
  return path ? path + '.' + add : add;
}

function setFalse(v) {
  return v !== undefined && !v;
}

function validateType(type) {
  var def = type.def;

  if (!def) {
    throw new Error('Missing schema definition.');
  }

  if (!def.name) {
    throw new Error('Missing "name" field in schema definition.');
  }

  if (!_.isString(def.name)) {
    throw new Error('"name" should be a string in schema definition.');
  }

  if (typesByName[def.name]) {
    throw new Error('Type ' + def.name + ' redefined.');
  }

  typesByName[def.name] = type;
}

// Types
// =====

function Type(def) {
  this.def = def;

  validateType(this);
}

Type.prototype.validate = function(validator, path, field) {
  var v = this.def.validate;
  if (v) {
    v(validator, path, field);
  }
}

Type.prototype.fromClient = function(field, value) {
  var a = this.def.fromClient;
  return a ? a(field, value) : value;
};

Type.prototype.toClient = function(field, value) {
  var def = this.def;

  if (setFalse(def.client) || setFalse(field.client)) {
    return undefined;
  }

  var a = def.toClient;
  return a ? a(field, value) : value;
};

// Instance
// ========

var instancePrototype = {
  $save: function() {
    console.log('save called for type ' + this.$name);
  }
};

// Collection
// ==========

function Collection(def) {

  //
  // instances of Collection are functions so that you can go "var User = ...; var user = new User();"
  //
  // i.e.  instances of Collection are collections
  //       instances of instances of Collection are collection instances
  //

  var ip = {};
  _.assign(ip, instancePrototype);

  var CollectionInstance = function() {
    this.__proto__ = ip;
    console.log('instance of ' + def.name + ' created');
  }

  ip.constructor = CollectionInstance;
  ip.__proto__ = CollectionInstance.prototype;
  ip.$name = def.name;

  CollectionInstance.constructor = Collection;
  CollectionInstance.__proto__ = Collection.prototype;
  CollectionInstance.def = def;

  validateType(CollectionInstance);

  if (!def.dbName) {
    def.dbName = def.name;
  }

  var db = def.db || config.db;

  if ( !db )
    throw new Error('The "db" parameter must be specified either in the Collection schema or in the Tyranid.config().');

  CollectionInstance.db = db.collection(CollectionInstance.def.dbName);

  collections.push(CollectionInstance);

  return CollectionInstance;
}

//Collection.prototype = Object.create( null );

Collection.prototype.fieldsBy = function( comparable ) {
  var results = [];

  var cb = _.createCallback( comparable );

  function fieldsBy( path, val ) {

    if ( _.isArray( val ) ) {
      fieldsBy( path, val[ 0 ] );

    } else if ( _.isObject( val ) ) {

      if ( val.type ) {
        if ( cb( val ) )
          results.push( path );
      } else {
        _.each( val, function( field, name ) {
          fieldsBy( pathAdd( path, name ), field );
        });
      }
    }
  }

  fieldsBy( '', this.def.fields );
  return results;
};

Collection.prototype.valuesFor = function( fields, callback ) {

  var fieldsObj = { _id: 0 };
  _.each( fields, function( field ) {
    fieldsObj[ field ] = 1;
  });

  this.db.find( {}, fieldsObj, function( err, curs ) {
    if ( err ) {
      callback( err );
      return;
    }

    var values = [];

    function extractValues( val ) {
      if ( _.isObject( val ) )
        _.each( val, extractValues );
      else
        values.push( val );
    }

    function next() {
      curs.nextObject( function( err, doc ) {
        if ( err ) {
          callback( err );
          return;
        } else if ( !doc ) {
          callback( null, _.uniq( values ) );
        } else {
          extractValues( doc );
          next();
        }
      });
    }

    next();
  });
};

/**
 * This creates a new record instance out of a POJO.  Values are copied by reference (not deep-cloned!).
 */
Collection.prototype.fromClient = function(pojo) {
  var fields = this.def.fields;

  var obj = {}; // TODO:  create a new instance of this record-class?

  _.each(pojo, function(v, k) {
    var field = fields[k];

    if (field) {
      if (!field.type ) {
        throw new Error('collection missing type, missing from schema?');
      }

      obj[k] = field.type.fromClient(field, v);
    }
  });

  return obj;
};

/**
 * This creates a new POJO out of a record instance.  Values are copied by reference (not deep-cloned!).
 */
Collection.prototype.toClient = function(pojo) {
  var fields = this.def.fields;

  var obj = {};

  _.each(pojo, function(v, k) {
    var field = fields[k];

    if (field) {
      var v = field.type.toClient(field, v);

      if (v !== undefined) {
        obj[k] = v;
      }
    }
  });

  return obj;
};

Collection.prototype.validateValues = function() {
  var def = this.def,
      rows = def.values;

  if (!rows) {
    return;
  }

  if (!_.isArray(rows)) {
    throw 'Expected values for collection ' + def.name + ' to be an array';
  }

  var ri,
      rlen = rows.length;
  if (!rlen) {
    return;
  }

  if (_.isArray(rows[0])) {
    // array format

    for (ri=0; ri<rlen; ri++) {
      if (!_.isArray(rows[ri])) {
        throw 'Expected value on row ' + ri + ' to be an array for collection ' + def.name;
      }
    }

    var header = rows[0],
        hi,
        hlen = header.length,
        newValues = [];

    for (hi=0; hi<hlen; hi++) {
      var name = header[hi];

      if (!_.isString(name)) {
        throw 'Expected value ' + hi + ' in the values header for collection ' + def.name + ' to be a string';
      }

      if (!def.fields[name]) {
        throw 'Field ' + name + ' does not exist on collection ' + def.name;
      }
    }

    for (ri=1; ri<rlen; ri++) {
      var orow = rows[ri],
          nrow = {};

      if (orow.length !== hlen && orow.length !== hlen+1) {
        throw 'Incorrect number of values on row ' + ri + ' in collection ' + def.name;
      }

      for (hi=0; hi<hlen; hi++) {
        var v = orow[hi];
        nrow[header[hi]] = v;
      }

      if (orow.length > hlen) {
        var extraVals = orow[hi];

        _.each(extraVals, function(v, n) {
          if (!def.fields[n]) {
            throw 'Field ' + n + ' does not exist on collection ' + def.name + ' on row ' + ri;
          }

          nrow[n] = v;
        });
      }

      newValues.push(nrow);
    }

    def.values = newValues;

  } else {
    // object format

    for (ri=0; ri<rlen; ri++) {
      if (!_.isObject(rows[ri])) {
        throw 'Expected value on row ' + ri + ' to be an object for collection ' + def.name;
      }
    }
  }
};

Collection.prototype.validateSchema = function() {
  var def = this.def;

  var validator = {
    err: function err(path, msg) {
      return new Error('Tyranid Schema Error| ' + def.name + (path ? path : '') + ': ' + msg);
    },

    field: function(path, field) {

      if (!_.isObject(field)) {
        throw validator.err('Invalid field definition, expected an object, got: ' + field);
      }

      if (field.type) {
        var type = typesByName[field.type];

        if (!type) {
          throw validator.err(path, 'Unknown type ' + field.type);
        }

        field.type = type;

        type.validate(validator, path, field);

      } else if (field.link) {
        var type = typesByName[field.link];

        if (!type) {
          throw validator.err(path, 'Unknown type ' + field.link);
        }

        if (!(type instanceof Collection)) {
          throw validator.err(path, 'Links must link to a collection, instead linked to ' + field.link);
        }

        field.type = typesByName.link;
        field.link = type;

      } else {
        throw validator.err('Unknown field definition');
      }
    },

    fields: function(path, val) {
      if (!val) {
        throw validator.err(path, 'Missing "fields"');
      }

      if (!_.isObject(val)) {
        throw validator.err(path, '"fields" should be an object, got: ' + val);
      }

      _.each(val, function(field, name) { return validator.field(path + '.' + name, field) });
    }
  };

  validator.fields('', this.def.fields);

  this.validateValues();
};

var Tyranid = {

  config: function( opts ) {
    config = opts;

    if ( !opts.db )
      throw new Error( 'Missing "db" in config.' );
  },

  validate: function( ) {
    collections.forEach(function(col) {
      col.validateSchema();
    });
  },

  Collection: Collection,

  collections: collections,

  valuesBy: function( comparable, callback ) {

    async.series(
      _.map( all, function( coll ) {
        return function( cb ) {
          coll.valuesFor( coll.fieldsBy( comparable ), cb );
        };
      }),
      function(err, arrs) {
        callback( err, _.union.apply( null, arrs ) );
      }
    );
  }
};

new Type({
  name: 'link',
  // TODO:  look up the data type of the id, and use that type from inside link, rather than just copying mongoids
  //        (i.e. if the primary id is an int)
  fromClient: function(field, value) {
    return ObjectId(value);
  },
  toClient: function(field, value) {
    return value ? value.toString() : value;
  }
});

new Type({ name: 'boolean' });
new Type({ name: 'integer' });
new Type({ name: 'string' });

new Type({
  name: 'array',
  validate: function(validator, path, field) {
    var of = field.of;

    if (!of) {
      throw validator.err(path, 'Missing "of" property on array definition');
    }

    if (_.isPlainObject(of)) {
      validator.field(path, of);
    } else {
      of = typesByName[of];
      if (!of) {
        throw validator.err(path, 'Unknown type for "of".');
      }

      field.of = of;
    }
  }
});

new Type({ name: 'object' });

new Type({
  name: 'mongoid',
  fromClient: function(field, value) {
    return ObjectId(value);
  },
  toClient: function(field, value) {
    return value ? value.toString() : value;
  }
});

new Type({ name: 'email' });
new Type({ name: 'password', client: false });
new Type({ name: 'image' });

module.exports = Tyranid;

<commit_msg>work on uids<commit_after>
var _     = require( 'lodash' ),
    async = require( 'async' );

/*

   /. collection class vs. record class

   /. server inherits client objects

   /. validation (server vs. client)

   /. authorized methods + filtering attributes to the client

   /. offline support

   /. store cache on client

   /. tids ?

      f23<mongo id (24 chars) >

      <letter + 2 alphanum><mongo id>

   ?. how do we share code between server and client from the same source .js file ?

  Collection Schema BNF:

  <field def>: {
    type:  <string, a field type>,
    as:    <string, a label>,
    desc:  <string, a description>,
    link:  <string, a collection name ... type is implied for this>
    ...
  }

  <field>:
      <field def>
    | [ <field> ]
    | <field object>

  <field object>:
    { ( <field name>: <field> )* }

  <schema>: {
    name:  <string>,
    db:    <mongodb database>, // optional, if not present will default to config.db
    fields: <field object>
  }

*/

var collections     = [],
    collectionsById = {},
    typesByName     = {},
    config          = {};

// Schema
// ======

function pathAdd(path, add) {
  return path ? path + '.' + add : add;
}

function setFalse(v) {
  return v !== undefined && !v;
}

function validateType(type) {
  var def = type.def;

  if (!def) {
    throw new Error('Missing schema definition.');
  }

  if (!def.name) {
    throw new Error('Missing "name" field in schema definition.');
  }

  if (!_.isString(def.name)) {
    throw new Error('"name" should be a string in schema definition.');
  }

  if (typesByName[def.name]) {
    throw new Error('Type ' + def.name + ' redefined.');
  }

  typesByName[def.name] = type;
}

// Types
// =====

function Type(def) {
  this.def = def;

  validateType(this);
}

Type.prototype.validate = function(validator, path, field) {
  var v = this.def.validate;
  if (v) {
    v(validator, path, field);
  }
}

Type.prototype.fromClient = function(field, value) {
  var a = this.def.fromClient;
  return a ? a(field, value) : value;
};

Type.prototype.toClient = function(field, value) {
  var def = this.def;

  if (setFalse(def.client) || setFalse(field.client)) {
    return undefined;
  }

  var a = def.toClient;
  return a ? a(field, value) : value;
};

// Document
// ========

var documentPrototype = {
  $save: function() {
    console.log('save called for type ' + this.$name);
  },

  $uid: function() {
    return this.idToUid(this._id);
  }
};

// Collection
// ==========

function Collection(def) {

  var colId = def.id;
  if (!colId) {
    throw new Error('The "id" for the collection was not specified.');
  }

  if (colId.length !== 3) {
    throw new Error('The collection "id" should be three characters long.');
  }

  if (collectionsById[colId]) {
    throw new Error('The collection "id" is already in use by ' + collectionsById[colId].name + '.');
  }

  //
  // instances of Collection are functions so that you can go "var User = ...; var user = new User();"
  //
  // i.e.  instances of Collection are collections
  //       instances of instances of Collection are documents (collection instances)
  //

  var dp = {};
  _.assign(dp, documentPrototype);

  dp.constructor = CollectionInstance;
  dp.__proto__ = CollectionInstance.prototype;
  dp.$name = def.name;

  var CollectionInstance = function() {
    this.__proto__ = dp;
  }

  CollectionInstance.constructor = Collection;
  CollectionInstance.__proto__ = Collection.prototype;
  CollectionInstance.def = def;
  CollectionInstance.id = colId;

  validateType(CollectionInstance);

  if (!def.dbName) {
    def.dbName = def.name;
  }

  var db = def.db || config.db;

  if ( !db )
    throw new Error('The "db" parameter must be specified either in the Collection schema or in the Tyranid.config().');

  CollectionInstance.db = db.collection(CollectionInstance.def.dbName);

  collections.push(CollectionInstance);

  collectionsById[def.id] = collection;

  return CollectionInstance;
}

Collection.prototype.idToUid = function(id) {
  return this.id + id;
};

//Collection.prototype = Object.create( null );

Collection.prototype.fieldsBy = function( comparable ) {
  var results = [];

  var cb = _.createCallback( comparable );

  function fieldsBy( path, val ) {

    if ( _.isArray( val ) ) {
      fieldsBy( path, val[ 0 ] );

    } else if ( _.isObject( val ) ) {

      if ( val.type ) {
        if ( cb( val ) )
          results.push( path );
      } else {
        _.each( val, function( field, name ) {
          fieldsBy( pathAdd( path, name ), field );
        });
      }
    }
  }

  fieldsBy( '', this.def.fields );
  return results;
};

Collection.prototype.valuesFor = function( fields, callback ) {

  var fieldsObj = { _id: 0 };
  _.each( fields, function( field ) {
    fieldsObj[ field ] = 1;
  });

  this.db.find( {}, fieldsObj, function( err, curs ) {
    if ( err ) {
      callback( err );
      return;
    }

    var values = [];

    function extractValues( val ) {
      if ( _.isObject( val ) )
        _.each( val, extractValues );
      else
        values.push( val );
    }

    function next() {
      curs.nextObject( function( err, doc ) {
        if ( err ) {
          callback( err );
          return;
        } else if ( !doc ) {
          callback( null, _.uniq( values ) );
        } else {
          extractValues( doc );
          next();
        }
      });
    }

    next();
  });
};

/**
 * This creates a new record instance out of a POJO.  Values are copied by reference (not deep-cloned!).
 */
Collection.prototype.fromClient = function(pojo) {
  var fields = this.def.fields;

  var obj = {}; // TODO:  create a new instance of this record-class?

  _.each(pojo, function(v, k) {
    var field = fields[k];

    if (field) {
      if (!field.type ) {
        throw new Error('collection missing type, missing from schema?');
      }

      obj[k] = field.type.fromClient(field, v);
    }
  });

  return obj;
};

/**
 * This creates a new POJO out of a record instance.  Values are copied by reference (not deep-cloned!).
 */
Collection.prototype.toClient = function(pojo) {
  var fields = this.def.fields;

  var obj = {};

  _.each(pojo, function(v, k) {
    var field = fields[k];

    if (field) {
      var v = field.type.toClient(field, v);

      if (v !== undefined) {
        obj[k] = v;
      }
    }
  });

  return obj;
};

Collection.prototype.validateValues = function() {
  var def = this.def,
      rows = def.values;

  if (!rows) {
    return;
  }

  if (!_.isArray(rows)) {
    throw 'Expected values for collection ' + def.name + ' to be an array';
  }

  var ri,
      rlen = rows.length;
  if (!rlen) {
    return;
  }

  if (_.isArray(rows[0])) {
    // array format

    for (ri=0; ri<rlen; ri++) {
      if (!_.isArray(rows[ri])) {
        throw 'Expected value on row ' + ri + ' to be an array for collection ' + def.name;
      }
    }

    var header = rows[0],
        hi,
        hlen = header.length,
        newValues = [];

    for (hi=0; hi<hlen; hi++) {
      var name = header[hi];

      if (!_.isString(name)) {
        throw 'Expected value ' + hi + ' in the values header for collection ' + def.name + ' to be a string';
      }

      if (!def.fields[name]) {
        throw 'Field ' + name + ' does not exist on collection ' + def.name;
      }
    }

    for (ri=1; ri<rlen; ri++) {
      var orow = rows[ri],
          nrow = {};

      if (orow.length !== hlen && orow.length !== hlen+1) {
        throw 'Incorrect number of values on row ' + ri + ' in collection ' + def.name;
      }

      for (hi=0; hi<hlen; hi++) {
        var v = orow[hi];
        nrow[header[hi]] = v;
      }

      if (orow.length > hlen) {
        var extraVals = orow[hi];

        _.each(extraVals, function(v, n) {
          if (!def.fields[n]) {
            throw 'Field ' + n + ' does not exist on collection ' + def.name + ' on row ' + ri;
          }

          nrow[n] = v;
        });
      }

      newValues.push(nrow);
    }

    def.values = newValues;

  } else {
    // object format

    for (ri=0; ri<rlen; ri++) {
      if (!_.isObject(rows[ri])) {
        throw 'Expected value on row ' + ri + ' to be an object for collection ' + def.name;
      }
    }
  }
};

Collection.prototype.validateSchema = function() {
  var def = this.def;

  var validator = {
    err: function err(path, msg) {
      return new Error('Tyranid Schema Error| ' + def.name + (path ? path : '') + ': ' + msg);
    },

    field: function(path, field) {

      if (!_.isObject(field)) {
        throw validator.err('Invalid field definition, expected an object, got: ' + field);
      }

      if (field.type) {
        var type = typesByName[field.type];

        if (!type) {
          throw validator.err(path, 'Unknown type ' + field.type);
        }

        field.type = type;

        type.validate(validator, path, field);

      } else if (field.link) {
        var type = typesByName[field.link];

        if (!type) {
          throw validator.err(path, 'Unknown type ' + field.link);
        }

        if (!(type instanceof Collection)) {
          throw validator.err(path, 'Links must link to a collection, instead linked to ' + field.link);
        }

        field.type = typesByName.link;
        field.link = type;

      } else {
        throw validator.err('Unknown field definition');
      }
    },

    fields: function(path, val) {
      if (!val) {
        throw validator.err(path, 'Missing "fields"');
      }

      if (!_.isObject(val)) {
        throw validator.err(path, '"fields" should be an object, got: ' + val);
      }

      _.each(val, function(field, name) { return validator.field(path + '.' + name, field) });
    }
  };

  validator.fields('', this.def.fields);

  this.validateValues();
};

var Tyranid = {

  config: function( opts ) {
    config = opts;

    if ( !opts.db )
      throw new Error( 'Missing "db" in config.' );
  },

  validate: function( ) {
    collections.forEach(function(col) {
      col.validateSchema();
    });
  },

  Collection: Collection,

  collections: collections,

  valuesBy: function( comparable, callback ) {

    async.series(
      _.map( all, function( coll ) {
        return function( cb ) {
          coll.valuesFor( coll.fieldsBy( comparable ), cb );
        };
      }),
      function(err, arrs) {
        callback( err, _.union.apply( null, arrs ) );
      }
    );
  }
};

new Type({
  name: 'link',
  // TODO:  look up the data type of the id, and use that type from inside link, rather than just copying mongoids
  //        (i.e. if the primary id is an int)
  fromClient: function(field, value) {
    return ObjectId(value);
  },
  toClient: function(field, value) {
    return value ? value.toString() : value;
  }
});

new Type({ name: 'boolean' });
new Type({ name: 'integer' });
new Type({ name: 'string' });

new Type({
  name: 'array',
  validate: function(validator, path, field) {
    var of = field.of;

    if (!of) {
      throw validator.err(path, 'Missing "of" property on array definition');
    }

    if (_.isPlainObject(of)) {
      validator.field(path, of);
    } else {
      of = typesByName[of];
      if (!of) {
        throw validator.err(path, 'Unknown type for "of".');
      }

      field.of = of;
    }
  }
});

new Type({ name: 'object' });

new Type({
  name: 'mongoid',
  fromClient: function(field, value) {
    return ObjectId(value);
  },
  toClient: function(field, value) {
    return value ? value.toString() : value;
  }
});

new Type({ name: 'email' });
new Type({ name: 'password', client: false });
new Type({ name: 'image' });

module.exports = Tyranid;

<|endoftext|>