<commit_before>import template from './resource-state.html';

export default function resourceState() {
  return {
    restrict: 'E',
    template: template,
    scope: {},
    bindToController: {
      resource: '='
    },
    controller: ResourceStateController,
    controllerAs: '$ctrl'
  };
}

// @ngInject
function ResourceStateController(resourceUtils) {
  this.context = resourceUtils.getResourceState(this.resource);
}
<commit_msg>Added watcher to display resource state (WAL-237).<commit_after>import template from './resource-state.html';

export default function resourceState() {
  return {
    restrict: 'E',
    template: template,
    scope: {},
    bindToController: {
      resource: '='
    },
    controller: ResourceStateController,
    controllerAs: '$ctrl'
  };
}

// @ngInject
function ResourceStateController(resourceUtils, $scope) {
  $scope.$watch(() => this.resource, () =>
    this.context = resourceUtils.getResourceState(this.resource)
  );
}
<|endoftext|>


<commit_before>const _fs = require('fs');
const _path = require('path');

const _ = require('lodash');
const async = require('async');
const webpack = require('webpack');
const workerFarm = require('worker-farm');

const workers = workerFarm(
  require.resolve('./build_worker')
);

const webpackConfig = require('../config/build');
const write = require('./write');

module.exports = function (config) {
  return new Promise(function (resolve, reject) {
    const output = config.antwar.output;
    const log = config.antwar.console.log;

    if (!output) {
      return reject(new Error('Missing output directory'));
    }

    return webpackConfig(config)
      .then(runWebpack())
      .then(generateParameters(config.antwar))
      .then(writeExtras())
      .then(executeTasks(log))
      .catch(reject);
  });
};

function runWebpack() {
  return config => new Promise(function (resolve, reject) {
    webpack(config, function (err, stats) {
      if (err) {
        return reject(err);
      }

      if (stats.hasErrors()) {
        return reject(stats.toString('errors-only'));
      }

      return resolve(stats);
    });
  });
}

function generateParameters(config) {
  return stats => new Promise(function (resolve) {
    const assets = stats.compilation.assets;
    const cssFiles = Object.keys(assets).map(asset => {
      if (_path.extname(asset) === '.css') {
        return assets[asset].existsAt;
      }

      return null;
    }).filter(a => a);
    const jsFiles = [];

    const cwd = process.cwd();
    const site = require(_path.join(cwd, config.output, 'site.js'));
    const parameters = {
      cwd,
      renderPage: site.renderPage,
      allPaths: site.allPaths,
      output: _path.join(cwd, config.output),
      config,
      cssFiles,
      jsFiles,
      templates: {
        page: {
          ...config.template,
          // XXX: sync operation
          file: _fs.readFileSync(
            (config.template && config.template.file) ||
            _path.join(__dirname, '../../templates/page.ejs'),
            {
              encoding: 'utf8'
            }
          ),
          cssFiles: cssFiles.map(cssFile => '/' + _path.basename(cssFile)),
          jsFiles
        },
        // TODO: expose to the user?
        interactive: {
          file: _fs.readFileSync(
            _path.join(__dirname, '../../templates/interactive.ejs'),
            {
              encoding: 'utf8'
            }
          )
        },
        interactiveIndex: {
          file: _fs.readFileSync(
            _path.join(__dirname, '../../templates/interactive_index.ejs'),
            {
              encoding: 'utf8'
            }
          )
        }
      }
    };

    resolve(parameters);
  });
}

function writeExtras() {
  return parameters => new Promise(function (resolve, reject) {
    const config = parameters.config;
    const assets = config && config.assets ? config.assets : [];

    // Extras
    const pluginExtras = _.map(config.plugins, 'extra').filter(_.identity);
    const extraFiles = _.map(pluginExtras, function (plugin) {
      return plugin(parameters.allPaths, config);
    });

    if (parameters.cssFiles) {
      parameters.cssFiles.forEach(cssFile => {
        assets.push({
          from: cssFile,
          to: './' + _path.basename(cssFile)
        });
      });
    }

    // get functions to execute
    return async.parallel([
      write.extras(parameters, extraFiles),
      write.pages(parameters),
      write.redirects(parameters)
    ], function (err, tasks) {
      if (err) {
        return reject(err);
      }

      return resolve(_.flatten(tasks).filter(_.identity));
    });
  });
}

function executeTasks(log) {
  return tasks => new Promise(function (resolve, reject) {
    async.each(tasks, function (o, cb) {
      log('Starting task', o.task);

      workers(o, function (err) {
        log('Finished task', o.task);

        cb(err);
      });
    }, function (err) {
      log('Tasks finished');

      workerFarm.end(workers);

      if (err) {
        return reject(err);
      }

      return resolve();
    });
  });
}
<commit_msg>fix(antwar) - Drop generated `site.js`<commit_after>const _fs = require('fs');
const _path = require('path');

const _ = require('lodash');
const async = require('async');
const rimraf = require('rimraf');
const webpack = require('webpack');
const workerFarm = require('worker-farm');

const workers = workerFarm(
  require.resolve('./build_worker')
);

const webpackConfig = require('../config/build');
const write = require('./write');

module.exports = function (config) {
  return new Promise(function (resolve, reject) {
    const output = config.antwar.output;
    const log = config.antwar.console.log;

    if (!output) {
      return reject(new Error('Missing output directory'));
    }

    return webpackConfig(config)
      .then(runWebpack())
      .then(generateParameters(config.antwar))
      .then(writeExtras())
      .then(executeTasks(log))
      .then(removeSiteBundle(config.antwar.output))
      .catch(reject);
  });
};

function runWebpack() {
  return config => new Promise(function (resolve, reject) {
    webpack(config, function (err, stats) {
      if (err) {
        return reject(err);
      }

      if (stats.hasErrors()) {
        return reject(stats.toString('errors-only'));
      }

      return resolve(stats);
    });
  });
}

function generateParameters(config) {
  return stats => new Promise(function (resolve) {
    const assets = stats.compilation.assets;
    const cssFiles = Object.keys(assets).map(asset => {
      if (_path.extname(asset) === '.css') {
        return assets[asset].existsAt;
      }

      return null;
    }).filter(a => a);
    const jsFiles = [];

    const cwd = process.cwd();
    const site = require(_path.join(cwd, config.output, 'site.js'));
    const parameters = {
      cwd,
      renderPage: site.renderPage,
      allPaths: site.allPaths,
      output: _path.join(cwd, config.output),
      config,
      cssFiles,
      jsFiles,
      templates: {
        page: {
          ...config.template,
          // XXX: sync operation
          file: _fs.readFileSync(
            (config.template && config.template.file) ||
            _path.join(__dirname, '../../templates/page.ejs'),
            {
              encoding: 'utf8'
            }
          ),
          cssFiles: cssFiles.map(cssFile => '/' + _path.basename(cssFile)),
          jsFiles
        },
        // TODO: expose to the user?
        interactive: {
          file: _fs.readFileSync(
            _path.join(__dirname, '../../templates/interactive.ejs'),
            {
              encoding: 'utf8'
            }
          )
        },
        interactiveIndex: {
          file: _fs.readFileSync(
            _path.join(__dirname, '../../templates/interactive_index.ejs'),
            {
              encoding: 'utf8'
            }
          )
        }
      }
    };

    resolve(parameters);
  });
}

function writeExtras() {
  return parameters => new Promise(function (resolve, reject) {
    const config = parameters.config;
    const assets = config && config.assets ? config.assets : [];

    // Extras
    const pluginExtras = _.map(config.plugins, 'extra').filter(_.identity);
    const extraFiles = _.map(pluginExtras, function (plugin) {
      return plugin(parameters.allPaths, config);
    });

    if (parameters.cssFiles) {
      parameters.cssFiles.forEach(cssFile => {
        assets.push({
          from: cssFile,
          to: './' + _path.basename(cssFile)
        });
      });
    }

    // get functions to execute
    return async.parallel([
      write.extras(parameters, extraFiles),
      write.pages(parameters),
      write.redirects(parameters)
    ], function (err, tasks) {
      if (err) {
        return reject(err);
      }

      return resolve(_.flatten(tasks).filter(_.identity));
    });
  });
}

function executeTasks(log) {
  return tasks => new Promise(function (resolve, reject) {
    async.each(tasks, function (o, cb) {
      log('Starting task', o.task);

      workers(o, function (err) {
        log('Finished task', o.task);

        cb(err);
      });
    }, function (err) {
      log('Tasks finished');

      workerFarm.end(workers);

      if (err) {
        return reject(err);
      }

      return resolve();
    });
  });
}

function removeSiteBundle(outputDirectory) {
  return () => new Promise(function (resolve, reject) {
    rimraf(
      _path.join(process.cwd(), outputDirectory, 'site.js'),
      function (err) {
        if (err) {
          return reject(err);
        }

        return resolve();
      }
    );
  });
}
<|endoftext|>


<commit_before>(function(ng) {
  'use strict';
  var AdminuiDaterange = function($compile, $parse, $timeout) {
    return {
      restrict: 'A',
      require: 'ngModel',
      link: function($scope, $element, $attributes, ngModel) {
        $element.attr('readonly', true);
        $element.css('background-color', 'white');
        var options = {};
        options.format = $attributes.format || 'YYYY-MM-DD';
        options.separator = $attributes.separator || ' - ';
        options.minDate = $attributes.minDate && moment($attributes.minDate);
        options.maxDate = $attributes.maxDate && moment($attributes.maxDate);
        options.dateLimit = $attributes.limit &&
          moment.duration.apply(
            this, $attributes.limit.split(' ').map(
              function(elem, index) {
                return index === 0 && parseInt(elem, 10) || elem;
              }
            )
          );
        options.ranges = $attributes.ranges &&
          $parse($attributes.ranges)($scope);
        options.locale = {
          'applyLabel': '应用',
          'cancelLabel': '取消',
          'customRangeLabel': '自定义'
        };

        options.opens = $attributes.opens || 'auto';

        $timeout(function() {
          var dateRangePicker = $element.data('daterangepicker');
          var oldMove = ng.copy(dateRangePicker.move);
          dateRangePicker.move = function() {
            var parentOffset = { top: 0, left: 0 };
            if (!this.parentEl.is('body')) {
              parentOffset = {
                top: this.parentEl.offset().top - this.parentEl.scrollTop(),
                left: this.parentEl.offset().left - this.parentEl.scrollLeft()
              };
            }
            if (options.opens == 'auto') {
              this.container.css({
                top: this.element.offset().top + this.element.outerHeight() - parentOffset.top,
                left: this.element.offset().left - parentOffset.left,
                right: 'auto'
              }).addClass('opensright');
              if (this.container.offset().left + this.container.outerWidth() > $(window).width()) {
                this.container.css({
                  top: this.element.offset().top + this.element.outerHeight() - parentOffset.top,
                  right: $(window).width() - this.element.offset().left - this.element.outerWidth() - parentOffset.left,
                  left: 'auto'
                }).addClass('opensleft').removeClass('opensright');
                if (this.container.offset().left < 0) {
                  this.container.css({
                    top: this.element.offset().top + this.element.outerHeight() - parentOffset.top,
                    left: this.element.offset().left - parentOffset.left,
                    right: 'auto'
                  }).addClass('opensright').removeClass('opensleft');
                }
              }

              var leftCalendar = this.container.find('.calendar.left');
              var rightCalendar = this.container.find('.calendar.right');

              if (this.container.hasClass('opensright')) {
                rightCalendar.after(leftCalendar);
              } else {
                leftCalendar.after(rightCalendar);
              }

            } else {
              if (this.opens == 'left') {
                this.container.css({
                  top: this.element.offset().top + this.element.outerHeight() - parentOffset.top,
                  right: $(window).width() - this.element.offset().left - this.element.outerWidth() - parentOffset.left,
                  left: 'auto'
                });
              } else {
                this.container.css({
                  top: this.element.offset().top + this.element.outerHeight() - parentOffset.top,
                  left: this.element.offset().left - parentOffset.left,
                  right: 'auto'
                });
              }
            }
          };

          var resetBtn = ng.element('<button>清空</button>')
            .addClass('cancelBtn btn btn-default').bind('click', function() {
              $scope.$apply(function() {
                ($parse($attributes.ngModel).assign)($scope, null);
              });
            });
          $element.data('daterangepicker')
            .container.find('.applyBtn').bind('click', function() {
            $scope.$apply(function() {
              ngModel.$setViewValue({
                startDate: $element.data('daterangepicker').startDate.toDate(),
                endDate: $element.data('daterangepicker').endDate.toDate()
              });
            });
          });
          $element.data('daterangepicker')
            .container.find('.applyBtn').after(resetBtn);
        });

        function format(date) {
          return date.format(options.format);
        }

        function formatted(dates) {
          return [format(dates.startDate), format(dates.endDate)]
            .join(options.separator);
        }

        ngModel.$formatters.unshift(function(modelValue) {
          if (!modelValue) return '';
          return modelValue;
        });

        ngModel.$parsers.unshift(function(viewValue) {
          return viewValue;
        });

        $scope.$watch($attributes.ngModel, function(modelValue) {
          if (!modelValue || (!modelValue.startDate)) {
            return;
          }
          $element.data('daterangepicker').startDate =
            moment(modelValue.startDate);
          $element.data('daterangepicker').endDate = moment(modelValue.endDate);
          $element.data('daterangepicker').updateView();
          $element.data('daterangepicker').updateCalendars();
          $element.data('daterangepicker').updateInputText();
        });

        $element.daterangepicker(options, function(start, end) {
          $scope.$apply(function() {
            ngModel.$setViewValue({
              startDate: start.toDate(),
              endDate: end.toDate()
            });
          });
        });
      }
    };
  };
  ng.module('ntd.directives')
    .directive('adminuiDaterangePicker',
      ['$compile', '$parse', '$timeout', AdminuiDaterange]
    );
})(angular);
<commit_msg>修改dataRangePick鼠标样式<commit_after>(function(ng) {
  'use strict';
  var AdminuiDaterange = function($compile, $parse, $timeout) {
    return {
      restrict: 'A',
      require: 'ngModel',
      link: function($scope, $element, $attributes, ngModel) {
        $element.attr('readonly', true);
        $element.css('cursor', 'default');
        $element.css('background-color', 'white');
        var options = {};
        options.format = $attributes.format || 'YYYY-MM-DD';
        options.separator = $attributes.separator || ' - ';
        options.minDate = $attributes.minDate && moment($attributes.minDate);
        options.maxDate = $attributes.maxDate && moment($attributes.maxDate);
        options.dateLimit = $attributes.limit &&
          moment.duration.apply(
            this, $attributes.limit.split(' ').map(
              function(elem, index) {
                return index === 0 && parseInt(elem, 10) || elem;
              }
            )
          );
        options.ranges = $attributes.ranges &&
          $parse($attributes.ranges)($scope);
        options.locale = {
          'applyLabel': '应用',
          'cancelLabel': '取消',
          'customRangeLabel': '自定义'
        };

        options.opens = $attributes.opens || 'auto';

        $timeout(function() {
          var dateRangePicker = $element.data('daterangepicker');
          var oldMove = ng.copy(dateRangePicker.move);
          dateRangePicker.move = function() {
            var parentOffset = { top: 0, left: 0 };
            if (!this.parentEl.is('body')) {
              parentOffset = {
                top: this.parentEl.offset().top - this.parentEl.scrollTop(),
                left: this.parentEl.offset().left - this.parentEl.scrollLeft()
              };
            }
            if (options.opens == 'auto') {
              this.container.css({
                top: this.element.offset().top + this.element.outerHeight() - parentOffset.top,
                left: this.element.offset().left - parentOffset.left,
                right: 'auto'
              }).addClass('opensright');
              if (this.container.offset().left + this.container.outerWidth() > $(window).width()) {
                this.container.css({
                  top: this.element.offset().top + this.element.outerHeight() - parentOffset.top,
                  right: $(window).width() - this.element.offset().left - this.element.outerWidth() - parentOffset.left,
                  left: 'auto'
                }).addClass('opensleft').removeClass('opensright');
                if (this.container.offset().left < 0) {
                  this.container.css({
                    top: this.element.offset().top + this.element.outerHeight() - parentOffset.top,
                    left: this.element.offset().left - parentOffset.left,
                    right: 'auto'
                  }).addClass('opensright').removeClass('opensleft');
                }
              }

              var leftCalendar = this.container.find('.calendar.left');
              var rightCalendar = this.container.find('.calendar.right');

              if (this.container.hasClass('opensright')) {
                rightCalendar.after(leftCalendar);
              } else {
                leftCalendar.after(rightCalendar);
              }

            } else {
              if (this.opens == 'left') {
                this.container.css({
                  top: this.element.offset().top + this.element.outerHeight() - parentOffset.top,
                  right: $(window).width() - this.element.offset().left - this.element.outerWidth() - parentOffset.left,
                  left: 'auto'
                });
              } else {
                this.container.css({
                  top: this.element.offset().top + this.element.outerHeight() - parentOffset.top,
                  left: this.element.offset().left - parentOffset.left,
                  right: 'auto'
                });
              }
            }
          };

          var resetBtn = ng.element('<button>清空</button>')
            .addClass('cancelBtn btn btn-default').bind('click', function() {
              $scope.$apply(function() {
                ($parse($attributes.ngModel).assign)($scope, null);
              });
            });
          $element.data('daterangepicker')
            .container.find('.applyBtn').bind('click', function() {
            $scope.$apply(function() {
              ngModel.$setViewValue({
                startDate: $element.data('daterangepicker').startDate.toDate(),
                endDate: $element.data('daterangepicker').endDate.toDate()
              });
            });
          });
          $element.data('daterangepicker')
            .container.find('.applyBtn').after(resetBtn);
        });

        function format(date) {
          return date.format(options.format);
        }

        function formatted(dates) {
          return [format(dates.startDate), format(dates.endDate)]
            .join(options.separator);
        }

        ngModel.$formatters.unshift(function(modelValue) {
          if (!modelValue) return '';
          return modelValue;
        });

        ngModel.$parsers.unshift(function(viewValue) {
          return viewValue;
        });

        $scope.$watch($attributes.ngModel, function(modelValue) {
          if (!modelValue || (!modelValue.startDate)) {
            return;
          }
          $element.data('daterangepicker').startDate =
            moment(modelValue.startDate);
          $element.data('daterangepicker').endDate = moment(modelValue.endDate);
          $element.data('daterangepicker').updateView();
          $element.data('daterangepicker').updateCalendars();
          $element.data('daterangepicker').updateInputText();
        });

        $element.daterangepicker(options, function(start, end) {
          $scope.$apply(function() {
            ngModel.$setViewValue({
              startDate: start.toDate(),
              endDate: end.toDate()
            });
          });
        });
      }
    };
  };
  ng.module('ntd.directives')
    .directive('adminuiDaterangePicker',
      ['$compile', '$parse', '$timeout', AdminuiDaterange]
    );
})(angular);
<|endoftext|>


<commit_before>const PROFILE = {
  summary: `Den Temple is a Javascript Developer specializing in Web and Mobile Design. He has additional expertise with SEO consulting, API integration, and Machine Learning analytics.`,
  email: 'developer@dentemple.com',
  forHire: true,
  location: 'Scranton, PA',
  year: 'April 2017',
  clientSkills: [
    'Web Apps',
    'UI/UX',
    'SEO',
    'Social Media APIs',
    'Payment APIs',
    'Based in U.S.'
  ],
  employerSkills: ['React', 'Node.js', 'Agile/Scrum', 'Git/Github']
}

export default PROFILE
<commit_msg>Add React VR and Javascript ES6+ to skillset<commit_after>const PROFILE = {
  summary: `Den Temple is a Javascript Developer specializing in Web and Mobile Design. He has additional expertise with VR experiences, API integration, and SEO consulting.`,
  email: 'developer@dentemple.com',
  forHire: true,
  location: 'Scranton, PA',
  year: 'April 2017',
  clientSkills: [
    'Web Apps',
    'UI/UX',
    'SEO',
    'Social Media APIs',
    'Payment APIs',
    'Based in U.S.'
  ],
  employerSkills: [
    'React/React VR',
    'Javascript ES6+',
    'Node.js',
    'Agile/Scrum',
    'Git/Github'
  ]
}

export default PROFILE
<|endoftext|>


<commit_before>/**
*	@module com.spinal.util.factories
*	@author Patricio Ferreira <3dimentionar@gmail.com>
**/
define(['core/spinal',
		'util/adt/stack',
		'util/factories/factory'], function(Spinal, Stack, Factory) {

	/**
	*	AsyncFactory Class
	*	This class uses a Stack ADT internally to enqueue resources and trigger the loading phase asynchronously.
	*	After all the resources are loaded, it register them in the factory.
	*
Usages:

	*
	*		var myAsyncFactory = new AsyncFactory();
	*			myAsyncFactory.set([{
	*				id: 'resourceA', path: 'path/to/resourceA',
	*				id: 'resourceB', path: 'path/to/resourceB'
	*			}]);
	*			myAsyncFactory.on(AsyncFactory.EVENTS.loaded, myLoadedCallback);
	*			// On each resource loaded and successfuly registered, the callback will be called.
	*			myAsyncFactory.load(_.bind(function(id, resource) {
	*				// make use of resource or the id of the resource ('resourceA' or 'resourceB');
	*			}, this));
	*
	*	@namespace com.spinal.util.factories
	*	@class com.spinal.util.factories.AsyncFactory
	*	@extends com.spinal.util.factories.Factory
	*
	*	@requires com.spinal.core.Spinal
	*	@requires com.spinal.util.adt.Stack
	*	@requires com.spinal.util.factories.Factory
	**/
	var AsyncFactory = Spinal.namespace('com.spinal.util.factories.AsyncFactory', Factory.inherit({

		/**
		*	Stack of factory constructors to load
		*	@public
		*	@property stack
		*	@type {com.spinal.util.adt.Stack}
		**/
		stack: null,

		/**
		*	Initialize
		*	@public
		*	@method initialize
		*	@param [opts] {Object} options
		*	@return {com.spinal.util.factories.AsyncFactory}
		**/
		initialize: function(opts) {
			opts || (opts = {});
			this.stack = new Stack([], opts);
			return AsyncFactory.__super__.initialize.apply(this, arguments);
		},

		/**
		*	Resets the factory stack
		*	@public
		*	@chainable
		*	@method reset
		*	@param [opts] {Object} options
		*	@return {com.spinal.util.factories.AsyncFactory}
		**/
		reset: function(opts) {
			this.stack.reset(opts);
			return this;
		},

		/**
		*	Set a new collection of elements to be inserted in the factory stack
		*	@public
		*	@chainable
		*	@method set
		*	@param arr {Array} new collection to be replaced
		*	@return {com.spinal.util.factories.AsyncFactory}
		**/
		set: function(arr) {
			if(!arr || !_.isArray(arr)) return false;
			this.stack.reset({ silent: true });
			this.stack.set(arr);
			return this;
		},

		/**
		*	Find a the resource inside the factory stack
		*	@public
		*	@method findById
		*	@param id {String} resource id
		*	@return Object
		**/
		findById: function(id) {
			return this.stack.find(_.bind(function(r) { return (r.id && id && r.id === id); }, this));
		},

		/**
		*	Find a the resource position (0-based) in the factory stack
		*	@public
		*	@method findPos
		*	@param resource {String} resource reference
		*	@return Number
		**/
		findPos: function(resource) {
			return this.stack.search(resource);
		},

		/**
		*	Inserts a resource into the factory stack
		*	@public
		*	@chainable
		*	@method push
		*	@param resource {Object} resource
		*	@return {com.spinal.util.factories.AsyncFactory}
		**/
		push: function(resource) {
			if(!resource || !resource.id || !resource.path) return this;
			this.stack.push(resource);
			return this;
		},

		/**
		*	Removes a resource from the factory stack
		*	@public
		*	@chainable
		*	@method remove
		*	@param resource {Object} resource reference
		*	@return {com.spinal.util.factories.AsyncFactory}
		**/
		remove: function(resource, opts) {
			opts || (opts = {});
			if(!resource) return this;
			this.stack.remove(this.stack.search(resource), opts);
			return this;
		},

		/**
		*	Checks if the resource already exists in the factory stack by id passed as parameter
		*	@public
		*	@method exists
		*	@param id {String} resource id
		*	@return Boolean
		**/
		exists: function(id) {
			return !_.isUndefined(this.findById(id));
		},

		/**
		*	Load all resources in the factory stack if the stack is not empty
		*	@public
		*	@chainable
		*	@method load
		*	@param callback {Function} callback to execute on every resource loaded
		*	@param [opts] {Object} options
		*	@return {com.spinal.util.factories.AsyncFactory}
		**/
		load: function(callback, opts) {
			opts || (opts = {});
			if(this.stack.size() <= 0) return this;
			if(!opts.silent) this.trigger(AsyncFactory.EVENTS.prepared, this.stack.collection);
			return this._execute(callback, opts);
		},

		/**
		*	Handle Resources loaded by the current factory stack
		*	@private
		*	@method _handle
		*	@param resources {Array} resource reference
		*	@param callback {Function} callback to execute on every resource loaded
		*	@param [opts] {Object} options
		*	@return Array
		**/
		_handle: function(resources, callback, opts) {
			return _.map(resources, function(resource) {
				var resourceId = this.stack.pop().id;
					registered = AsyncFactory.__super__.register.call(this, resourceId, resource);
				if(callback && _.isFunction(callback)) callback(resourceId, registered);
				return registered;
			}, this);
		},

		/**
		*	Triggers loading phase of the current resources in the factory stack
		*	@private
		*	@method _execute
		*	@param callback {Function} callback to execute on every resource loaded
		*	@param [opts] {Object} options
		*	@return {com.spinal.util.factories.AsyncFactory}
		**/
		_execute: function(callback, opts) {
			var paths = this.stack.map(function(resource) { return resource.path; });
			require(paths, _.bind(function() {
				var resources = this._handle(Array.prototype.slice.call(arguments), callback, opts);
				if(!opts.silent) this.trigger(AsyncFactory.EVENTS.loaded, resources);
			}, this), _.bind(function(err) { this.trigger(AsyncFactory.EVENTS.failed, err); }, this));
			return this;
		}

	}, {

		/**
		*	@static
		*	@property NAME
		*	@type String
		**/
		NAME: 'AsyncFactory',

		/**
		*	@static
		*	@property EVENTS
		*	@type Object
		**/
		EVENTS: {
			/**
			* @event loaded
			**/
			loaded: 'com:spinal:util:factories:async:loaded',
			/**
			* @event loaded
			**/
			failed: 'com:spinal:util:factories:async:failed',
			/**
			* @event prepared
			**/
			prepared: 'com:spinal:util:factories:async:prepared'
		}

	}));

	return AsyncFactory;

});
<commit_msg>Added some comments in the AsyncFactory class about the internal mechanisms.<commit_after>/**
*	@module com.spinal.util.factories
*	@author Patricio Ferreira <3dimentionar@gmail.com>
**/
define(['core/spinal',
		'util/adt/stack',
		'util/factories/factory'], function(Spinal, Stack, Factory) {

	/**
	*	AsyncFactory Class
	*	This class uses a Stack ADT internally to enqueue resources and trigger the loading phase asynchronously.
	*	After all the resources are loaded, it register them in the factory.
	*
Usages:

	*
	*		var myAsyncFactory = new AsyncFactory();
	*			myAsyncFactory.set([{
	*				id: 'resourceA', path: 'path/to/resourceA',
	*				id: 'resourceB', path: 'path/to/resourceB'
	*			}]);
	*			myAsyncFactory.on(AsyncFactory.EVENTS.loaded, myLoadedCallback);
	*			// On each resource loaded and successfuly registered, the callback will be called.
	*			myAsyncFactory.load(_.bind(function(id, resource) {
	*				// make use of resource or the id of the resource ('resourceA' or 'resourceB');
	*			}, this));
	*
	*	Needless to say that the main purpose of having a Stack class supporting the asynchronous factory implementation
	*	is essentially, to provide a common interface to manage the resources list by easily 'convention' rather than
	*	supporting the intrinsect mechanisms to load the resources "one by one", since the resource queue is being managed
	*	internally by requirejs itself.
	*	With that being said, this class is suceptible to be changed to "inject" different async strategies as
	*	"Adapter" classes instead. (The developer should NOT notice any difference on the High-level API).
	*
	*	@namespace com.spinal.util.factories
	*	@class com.spinal.util.factories.AsyncFactory
	*	@extends com.spinal.util.factories.Factory
	*
	*	@requires com.spinal.core.Spinal
	*	@requires com.spinal.util.adt.Stack
	*	@requires com.spinal.util.factories.Factory
	**/
	var AsyncFactory = Spinal.namespace('com.spinal.util.factories.AsyncFactory', Factory.inherit({

		/**
		*	Stack of factory constructors to load
		*	@public
		*	@property stack
		*	@type {com.spinal.util.adt.Stack}
		**/
		stack: null,

		/**
		*	Initialize
		*	@public
		*	@method initialize
		*	@param [opts] {Object} options
		*	@return {com.spinal.util.factories.AsyncFactory}
		**/
		initialize: function(opts) {
			opts || (opts = {});
			this.stack = new Stack([], opts);
			return AsyncFactory.__super__.initialize.apply(this, arguments);
		},

		/**
		*	Resets the factory stack
		*	@public
		*	@chainable
		*	@method reset
		*	@param [opts] {Object} options
		*	@return {com.spinal.util.factories.AsyncFactory}
		**/
		reset: function(opts) {
			this.stack.reset(opts);
			return this;
		},

		/**
		*	Set a new collection of elements to be inserted in the factory stack
		*	@public
		*	@chainable
		*	@method set
		*	@param arr {Array} new collection to be replaced
		*	@return {com.spinal.util.factories.AsyncFactory}
		**/
		set: function(arr) {
			if(!arr || !_.isArray(arr)) return false;
			this.stack.reset({ silent: true });
			this.stack.set(arr);
			return this;
		},

		/**
		*	Find a the resource inside the factory stack
		*	@public
		*	@method findById
		*	@param id {String} resource id
		*	@return Object
		**/
		findById: function(id) {
			return this.stack.find(_.bind(function(r) { return (r.id && id && r.id === id); }, this));
		},

		/**
		*	Find a the resource position (0-based) in the factory stack
		*	@public
		*	@method findPos
		*	@param resource {String} resource reference
		*	@return Number
		**/
		findPos: function(resource) {
			return this.stack.search(resource);
		},

		/**
		*	Inserts a resource into the factory stack
		*	@public
		*	@chainable
		*	@method push
		*	@param resource {Object} resource
		*	@return {com.spinal.util.factories.AsyncFactory}
		**/
		push: function(resource) {
			if(!resource || !resource.id || !resource.path) return this;
			this.stack.push(resource);
			return this;
		},

		/**
		*	Removes a resource from the factory stack
		*	@public
		*	@chainable
		*	@method remove
		*	@param resource {Object} resource reference
		*	@return {com.spinal.util.factories.AsyncFactory}
		**/
		remove: function(resource, opts) {
			opts || (opts = {});
			if(!resource) return this;
			this.stack.remove(this.stack.search(resource), opts);
			return this;
		},

		/**
		*	Checks if the resource already exists in the factory stack by id passed as parameter
		*	@public
		*	@method exists
		*	@param id {String} resource id
		*	@return Boolean
		**/
		exists: function(id) {
			return !_.isUndefined(this.findById(id));
		},

		/**
		*	Load all resources in the factory stack if the stack is not empty
		*	@public
		*	@chainable
		*	@method load
		*	@param callback {Function} callback to execute on every resource loaded
		*	@param [opts] {Object} options
		*	@return {com.spinal.util.factories.AsyncFactory}
		**/
		load: function(callback, opts) {
			opts || (opts = {});
			if(this.stack.size() <= 0) return this;
			if(!opts.silent) this.trigger(AsyncFactory.EVENTS.prepared, this.stack.collection);
			return this._execute(callback, opts);
		},

		/**
		*	Handle Resources loaded by the current factory stack
		*	@private
		*	@method _handle
		*	@param resources {Array} resource reference
		*	@param callback {Function} callback to execute on every resource loaded
		*	@param [opts] {Object} options
		*	@return Array
		**/
		_handle: function(resources, callback, opts) {
			return _.map(resources, function(resource) {
				var resourceId = this.stack.pop().id;
					registered = AsyncFactory.__super__.register.call(this, resourceId, resource);
				if(callback && _.isFunction(callback)) callback(resourceId, registered);
				return registered;
			}, this);
		},

		/**
		*	Triggers loading phase of the current resources in the factory stack
		*	@private
		*	@method _execute
		*	@param callback {Function} callback to execute on every resource loaded
		*	@param [opts] {Object} options
		*	@return {com.spinal.util.factories.AsyncFactory}
		**/
		_execute: function(callback, opts) {
			var paths = this.stack.map(function(resource) { return resource.path; });
			require(paths, _.bind(function() {
				var resources = this._handle(Array.prototype.slice.call(arguments), callback, opts);
				if(!opts.silent) this.trigger(AsyncFactory.EVENTS.loaded, resources);
			}, this), _.bind(function(err) { this.trigger(AsyncFactory.EVENTS.failed, err); }, this));
			return this;
		}

	}, {

		/**
		*	@static
		*	@property NAME
		*	@type String
		**/
		NAME: 'AsyncFactory',

		/**
		*	@static
		*	@property EVENTS
		*	@type Object
		**/
		EVENTS: {
			/**
			* @event loaded
			**/
			loaded: 'com:spinal:util:factories:async:loaded',
			/**
			* @event loaded
			**/
			failed: 'com:spinal:util:factories:async:failed',
			/**
			* @event prepared
			**/
			prepared: 'com:spinal:util:factories:async:prepared'
		}

	}));

	return AsyncFactory;

});
<|endoftext|>


<commit_before>// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE

(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    mod(require("../../lib/codemirror"));
  else if (typeof define == "function" && define.amd) // AMD
    define(["../../lib/codemirror"], mod);
  else // Plain browser env
    mod(CodeMirror);
})(function(CodeMirror) {
"use strict";

CodeMirror.defineMode('shell', function() {

  var words = {};
  function define(style, dict) {
    for(var i = 0; i < dict.length; i++) {
      words[dict[i]] = style;
    }
  };

  var commonAtoms = ["true", "false"];
  var commonKeywords = ["if", "then", "do", "else", "elif", "while", "until", "for", "in", "esac", "fi",
    "fin", "fil", "done", "exit", "set", "unset", "export", "function"];
  var commonCommands = ["ab", "awk", "bash", "beep", "cat", "cc", "cd", "chown", "chmod", "chroot", "clear",
    "cp", "curl", "cut", "diff", "echo", "find", "gawk", "gcc", "get", "git", "grep", "hg", "kill", "killall",
    "ln", "ls", "make", "mkdir", "openssl", "mv", "nc", "nl", "node", "npm", "ping", "ps", "restart", "rm",
    "rmdir", "sed", "service", "sh", "shopt", "shred", "source", "sort", "sleep", "ssh", "start", "stop",
    "su", "sudo", "svn", "tee", "telnet", "top", "touch", "vi", "vim", "wall", "wc", "wget", "who", "write",
    "yes", "zsh"];

  CodeMirror.registerHelper("hintWords", "shell", commonAtoms.concat(commonKeywords, commonCommands));

  define('atom', commonAtoms);
  define('keyword', commonKeywords);
  define('builtin', commonCommands);

  function tokenBase(stream, state) {
    if (stream.eatSpace()) return null;

    var sol = stream.sol();
    var ch = stream.next();

    if (ch === '\\') {
      stream.next();
      return null;
    }
    if (ch === '\'' || ch === '"' || ch === '`') {
      state.tokens.unshift(tokenString(ch, ch === "`" ? "quote" : "string"));
      return tokenize(stream, state);
    }
    if (ch === '#') {
      if (sol && stream.eat('!')) {
        stream.skipToEnd();
        return 'meta'; // 'comment'?
      }
      stream.skipToEnd();
      return 'comment';
    }
    if (ch === '$') {
      state.tokens.unshift(tokenDollar);
      return tokenize(stream, state);
    }
    if (ch === '+' || ch === '=') {
      return 'operator';
    }
    if (ch === '-') {
      stream.eat('-');
      stream.eatWhile(/\w/);
      return 'attribute';
    }
    if (ch == "<") {
      var heredoc = stream.match(/^<-?\s+(.*)/)
      if (heredoc) {
        state.tokens.unshift(tokenHeredoc(heredoc[1]))
        return 'string-2'
      }
    }
    if (/\d/.test(ch)) {
      stream.eatWhile(/\d/);
      if(stream.eol() || !/\w/.test(stream.peek())) {
        return 'number';
      }
    }
    stream.eatWhile(/[\w-]/);
    var cur = stream.current();
    if (stream.peek() === '=' && /\w+/.test(cur)) return 'def';
    return words.hasOwnProperty(cur) ? words[cur] : null;
  }

  function tokenString(quote, style) {
    var close = quote == "(" ? ")" : quote == "{" ? "}" : quote
    return function(stream, state) {
      var next, escaped = false;
      while ((next = stream.next()) != null) {
        if (next === close && !escaped) {
          state.tokens.shift();
          break;
        } else if (next === '$' && !escaped && quote !== "'" && stream.peek() != close) {
          escaped = true;
          stream.backUp(1);
          state.tokens.unshift(tokenDollar);
          break;
        } else if (!escaped && quote !== close && next === quote) {
          state.tokens.unshift(tokenString(quote, style))
          return tokenize(stream, state)
        } else if (!escaped && /['"]/.test(next) && !/['"]/.test(quote)) {
          state.tokens.unshift(tokenStringStart(next, "string"));
          stream.backUp(1);
          break;
        }
        escaped = !escaped && next === '\\';
      }
      return style;
    };
  };

  function tokenStringStart(quote, style) {
    return function(stream, state) {
      state.tokens[0] = tokenString(quote, style)
      stream.next()
      return tokenize(stream, state)
    }
  }

  var tokenDollar = function(stream, state) {
    if (state.tokens.length > 1) stream.eat('$');
    var ch = stream.next()
    if (/['"({]/.test(ch)) {
      state.tokens[0] = tokenString(ch, ch == "(" ? "quote" : ch == "{" ? "def" : "string");
      return tokenize(stream, state);
    }
    if (!/\d/.test(ch)) stream.eatWhile(/\w/);
    state.tokens.shift();
    return 'def';
  };

  function tokenHeredoc(delim) {
    return function(stream, state) {
      if (stream.sol() && stream.string == delim) state.tokens.shift()
      stream.skipToEnd()
      return "string-2"
    }
  }

  function tokenize(stream, state) {
    return (state.tokens[0] || tokenBase) (stream, state);
  };

  return {
    startState: function() {return {tokens:[]};},
    token: function(stream, state) {
      return tokenize(stream, state);
    },
    closeBrackets: "()[]{}''\"\"``",
    lineComment: '#',
    fold: "brace"
  };
});

CodeMirror.defineMIME('text/x-sh', 'shell');
// Apache uses a slightly different Media Type for Shell scripts
// http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
CodeMirror.defineMIME('application/x-sh', 'shell');

});
<commit_msg>[shell mode] Fix Heredoc to allow quotes and not require a space<commit_after>// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE

(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    mod(require("../../lib/codemirror"));
  else if (typeof define == "function" && define.amd) // AMD
    define(["../../lib/codemirror"], mod);
  else // Plain browser env
    mod(CodeMirror);
})(function(CodeMirror) {
"use strict";

CodeMirror.defineMode('shell', function() {

  var words = {};
  function define(style, dict) {
    for(var i = 0; i < dict.length; i++) {
      words[dict[i]] = style;
    }
  };

  var commonAtoms = ["true", "false"];
  var commonKeywords = ["if", "then", "do", "else", "elif", "while", "until", "for", "in", "esac", "fi",
    "fin", "fil", "done", "exit", "set", "unset", "export", "function"];
  var commonCommands = ["ab", "awk", "bash", "beep", "cat", "cc", "cd", "chown", "chmod", "chroot", "clear",
    "cp", "curl", "cut", "diff", "echo", "find", "gawk", "gcc", "get", "git", "grep", "hg", "kill", "killall",
    "ln", "ls", "make", "mkdir", "openssl", "mv", "nc", "nl", "node", "npm", "ping", "ps", "restart", "rm",
    "rmdir", "sed", "service", "sh", "shopt", "shred", "source", "sort", "sleep", "ssh", "start", "stop",
    "su", "sudo", "svn", "tee", "telnet", "top", "touch", "vi", "vim", "wall", "wc", "wget", "who", "write",
    "yes", "zsh"];

  CodeMirror.registerHelper("hintWords", "shell", commonAtoms.concat(commonKeywords, commonCommands));

  define('atom', commonAtoms);
  define('keyword', commonKeywords);
  define('builtin', commonCommands);

  function tokenBase(stream, state) {
    if (stream.eatSpace()) return null;

    var sol = stream.sol();
    var ch = stream.next();

    if (ch === '\\') {
      stream.next();
      return null;
    }
    if (ch === '\'' || ch === '"' || ch === '`') {
      state.tokens.unshift(tokenString(ch, ch === "`" ? "quote" : "string"));
      return tokenize(stream, state);
    }
    if (ch === '#') {
      if (sol && stream.eat('!')) {
        stream.skipToEnd();
        return 'meta'; // 'comment'?
      }
      stream.skipToEnd();
      return 'comment';
    }
    if (ch === '$') {
      state.tokens.unshift(tokenDollar);
      return tokenize(stream, state);
    }
    if (ch === '+' || ch === '=') {
      return 'operator';
    }
    if (ch === '-') {
      stream.eat('-');
      stream.eatWhile(/\w/);
      return 'attribute';
    }
    if (ch == "<") {
      var heredoc = stream.match(/^<-?\s*['"]?([^'"]*)['"]?/)
      if (heredoc) {
        state.tokens.unshift(tokenHeredoc(heredoc[1]))
        return 'string-2'
      }
    }
    if (/\d/.test(ch)) {
      stream.eatWhile(/\d/);
      if(stream.eol() || !/\w/.test(stream.peek())) {
        return 'number';
      }
    }
    stream.eatWhile(/[\w-]/);
    var cur = stream.current();
    if (stream.peek() === '=' && /\w+/.test(cur)) return 'def';
    return words.hasOwnProperty(cur) ? words[cur] : null;
  }

  function tokenString(quote, style) {
    var close = quote == "(" ? ")" : quote == "{" ? "}" : quote
    return function(stream, state) {
      var next, escaped = false;
      while ((next = stream.next()) != null) {
        if (next === close && !escaped) {
          state.tokens.shift();
          break;
        } else if (next === '$' && !escaped && quote !== "'" && stream.peek() != close) {
          escaped = true;
          stream.backUp(1);
          state.tokens.unshift(tokenDollar);
          break;
        } else if (!escaped && quote !== close && next === quote) {
          state.tokens.unshift(tokenString(quote, style))
          return tokenize(stream, state)
        } else if (!escaped && /['"]/.test(next) && !/['"]/.test(quote)) {
          state.tokens.unshift(tokenStringStart(next, "string"));
          stream.backUp(1);
          break;
        }
        escaped = !escaped && next === '\\';
      }
      return style;
    };
  };

  function tokenStringStart(quote, style) {
    return function(stream, state) {
      state.tokens[0] = tokenString(quote, style)
      stream.next()
      return tokenize(stream, state)
    }
  }

  var tokenDollar = function(stream, state) {
    if (state.tokens.length > 1) stream.eat('$');
    var ch = stream.next()
    if (/['"({]/.test(ch)) {
      state.tokens[0] = tokenString(ch, ch == "(" ? "quote" : ch == "{" ? "def" : "string");
      return tokenize(stream, state);
    }
    if (!/\d/.test(ch)) stream.eatWhile(/\w/);
    state.tokens.shift();
    return 'def';
  };

  function tokenHeredoc(delim) {
    return function(stream, state) {
      if (stream.sol() && stream.string == delim) state.tokens.shift()
      stream.skipToEnd()
      return "string-2"
    }
  }

  function tokenize(stream, state) {
    return (state.tokens[0] || tokenBase) (stream, state);
  };

  return {
    startState: function() {return {tokens:[]};},
    token: function(stream, state) {
      return tokenize(stream, state);
    },
    closeBrackets: "()[]{}''\"\"``",
    lineComment: '#',
    fold: "brace"
  };
});

CodeMirror.defineMIME('text/x-sh', 'shell');
// Apache uses a slightly different Media Type for Shell scripts
// http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
CodeMirror.defineMIME('application/x-sh', 'shell');

});
<|endoftext|>


<commit_before>'use strict';
const thinky = require('../db');
const r = thinky.r;
const type = thinky.type;
const mlString = require('./helpers/ml-string');
const revision = require('./helpers/revision');
const isValidLanguage = require('../locales/languages').isValid;
const User = require('./user');
const TeamSlug = require('./team-slug');

/* eslint-disable newline-per-chained-call */ /* for schema readability */

let blogPostSchema = {

  id: type.string(),
  teamID: type.string().uuid(4),
  title: mlString.getSchema({
    maxLength: 100
  }),
  post: {
    text: mlString.getSchema(),
    html: mlString.getSchema()
  },
  // Track original authorship
  createdOn: type.date(),
  createdBy: type.string(),
  originalLanguage: type.string().max(4).required().validator(isValidLanguage),
  userCanEdit: type.virtual().default(false),
  userCanDelete: type.virtual().default(false)
};

/* eslint-enable newline-per-chained-call */

// Add versioning related fields
Object.assign(blogPostSchema, revision.getSchema());
let BlogPost = thinky.createModel("blog_posts", blogPostSchema);

BlogPost.ensureIndex("createdOn");
BlogPost.belongsTo(User, "creator", "createdBy", "id");

BlogPost.createFirstRevision = revision.getFirstRevisionHandler(BlogPost);
BlogPost.getNotStaleOrDeleted = revision.getNotStaleOrDeletedGetHandler(BlogPost);

BlogPost.define("newRevision", revision.getNewRevisionHandler(BlogPost));
BlogPost.define("deleteAllRevisions", revision.getDeleteAllRevisionsHandler(BlogPost));

BlogPost.define("populateUserInfo", function(user) {

  if (!user) // Keep at permissions at default (false)
    return;

  if (user.isSuperUser || user.id === this.createdBy)
    this.userCanEdit = true;

  if (user.isSuperUser || user.id === this.createdBy || user.isSiteModerator)
    this.userCanDelete = true;

});

BlogPost.getWithCreator = function(id) {
  return new Promise((resolve, reject) => {
    BlogPost
      .get(id)
      .getJoin({
        creator: {
          _apply: seq => seq.without('password')
        }
      })
      .then(post => {
        if (post._revDeleted)
          return reject(revision.deletedError);

        if (post._oldRevOf)
          return reject(revision.staleError);

        resolve(post);

      })
      .catch(error => reject(error));
  });
};

BlogPost.getMostRecentBlogPosts = function(teamID, options) {
  if (!teamID)
    throw new Error('We require a team ID to fetch blog posts.');

  options = Object.assign({ // Defaults
    limit: 10,
    // Only show posts older than this date. Must be JavaScript Date object.
    offsetDate: undefined
  }, options);

  let query = BlogPost;

  if (options.offsetDate && options.offsetDate.valueOf)
    query = query.between(r.minval, r.epochTime(options.offsetDate.valueOf() / 1000), {
      index: 'createdOn',
      rightBound: 'open' // Do not return previous record that exactly matches offset
    });

  query = query.orderBy({
      index: r.desc('createdOn')
    })
    .filter({
      teamID
    })
    .filter({
      _revDeleted: false
    }, {
      default: true
    })
    .filter({
      _oldRevOf: false
    }, {
      default: true
    })
    .limit(options.limit + 1) // One over limit to check if we need potentially another set
    .getJoin({
      creator: {
        _apply: seq => seq.without('password')
      }
    });

  return new Promise((resolve, reject) => {

    query
      .then(blogPosts => {

        let result = {};

        // At least one additional document available, set offset for pagination
        if (blogPosts.length == options.limit + 1) {
          result.offsetDate = blogPosts[options.limit - 1].createdOn;
          blogPosts.pop();
        }

        result.blogPosts = blogPosts;

        resolve(result);
      })
      .catch(error => reject(error));

  });

};

// Helper function to quickly get blog posts for a given slug
BlogPost.getMostRecentBlogPostsBySlug = function(teamSlugName, options) {
  return new Promise((resolve, reject) => {
    TeamSlug
      .get(teamSlugName)
      .then(slug => {
        BlogPost
          .getMostRecentBlogPosts(slug.teamID, options)
          .then(result => resolve(result))
          .catch(error => reject(error));
      })
      .catch(error => reject(error)); // Slug not found?
  });
};

module.exports = BlogPost;
<commit_msg>async/await refactor, jsdoc<commit_after>'use strict';

/**
 * Model for blog posts. Blog posts can currently only be authored by teams: one
 * team can have many blog posts.
 *
 * @namespace BlogPost
 */
const thinky = require('../db');
const r = thinky.r;
const type = thinky.type;
const mlString = require('./helpers/ml-string');
const revision = require('./helpers/revision');
const isValidLanguage = require('../locales/languages').isValid;
const User = require('./user');
const TeamSlug = require('./team-slug');

/* eslint-disable newline-per-chained-call */
/* for schema readability */

let blogPostSchema = {

  id: type.string(),
  teamID: type.string().uuid(4),
  title: mlString.getSchema({
    maxLength: 100
  }),
  post: {
    text: mlString.getSchema(),
    html: mlString.getSchema()
  },
  // Track original authorship
  createdOn: type.date(),
  createdBy: type.string(),
  originalLanguage: type.string().max(4).required().validator(isValidLanguage),
  userCanEdit: type.virtual().default(false),
  userCanDelete: type.virtual().default(false)
};

/* eslint-enable newline-per-chained-call */

// Add versioning related fields
Object.assign(blogPostSchema, revision.getSchema());
let BlogPost = thinky.createModel("blog_posts", blogPostSchema);

BlogPost.ensureIndex("createdOn");
BlogPost.belongsTo(User, "creator", "createdBy", "id");

// NOTE: STATIC METHODS --------------------------------------------------------

// Standard handlers

BlogPost.createFirstRevision = revision.getFirstRevisionHandler(BlogPost);
BlogPost.getNotStaleOrDeleted = revision.getNotStaleOrDeletedGetHandler(BlogPost);

// Custom methods

/**
 * Get a blog post and the user object representing its original author
 *
 * @param {String} id
 *  blog post ID
 * @returns {BlogPost}
 *  post with populated `.creator` property
 * @async
 */
BlogPost.getWithCreator = async function(id) {
  const post = await BlogPost
    .get(id)
    .getJoin({
      creator: {
        _apply: seq => seq.without('password')
      }
    });

  if (post._revDeleted)
    throw revision.deletedError;

  if (post._oldRevOf)
    throw revision.staleError;

  return post;
};

/**
 * Get the most recent blog posts for a given team
 *
 * @param {String} teamID
 *  team ID to look up
 * @param {Object} [options]
 *  query criteria
 * @param {Number} options.limit=10
 *  number of posts to retrieve
 * @param {Date} options.offsetDate
 *  only get posts older than this date
 * @returns {BlogPosts[]}
 * @async
 */
BlogPost.getMostRecentBlogPosts = async function(teamID, {
  limit = 10,
  offsetDate = undefined
} = {}) {

  if (!teamID)
    throw new Error('We require a team ID to fetch blog posts.');

  let query = BlogPost;

  if (offsetDate && offsetDate.valueOf)
    query = query.between(r.minval, r.epochTime(offsetDate.valueOf() / 1000), {
      index: 'createdOn',
      rightBound: 'open' // Do not return previous record that exactly matches offset
    });

  query = query
    .orderBy({ index: r.desc('createdOn') })
    .filter({ teamID })
    .filter({ _revDeleted: false }, { default: true })
    .filter({ _oldRevOf: false }, { default: true })
    .limit(limit + 1) // One over limit to check if we need potentially another set
    .getJoin({
      creator: {
        _apply: seq => seq.without('password')
      }
    });

  const posts = await query;
  const result = {};

  // At least one additional document available, set offset for pagination
  if (posts.length == limit + 1) {
    result.offsetDate = posts[limit - 1].createdOn;
    posts.pop();
  }

  result.blogPosts = posts;
  return result;
};

/**
 * Get blog posts for a given team slug.
 *
 * @param {String} teamSlugName
 *  slug (short human-readable identifier) for a team
 * @param {Object} [options]
 *  options supported by {@link BlogPost.getMostRecentBlogPosts}
 * @returns {BlogPost[]}
 * @async
 */
BlogPost.getMostRecentBlogPostsBySlug = async function(teamSlugName, options) {
  const slug = await TeamSlug.get(teamSlugName);
  const posts = BlogPost.getMostRecentBlogPosts(slug.teamID, options);
  return posts;
};

// NOTE: INSTANCE METHODS ------------------------------------------------------

// Standard handlers

BlogPost.define("newRevision", revision.getNewRevisionHandler(BlogPost));
BlogPost.define("deleteAllRevisions", revision.getDeleteAllRevisionsHandler(BlogPost));

// Custom methods

BlogPost.define("populateUserInfo", populateUserInfo);

/**
 * Populate virtual permission fields for this post based on a given user
 *
 * @param {User} user
 *  the user whose permissions to check
 * @instance
 * @memberof BlogPost
 */
function populateUserInfo(user) {

  if (!user) // Keep at permissions at default (false)
    return;

  if (user.isSuperUser || user.id === this.createdBy)
    this.userCanEdit = true;

  if (user.isSuperUser || user.id === this.createdBy || user.isSiteModerator)
    this.userCanDelete = true;
}

module.exports = BlogPost;
<|endoftext|>


<commit_before>/* eslint-disable */
/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule TouchableHighlight
 * @noflow
 */
'use strict';

// Note (avik): add @flow when Flow supports spread properties in propTypes

var ColorPropType = require('../../apis/StyleSheet/ColorPropType');
var NativeMethodsMixin = require('../../modules/NativeMethodsMixin');
var React = require('react');
var StyleSheet = require('../../apis/StyleSheet');
var TimerMixin = require('react-timer-mixin');
var Touchable = require('./Touchable');
var TouchableWithoutFeedback = require('./TouchableWithoutFeedback');
var View = require('../View');

var ensureComponentIsNative = require('./ensureComponentIsNative');
var ensurePositiveDelayProps = require('./ensurePositiveDelayProps');
var keyOf = require('fbjs/lib/keyOf');
var merge = require('../../modules/merge');

type Event = Object;

var DEFAULT_PROPS = {
  activeOpacity: 0.8,
  underlayColor: 'black',
};

var PRESS_RETENTION_OFFSET = {top: 20, left: 20, right: 20, bottom: 30};

/**
 * A wrapper for making views respond properly to touches.
 * On press down, the opacity of the wrapped view is decreased, which allows
 * the underlay color to show through, darkening or tinting the view.  The
 * underlay comes from adding a view to the view hierarchy, which can sometimes
 * cause unwanted visual artifacts if not used correctly, for example if the
 * backgroundColor of the wrapped view isn't explicitly set to an opaque color.
 *
 * Example:
 *
 * ```
 * renderButton: function() {
 *   return (
 *     <TouchableHighlight onPress={this._onPressButton}>
 *       <Image
 *         style={styles.button}
 *         source={require('image!myButton')}
 *       />
 *     </TouchableHighlight>
 *   );
 * },
 * ```
 * > **NOTE**: TouchableHighlight supports only one child
 * >
 * > If you wish to have several child components, wrap them in a View.
 */

var TouchableHighlight = React.createClass({
  propTypes: {
    ...TouchableWithoutFeedback.propTypes,
    /**
     * Determines what the opacity of the wrapped view should be when touch is
     * active.
     */
    activeOpacity: React.PropTypes.number,
    /**
     * The color of the underlay that will show through when the touch is
     * active.
     */
    underlayColor: ColorPropType,
    style: View.propTypes.style,
    /**
     * Called immediately after the underlay is shown
     */
    onShowUnderlay: React.PropTypes.func,
    /**
     * Called immediately after the underlay is hidden
     */
    onHideUnderlay: React.PropTypes.func,
  },

  mixins: [NativeMethodsMixin, TimerMixin, Touchable.Mixin],

  getDefaultProps: () => DEFAULT_PROPS,

  // Performance optimization to avoid constantly re-generating these objects.
  computeSyntheticState: function(props) {
    return {
      activeProps: {
        style: {
          opacity: props.activeOpacity,
        }
      },
      activeUnderlayProps: {
        style: {
          backgroundColor: props.underlayColor,
        }
      },
      underlayStyle: [
        INACTIVE_UNDERLAY_PROPS.style
      ]
    };
  },

  getInitialState: function() {
    return merge(this.touchableGetInitialState(), this.computeSyntheticState(this.props))
  },

  componentDidMount: function() {
    ensurePositiveDelayProps(this.props);
    ensureComponentIsNative(this.refs[CHILD_REF]);
  },

  componentDidUpdate: function() {
    ensureComponentIsNative(this.refs[CHILD_REF]);
  },

  componentWillReceiveProps: function(nextProps) {
    ensurePositiveDelayProps(nextProps);
    if (nextProps.activeOpacity !== this.props.activeOpacity ||
        nextProps.underlayColor !== this.props.underlayColor ||
        nextProps.style !== this.props.style) {
      this.setState(this.computeSyntheticState(nextProps));
    }
  },

  // viewConfig: {
  //   uiViewClassName: 'RCTView',
  //   validAttributes: ReactNativeViewAttributes.RCTView
  // },

  /**
   * `Touchable.Mixin` self callbacks. The mixin will invoke these if they are
   * defined on your component.
   */
  touchableHandleActivePressIn: function(e: Event) {
    this.clearTimeout(this._hideTimeout);
    this._hideTimeout = null;
    this._showUnderlay();
    this.props.onPressIn && this.props.onPressIn(e);
  },

  touchableHandleActivePressOut: function(e: Event) {
    if (!this._hideTimeout) {
      this._hideUnderlay();
    }
    this.props.onPressOut && this.props.onPressOut(e);
  },

  touchableHandlePress: function(e: Event) {
    this.clearTimeout(this._hideTimeout);
    this._showUnderlay();
    this._hideTimeout = this.setTimeout(this._hideUnderlay,
      this.props.delayPressOut || 100);
    this.props.onPress && this.props.onPress(e);
  },

  touchableHandleLongPress: function(e: Event) {
    this.props.onLongPress && this.props.onLongPress(e);
  },

  touchableGetPressRectOffset: function() {
    return this.props.pressRetentionOffset || PRESS_RETENTION_OFFSET;
  },

  touchableGetHitSlop: function() {
    return this.props.hitSlop;
  },

  touchableGetHighlightDelayMS: function() {
    return this.props.delayPressIn;
  },

  touchableGetLongPressDelayMS: function() {
    return this.props.delayLongPress;
  },

  touchableGetPressOutDelayMS: function() {
    return this.props.delayPressOut;
  },

  _showUnderlay: function() {
    if (!this.isMounted() || !this._hasPressHandler()) {
      return;
    }

    this.refs[UNDERLAY_REF].setNativeProps(this.state.activeUnderlayProps);
    this.refs[CHILD_REF].setNativeProps(this.state.activeProps);
    this.props.onShowUnderlay && this.props.onShowUnderlay();
  },

  _hideUnderlay: function() {
    this.clearTimeout(this._hideTimeout);
    this._hideTimeout = null;
    if (this._hasPressHandler() && this.refs[UNDERLAY_REF]) {
      this.refs[CHILD_REF].setNativeProps(INACTIVE_CHILD_PROPS);
      this.refs[UNDERLAY_REF].setNativeProps({
        ...INACTIVE_UNDERLAY_PROPS,
        style: this.state.underlayStyle,
      });
      this.props.onHideUnderlay && this.props.onHideUnderlay();
    }
  },

  _hasPressHandler: function() {
    return !!(
      this.props.onPress ||
      this.props.onPressIn ||
      this.props.onPressOut ||
      this.props.onLongPress
    );
  },

  _onKeyEnter(e, callback) {
    var ENTER = 13
    if (e.keyCode === ENTER) {
      callback && callback(e)
    }
  },

  render: function() {
    return (
      <View
        accessible={true}
        accessibilityLabel={this.props.accessibilityLabel}
        accessibilityRole={this.props.accessibilityRole || this.props.accessibilityTraits || 'button'}
        ref={UNDERLAY_REF}
        style={[styles.root, this.state.underlayStyle, this.props.style]}
        onLayout={this.props.onLayout}
        hitSlop={this.props.hitSlop}
        onKeyDown={(e) => { this._onKeyEnter(e, this.touchableHandleActivePressIn) }}
        onKeyPress={(e) => { this._onKeyEnter(e, this.touchableHandlePress) }}
        onKeyUp={(e) => { this._onKeyEnter(e, this.touchableHandleActivePressOut) }}
        onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder}
        onResponderTerminationRequest={this.touchableHandleResponderTerminationRequest}
        onResponderGrant={this.touchableHandleResponderGrant}
        onResponderMove={this.touchableHandleResponderMove}
        onResponderRelease={this.touchableHandleResponderRelease}
        onResponderTerminate={this.touchableHandleResponderTerminate}
        tabIndex='0'
        testID={this.props.testID}>
        {React.cloneElement(
          React.Children.only(this.props.children),
          {
            ref: CHILD_REF,
          }
        )}
      </View>
    );
  }
});

var CHILD_REF = keyOf({childRef: null});
var UNDERLAY_REF = keyOf({underlayRef: null});
var INACTIVE_CHILD_PROPS = {
  style: StyleSheet.create({x: {opacity: 1.0}}).x,
};
var INACTIVE_UNDERLAY_PROPS = {
  style: {backgroundColor: null}
};

var styles = StyleSheet.create({
  root: {
    cursor: 'pointer',
    userSelect: 'none'
  }
});

module.exports = TouchableHighlight;
<commit_msg>[fix] TouchableHighlight inactive state<commit_after>/* eslint-disable */
/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule TouchableHighlight
 * @noflow
 */
'use strict';

// Note (avik): add @flow when Flow supports spread properties in propTypes

var ColorPropType = require('../../apis/StyleSheet/ColorPropType');
var NativeMethodsMixin = require('../../modules/NativeMethodsMixin');
var React = require('react');
var StyleSheet = require('../../apis/StyleSheet');
var TimerMixin = require('react-timer-mixin');
var Touchable = require('./Touchable');
var TouchableWithoutFeedback = require('./TouchableWithoutFeedback');
var View = require('../View');

var ensureComponentIsNative = require('./ensureComponentIsNative');
var ensurePositiveDelayProps = require('./ensurePositiveDelayProps');
var keyOf = require('fbjs/lib/keyOf');
var merge = require('../../modules/merge');

type Event = Object;

var DEFAULT_PROPS = {
  activeOpacity: 0.8,
  underlayColor: 'black'
};

var PRESS_RETENTION_OFFSET = {top: 20, left: 20, right: 20, bottom: 30};

/**
 * A wrapper for making views respond properly to touches.
 * On press down, the opacity of the wrapped view is decreased, which allows
 * the underlay color to show through, darkening or tinting the view.  The
 * underlay comes from adding a view to the view hierarchy, which can sometimes
 * cause unwanted visual artifacts if not used correctly, for example if the
 * backgroundColor of the wrapped view isn't explicitly set to an opaque color.
 *
 * Example:
 *
 * ```
 * renderButton: function() {
 *   return (
 *     <TouchableHighlight onPress={this._onPressButton}>
 *       <Image
 *         style={styles.button}
 *         source={require('image!myButton')}
 *       />
 *     </TouchableHighlight>
 *   );
 * },
 * ```
 * > **NOTE**: TouchableHighlight supports only one child
 * >
 * > If you wish to have several child components, wrap them in a View.
 */

var TouchableHighlight = React.createClass({
  propTypes: {
    ...TouchableWithoutFeedback.propTypes,
    /**
     * Determines what the opacity of the wrapped view should be when touch is
     * active.
     */
    activeOpacity: React.PropTypes.number,
    /**
     * The color of the underlay that will show through when the touch is
     * active.
     */
    underlayColor: ColorPropType,
    style: View.propTypes.style,
    /**
     * Called immediately after the underlay is shown
     */
    onShowUnderlay: React.PropTypes.func,
    /**
     * Called immediately after the underlay is hidden
     */
    onHideUnderlay: React.PropTypes.func,
  },

  mixins: [NativeMethodsMixin, TimerMixin, Touchable.Mixin],

  getDefaultProps: () => DEFAULT_PROPS,

  // Performance optimization to avoid constantly re-generating these objects.
  computeSyntheticState: function(props) {
    return {
      activeProps: {
        style: {
          opacity: props.activeOpacity,
        }
      },
      activeUnderlayProps: {
        style: {
          backgroundColor: props.underlayColor,
        }
      },
      underlayStyle: [
        INACTIVE_UNDERLAY_PROPS.style
      ]
    };
  },

  getInitialState: function() {
    return merge(this.touchableGetInitialState(), this.computeSyntheticState(this.props))
  },

  componentDidMount: function() {
    ensurePositiveDelayProps(this.props);
    ensureComponentIsNative(this.refs[CHILD_REF]);
  },

  componentDidUpdate: function() {
    ensureComponentIsNative(this.refs[CHILD_REF]);
  },

  componentWillReceiveProps: function(nextProps) {
    ensurePositiveDelayProps(nextProps);
    if (nextProps.activeOpacity !== this.props.activeOpacity ||
        nextProps.underlayColor !== this.props.underlayColor ||
        nextProps.style !== this.props.style) {
      this.setState(this.computeSyntheticState(nextProps));
    }
  },

  // viewConfig: {
  //   uiViewClassName: 'RCTView',
  //   validAttributes: ReactNativeViewAttributes.RCTView
  // },

  /**
   * `Touchable.Mixin` self callbacks. The mixin will invoke these if they are
   * defined on your component.
   */
  touchableHandleActivePressIn: function(e: Event) {
    this.clearTimeout(this._hideTimeout);
    this._hideTimeout = null;
    this._showUnderlay();
    this.props.onPressIn && this.props.onPressIn(e);
  },

  touchableHandleActivePressOut: function(e: Event) {
    if (!this._hideTimeout) {
      this._hideUnderlay();
    }
    this.props.onPressOut && this.props.onPressOut(e);
  },

  touchableHandlePress: function(e: Event) {
    this.clearTimeout(this._hideTimeout);
    this._showUnderlay();
    this._hideTimeout = this.setTimeout(this._hideUnderlay,
      this.props.delayPressOut || 100);
    this.props.onPress && this.props.onPress(e);
  },

  touchableHandleLongPress: function(e: Event) {
    this.props.onLongPress && this.props.onLongPress(e);
  },

  touchableGetPressRectOffset: function() {
    return this.props.pressRetentionOffset || PRESS_RETENTION_OFFSET;
  },

  touchableGetHitSlop: function() {
    return this.props.hitSlop;
  },

  touchableGetHighlightDelayMS: function() {
    return this.props.delayPressIn;
  },

  touchableGetLongPressDelayMS: function() {
    return this.props.delayLongPress;
  },

  touchableGetPressOutDelayMS: function() {
    return this.props.delayPressOut;
  },

  _showUnderlay: function() {
    if (!this.isMounted() || !this._hasPressHandler()) {
      return;
    }

    this.refs[UNDERLAY_REF].setNativeProps(this.state.activeUnderlayProps);
    this.refs[CHILD_REF].setNativeProps(this.state.activeProps);
    this.props.onShowUnderlay && this.props.onShowUnderlay();
  },

  _hideUnderlay: function() {
    this.clearTimeout(this._hideTimeout);
    this._hideTimeout = null;
    if (this._hasPressHandler() && this.refs[UNDERLAY_REF]) {
      this.refs[CHILD_REF].setNativeProps(INACTIVE_CHILD_PROPS);
      this.refs[UNDERLAY_REF].setNativeProps({
        ...INACTIVE_UNDERLAY_PROPS,
        style: this.state.underlayStyle,
      });
      this.props.onHideUnderlay && this.props.onHideUnderlay();
    }
  },

  _hasPressHandler: function() {
    return !!(
      this.props.onPress ||
      this.props.onPressIn ||
      this.props.onPressOut ||
      this.props.onLongPress
    );
  },

  _onKeyEnter(e, callback) {
    var ENTER = 13
    if (e.keyCode === ENTER) {
      callback && callback(e)
    }
  },

  render: function() {
    return (
      <View
        accessible={true}
        accessibilityLabel={this.props.accessibilityLabel}
        accessibilityRole={this.props.accessibilityRole || this.props.accessibilityTraits || 'button'}
        ref={UNDERLAY_REF}
        style={[styles.root, this.props.style]}
        onLayout={this.props.onLayout}
        hitSlop={this.props.hitSlop}
        onKeyDown={(e) => { this._onKeyEnter(e, this.touchableHandleActivePressIn) }}
        onKeyPress={(e) => { this._onKeyEnter(e, this.touchableHandlePress) }}
        onKeyUp={(e) => { this._onKeyEnter(e, this.touchableHandleActivePressOut) }}
        onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder}
        onResponderTerminationRequest={this.touchableHandleResponderTerminationRequest}
        onResponderGrant={this.touchableHandleResponderGrant}
        onResponderMove={this.touchableHandleResponderMove}
        onResponderRelease={this.touchableHandleResponderRelease}
        onResponderTerminate={this.touchableHandleResponderTerminate}
        tabIndex='0'
        testID={this.props.testID}>
        {React.cloneElement(
          React.Children.only(this.props.children),
          {
            ref: CHILD_REF,
          }
        )}
      </View>
    );
  }
});

var CHILD_REF = keyOf({childRef: null});
var UNDERLAY_REF = keyOf({underlayRef: null});
var INACTIVE_CHILD_PROPS = {
  style: StyleSheet.create({x: {opacity: 1.0}}).x,
};
var INACTIVE_UNDERLAY_PROPS = {
  style: {backgroundColor: null}
};

var styles = StyleSheet.create({
  root: {
    cursor: 'pointer',
    userSelect: 'none'
  }
});

module.exports = TouchableHighlight;
<|endoftext|>


<commit_before>console.log('form-faker: faking data...');
const dictionary = getDictionary();
var inputs = document.getElementsByTagName('input');

for (var i = 0; i < inputs.length; i++) {
  var input = inputs[i];
  if (input.type.toLowerCase() !== 'text' || input.value) {
    continue;
  }

  var name = input.name.toLowerCase();
  var mapping = dictionary.get(name);
  switch (mapping) {
    case 'firstName':
      input.value = faker.name.firstName();
      break;
    case 'lastName':
      input.value = faker.name.firstName();
      break;
    case 'date-past':
      var date = faker.date.past(50);
      input.value = date.toLocaleDateString();
      break;
    case 'account':
      input.value = faker.finance.account();
      break;
    default:
      if (input.attributes.required) {
        input.value = faker.random.word();
      }
      break;
  }
}

console.log('form-faker: Done!');

function getDictionary() {
  return new Map([
    // firstName
    ['firstname', 'firstName'],
    ['first_name', 'firstName'],
    ['first-name', 'firstName'],
    ['fname', 'firstName'],
    // lastName
    ['lastname', 'lastName'],
    ['last_name', 'lastName'],
    ['last-name', 'lastName'],
    ['lname', 'lastName'],
    // date-past
    ['birthdate', 'date-past'],
    // account
    ['mrn', 'account'],
  ]);
}<commit_msg>Add email address #4<commit_after>console.log('form-faker: faking data...');
const dictionary = getDictionary();
var inputs = document.getElementsByTagName('input');

for (var i = 0; i < inputs.length; i++) {
  var input = inputs[i];
  if (input.type.toLowerCase() !== 'text' || input.value) {
    continue;
  }

  var name = input.name.toLowerCase();
  var mapping = dictionary.get(name);
  switch (mapping) {
    case 'firstName':
      input.value = faker.name.firstName();
      break;
    case 'lastName':
      input.value = faker.name.firstName();
      break;
    case 'date-past':
      var date = faker.date.past(50);
      input.value = date.toLocaleDateString();
      break;
    case 'email':
      input.value = faker.internet.email();
      break;
    case 'account':
      input.value = faker.finance.account();
      break;
    default:
      if (input.attributes.required) {
        input.value = faker.random.word();
      }
      break;
  }
}

console.log('form-faker: Done!');

function getDictionary() {
  return new Map([
    // firstName
    ['firstname', 'firstName'],
    ['first_name', 'firstName'],
    ['first-name', 'firstName'],
    ['fname', 'firstName'],
    // lastName
    ['lastname', 'lastName'],
    ['last_name', 'lastName'],
    ['last-name', 'lastName'],
    ['lname', 'lastName'],
    // email
    ['email', 'email'],
    ['emailaddress', 'email'],
    ['email_address', 'email'],
    ['email-address', 'email'],
    // date-past
    ['birthdate', 'date-past'],
    // account
    ['mrn', 'account'],
  ]);
}
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import TwemojiComp from 'react-twemoji';
import Twemoji from 'twemoji';
import rantscript from '../../consts/rantscript';
import EmojiPicker from '../emoji_picker/emoji_picker';

const HELPER_TYPES = {
  EMOJI: 'EMOJI',
};

class PostRant extends Component {
  constructor() {
    super();
    this.state = {
      rant_content: '',
      tags: '',
      posting: false,
      limitCrossed: false,
      activeHelper: null,
    };
  }

  parseHtml(str) {
    const parser = new DOMParser();
    const imgtags = str.match(/<(img+)\s+\w+.*?>/g);
    console.log(imgtags);
    for (const tag of imgtags) {
      console.log(tag, tag.match(/alt="(.*?)"/)[1]);
      str = str.replace(tag, tag.match(/alt="(.*?)"/)[1]);
    }
    return parser.parseFromString(`<!doctype html><body> ${str}`, 'text/html').body.textContent;
  }

  onPost() {
    const { auth } = this.props;
    this.setState({ posting: true });
    const rantText = this.parseHtml(document.getElementById('post_rant_content').innerHTML);
    console.log(rantText);
    rantscript
    .postRant(rantText, this.state.tags, auth.user.authToken)
    .then((res) => {
      if (!res.success) {
        this.setState({ limitCrossed: true });
      }
      this.setState({ posting: false, rant_content: '', tags: '' });
    })
    .catch(() => {
      this.setState({ posting: false });
    });
  }

  toggleEmoji() {
    if (this.state.activeHelper === HELPER_TYPES.EMOJI) {
      this.setState({ activeHelper: null });
      return;
    }
    this.setState({ activeHelper: HELPER_TYPES.EMOJI });
  }

  static addEmoji(emoji) {
    const div = document.getElementById('post_rant_content');

    let range;
    const sel = window.getSelection();
    if (sel.anchorNode && sel.anchorNode.parentElement.id === 'post_rant_content') {
      if (sel.getRangeAt && sel.rangeCount) {
        range = sel.getRangeAt(0);
        range.deleteContents();
        const textNode = document.createTextNode(` ${emoji}\u00A0`);
        range.insertNode(textNode);
      }
    } else {
      div.innerHTML += (`${emoji} `);
    }

    Twemoji.parse(div);
  }
  render() {
    return (

              <TwemojiComp
                className="emoji_trigger"
                onClick={() => this.toggleEmoji()}
              >
                🙂
              </TwemojiComp>

            {
              this.state.activeHelper === HELPER_TYPES.EMOJI ?
                <EmojiPicker onPick={emoji => PostRant.addEmoji(emoji)} /> : null
            }
            <textarea
              onChange={e => this.setState({ tags: e.target.value })}
              value={this.state.tags}
              placeholder="Tags"
              className="tags"
            />

              <button
                onClick={() => this.onPost()}
                disabled={this.state.posting}
              >Post Rant</button>

            { this.state.limitCrossed ?
Right now you can only add 1 rant every 2 hours
               (every 1 hour for devRant++ members) because we want
                to make sure everyones content gets good exposure! Please contact
                 info@devrant.io if you have any questions :)
 : null}

    );
  }
}

PostRant.propTypes = {
  auth: PropTypes.object.isRequired,
};

export default PostRant;
<commit_msg>Added image upload<commit_after>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import TwemojiComp from 'react-twemoji';
import Twemoji from 'twemoji';
import rantscript from '../../consts/rantscript';
import EmojiPicker from '../emoji_picker/emoji_picker';

const electron = require('electron');

const HELPER_TYPES = {
  EMOJI: 'EMOJI',
};

class PostRant extends Component {
  constructor() {
    super();
    this.state = {
      rant_content: '',
      tags: '',
      posting: false,
      limitCrossed: false,
      activeHelper: null,
      image: null,
    };
  }

  parseHtml(str) {
    const parser = new DOMParser();
    const imgtags = str.match(/<(img+)\s+\w+.*?>/g);
    console.log(imgtags);
    if (imgtags === null) {
      return parser.parseFromString(`<!doctype html><body> ${str}`, 'text/html').body.textContent;
    }
    for (const tag of imgtags) {
      console.log(tag, tag.match(/alt="(.*?)"/)[1]);
      str = str.replace(tag, tag.match(/alt="(.*?)"/)[1]);
    }
    return parser.parseFromString(`<!doctype html><body> ${str}`, 'text/html').body.textContent;
  }

  onPost() {
    const { auth } = this.props;
    this.setState({ posting: true });
    const rantText = this.parseHtml(document.getElementById('post_rant_content').innerHTML);
    console.log(rantText);
    if (this.state.image !== null) {
      rantscript
        .postRant(rantText, this.state.tags, auth.user.authToken, this.state.image)
        .then((res) => {
          if (!res.success) {
            this.setState({ limitCrossed: true });
          }
          this.setState({ posting: false, rant_content: '', tags: '' });
        })
        .catch(() => {
          this.setState({ posting: false });
        });
    } else {
      rantscript
      .postRant(rantText, this.state.tags, auth.user.authToken)
      .then((res) => {
        if (!res.success) {
          this.setState({ limitCrossed: true });
        }
        this.setState({ posting: false, rant_content: '', tags: '' });
      })
      .catch(() => {
        this.setState({ posting: false });
      });
    }
  }

  selectImage() {
    if (this.state.image !== null) {
      this.setState({ image: null });
    } else {
      const { dialog } = electron.remote;
      dialog.showOpenDialog({
        title: 'Upload image',
        filters: [{ name: 'Images', extensions: ['jpg', 'png', 'gif'] }],
      }, (image) => {
        this.setState({ image: image[0] });
      });
    }
  }

  toggleEmoji() {
    if (this.state.activeHelper === HELPER_TYPES.EMOJI) {
      this.setState({ activeHelper: null });
      return;
    }
    this.setState({ activeHelper: HELPER_TYPES.EMOJI });
  }

  static addEmoji(emoji) {
    const div = document.getElementById('post_rant_content');

    let range;
    const sel = window.getSelection();
    if (sel.anchorNode && sel.anchorNode.parentElement.id === 'post_rant_content') {
      if (sel.getRangeAt && sel.rangeCount) {
        range = sel.getRangeAt(0);
        range.deleteContents();
        const textNode = document.createTextNode(` ${emoji}\u00A0`);
        range.insertNode(textNode);
      }
    } else {
      div.innerHTML += (`${emoji} `);
    }

    Twemoji.parse(div);
  }
  render() {
    return (

              <TwemojiComp
                className="emoji_trigger"
                onClick={() => this.toggleEmoji()}
              >
                🙂
              </TwemojiComp>

            {
              this.state.activeHelper === HELPER_TYPES.EMOJI ?
                <EmojiPicker onPick={emoji => PostRant.addEmoji(emoji)} /> : null
            }
            <textarea
              onChange={e => this.setState({ tags: e.target.value })}
              value={this.state.tags}
              placeholder="Tags"
              className="tags"
            />

              <button
                onClick={() => this.selectImage()}
              >
                {this.state.image === null && 'Add Image'}
                {this.state.image !== null && 'Remove Image'}
              </button>
              <button
                onClick={() => this.onPost()}
                disabled={this.state.posting}
              >Post Rant</button>

            { this.state.limitCrossed ?
Right now you can only add 1 rant every 2 hours
               (every 1 hour for devRant++ members) because we want
                to make sure everyones content gets good exposure! Please contact
                 info@devrant.io if you have any questions :)
 : null}

    );
  }
}

PostRant.propTypes = {
  auth: PropTypes.object.isRequired,
};

export default PostRant;
<|endoftext|>


<commit_before>'use strict';

import React from 'react';
import ProfileImage from 'components/atoms/profile_image/ProfileImage';
import Username from 'components/atoms/username/Username';
import Badges from 'components/molecules/badges/Badges';

require('./stylesheets/user_info.scss');

class UserInfo extends React.Component {
  render() {
    return (

        <ProfileImage email='cassioscabral@gmail.com' />

          <Username username='cassioscabral' />
          <Badges points='500' />

    );
  }
}

UserInfo.displayName = 'MoleculeUserInfo';

// Uncomment properties you need
// UserInfo.propTypes = {};
// UserInfo.defaultProps = {};

export default UserInfo;
<commit_msg>collect real data in userInfo from the user after login via auth0<commit_after>'use strict';

import React from 'react';
import ProfileImage from 'components/atoms/profile_image/ProfileImage';
import Username from 'components/atoms/username/Username';
import Badges from 'components/molecules/badges/Badges';

require('./stylesheets/user_info.scss');

class UserInfo extends React.Component {
  getUserProfile() {
    return JSON.parse(localStorage.getItem('userProfile'));
  }
  render() {

    let userProfile = this.getUserProfile();
    console.log(userProfile);
    return (

        <ProfileImage email={userProfile.email} />

          <Username username={userProfile.nickname} />
          <Badges points='500' />

    );
  }
}

UserInfo.displayName = 'MoleculeUserInfo';

// Uncomment properties you need
// UserInfo.propTypes = {};
// UserInfo.defaultProps = {};

export default UserInfo;
<|endoftext|>


<commit_before>import FS from "fs";
import Path from "path";
import _ from "lodash-fp";
import { split, remove } from "../helpers";
import { getCombinations } from "../day-17";

export const title = "Day 24: It Hangs in the Balance";

export const parse = _.compose(
    _.map(_.parseInt(10)),
    split("\n")
);

export function getPermutations(values, size, partial = [], result = []) {
    if (partial.length >= size)
	return result.push(partial);

    for (let i = 0; i < values.length; i++) {
	getPermutations(values, size, partial.concat(values[i]), result);
    }

    return result;
}

export function unique(permutations, sort) {
    let store = {};

    return _.reduce((result, permutation) => {
        const sorted = _.sortBy(_.identity, permutation);

        if (store[sorted])
            return result;

        store[sorted] = true;
        result.push(sort ? sorted : permutation);

        return result;
    }, [], permutations);
}

export function getRanges(blueprint) {
    let sum = 0;

    return _.reduce((result, length) => {
        result.push([sum, sum + length]);
        sum += length;

        return result;
    }, [], blueprint);
}

export const chunkBy = _.curry((blueprint, values, result = []) => {
    if (!blueprint.length)
        return result;

    const length = _.first(blueprint);
    const group = values.slice(0, length);

    return chunkBy(blueprint.slice(1), values.slice(length), result.concat([group]));
}, 2);

function inRange(a, b, x) {
    return  (x >= a && x <= b);
}

export function getConfigurations(blueprint, values) {
    const first = _.first(blueprint);
    const ranges = getRanges(blueprint);
    const getIndex = (length) => {
        return _.find((index) => {
            return inRange(...ranges[index], length);
        }, _.range(0, ranges.length));
    };

    const findConfigurations = (values, target = null, partial = [], result = []) => {
        if (!target && partial.length >= first)
            target = _.sum(partial.slice(0, first));

        const index = getIndex(partial.length);

        // Last group and the values left can't possibly equal the target.
        if (values.length == _.last(blueprint) && _.sum(values) != target)
            return result;

        const range = ranges[index];
        const group = partial.slice(...range);
        const total = _.sum(group);

        // The target is determined by the sum of the first group.
        // It's null if we are yet to complete the first group.
        if (target) {
            // The group has exceeded the limit.
            if (total > target)
                return result;

            // We completed the group, but failed to hit the target.
            if (group.length == blueprint[index] && total != target)
                return result;
        }

        if (!values.length)
            result.push(partial);

        // Pick each value from values.
        _.forEach((index) => {
            findConfigurations(remove(values, index), target, partial.concat(values[index]), result);
        }, _.range(0, values.length));

        return result;
    };

    return _.compose(
        _.map(chunkBy(blueprint)),
        unique,
        findConfigurations
    )(values);
}

export function run() {
    const inputPath = Path.join(__dirname, "input.txt");
    const input = FS.readFileSync(inputPath, "utf-8").trim();

    console.log("What is the quantum entanglement of the first group of packages in the ideal configuration?");
}
<commit_msg>Revised getCombinations from day 17 to accept a limit parameter. - getPermutations now only returns unique permutations. - Had another shot at getConfigurations. Currently a work in progress (probably should change name too).<commit_after>import FS from "fs";
import Path from "path";
import _ from "lodash-fp";
import { split, remove } from "../helpers";

export const title = "Day 24: It Hangs in the Balance";

export const parse = _.compose(
    _.map(_.parseInt(10)),
    split("\n")
);

export function getPermutations(values, size) {
    let store = {};
    let result = [];

    const findPermutations = (values, partial = []) => {
        if (partial.length >= size) {
            const sorted = _.sortBy(_.identity, partial);

            if (store[sorted])
                return result;

            store[sorted] = true;
            result.push(sorted);

	    return result;
        }

        // Pick each value from values.
        _.forEach((index) => {
            getPermutations(remove(values, index), partial.concat(values[index]));
        }, _.range(0, values.length));

        return result;
    };

    return findPermutations(values);
}

export function getCombinations(values, target, limit = null) {
    let store = {};
    let result = [];

    const findCombinations = (values, partial = []) => {
        const total = _.sum(partial);

        if (total === target) {
            const sorted = _.sortBy(_.identity, partial);

            if (store[sorted])
                return result;

            store[sorted] = true;
            result.push(sorted);
        }

        if (total >= target)
            return result;

        if (limit && partial.length >= limit)
            return result;

        // Pick each value from values.
        _.forEach((index) => {
            const value = values[index];
            const rest = values.slice(index + 1);

            findCombinations(rest, partial.concat(value));
        }, _.range(0, values.length));

        return result;
    };

    return findCombinations(values);
};

export function unique(permutations, sort) {
    let store = {};

    return _.reduce((result, permutation) => {
        const sorted = _.sortBy(_.identity, permutation);

        if (store[sorted])
            return result;

        store[sorted] = true;
        result.push(sort ? sorted : permutation);

        return result;
    }, [], permutations);
}

export function getRanges(blueprint) {
    let sum = 0;

    return _.reduce((result, length) => {
        result.push([sum, sum + length]);
        sum += length;

        return result;
    }, [], blueprint);
}

export const chunkBy = _.curry((blueprint, values, result = []) => {
    if (!blueprint.length)
        return result;

    const length = _.first(blueprint);
    const group = values.slice(0, length);

    return chunkBy(blueprint.slice(1), values.slice(length), result.concat([group]));
}, 2);

function inRange(a, b, x) {
    return  (x >= a && x <= b);
}

export function getConfigurations(values) {
    // Looking at the example, each group has a sum of exactly one third of the total.
    const target = _.sum(values) / 3;
    // Since we want the least amount of packages, it can't be less than the total / 3.
    const limit = Math.floor(values.length / 3);
    // Get a list of combinations which have sum of 'target'.
    const combinations = getCombinations(values, target, limit);

    console.log("combinations.length:", combinations.length);
}

export function run() {
    const inputPath = Path.join(__dirname, "input.txt");
    const input = FS.readFileSync(inputPath, "utf-8").trim();
    const values = parse(input);

    getCombinations(values);

    console.log("What is the quantum entanglement of the first group of packages in the ideal configuration?");
}
<|endoftext|>