<commit_before>/*jshint multistr: true */

'use strict'

var $ = require('jquery'),
    history = require('../../../libs/history.js/scripts/bundled/html5/jquery.history'),
    tab = require('../../../js/libs/tab'),
    dropdown = require('../../../js/libs/dropdown'),
    PulsarUIComponent = require('../../../js/PulsarUIComponent');

    $.fx.off = !$.fx.off;

describe('Pulsar UI Component', function() {

    beforeEach(function() {
        this.$html = $('

');
        this.$body = $('

').appendTo(this.$html);
        this.$code = $(`

                        foo
                        bar

                                <button data-toggle="dropdown" class="btn dropdown__toggle row-actions">
                                    Actions
                                </button>

action

            foo
            foo

            Expires in 6 hours\
`).appendTo(this.$html);

        this.$tabLink = this.$html.find('a[href="#tab"]');
        this.$pushStateTabLink = this.$html.find('a[href="#tab-foo"]');
        this.$isDisabled = this.$html.find('a[disabled]');
        this.$basicTable = this.$html.find('.qa-table');
        this.$datagridTable = this.$html.find('.qa-datagrid');
        this.$datatableTable = this.$html.find('.qa-datatable');
        this.$datatableWithCustomMessage = this.$html.find('.qa-datatable-empty-message');
        this.$datatableDisableSelection = this.$html.find('.qa-datatable-no-selection');
        this.$tableDupe = this.$html.find('.qa-table-dupe');
        this.$countdownOne = this.$html.find('.qa-countdown-one');
        this.$datatableActions = this.$html.find('.qa-datatable-actions');

        this.$rowActions = this.$html.find('.row-actions');

        this.history = {
            pushState: sinon.stub()
        };

        this.pulsarUIComponent = new PulsarUIComponent(this.$html, this.history);

    });

    describe('A countdown element', function() {

        beforeEach(function() {
            sinon.spy($.fn, 'countdown');
            this.pulsarUIComponent.init();
        });

        it('should call the countdown plugin', function() {
            expect($.fn.countdown).to.have.been.called;
        });
    });

    describe('disabled links', function() {

        beforeEach(function() {
            this.pulsarUIComponent.init();
        });

        it('should preventDefault', function() {
            var clickEvent = $.Event('click');
            this.$isDisabled.trigger(clickEvent);
            expect(clickEvent.isDefaultPrevented()).to.be.true;
        });

    });

    describe('initialising tables', function() {

        beforeEach(function() {
            this.pulsarUIComponent.init();
        });

        it('should wrap basic tables with the container', function() {
            expect(this.$basicTable.parent().hasClass('table-container')).to.be.true;
        });

        it('should wrap datagrid tables with the container', function() {
            expect(this.$datagridTable.parent().hasClass('table-container')).to.be.true;
        });

        it('should NOT wrap datatable tables with the container', function() {
            expect(this.$datatableTable.parent().hasClass('table-container')).to.be.false;
        });

        it('should NOT wrap tables which already have the container', function() {
            expect(this.$tableDupe.parent().parent().hasClass('table-container')).to.be.false;
        });

        it('should show the default empty table message', function() {
            expect(this.$datatableTable.find('.dataTables_empty').text()).to.equal('There are currently no items to display');
        });

        it('should allow the empty table message to be overridden', function() {
            expect(this.$datatableWithCustomMessage.find('.dataTables_empty').text()).to.equal('foo');
        });

        it('should wrap the table with the disable class when the data-select attribute is false', function() {
            expect(this.$datatableDisableSelection.parent().hasClass('dt-disable-selection')).to.be.true;
        });
    });

    describe('Table row-actions', function() {

        beforeEach(function() {
            sinon.spy(this.pulsarUIComponent, 'closeRowActions');
            this.pulsarUIComponent.init();
        });

        it('should be opened when clicked', function() {
            this.$rowActions.click();
            setTimeout(function(done){
                expect(this.$rowActions.parent().hasClass('open')).to.be.true;
                done();
            }, 1000);
        });

        it('should be closed when clicked again', function() {
            this.$rowActions.click();
            this.$rowActions.click();
            setTimeout(function(done){
                expect(this.$rowActions.parent().hasClass('open')).to.be.false;
                done();
            }, 1000);
        });

        it('should call the closeRowActions method when window resized', function() {
            this.$rowActions.click();
            this.$html.trigger('resize');
            setTimeout(function(done){
                expect(this.closeRowActions).to.have.been.called;
                done();
            }, 1000);
        });

        it('should call the closeRowActions method when table scrolled', function() {
            this.$rowActions.click();
            this.$datatableActions.trigger('scroll');
            setTimeout(function(){
                expect(this.closeRowActions).to.have.been.called;
            }, 1000);
        });

        it('should be closed when the window resized', function() {
            this.$rowActions.click();
            this.$html.trigger('resize');
            expect(this.$rowActions.parent().hasClass('open')).to.be.false;
        });

        it('should be closed when the table is scrolled resized', function() {
            this.$rowActions.click();
            this.$datatableActions.trigger('scroll');
            expect(this.$rowActions.parent().hasClass('open')).to.be.false;
        });

    });

    describe('Clicking a tab toggle with the data-href attribute', function() {

        beforeEach(function() {
            this.pulsarUIComponent.init();
        });

        it('should call the history plugin', function() {
            this.$pushStateTabLink.click();
            expect(this.history.pushState).to.have.been.calledOnce;
        });

        it('should push the correct state object onto the history stack', function () {
            this.$pushStateTabLink.click();
            expect(this.history.pushState).to.have.been.calledWith({state: 1});
        });

        it('should push the correct href onto the history stack', function () {
            this.$pushStateTabLink.click();
            expect(this.history.pushState).to.have.been.calledWith(sinon.match.any, '?tab=foo', '?tab=foo');
        });
    });

    describe('clicking a tab toggle', function() {

        beforeEach(function() {

            this.recalc = sinon.stub();

            $.fn.dataTable = {
                tables: sinon.stub()
            };

            $.fn.DataTable = sinon.stub().returns({
                columns: {
                    adjust: sinon.stub().returns({
                        responsive: {
                            recalc: this.recalc
                        }
                    })
                }
            });

            this.pulsarUIComponent.init();
            this.$tabLink.click();
            this.$tabLink.trigger('shown.bs.tab');

        });

        afterEach(function () {
            delete $.fn.dataTable;
            delete $.fn.DataTable;
        });

        it('should recalculate the table', function() {
            expect(this.recalc).to.have.been.calledOnce;
        });

    });
});
<commit_msg>Attempts to fix undefined $rowActions<commit_after>/*jshint multistr: true */

'use strict'

var $ = require('jquery'),
    history = require('../../../libs/history.js/scripts/bundled/html5/jquery.history'),
    tab = require('../../../js/libs/tab'),
    dropdown = require('../../../js/libs/dropdown'),
    PulsarUIComponent = require('../../../js/PulsarUIComponent');

    $.fx.off = !$.fx.off;

describe('Pulsar UI Component', function() {

    beforeEach(function() {
        this.$html = $('

');
        this.$body = $('

').appendTo(this.$html);
        this.$code = $(`

                        foo
                        bar

                                <button data-toggle="dropdown" class="btn dropdown__toggle row-actions">
                                    Actions
                                </button>

action

            foo
            foo

            Expires in 6 hours\
`).appendTo(this.$html);

        this.$tabLink = this.$html.find('a[href="#tab"]');
        this.$pushStateTabLink = this.$html.find('a[href="#tab-foo"]');
        this.$isDisabled = this.$html.find('a[disabled]');
        this.$basicTable = this.$html.find('.qa-table');
        this.$datagridTable = this.$html.find('.qa-datagrid');
        this.$datatableTable = this.$html.find('.qa-datatable');
        this.$datatableWithCustomMessage = this.$html.find('.qa-datatable-empty-message');
        this.$datatableDisableSelection = this.$html.find('.qa-datatable-no-selection');
        this.$tableDupe = this.$html.find('.qa-table-dupe');
        this.$countdownOne = this.$html.find('.qa-countdown-one');
        this.$datatableActions = this.$html.find('.qa-datatable-actions');

        this.$rowActions = this.$html.find('.row-actions');
        this.$rowActionsParent = this.$rowActions.parent();;

        this.history = {
            pushState: sinon.stub()
        };

        this.pulsarUIComponent = new PulsarUIComponent(this.$html, this.history);

    });

    describe('A countdown element', function() {

        beforeEach(function() {
            sinon.spy($.fn, 'countdown');
            this.pulsarUIComponent.init();
        });

        it('should call the countdown plugin', function() {
            expect($.fn.countdown).to.have.been.called;
        });
    });

    describe('disabled links', function() {

        beforeEach(function() {
            this.pulsarUIComponent.init();
        });

        it('should preventDefault', function() {
            var clickEvent = $.Event('click');
            this.$isDisabled.trigger(clickEvent);
            expect(clickEvent.isDefaultPrevented()).to.be.true;
        });

    });

    describe('initialising tables', function() {

        beforeEach(function() {
            this.pulsarUIComponent.init();
        });

        it('should wrap basic tables with the container', function() {
            expect(this.$basicTable.parent().hasClass('table-container')).to.be.true;
        });

        it('should wrap datagrid tables with the container', function() {
            expect(this.$datagridTable.parent().hasClass('table-container')).to.be.true;
        });

        it('should NOT wrap datatable tables with the container', function() {
            expect(this.$datatableTable.parent().hasClass('table-container')).to.be.false;
        });

        it('should NOT wrap tables which already have the container', function() {
            expect(this.$tableDupe.parent().parent().hasClass('table-container')).to.be.false;
        });

        it('should show the default empty table message', function() {
            expect(this.$datatableTable.find('.dataTables_empty').text()).to.equal('There are currently no items to display');
        });

        it('should allow the empty table message to be overridden', function() {
            expect(this.$datatableWithCustomMessage.find('.dataTables_empty').text()).to.equal('foo');
        });

        it('should wrap the table with the disable class when the data-select attribute is false', function() {
            expect(this.$datatableDisableSelection.parent().hasClass('dt-disable-selection')).to.be.true;
        });
    });

    describe('Table row-actions', function() {

        beforeEach(function() {
            sinon.spy(this.pulsarUIComponent, 'closeRowActions');
            this.pulsarUIComponent.init();
        });

        it('should be opened when clicked', function() {
            this.$rowActions.click();
            setTimeout(function(done){
                expect(this.$rowActionsParent.hasClass('open')).to.be.true;
                done();
            }, 1000);
        });

        it('should be closed when clicked again', function() {
            this.$rowActions.click();
            this.$rowActions.click();
            setTimeout(function(done){
                expect(this.$rowActionsParent.hasClass('open')).to.be.false;
                done();
            }, 1000);
        });

        it('should call the closeRowActions method when window resized', function() {
            this.$rowActions.click();
            this.$html.trigger('resize');
            setTimeout(function(done){
                expect(this.closeRowActions).to.have.been.called;
                done();
            }, 1000);
        });

        it('should call the closeRowActions method when table scrolled', function() {
            this.$rowActions.click();
            this.$datatableActions.trigger('scroll');
            setTimeout(function(){
                expect(this.closeRowActions).to.have.been.called;
            }, 1000);
        });

        it('should be closed when the window resized', function() {
            this.$rowActions.click();
            this.$html.trigger('resize');
            expect(this.$rowActionsParent.hasClass('open')).to.be.false;
        });

        it('should be closed when the table is scrolled resized', function() {
            this.$rowActions.click();
            this.$datatableActions.trigger('scroll');
            expect(this.$rowActionsParent.hasClass('open')).to.be.false;
        });

    });

    describe('Clicking a tab toggle with the data-href attribute', function() {

        beforeEach(function() {
            this.pulsarUIComponent.init();
        });

        it('should call the history plugin', function() {
            this.$pushStateTabLink.click();
            expect(this.history.pushState).to.have.been.calledOnce;
        });

        it('should push the correct state object onto the history stack', function () {
            this.$pushStateTabLink.click();
            expect(this.history.pushState).to.have.been.calledWith({state: 1});
        });

        it('should push the correct href onto the history stack', function () {
            this.$pushStateTabLink.click();
            expect(this.history.pushState).to.have.been.calledWith(sinon.match.any, '?tab=foo', '?tab=foo');
        });
    });

    describe('clicking a tab toggle', function() {

        beforeEach(function() {

            this.recalc = sinon.stub();

            $.fn.dataTable = {
                tables: sinon.stub()
            };

            $.fn.DataTable = sinon.stub().returns({
                columns: {
                    adjust: sinon.stub().returns({
                        responsive: {
                            recalc: this.recalc
                        }
                    })
                }
            });

            this.pulsarUIComponent.init();
            this.$tabLink.click();
            this.$tabLink.trigger('shown.bs.tab');

        });

        afterEach(function () {
            delete $.fn.dataTable;
            delete $.fn.DataTable;
        });

        it('should recalculate the table', function() {
            expect(this.recalc).to.have.been.calledOnce;
        });

    });
});
<|endoftext|>


<commit_before>$(document).ready(function(){
	var map;
	var overlay;
	var select;

	var epsg3857   = new OpenLayers.Projection("EPSG:3857");
	var epsg4326   = new OpenLayers.Projection("EPSG:4326");
	var epsg900913 = new OpenLayers.Projection("EPSG:900913");

	function init() {
		// The overlay layer for our marker, with a simple diamond as symbol
		overlay = new OpenLayers.Layer.Vector('Overlay', {
		  styleMap: new OpenLayers.StyleMap({
		      externalGraphic: 'http://dev.openlayers.org/img/marker.png',
		      graphicWidth: 20, graphicHeight: 24, graphicYOffset: -24,
		      title: '${tooltip}'
		  })
		});

		overlay.events.on({
		  'featureselected': onFeatureSelect,
		  'featureunselected': onFeatureUnselect
		});

		// Finally we create the map
		map = new OpenLayers.Map("map");

		map.addLayers([new OpenLayers.Layer.OSM(), overlay]);

		// Create a select feature control and add it to the map.
		select = new OpenLayers.Control.SelectFeature(overlay);
		map.addControl(select);
		select.activate();

		// 緯度、経度変更イベントの登録
		map.events.register("moveend" , map, onMapChange);

		// 中心点を日本にする
		var lonlat = new OpenLayers.LonLat(center.lon, center.lat).transform(epsg4326,epsg900913)
		map.setCenter(lonlat, center.zoom);
    }

	function onMapChange() {

		// 中心点
	    var lonLat = map.getCenter().transform(epsg3857,epsg4326);
	    // 切捨て
	    lonLat.lat = Math.round(lonLat.lat*1000000)/1000000;
	    lonLat.lon = Math.round(lonLat.lon*1000000)/1000000;

	    // 表示範囲
	    var bounds = map.getExtent().transform(new OpenLayers.Projection("EPSG:900913"), new OpenLayers.Projection("EPSG:4326"));

	    // データ取ってくる
		var url = '/api/map/' + bounds.top + ',' + bounds.left + ',' + bounds.bottom + ',' + bounds.right;
		$.getJSON(url, function(rows) {
			var features = [];
			$.each(rows, function(key,val) {
				// マーカー座標
				var point = new OpenLayers.Geometry.Point(val.google_latlon.Y, val.google_latlon.X)
					.transform(epsg4326, epsg900913);
				// マーカー生成
				var feature = new OpenLayers.Feature.Vector(point, {
			        tooltip: val.address,
					raw: val
			    });
				features.push(feature);
			});
			overlay.removeAllFeatures();
			overlay.addFeatures(features);
		});
	}

	function onPopupClose(evt) {
	    select.unselect(this.feature);
	}

	function onFeatureSelect(evt) {
	    var feature = evt.feature;
	    var data = feature.data.raw;
	    var address = data.address_fix || data.address;

	    var content = ''
	    			+ '
'
	    			+ '
'
					+ '    '
	    			+ '
'
	    			+ '
'
	    			;

	    popup = new OpenLayers.Popup.FramedCloud("featurePopup",
	                        feature.geometry.getBounds().getCenterLonLat(),
	                        new OpenLayers.Size(100, 100),
	                        content,
	                        null, true, onPopupClose);
	    feature.popup = popup;
	    popup.feature = feature;
	    map.addPopup(popup);

	    // コピペ用にブラウザのアドレスバーを書き換える
	    history.replaceState('','','/map/' + data.google_latlon.X + ',' + data.google_latlon.Y + ',' + map.getZoom());
	}

	function onFeatureUnselect(evt) {
	    var feature = evt.feature;
	    if (feature.popup) {
	        popup.feature = null;
	        map.removePopup(feature.popup);
	        feature.popup.destroy();
	        feature.popup = null;
	    }
	}

	init();
});
<commit_msg>ポップアップが消えないバグを修正。たぶん直っている。<commit_after>$(document).ready(function(){
	var map;
	var overlay;
	var select;
	var popup = {};

	var epsg3857   = new OpenLayers.Projection("EPSG:3857");
	var epsg4326   = new OpenLayers.Projection("EPSG:4326");

	function init() {
		// The overlay layer for our marker, with a simple diamond as symbol
		overlay = new OpenLayers.Layer.Vector('Overlay', {
		  styleMap: new OpenLayers.StyleMap({
		      externalGraphic: 'http://dev.openlayers.org/img/marker.png',
		      graphicWidth: 20, graphicHeight: 24, graphicYOffset: -24,
		      title: '${tooltip}'
		  })
		});

		overlay.events.on({
		  'featureselected': onFeatureSelect,
		  'featureunselected': onFeatureUnselect
		});

		// Finally we create the map
		map = new OpenLayers.Map("map");

		map.addLayers([new OpenLayers.Layer.OSM(), overlay]);

		// Create a select feature control and add it to the map.
		select = new OpenLayers.Control.SelectFeature(overlay);
		map.addControl(select);
		select.activate();

		// 緯度、経度変更イベントの登録
		map.events.register("moveend" , map, onMapChange);

		// 中心点を日本にする
		var lonlat = new OpenLayers.LonLat(center.lon, center.lat).transform(epsg4326,epsg3857)
		map.setCenter(lonlat, center.zoom);
    }

	function onMapChange() {

		// 中心点
	    var lonLat = map.getCenter().transform(epsg3857,epsg4326);
	    // 切捨て
	    lonLat.lat = Math.round(lonLat.lat*1000000)/1000000;
	    lonLat.lon = Math.round(lonLat.lon*1000000)/1000000;

	    // 表示範囲
	    var bounds = map.getExtent().transform(epsg3857, epsg4326);

	    // データ取ってくる
		var url = '/api/map/' + bounds.top + ',' + bounds.left + ',' + bounds.bottom + ',' + bounds.right;
		$.getJSON(url, function(rows) {
			var features = [];
			$.each(rows, function(key,val) {
				// マーカー座標
				var point = new OpenLayers.Geometry.Point(val.google_latlon.Y, val.google_latlon.X)
					.transform(epsg4326, epsg3857);
				// マーカー生成
				var feature = new OpenLayers.Feature.Vector(point, {
			        tooltip: val.address,
					raw: val
			    });
				features.push(feature);
			});

			if (popup.feature != null)
			{// ポップアップが開いている場合は1個だけ残す
				var tmp = overlay.features.concat();
				var pos = tmp.indexOf(popup.feature);
				tmp.splice(pos,1);
				overlay.removeFeatures(tmp);
			}
			else
			{// ポップアップが開いていない場合は全部消す
				overlay.removeAllFeatures();
			}
			overlay.addFeatures(features);

		});
	}

	function onPopupClose(evt) {
	    select.unselect(this.feature);
	    this.feature = null;
	}

	function onFeatureSelect(evt) {
	    var feature = evt.feature;
	    var data = feature.data.raw;
	    var address = data.address_fix || data.address;

	    var content = ''
	    			+ '
'
	    			+ '
'
					+ '    '
	    			+ '
'
	    			+ '
'
	    			;

	    popup = new OpenLayers.Popup.FramedCloud("featurePopup",
	                        feature.geometry.getBounds().getCenterLonLat(),
	                        new OpenLayers.Size(100, 100),
	                        content,
	                        null, true, onPopupClose);
	    feature.popup = popup;
	    popup.feature = feature;
	    map.addPopup(popup);

	    // コピペ用にブラウザのアドレスバーを書き換える
	    history.replaceState('','','/map/' + data.google_latlon.X + ',' + data.google_latlon.Y + ',' + map.getZoom());
	}

	function onFeatureUnselect(evt) {
	    var feature = evt.feature;
	    if (feature.popup) {
	        popup.feature = null;
	        map.removePopup(feature.popup);
	        feature.popup.destroy();
	        feature.popup = null;
	    }
	}

	init();
});
<|endoftext|>


<commit_before>const test = require("ava")
const css = require(process.env.PWD)
const fs = require("fs")
const glob = require("glob")

var selectors, classnames = null
const classRegex = /class="([^"]+)"/ig

function uniqueSelectors(s) {
  return s.filter(s => {
    // remove any selectors with hover states
    return s.match(/^\.[a-z\-_\s\.]+$/ig)
  }).map(s => {
    // split multi-selectors into last class used .foo .bar .baz
    return s.split(" ").pop()
  })
}

function documentedClassnames(sources) {
  var cn = []
  sources.forEach( f => {
    glob.sync(f).forEach( g => {
      var match = null
      while ((match = classRegex.exec(fs.readFileSync(g).toString())) != null) {
        cn = cn.concat(match[1].split(" "))
      }
    })
  })
  return [...new Set(cn)]
}

test.before(async t => {
  selectors = await uniqueSelectors(css.cssstats.selectors.values)
  classnames = await documentedClassnames([
    'docs/*.md',
    'README.md'
  ])
})

test("Every selector class is documented", t => {
  var undocumented = []
  selectors.forEach( selector => {
    if (!classnames.includes(selector.replace(".", ""))) {
      undocumented.push(selector)
    }
  })
  t.is(undocumented.length, 0, `I did not find documentation for the "${undocumented.join(", ")}" selector(s) in the ${process.env.npm_package_name} module.`);
})
<commit_msg>Some docs<commit_after>const test = require("ava")
const css = require(process.env.PWD)
const fs = require("fs")
const glob = require("glob")

var selectors, classnames = null
const classRegex = /class="([^"]+)"/ig

// Find unique selectors from the cssstats selector list
function uniqueSelectors(s) {
  s = s.map(s => {
    // split multi-selectors into last class used .foo .bar .baz
    return s.split(" ").pop()
  }).filter(s => {
    // remove any selector that aren't just regular classnames eg. ::hover [type]
    return s.match(/^\.[a-z\-_]+$/ig)
  })

  // return only the unique selectors
  return [...new Set(s)]
}

// From the given glob sources array, read the files and return found classnames
function documentedClassnames(sources) {
  var cn = []
  sources.forEach( f => {
    glob.sync(f).forEach( g => {
      var match = null

      // While we match a classRegex in the source
      while ((match = classRegex.exec(fs.readFileSync(g).toString())) != null) {

        // Get the matched classnames "..." and split by space into classes
        cn = cn.concat(match[1].split(" "))
      }
    })
  })

  // return only the unique classnames
  return [...new Set(cn)]
}

// Before all the tests get the selectors and classnames
test.before(async t => {
  selectors = await uniqueSelectors(css.cssstats.selectors.values)
  classnames = await documentedClassnames([
    'docs/*.md',
    'README.md'
  ])
})

test("Every selector class is documented", t => {
  var undocumented = []
  selectors.forEach( selector => {
    if (!classnames.includes(selector.replace(".", ""))) {
      undocumented.push(selector)
    }
  })
  t.is(undocumented.length, 0, `I did not find documentation for the "${undocumented.join(", ")}" selector(s) in the ${process.env.npm_package_name} module.`);
})
<|endoftext|>


<commit_before>/*jshint node: true*/

/**
 * @author lattmann / https://github.com/lattmann
 */

'use strict';

var main;

main = function (argv, callback) {
    var path = require('path'),
        gmeConfig = require(path.join(process.cwd(), 'config')),
        mongodb = require('mongodb'),
        Q = require('q'),
        webgme = require('../../webgme'),
        StorageUtil,
        Command = require('commander').Command,
        logger = webgme.Logger.create('gme:bin:migrateprojects', gmeConfig.bin.log),
        program = new Command(),
        dryRun,

        gmeAuth,
        users,

        error,
        db;

    callback = callback || function () {
        };

    webgme.addToRequireJsPaths(gmeConfig);
    StorageUtil = webgme.requirejs('common/storage/util');

    program.option('-f, --force', 'Migrate all projects.');

    program.parse(argv);

    if (program.force) {
        dryRun = false;
        logger.info('Database will be updated.');
    } else {
        dryRun = true;
        logger.warn('Before you run this script with a database update, it is strongly recommended to do a mongodump.');
        logger.info('Dry run, please add -f as a command line argument if you like to update the database.');
    }

    logger.info('Connecting to database: ' + gmeConfig.mongo.uri);
    webgme.getGmeAuth(gmeConfig)
        .then(function (gmeAuth_) {
            gmeAuth = gmeAuth_;
            return gmeAuth.listUsers();
        })
        .then(function (users_) {
            users = users_;
            return Q.ninvoke(mongodb.MongoClient, 'connect', gmeConfig.mongo.uri, gmeConfig.mongo.options);
        })
        .then(function (db_) {
            db = db_;
            logger.debug('Connected to database: ' + gmeConfig.mongo.uri);

            return getProjects();
        })
        .then(function (upgradeInfo) {
            upgradeInfo.map(function (detail) {
                // print out upgrade details.
                logger.info(detail.collectionName + ' will be owned by ' + detail.owner);
            });
            if (dryRun) {
                return Q();
            } else {
                return doUpgrade(upgradeInfo);
            }
        })
        .then(function (details) {
            if (details) {
                logger.info(details);
                //details.map(function (detail) {
                //    // print out upgrade details.
                //    logger.info(detail.collectionName + ' will be owned by ' + detail.owner);
                //});
            }
        })
        .catch(function (err) {
            error = err;
            logger.error('Script execution failed.', error);
        })
        .finally(function () {
            logger.debug('Closing database: ' + gmeConfig.mongo.uri);
            Q.all([
                gmeAuth.unload(),
                Q.ninvoke(db, 'close')
            ])
                .catch(function (err) {
                    error = error || err;
                    logger.error(err);
                })
                .finally(function () {
                    callback(error);
                });
        });

    function getProjects(cb) {
        logger.info('Fetching old projects and their information ...');

        return Q.ninvoke(db, 'collectionNames')
            .then(function (collections) {
                var upgradeInfo = [],
                    projectDetail;

                // discover old projects
                collections.map(function (collection) {
                    if (collection.name === 'system.indexes' ||
                        collection.name.indexOf('_') === 0 ||
                        collection.name.indexOf('+') > -1) {

                        logger.warn('Project/collection will not be migrated: ' + collection.name);
                    } else {
                        // good old project name
                        projectDetail = {
                            collectionName: collection.name,
                            owner: null
                        };

                        // get existing authorization information for old project
                        users.map(function (user) {
                            // identify possible owner of the project
                            if (user.projects.hasOwnProperty(collection.name) &&
                                user.projects[collection.name].delete) {

                                if (projectDetail.owner) {
                                    // owner is already selected
                                } else {
                                    projectDetail.owner = user._id;
                                }
                            }
                        });

                        if (projectDetail.owner === null) {
                            projectDetail.owner = gmeConfig.authentication.guestAccount;
                            logger.warn(projectDetail.collectionName +
                                        ' Project was owned nobody, assigning it to guest account: ' +
                                        projectDetail.owner);
                        }

                        upgradeInfo.push(projectDetail);
                    }
                });

                return Q(upgradeInfo);
            })
            .nodeify(cb);
    }

    function doUpgrade(upgradeInfo, gmeAuth) {
        var promises = [];

        throw new Error('Not implemented yet.');
        upgradeInfo.map(function (info) {
            var newProjectId = StorageUtil.getProjectIdFromUserIdAndProjectName(info.owner, info.collectionName),
                deferred = Q.defer();

            promises.push(
                Q.allSettled([
                    // renameCollection
                    Q.ninvoke(db, 'renameCollection', info.collectionName, newProjectId),
                    // add project ownership info
                    gmeAuth.addProject(info.owner, info.collectionName),
                    deferred.promise
                ])
            );

            users.map(function (user) {
                if (user.projects.hasOwnProperty(info.collectionName)) {
                    logger.info('Authorizing ' + user._id + ' for ' + newProjectId);
                    gmeAuth.authorizeByUserId(user._id, newProjectId, 'create', user.projects[info.collectionName])
                        .then(function () {
                            return gmeAuth.authorizeByUserId(user._id, info.collectionName, 'delete');
                        })
                        .then(deferred.resolve)
                        .catch(deferred.reject);
                }
            });
        });

        return Q.all(promises);
    }
};

module.exports = {
    main: main
};

if (require.main === module) {
    main(process.argv);
}
<commit_msg>#419 fix rename collection and authorization changes.<commit_after>/*jshint node: true*/

/**
 * @author lattmann / https://github.com/lattmann
 */

'use strict';

var main;

main = function (argv, callback) {
    var path = require('path'),
        gmeConfig = require(path.join(process.cwd(), 'config')),
        mongodb = require('mongodb'),
        Q = require('q'),
        webgme = require('../../webgme'),
        StorageUtil,
        Command = require('commander').Command,
        logger = webgme.Logger.create('gme:bin:migrateprojects', gmeConfig.bin.log),
        program = new Command(),
        dryRun,

        gmeAuth,
        users,

        error,
        db;

    callback = callback || function () {
        };

    webgme.addToRequireJsPaths(gmeConfig);
    StorageUtil = webgme.requirejs('common/storage/util');

    program.option('-f, --force', 'Migrate all projects.');

    program.parse(argv);

    if (program.force) {
        dryRun = false;
        logger.info('Database will be updated.');
    } else {
        dryRun = true;
        logger.warn('Before you run this script with a database update, it is strongly recommended to do a mongodump.');
        logger.info('Dry run, please add -f as a command line argument if you like to update the database.');
    }

    logger.info('Connecting to database: ' + gmeConfig.mongo.uri);
    webgme.getGmeAuth(gmeConfig)
        .then(function (gmeAuth_) {
            gmeAuth = gmeAuth_;
            return gmeAuth.listUsers();
        })
        .then(function (users_) {
            users = users_;
            return Q.ninvoke(mongodb.MongoClient, 'connect', gmeConfig.mongo.uri, gmeConfig.mongo.options);
        })
        .then(function (db_) {
            db = db_;
            logger.debug('Connected to database: ' + gmeConfig.mongo.uri);

            return getProjects();
        })
        .then(function (upgradeInfo) {
            upgradeInfo.map(function (detail) {
                // print out upgrade details.
                logger.info(detail.collectionName + ' will be owned by ' + detail.owner);
            });
            if (dryRun) {
                return Q();
            } else {
                return doUpgrade(upgradeInfo);
            }
        })
        .then(function (details) {
            if (details) {
                logger.info(details);
                //details.map(function (detail) {
                //    // print out upgrade details.
                //    logger.info(detail.collectionName + ' will be owned by ' + detail.owner);
                //});
            }
        })
        .catch(function (err) {
            error = err;
            logger.error('Script execution failed.', error);
        })
        .finally(function () {
            logger.debug('Closing database: ' + gmeConfig.mongo.uri);
            Q.all([
                gmeAuth.unload(),
                Q.ninvoke(db, 'close')
            ])
                .catch(function (err) {
                    error = error || err;
                    logger.error(err);
                })
                .finally(function () {
                    callback(error);
                });
        });

    function getProjects(cb) {
        logger.info('Fetching old projects and their information ...');

        return Q.ninvoke(db, 'collectionNames')
            .then(function (collections) {
                var upgradeInfo = [],
                    projectDetail;

                // discover old projects
                collections.map(function (collection) {
                    if (collection.name === 'system.indexes' ||
                        collection.name.indexOf('_') === 0 ||
                        collection.name.indexOf('+') > -1) {

                        logger.warn('Project/collection will not be migrated: ' + collection.name);
                    } else {
                        // good old project name
                        projectDetail = {
                            collectionName: collection.name,
                            owner: null
                        };

                        // get existing authorization information for old project
                        users.map(function (user) {
                            // identify possible owner of the project
                            if (user.projects.hasOwnProperty(collection.name) &&
                                user.projects[collection.name].delete) {

                                if (projectDetail.owner) {
                                    // owner is already selected
                                } else {
                                    projectDetail.owner = user._id;
                                }
                            }
                        });

                        if (projectDetail.owner === null) {
                            projectDetail.owner = gmeConfig.authentication.guestAccount;
                            logger.warn(projectDetail.collectionName +
                                        ' Project was owned nobody, assigning it to guest account: ' +
                                        projectDetail.owner);
                        }

                        upgradeInfo.push(projectDetail);
                    }
                });

                return Q(upgradeInfo);
            })
            .nodeify(cb);
    }

    function doUpgrade(upgradeInfo) {
        var promises = [],
            promise;

        upgradeInfo.map(function (info) {
            var newProjectId = StorageUtil.getProjectIdFromUserIdAndProjectName(info.owner, info.collectionName);

            promise = Q.ninvoke(db, 'collection', info.collectionName)
                .then(function (collection) {
                    return Q.ninvoke(collection, 'rename', newProjectId);
                })
                .then(function () {
                    return gmeAuth.addProject(info.owner, info.collectionName, {} /*info*/);
                })
                .then(function () {
                    var authorizePromises = [],
                        authorizePromise;

                    users.map(function (user) {
                        if (user.projects.hasOwnProperty(info.collectionName)) {
                            logger.info('Authorizing ' + user._id + ' for ' + newProjectId);

                            authorizePromise = gmeAuth.authorizeByUserId(user._id, newProjectId, 'create', user.projects[info.collectionName])
                                .then(function () {
                                    return gmeAuth.authorizeByUserId(user._id, info.collectionName, 'delete');
                                });

                            authorizePromises.push(authorizePromise);
                        }
                    });

                    return Q.allSettled(authorizePromises);
                });

            promises.push(promise);
        });

        return Q.allSettled(promises);
    }
};

module.exports = {
    main: main
};

if (require.main === module) {
    main(process.argv);
}
<|endoftext|>


<commit_before>#!/usr/bin/env node

import path from 'path';
import program from 'commander';
import shelljs from 'shelljs';
import Server from '../server';

program
  .option('-h, --host <host>', 'host to listen on')
  .option('-p, --port <port>', 'port to listen on')
  .option('-c, --config-dir [dir-name]', 'storybook config directory')
  .parse(process.argv);

const projectDir = path.resolve();
const configDir = path.resolve(program.configDir || './storybook');
const listenAddr = [program.port];
if (program.host) {
  listenAddr.push(program.host);
}

const server = new Server({projectDir, configDir});
server.listen(...listenAddr, function (err) {
  if (err) {
    throw err;
  }
  const address = `http://${program.host || 'localhost'}:${program.port}/`;
  console.info(`\nReact Native Storybook started on => ${address}\n`);
});

// RN packager
shelljs.exec([
  'node node_modules/react-native/local-cli/cli.js start',
  `--projectRoots ${configDir}`,
  `--root ${projectDir}`,
].join(' '), {async: true});
<commit_msg>Support react-native 0.42<commit_after>#!/usr/bin/env node

import path from 'path';
import program from 'commander';
import shelljs from 'shelljs';
import Server from '../server';

program
  .option('-h, --host <host>', 'host to listen on')
  .option('-p, --port <port>', 'port to listen on')
  .option('-c, --config-dir [dir-name]', 'storybook config directory')
  .parse(process.argv);

const projectDir = path.resolve();
const configDir = path.resolve(program.configDir || './storybook');
const listenAddr = [program.port];
if (program.host) {
  listenAddr.push(program.host);
}

const server = new Server({projectDir, configDir});
server.listen(...listenAddr, function (err) {
  if (err) {
    throw err;
  }
  const address = `http://${program.host || 'localhost'}:${program.port}/`;
  console.info(`\nReact Native Storybook started on => ${address}\n`);
});

// RN packager
shelljs.exec([
  'node node_modules/react-native/local-cli/cli.js start',
  `--projectRoots ${configDir},${projectDir}`,
].join(' '), {async: true});
<|endoftext|>


<commit_before>define(function (require, exports, module) {
	'use strict';

	var LanguageManager = brackets.getModule("language/LanguageManager");
	var CodeMirror 		= brackets.getModule("thirdparty/CodeMirror2/lib/codemirror");
    var highlightClass  = "keyword";

	CodeMirror.defineMode("laravelblade", function (config, parserConfig) {

		var laravelOverlay = {
			startState: function() {
				return {
					inComment: false,
                    inEcho: false,
                    inEcho5: false,
                    inForm: false
				}
			},
			token: function(stream, state) {
				var ch;

				// Laravel Comment Syntax (Single Line and Multiline)
				if (state.inComment) {
					if (!stream.skipTo("--}}")) {
						stream.skipToEnd();
					} else {
						stream.match("--}}");
						state.inComment = false;
					}
					return "comment";
				} else {
					if (stream.match("{{--")) {
						state.inComment = true;
						if (!stream.skipTo("--}}")) {
							stream.skipToEnd();
						} else {
							stream.match("--}}");
							state.inComment = false;
						}
						return "comment";
					}
				}

				//Laravel5 Echo Syntax (Single Line and Multiline)
				if (state.inEcho5) {
					if (!stream.skipTo("%}")) {
						stream.skipToEnd();
					} else {
						stream.match("%}");
						state.inEcho5 = false;
					}
					return highlightClass;
				} else {
					if (stream.match("{%")) {
						state.inEcho5 = true;
						if (!stream.skipTo("%}")) {
							stream.skipToEnd();
						} else {
							stream.match("%}");
							state.inEcho5 = false;
						}
						return highlightClass;
					}
				}

                //Laravel Echo Syntax (Single Line and Multiline)
				if (state.inEcho) {
					if (!stream.skipTo("}}")) {
						stream.skipToEnd();
					} else {
						stream.match("}}");
						state.inEcho = false;
					}
					return highlightClass;
				} else {
					if (stream.match("{{")) {
						state.inEcho = true;
						if (!stream.skipTo("}}")) {
							stream.skipToEnd();
						} else {
							stream.match("}}");
							state.inEcho = false;
						}
						return highlightClass;
					}
				}

				//Laravel5 Form Syntax (Single Line and Multiline)
				if (state.inForm) {
					if (!stream.skipTo("!!}")) {
						stream.skipToEnd();
					} else {
						stream.match("!!}");
						state.inForm = false;
					}
					return highlightClass;
				} else {
					if (stream.match("{!!")) {
						state.inForm = true;
						if (!stream.skipTo("!!}")) {
							stream.skipToEnd();
						} else {
							stream.match("!!}");
							state.inForm = false;
						}
						return highlightClass;
					}
				}

                //listen for opening tags
				while (stream.next() != null &&
					   !stream.match("{--", false) &&
					   !stream.match("{%", false) &&
					   !stream.match("{!!", false) &&
					   !stream.match("{{", false)
					  ) {}
				return null;

			}
		  };
		  return CodeMirror.overlayMode(CodeMirror.getMode(config, parserConfig.backdrop || "php"), laravelOverlay);
	});

	LanguageManager.defineLanguage("laravelblade", {
		"name": "Laravel Blade",
		"mode": "laravelblade",
		"fileExtensions": ["blade.php"],
		"blockComment": ["{{--", "--}}"]
	});
});
<commit_msg>Fixed typo on opening comment<commit_after>define(function (require, exports, module) {
	'use strict';

	var LanguageManager = brackets.getModule("language/LanguageManager");
	var CodeMirror 		= brackets.getModule("thirdparty/CodeMirror2/lib/codemirror");
    var highlightClass  = "keyword";

	CodeMirror.defineMode("laravelblade", function (config, parserConfig) {

		var laravelOverlay = {
			startState: function() {
				return {
					inComment: false,
                    inEcho: false,
                    inEcho5: false,
                    inForm: false
				}
			},
			token: function(stream, state) {
				var ch;

				// Laravel Comment Syntax (Single Line and Multiline)
				if (state.inComment) {
					if (!stream.skipTo("--}}")) {
						stream.skipToEnd();
					} else {
						stream.match("--}}");
						state.inComment = false;
					}
					return "comment";
				} else {
					if (stream.match("{{--")) {
						state.inComment = true;
						if (!stream.skipTo("--}}")) {
							stream.skipToEnd();
						} else {
							stream.match("--}}");
							state.inComment = false;
						}
						return "comment";
					}
				}

				//Laravel5 Echo Syntax (Single Line and Multiline)
				if (state.inEcho5) {
					if (!stream.skipTo("%}")) {
						stream.skipToEnd();
					} else {
						stream.match("%}");
						state.inEcho5 = false;
					}
					return highlightClass;
				} else {
					if (stream.match("{%")) {
						state.inEcho5 = true;
						if (!stream.skipTo("%}")) {
							stream.skipToEnd();
						} else {
							stream.match("%}");
							state.inEcho5 = false;
						}
						return highlightClass;
					}
				}

                //Laravel Echo Syntax (Single Line and Multiline)
				if (state.inEcho) {
					if (!stream.skipTo("}}")) {
						stream.skipToEnd();
					} else {
						stream.match("}}");
						state.inEcho = false;
					}
					return highlightClass;
				} else {
					if (stream.match("{{")) {
						state.inEcho = true;
						if (!stream.skipTo("}}")) {
							stream.skipToEnd();
						} else {
							stream.match("}}");
							state.inEcho = false;
						}
						return highlightClass;
					}
				}

				//Laravel5 Form Syntax (Single Line and Multiline)
				if (state.inForm) {
					if (!stream.skipTo("!!}")) {
						stream.skipToEnd();
					} else {
						stream.match("!!}");
						state.inForm = false;
					}
					return highlightClass;
				} else {
					if (stream.match("{!!")) {
						state.inForm = true;
						if (!stream.skipTo("!!}")) {
							stream.skipToEnd();
						} else {
							stream.match("!!}");
							state.inForm = false;
						}
						return highlightClass;
					}
				}

                //listen for opening tags
				while (stream.next() != null &&
					   !stream.match("{{--", false) &&
					   !stream.match("{%", false) &&
					   !stream.match("{!!", false) &&
					   !stream.match("{{", false)
					  ) {}
				return null;

			}
		  };
		  return CodeMirror.overlayMode(CodeMirror.getMode(config, parserConfig.backdrop || "php"), laravelOverlay);
	});

	LanguageManager.defineLanguage("laravelblade", {
		"name": "Laravel Blade",
		"mode": "laravelblade",
		"fileExtensions": ["blade.php"],
		"blockComment": ["{{--", "--}}"]
	});
});
<|endoftext|>


<commit_before>'use strict';

const globalHooks = require('../../../hooks');
const hooks = require('feathers-hooks');
const auth = require('feathers-authentication');

//TODO: after hook for get that checks access.
//TODO: rethink security, due to no schoolId we can't restrict anything.

const restrictToUserOrRole = (hook) => {
	let userService = hook.app.service("users");
	return userService.find({
		query: {
			_id: hook.params.account.userId,
			$populate: 'roles'
		}
	}).then(res => {
		let access = false;
		res.data[0].roles.map(role => {
			if (role.name === 'superhero' || role.name === 'teacher' || role.name === 'administrator')
				access = true;
		});
		if (access)
			return hook;
		else
			hook.params.query.userId = hook.params.account.userId;

		return hook;
	});
};

const addDates = (hook) => {
	if (hook.data.parentConsents) {
		let parentConsent = hook.data.parentConsents[0];
		if ("privacyConsent" in parentConsent) {
			parentConsent.dateOfPrivacyConsent = Date.now();
		}
		if ("researchConsent" in parentConsent) {
			parentConsent.dateOfResearchConsent = Date.now();
		}
		if ("termsOfUseConsent" in parentConsent) {
			parentConsent.dateOfTermsOfUseConsent = Date.now();
		}
		if ("thirdPartyConsent" in parentConsent) {
			parentConsent.dateOfThirdPartyConsent = Date.now();
		}
	}
	if (hook.data.userConsent) {
		let userConsent = hook.data.userConsent;
		if ("privacyConsent" in userConsent) {
			userConsent.dateOfPrivacyConsent = Date.now();
		}
		if ("researchConsent" in userConsent) {
			userConsent.dateOfResearchConsent = Date.now();
		}
		if ("termsOfUseConsent" in userConsent) {
			userConsent.dateOfTermsOfUseConsent = Date.now();
		}
		if ("thirdPartyConsent" in userConsent) {
			userConsent.dateOfThirdPartyConsent = Date.now();
		}
	}
};

exports.before = {
	all: [],
	find: [auth.hooks.authenticate('jwt'), globalHooks.ifNotLocal(restrictToUserOrRole)],
	get: [auth.hooks.authenticate('jwt')],
	create: [addDates],
	update: [auth.hooks.authenticate('jwt'), addDates],
	patch: [auth.hooks.authenticate('jwt'), addDates],
	remove: [auth.hooks.authenticate('jwt'),]
};

const redirectDic = {
	u14: '/firstLogin/U14/',
	u18: '/firstLogin/14_17/',
	ue18: '/firstLogin/UE18/',
	existing: '/firstLogin/existing/',
	normal: '/dashboard/',
	err: '/consentError'
};

const accessCheck = (hook) => {
	let access = true;
	let redirect = redirectDic['ue18'];
	let requiresParentConsent = true;
	let data = hook.result.data || hook.result;
	data = (Array.isArray(data)) ? (data) : ([data]);

	if (data.length == 0)
		return hook;

	return hook.app.service('users').get(data[0].userId)
		.then(user => {
			if (!user.birthday) {
				access = false;
				requiresParentConsent = false;
				redirect = redirectDic['existing'];
				return Promise.resolve;
			}
			let age = user.age;

			if (age < 18) {
				redirect = redirectDic['u14'];
				let parentConsent = data[0].parentConsents[0] || {};
				//check parent consents
				if (!(parentConsent.privacyConsent && parentConsent.termsOfUseConsent &&
					parentConsent.thirdPartyConsent && parentConsent.researchConsent)) {
					access = false;
					redirect = redirectDic['err'];
					return Promise.resolve();
				}
			}
			if (age > 13) {
				redirect = redirectDic['u18'];
				//check user consents
				let userConsent = data[0].userConsent || {};
				if (!(userConsent.privacyConsent && userConsent.termsOfUseConsent &&
					userConsent.thirdPartyConsent && userConsent.researchConsent)) {
					access = false;
					if ((user.preferences || {}).firstLogin) {
						redirect = redirectDic['err'];
						return Promise.resolve();
					}
				}
			}
			if (age > 17)
				redirect = redirectDic['ue18'];
				requiresParentConsent = false;
			if ((user.preferences || {}).firstLogin)
				redirect = redirectDic['normal'];
		})
		.then(() => {
			if (hook.result.data) {
				hook.result.data[0].access = access;
				hook.result.data[0].redirect = redirect;
				hook.result.data[0].requiresParentConsent = requiresParentConsent;
			} else {
				hook.result.access = access;
				hook.result.redirect = redirect;
				hook.result.requiresParentConsent = requiresParentConsent;
			};
			return Promise.resolve(hook);
		})
		.catch(err => {
			return Promise.reject(err);
		});

};

exports.after = {
	all: [],
	find: [accessCheck],
	get: [accessCheck],
	create: [],
	update: [],
	patch: [],
	remove: []
};
<commit_msg>check access for multiple consents<commit_after>'use strict';

const globalHooks = require('../../../hooks');
const hooks = require('feathers-hooks');
const auth = require('feathers-authentication');

//TODO: after hook for get that checks access.
//TODO: rethink security, due to no schoolId we can't restrict anything.

const restrictToUserOrRole = (hook) => {
	let userService = hook.app.service("users");
	return userService.find({
		query: {
			_id: hook.params.account.userId,
			$populate: 'roles'
		}
	}).then(res => {
		let access = false;
		res.data[0].roles.map(role => {
			if (role.name === 'superhero' || role.name === 'teacher' || role.name === 'administrator')
				access = true;
		});
		if (access)
			return hook;
		else
			hook.params.query.userId = hook.params.account.userId;

		return hook;
	});
};

const addDates = (hook) => {
	if (hook.data.parentConsents) {
		let parentConsent = hook.data.parentConsents[0];
		if ("privacyConsent" in parentConsent) {
			parentConsent.dateOfPrivacyConsent = Date.now();
		}
		if ("researchConsent" in parentConsent) {
			parentConsent.dateOfResearchConsent = Date.now();
		}
		if ("termsOfUseConsent" in parentConsent) {
			parentConsent.dateOfTermsOfUseConsent = Date.now();
		}
		if ("thirdPartyConsent" in parentConsent) {
			parentConsent.dateOfThirdPartyConsent = Date.now();
		}
	}
	if (hook.data.userConsent) {
		let userConsent = hook.data.userConsent;
		if ("privacyConsent" in userConsent) {
			userConsent.dateOfPrivacyConsent = Date.now();
		}
		if ("researchConsent" in userConsent) {
			userConsent.dateOfResearchConsent = Date.now();
		}
		if ("termsOfUseConsent" in userConsent) {
			userConsent.dateOfTermsOfUseConsent = Date.now();
		}
		if ("thirdPartyConsent" in userConsent) {
			userConsent.dateOfThirdPartyConsent = Date.now();
		}
	}
};

exports.before = {
	all: [],
	find: [auth.hooks.authenticate('jwt'), globalHooks.ifNotLocal(restrictToUserOrRole)],
	get: [auth.hooks.authenticate('jwt')],
	create: [addDates],
	update: [auth.hooks.authenticate('jwt'), addDates],
	patch: [auth.hooks.authenticate('jwt'), addDates],
	remove: [auth.hooks.authenticate('jwt'),]
};

const redirectDic = {
	u14: '/firstLogin/U14/',
	u18: '/firstLogin/14_17/',
	ue18: '/firstLogin/UE18/',
	existing: '/firstLogin/existing/',
	normal: '/dashboard/',
	err: '/consentError'
};

const accessCheck = (consent, app) => {
	let access = true;
	let redirect = redirectDic['ue18'];
	let requiresParentConsent = true;

	return app.service('users').get(consent.userId)
		.then(user => {
			if (!user.birthday) {
				access = false;
				requiresParentConsent = false;
				redirect = redirectDic['existing'];
				return Promise.resolve;
			}
			let age = user.age;

			if (age < 18) {
				redirect = redirectDic['u14'];
				let parentConsent = consent.parentConsents[0] || {};
				//check parent consents
				if (!(parentConsent.privacyConsent && parentConsent.termsOfUseConsent &&
					parentConsent.thirdPartyConsent && parentConsent.researchConsent)) {
					access = false;
					redirect = redirectDic['err'];
					return Promise.resolve();
				}
			}
			if (age > 13) {
				redirect = redirectDic['u18'];
				//check user consents
				let userConsent = consent.userConsent || {};
				if (!(userConsent.privacyConsent && userConsent.termsOfUseConsent &&
					userConsent.thirdPartyConsent && userConsent.researchConsent)) {
					access = false;
					if ((user.preferences || {}).firstLogin) {
						redirect = redirectDic['err'];
						return Promise.resolve();
					}
				}
			}
			if (age > 17)
				redirect = redirectDic['ue18'];
				requiresParentConsent = false;
			if ((user.preferences || {}).firstLogin)
				redirect = redirectDic['normal'];
		})
		.then(() => {
			consent.access = access;
			consent.redirect = redirect;
			consent.requiresParentConsent = requiresParentConsent;
			return Promise.resolve(consent);
		})
		.catch(err => {
			return Promise.reject(err);
		});

};

const decorateConsent = (hook) => {
	return accessCheck(hook.result, hook.app)
		.then(consent => {
			hook.result = (hook.result.constructor.name === 'model') ? hook.result.toObject() : hook.result;
			hook.result = consent;
		})
	.then(() => Promise.resolve(hook));
}

const decorateConsents = (hook) => {
	hook.result = (hook.result.constructor.name === 'model') ? hook.result.toObject() : hook.result;
	const consentPromises = (hook.result.data || []).map(consent => {
		return accessCheck(consent, hook.app).then(result => {
			return result
		});
	});

	return Promise.all(consentPromises).then(users => {
		hook.result.data = users;
		return Promise.resolve(hook);
	});
}

exports.after = {
	all: [],
	find: [decorateConsents],
	get: [decorateConsent],
	create: [],
	update: [],
	patch: [],
	remove: []
};
<|endoftext|>


<commit_before>import Ember from 'ember';
import layout from './template';

import EmberD3 from '../../mixins/d3-support';

//import { join, translateX } from '../../utils/d3';

export default Ember.Component.extend(EmberD3, {
  layout,

  tickSize: 6,
  transform: null,

  axis: Ember.computed('scale', 'tickSize', {
    get() {
      var tickSize = this.get('tickSize');
      var scale = this.get('scale');
      var orient = this.get('orient');

      return scale && d3.svg.axis()
        .scale(scale)
        .orient(orient)
        .tickSize(tickSize);
    }
  }).readOnly(),

  call(sel) {
    var axis = this.get('axis');
    var transform = this.get('transform');

    if (axis) {
      sel
        .attr('transform', transform)
        .call(axis)
        .each(function () {
          d3.select(this).selectAll('.tick')
            .classed('zero', (data) => !data);
        });
    }
  }

});
<commit_msg>add more options to axis<commit_after>import Ember from 'ember';
import layout from './template';

import EmberD3 from '../../mixins/d3-support';

import { assign } from '../../utils/d3';

export default Ember.Component.extend(EmberD3, {
  layout,

  transform: null,

  tickSize: 6,
  tickFormat: null,
  tickPadding: null,

  scale: null,

  axis: Ember.computed('scale', 'orient', 'tickSize', 'tickFormat', 'tickPadding', {
    get() {
      var props = this.getProperties('scale', 'orient', 'tickSize', 'tickFormat', 'tickPadding');

      return props.scale && assign(d3.svg.axis(), props);
    }
  }).readOnly(),

  call(sel) {
    var axis = this.get('axis');
    var transform = this.get('transform');

    if (axis) {
      sel
        .attr('transform', transform)
        .call(axis)
        .each(function () {
          d3.select(this).selectAll('.tick')
            .classed('zero', (data) => !data);
        });
    }
  }

});
<|endoftext|>


<commit_before>import React, {Component} from 'react';
import {render} from 'react-dom';
import PropTypes from 'prop-types';
import {Meteor} from 'meteor/meteor';
import {createContainer} from 'meteor/react-meteor-data';
import styled, {injectGlobal} from 'styled-components';
import {grey} from '@quarterto/colours';

import {Cards} from '../src/collections';

injectGlobal`
	body {
		margin: 0;
		font-family: system-ui;
	}
`;

const getValue = ev =>
	ev.target[
		{
			number: 'valueAsNumber',
			range: 'valueAsNumber',
			date: 'valueAsDate',
		}[ev.target.type] || 'value'
	];

class Field extends Component {
	static get contextTypes() {
		return {
			state: PropTypes.object,
			setState: PropTypes.func,
		};
	}

	render() {
		const {name} = this.props;
		return (
			<input
				type="text"
				{...this.props}
				value={this.context.state[name] || ''}
				onChange={ev => this.context.setState({[name]: getValue(ev)})}
			/>
		);
	}
}

class Form extends Component {
	constructor(props, ...args) {
		super(props, ...args);

		this.state = props.initialData;

		this.setState = this.setState.bind(this);
		this.onSubmit = this.onSubmit.bind(this);
	}

	getChildContext() {
		return {
			state: this.state,
			setState: this.setState,
		};
	}

	static get childContextTypes() {
		return {
			state: PropTypes.object,
			setState: PropTypes.func,
		};
	}

	onSubmit(ev) {
		ev.preventDefault();
		this.props.onSubmit(this.state);
	}

	render() {
		return (
			<form onSubmit={this.onSubmit}>
				{this.props.children}
			</form>
		);
	}
}

const CardPrimitive = styled.div`
	border: 1px solid ${grey[5]};
	padding: 1em;
	border-radius: 2px;
`;

class Toggler extends Component {
	constructor(...args) {
		super(...args);
		this.state = {on: false};
		this.toggle = this.toggle.bind(this);
	}

	toggle() {
		this.setState(({on}) => ({on: !on}));
	}

	render() {
		return this.state.on
			? <this.props.active toggle={this.toggle} {...this.props} />
			: <this.props.inactive toggle={this.toggle} {...this.props} />;
	}
}

const preventingDefault = fn => ev => {
	ev.preventDefault();
	fn(ev);
};

const EditCard = ({card, saveCard, toggle}) =>
	<Form
		onSubmit={data => {
			saveCard(data);
			if (toggle) toggle();
		}}
		initialData={card}
	>
		<Field name="title" />
		<Field name="text" />
		<button>{toggle ? '✓' : '+'}</button>
		{toggle && <button onClick={preventingDefault(toggle)}>×</button>}
	</Form>;

const ShowCard = ({card: {title, text}, toggle}) =>

		{toggle && <button onClick={toggle}>Edit</button>}

{title}

{text}

;

const Card = props =>
	<CardPrimitive>
		<Toggler active={EditCard} inactive={ShowCard} {...props} />
	</CardPrimitive>;

const List = styled.div`
	display: grid;
	padding: 1em;
	grid-gap: 1em;
	grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
`;

const CardList = ({cards, saveCard}) =>
	<List>
		{cards.map(card =>
			<Card key={card._id} card={card} saveCard={saveCard} />
		)}

		<EditCard card={{}} saveCard={saveCard} />
	</List>;

const App = createContainer(
	() => ({
		cards: Cards.find({}).fetch(),

		saveCard(card) {
			if (card._id) {
				Cards.update(card._id, card);
			} else {
				Cards.insert(card);
			}
		},
	}),
	CardList
);

Meteor.startup(() => {
	render(<App />, document.querySelector('main'));
});
<commit_msg>clearing form<commit_after>import React, {Component} from 'react';
import {render} from 'react-dom';
import PropTypes from 'prop-types';
import {Meteor} from 'meteor/meteor';
import {createContainer} from 'meteor/react-meteor-data';
import styled, {injectGlobal} from 'styled-components';
import {grey} from '@quarterto/colours';

import {Cards} from '../src/collections';

injectGlobal`
	body {
		margin: 0;
		font-family: system-ui;
	}
`;

const getValue = ev =>
	ev.target[
		{
			number: 'valueAsNumber',
			range: 'valueAsNumber',
			date: 'valueAsDate',
		}[ev.target.type] || 'value'
	];

class Field extends Component {
	static get contextTypes() {
		return {
			state: PropTypes.object,
			setState: PropTypes.func,
		};
	}

	render() {
		const {name} = this.props;
		return (
			<input
				type="text"
				{...this.props}
				value={this.context.state[name] || ''}
				onChange={ev => this.context.setState({[name]: getValue(ev)})}
			/>
		);
	}
}

class Form extends Component {
	constructor(props, ...args) {
		super(props, ...args);

		this.state = props.initialData;

		this.setState = this.setState.bind(this);
		this.onSubmit = this.onSubmit.bind(this);
	}

	getChildContext() {
		return {
			state: this.state,
			setState: this.setState,
		};
	}

	static get childContextTypes() {
		return {
			state: PropTypes.object,
			setState: PropTypes.func,
		};
	}

	static get defaultProps() {
		return {initialData: {}};
	}

	onSubmit(ev) {
		ev.preventDefault();
		Promise.resolve(this.props.onSubmit(this.state)).then(() => {
			this.state = this.props.initialData;
			this.forceUpdate();
		});
	}

	render() {
		return (
			<form onSubmit={this.onSubmit}>
				{this.props.children}
			</form>
		);
	}
}

const CardPrimitive = styled.div`
	border: 1px solid ${grey[5]};
	padding: 1em;
	border-radius: 2px;
`;

class Toggler extends Component {
	constructor(...args) {
		super(...args);
		this.state = {on: false};
		this.toggle = this.toggle.bind(this);
	}

	toggle() {
		this.setState(({on}) => ({on: !on}));
	}

	render() {
		return this.state.on
			? <this.props.active toggle={this.toggle} {...this.props} />
			: <this.props.inactive toggle={this.toggle} {...this.props} />;
	}
}

const preventingDefault = fn => ev => {
	ev.preventDefault();
	fn(ev);
};

const EditCard = ({card, saveCard, toggle}) =>
	<Form
		onSubmit={data => {
			saveCard(data);
			if (toggle) toggle();
		}}
		initialData={card}
	>
		<Field name="title" />
		<Field name="text" />
		<button>{toggle ? '✓' : '+'}</button>
		{toggle && <button onClick={preventingDefault(toggle)}>×</button>}
	</Form>;

const ShowCard = ({card: {title, text}, toggle}) =>

		{toggle && <button onClick={toggle}>Edit</button>}

{title}

{text}

;

const Card = props =>
	<CardPrimitive>
		<Toggler active={EditCard} inactive={ShowCard} {...props} />
	</CardPrimitive>;

const List = styled.div`
	display: grid;
	padding: 1em;
	grid-gap: 1em;
	grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
`;

const CardList = ({cards, saveCard}) =>
	<List>
		{cards.map(card =>
			<Card key={card._id} card={card} saveCard={saveCard} />
		)}

		<CardPrimitive>
			<EditCard card={{}} saveCard={saveCard} />
		</CardPrimitive>
	</List>;

const App = createContainer(
	() => ({
		cards: Cards.find({}).fetch(),

		saveCard(card) {
			if (card._id) {
				Cards.update(card._id, card);
			} else {
				Cards.insert(card);
			}
		},
	}),
	CardList
);

Meteor.startup(() => {
	render(<App />, document.querySelector('main'));
});
<|endoftext|>


<commit_before>!function($, wysi) {
	"use strict"

	var templates = {
		"font-styles": "
" +
							"" +
								" Normal text " +
							"" +
						    "
" +
						      	"
Normal text
" +
					            "
Heading 1
" +
					            "
Heading 2
" +
						    "
" +
						"
",
		"emphasis":     "
" +
							"
" +
							    "Bold" +
							    "Italic" +
							"
" +
						"
",
		"lists": 		"
" +
							"
" +
						    	"" +
							    "" +
							    "" +
							    "" +
							"
" +
						"
"
	};

	var defaultOptions = {
		"font-styles": true,
		"emphasis": true,
		"lists": true
	};

	var parserRules = {
		tags: {
			b:  {},
			i:  {},
			br: {},
			ol: {},
			ul: {},
			li: {},
			h1: {},
			h2: {},
			a:  {
				set_attributes: {
					target: "_blank",
					rel:    "nofollow"
				},
				check_attributes: {
					href:   "url" // important to avoid XSS
				}
			}
		}
	};

	var Wysihtml5 = function(el, options) {
		this.el = el;
		this.toolbar = this.createToolbar(el, options || defaultOptions);
		this.editor =  new wysi.Editor(this.el.attr('id'), {
    		toolbar: this.toolbar.attr('id'),
		parserRules: parserRules
  		});

  		$('iframe.wysihtml5-sandbox').each(function(i, el){
			$(el.contentWindow).off('focus.wysihtml5').on({
			  'focus.wysihtml5' : function(){
			     $('li.dropdown').removeClass('open');
			   }
			});
		});
	};

	Wysihtml5.prototype = {
		constructor: Wysihtml5,

		createToolbar: function(el, options) {
			var toolbar = $("
", {
					id : el.attr('id') + "-wysihtml5-toolbar",
					class : "wysihtml5-toolbar",
					style: "display:none"
				});

			for(var key in defaultOptions) {
				var value;

				if(options[key] != undefined) {
					if(options[key] == true) {
						value = true;
					}
				} else {
					value = defaultOptions[key];
				}

				if(value == true) {
					toolbar.append(templates[key]);
				}
			}

			var self = this;

			toolbar.find("a[data-wysihtml5-command='formatBlock']").click(function(e) {
				var el = $(e.srcElement);
				self.toolbar.find('.current-font').text(el.html())
			});

			this.el.before(toolbar);

			return toolbar;
		}
	};

	$.fn.wysihtml5 = function (options) {
		return this.each(function () {
			var $this = $(this);
	      	$this.data('wysihtml5', new Wysihtml5($this, options));
	    })
  	};

  	$.fn.wysihtml5.Constructor = Wysihtml5;

}(window.jQuery, window.wysihtml5);<commit_msg>fixes #30 - IE8 bug<commit_after>!function($, wysi) {
	"use strict"

	var templates = {
		"font-styles": "
" +
							"" +
								" Normal text " +
							"" +
						    "
" +
						      	"
Normal text
" +
					            "
Heading 1
" +
					            "
Heading 2
" +
						    "
" +
						"
",
		"emphasis":     "
" +
							"
" +
							    "Bold" +
							    "Italic" +
							"
" +
						"
",
		"lists": 		"
" +
							"
" +
						    	"" +
							    "" +
							    "" +
							    "" +
							"
" +
						"
"
	};

	var defaultOptions = {
		"font-styles": true,
		"emphasis": true,
		"lists": true
	};

	var parserRules = {
		tags: {
			b:  {},
			i:  {},
			br: {},
			ol: {},
			ul: {},
			li: {},
			h1: {},
			h2: {},
			a:  {
				set_attributes: {
					target: "_blank",
					rel:    "nofollow"
				},
				check_attributes: {
					href:   "url" // important to avoid XSS
				}
			}
		}
	};

	var Wysihtml5 = function(el, options) {
		this.el = el;
		this.toolbar = this.createToolbar(el, options || defaultOptions);
		this.editor =  new wysi.Editor(this.el.attr('id'), {
    		toolbar: this.toolbar.attr('id'),
		parserRules: parserRules
  		});

  		$('iframe.wysihtml5-sandbox').each(function(i, el){
			$(el.contentWindow).off('focus.wysihtml5').on({
			  'focus.wysihtml5' : function(){
			     $('li.dropdown').removeClass('open');
			   }
			});
		});
	};

	Wysihtml5.prototype = {
		constructor: Wysihtml5,

		createToolbar: function(el, options) {
			var toolbar = $("
", {
					id : el.attr('id') + "-wysihtml5-toolbar",
					'class' : "wysihtml5-toolbar",
					style: "display:none"
				});

			for(var key in defaultOptions) {
				var value;

				if(options[key] != undefined) {
					if(options[key] == true) {
						value = true;
					}
				} else {
					value = defaultOptions[key];
				}

				if(value == true) {
					toolbar.append(templates[key]);
				}
			}

			var self = this;

			toolbar.find("a[data-wysihtml5-command='formatBlock']").click(function(e) {
				var el = $(e.srcElement);
				self.toolbar.find('.current-font').text(el.html())
			});

			this.el.before(toolbar);

			return toolbar;
		}
	};

	$.fn.wysihtml5 = function (options) {
		return this.each(function () {
			var $this = $(this);
	      	$this.data('wysihtml5', new Wysihtml5($this, options));
	    })
  	};

  	$.fn.wysihtml5.Constructor = Wysihtml5;

}(window.jQuery, window.wysihtml5);
<|endoftext|>


<commit_before>// This is a template for a Node.js scraper on morph.io (https://morph.io)

//var request = require("request");
var sqlite3 = require("sqlite3").verbose();
var http = require('http');
var https = require('https');
var api = require('bbcparse/nitroApi/api.js');
var helper = require('bbcparse/apiHelper.js');
var nitro = require('bbcparse/nitroCommon.js');

var api_key = process.env.MORPH_API_KEY;
var gdb;
var domain = '/nitro/api';
var feed = '/programmes'
var index = 10000;

function iso8601durationToSeconds(input) {
	var reptms = /^PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?$/;
	var hours = 0, minutes = 0, seconds = 0, totalseconds;

	if (reptms.test(input)) {
		var matches = reptms.exec(input);
		if (matches[1]) hours = Number(matches[1]);
		if (matches[2]) minutes = Number(matches[2]);
		if (matches[3]) seconds = Number(matches[3]);
		totalseconds = hours * 3600  + minutes * 60 + seconds;
	}
	return totalseconds;
}

function toArray(item) {
	if (!(item instanceof Array)) {
		var newitem = [];
		if (item) {
			newitem.push(item);
		}
		return newitem;
	}
	else {
		return item;
	}
}

function initDatabase(callback) {
	// Set up sqlite database.
	var db = new sqlite3.Database("data.sqlite");

	var fields =['#index',
		'type',
		'name',
		'pid',
		'available',
		'expires',
		'episode',
		'seriesnum',
		'episodenum',
		'versions',
		'duration',
		'desc',
		'channel',
		'categories',
		'thumbnail',
		'timeadded',
		'guidance',
		'web'];

	var fieldStr = '';
	for (var i in fields) {
		if (fieldStr) fieldStr += ',';
		fieldStr += '"'+fields[i]+'"' + ' TEXT';
	}

	db.serialize(function() {
		db.run('CREATE TABLE IF NOT EXISTS data ('+fieldStr+')');
		callback(db);
	});
}

function updateRow(db, prog) {
	// Insert some data.
	var statement = db.prepare("INSERT INTO data VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
	var value = [];
	for (var i in prog) {
		value.push(prog[i]);
	}
	//console.log(value);
	statement.run(value);
	statement.finalize();
}

function readRows(db) {
	// Read some data.
	db.each("SELECT rowid AS id, pid FROM data LIMIT 10", function(err, row) {
		if (!err) {
			console.log(row.id + ": " + row.pid);
		}
		else {
			console.log(err);
		}
	});
}

function persist(db,res) {
	for (var i in res.items) {
		var p = res.items[i];
		var prog = {};
		prog["#index"] = index++;
		prog.type = p.media_type == 'Audio' ? 'radio' : 'tv'; //TODO podcast
		prog.name = '';
		if (p.ancestor_titles) prog.name = p.ancestor_titles[0].title;
		prog.pid = p.pid;
		prog.available = p.updated_time;
		prog.expires = Math.floor(new Date()/1000.0)+2419200;;
		prog.episode = p.title;
		prog.episodenum = '';
		prog.seriesnum = '';
		prog.versions = 'default'; //TODO
		prog.duration = iso8601durationToSeconds(p.available_versions.version[0].duration);
		var desc = '';
		if (p.synopses) {
			if (p.synopses.short) desc = p.synopses.short;
		}
		prog.desc = desc;
		prog.channel = p.master_brand.mid;
		prog.categories = '';
		prog.thumbnail = 'http://ichef.bbci.co.uk/images/ic/150x84/'+p.pid+'.jpg';
		prog.timeadded = Math.floor(new Date()/1000.0);
		prog.guidance = p.has_guidance;
		prog.web = 'http://www.bbc.co.uk/programmes/'+p.pid;

		updateRow(db, prog);
	}
	return index;
}

var processResponse = function(obj) {
	var nextHref = '';
	if ((obj.nitro.pagination) && (obj.nitro.pagination.next)) {
		nextHref = obj.nitro.pagination.next.href;
		//console.log(nextHref);
	}
	var pageNo = obj.nitro.results.page;
	var top = obj.nitro.results.total;
	if (!top) {
		top = obj.nitro.results.more_than+1;
	}
	var last = Math.ceil(top/obj.nitro.results.page_size);
	process.stdout.write('.');

	var res = obj.nitro.results;
	persist(gdb,res);

	var dest = {};
	if (pageNo < last) {
		dest.path = domain+feed;
		dest.query = helper.queryFrom(nextHref,true);
		dest.callback = processResponse;
	}
	// if we need to go somewhere else, e.g. after all pages received set callback and/or path
	nitro.setReturn(dest);
	return true;
}

function run(db) {
	// Use bbcparse nitro SDK to read in pages.

	db.run('delete from "data"');
	gdb = db; //

	var host = 'programmes.api.bbc.com';
	var path = '/nitro/api/programmes';

	var query = helper.newQuery();
	query.add(api.fProgrammesMediaSet,'pc',true)
		.add(api.fProgrammesAvailabilityAvailable)
		.add(api.fProgrammesAvailabilityEntityTypeEpisode)
		.add(api.fProgrammesMediaTypeAudio)
		.add(api.fProgrammesEntityTypeEpisode)
		.add(api.mProgrammesAncestorTitles)
		.add(api.mProgrammesAvailability)
		.add(api.mProgrammesAvailableVersions);

	nitro.make_request(host,path,api_key,query,{Accept:'application/json'},processResponse);
}

//----------------------------------------------------------------------------

initDatabase(run);

process.on('exit', function(code) {
	readRows(gdb);
	gdb.close();
});<commit_msg>Parallelize queries upto 36x<commit_after>// This is a template for a Node.js scraper on morph.io (https://morph.io)

//var request = require("request");
var sqlite3 = require("sqlite3").verbose();
var http = require('http');
var https = require('https');
var api = require('bbcparse/nitroApi/api.js');
var helper = require('bbcparse/apiHelper.js');
var nitro = require('bbcparse/nitroCommon.js');

var api_key = process.env.MORPH_API_KEY;
var gdb;
var domain = '/nitro/api';
var feed = '/programmes'
var index = 10000;

function iso8601durationToSeconds(input) {
	var reptms = /^PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?$/;
	var hours = 0, minutes = 0, seconds = 0, totalseconds;

	if (reptms.test(input)) {
		var matches = reptms.exec(input);
		if (matches[1]) hours = Number(matches[1]);
		if (matches[2]) minutes = Number(matches[2]);
		if (matches[3]) seconds = Number(matches[3]);
		totalseconds = hours * 3600  + minutes * 60 + seconds;
	}
	return totalseconds;
}

function toArray(item) {
	if (!(item instanceof Array)) {
		var newitem = [];
		if (item) {
			newitem.push(item);
		}
		return newitem;
	}
	else {
		return item;
	}
}

function initDatabase(callback) {
	// Set up sqlite database.
	var db = new sqlite3.Database("data.sqlite");

	var fields =['#index',
		'type',
		'name',
		'pid',
		'available',
		'expires',
		'episode',
		'seriesnum',
		'episodenum',
		'versions',
		'duration',
		'desc',
		'channel',
		'categories',
		'thumbnail',
		'timeadded',
		'guidance',
		'web'];

	var fieldStr = '';
	for (var i in fields) {
		if (fieldStr) fieldStr += ',';
		fieldStr += '"'+fields[i]+'"' + ' TEXT';
	}

	db.serialize(function() {
		db.run('CREATE TABLE IF NOT EXISTS data ('+fieldStr+')');
		callback(db);
	});
}

function updateRow(db, prog) {
	// Insert some data.
	var statement = db.prepare("INSERT INTO data VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
	var value = [];
	for (var i in prog) {
		value.push(prog[i]);
	}
	//console.log(value);
	statement.run(value);
	statement.finalize();
}

function readRows(db) {
	// Read some data.
	db.each("SELECT rowid AS id, pid FROM data LIMIT 10", function(err, row) {
		if (!err) {
			console.log(row.id + ": " + row.pid);
		}
		else {
			console.log(err);
		}
	});
}

function persist(db,res) {
	for (var i in res.items) {
		var p = res.items[i];
		var prog = {};
		prog["#index"] = index++;
		prog.type = p.media_type == 'Audio' ? 'radio' : 'tv'; //TODO podcast
		prog.name = '';
		if (p.ancestor_titles) prog.name = p.ancestor_titles[0].title;
		prog.pid = p.pid;
		prog.available = p.updated_time;
		prog.expires = Math.floor(new Date()/1000.0)+2419200;;
		prog.episode = p.title;
		prog.episodenum = '';
		prog.seriesnum = '';
		prog.versions = 'default'; //TODO
		prog.duration = iso8601durationToSeconds(p.available_versions.version[0].duration);
		var desc = '';
		if (p.synopses) {
			if (p.synopses.short) desc = p.synopses.short;
		}
		prog.desc = desc;
		prog.channel = p.master_brand.mid;
		prog.categories = '';
		prog.thumbnail = 'http://ichef.bbci.co.uk/images/ic/150x84/'+p.pid+'.jpg';
		prog.timeadded = Math.floor(new Date()/1000.0);
		prog.guidance = p.has_guidance;
		prog.web = 'http://www.bbc.co.uk/programmes/'+p.pid;

		updateRow(db, prog);
	}
	return index;
}

var processResponse = function(obj) {
	var nextHref = '';
	if ((obj.nitro.pagination) && (obj.nitro.pagination.next)) {
		nextHref = obj.nitro.pagination.next.href;
		//console.log(nextHref);
	}
	var pageNo = obj.nitro.results.page;
	var top = obj.nitro.results.total;
	if (!top) {
		top = obj.nitro.results.more_than+1;
	}
	var last = Math.ceil(top/obj.nitro.results.page_size);
	process.stdout.write('.');

	var res = obj.nitro.results;
	persist(gdb,res);

	var dest = {};
	if (pageNo < last) {
		dest.path = domain+feed;
		dest.query = helper.queryFrom(nextHref,true);
		dest.callback = processResponse;
	}
	// if we need to go somewhere else, e.g. after all pages received set callback and/or path
	nitro.setReturn(dest);
	return true;
}

function run(db) {
	// Use bbcparse nitro SDK to read in pages.

	db.run('delete from "data"');
	gdb = db; //

	var host = 'programmes.api.bbc.com';
	var path = '/nitro/api/programmes';

	var query = helper.newQuery();
	query.add(api.fProgrammesMediaSet,'pc',true)
		.add(api.fProgrammesAvailabilityAvailable)
		.add(api.fProgrammesAvailabilityEntityTypeEpisode)
		.add(api.fProgrammesMediaTypeAudio)
		.add(api.fProgrammesEntityTypeEpisode)
		.add(api.fProgrammesPaymentTypeFree)
		.add(api.mProgrammesAncestorTitles)
		.add(api.mProgrammesAvailability)
		.add(api.mProgrammesAvailableVersions);

	// parallelize the queries by 36 times
	var letters = '0123456789abcdefghijklmnopqrstuvwxyz';
	for (var l in letters) {
		var lQuery = query.clone();
		lQuery.add(api.fProgrammesInitialLetter,letters[l]);
		nitro.make_request(host,path,api_key,lQuery,{Accept:'application/json'},processResponse);
	}
}

//----------------------------------------------------------------------------

initDatabase(run);

process.on('exit', function(code) {
	readRows(gdb);
	gdb.close();
});<|endoftext|>


<commit_before>'use strict';

const request  = require('request');
const cheerio  = require('cheerio');
const async    = require('async');
const moment   = require('moment');
const argv     = require('yargs').argv;

const Course   = require('./schemas').Course;
const Section  = require('./schemas').Section;

const CATALOG_URL = 'http://catalog.oregonstate.edu/';
const COURSE_SEARCH_URL = CATALOG_URL + 'CourseSearcher.aspx?chr=abg';
const SAVE = !argv.nosave;

if (argv.help) {
  console.log('OSU Course Catalog scraper. Supply a Postgres database to ' +
  'save into with -db or supply a DB_URL environment var. Skip saving with ' +
  '--nosave option.');
  process.exit();
}

getCourseLinks(function scrapeComplete() {
  console.log('Scrape complete!');
  process.exit();
});

/*
 * DATABASE
 */

function insertCourseAndSections(courseObject, sectionObjects) {
  // Insert a Course
  const course = new Course({
    title: courseObject.title,
    abbr: courseObject.abbr,
    credits: courseObject.credits,
    description: courseObject.description,
  });

  // Iterate through the array of sectionObjects and insert each
  const sections = sectionObjects.map((s) => {
    const section = new Section({
      term: s.term,
      session: s.session,
      crn: s.crn,
      section: s.sec,
      credits: s.cr,
      instructor: s.instructor,
      startTime: s.startTime,
      endTime: s.endTime,
      days: s.days,
      startDate: s.startDate,
      endDate: s.endDate,
      location: s.location,
      campus: s.campus,
      type: s.type,
      status: s.status,
      capacity: s.cap,
      currentEnrollment: s.curr,
      waitlistCapacity: s.wlcap,
      waitlistCurrent: s.wlcurr,
      fees: s.fees,
      restrictions: s.restrictions,
      comments: s.comments,
    });

    // Join sections with their course
    section.course = course;
    return section;
  });
  course.sections = sections;
  course.saveAll();
}

/*
 * MAIN
 */

// Scrapes the search page for links to all course pages
function getCourseLinks(callback) {
  request(COURSE_SEARCH_URL, function parseSearchPage(error, res, body) {
    if (!error && res.statusCode === 200) {
      const classURLs = [];
      const $ = cheerio.load(body);

      $('a[id^=\'ctl00_ContentPlaceHolder\']').each(function getLink() {
        const link = $(this).attr('href');
        classURLs.push(link);
      });

      // First two elements are currently trash, don't attempt to scrape
      classURLs.splice(0, 2);
      return scrapeCourses(CATALOG_URL, classURLs, callback);
    }
  });
}

// Loads each course page into cheerio and calls parseCourse on it
function scrapeCourses(baseURL, classURLs, callback) {
  let index = 1;

  async.eachSeries(classURLs, function iterateThroughCourses(url, asyncCallback) {
    const classURL = baseURL + url;
    console.log('Scraping ' + index++ + ' of ' + classURLs.length);
    console.log('URL: ' + classURL);

    request(classURL, function requestCoursePage(error, response, body) {
      if (error) {
        console.error('Error scraping ' + classURL + '\n' + error);
      } else if (response.statusCode !== 200) {
        console.error('Reponse status code == ' + response.statusCode);
      } else {
        const $ = cheerio.load(body);

        const courseObject = parseCourse($);
        const sectionObjects = parseSections($);
        if (SAVE) {
          insertCourseAndSections(courseObject, sectionObjects);
        }
      }

      asyncCallback();
    });
  },

  function doneScraping(err) {
    if (err) {
      console.error('An error occured scraping: ' + err);
    } else {
      callback();
    }
  });
}

/*
 * PARSERS
 */

function parseCourse($) {
  return {
    abbr: parseAbbr($),
    title: parseTitle($),
    credits: parseCredits($),
    description: parseDesc($),
  };
}

// Gets course title from the class site. Regex's follow these steps:
// Select h3, remove abbreviation, remove credits, replace
// non-words/whitespace with whitespace, remove spaces, tabs &
// newlines, turn multiple spaces into one, remove spaces on ends
function parseTitle($) {
  const title = $('h3').text()
  .trim()
  .replace(/(^[A-Z]{1,4}\s[0-9]{2,3})/, '')
  .replace(/\(([^\)]+)\)/i, '')
  .replace(/[^\w\s]/gi, ' ')
  .replace(/\r?\n|\r|\t/g, '')
  .replace(/\ {2,}/g, ' ');
  return title;
}

// Gets course abbreviation from the class site
function parseAbbr($) {
  return $('h3').text().trim().match(/^[A-Z]{1,4}\s[0-9]{2,3}/)[0];
}

// Gets credits from the class site and parses string array to integer array
function parseCredits($) {
  return $('h3')
  .text()
  .match(/\(([^\)]+)\)/i)[1]
  .split('-')
  .map((value) => {
    return parseInt(value, 10);
  });
}

// Gets course description from the class site
function parseDesc($) {
  let desc = $('#aspnetForm')
  .first()
  .clone()
  .children()
  .remove()
  .end()
  .text()
  .trim();
  desc = trimNewlines(desc);
  return desc;
}

/**
 * Parses a table of class sections
 * @param {object} $ - Cheerio object loaded with a course page's HTML
 * @returns {object} Dictionary containing key: value pairs of scraped table
 */

function parseSections($) {
  const columnNames = [];
  $('th').each(function parseSectionTableHeader() {
    const columnName = $(this).text()
    .replace(/\r?\n|\r|\t/g, '')
    .replace(/\ {2,}/g, '');
    columnNames.push(columnName);
  });

  const tableRows = $('#ctl00_ContentPlaceHolder1_SOCListUC1_gvOfferings > tr ');
  const sections = [];

  tableRows.each(function parseSection() {
    const sectionDict = {};
    const td = $(this).children('td');

    td.each(function parseSectionTableRow(i) {
      // Trim is likely not necessary
      const data = $(this).text().replace(/\s{2,}/g, ' ').trim();
      const key = formatKey(columnNames[i % columnNames.length]);

      // Parse date
      if (key === 'daytimedate') {
        const text = $(this).text();
        parseSectionDate(text, sectionDict);
      } else if (key === 'cr') {
        const cr = data.split('-');
        // Check if the first element is blank
        sectionDict.cr = cr[0].match(/\d{1,2}/g) ? cr : null;
      } else {
        sectionDict[key] = data;
      }
    });

    sections.push(sectionDict);
  });

  // First element will be categories, so remove it
  sections.splice(0, 1);
  return sections;
}

// Returns object w/ keys for days, startTime, endTime, startDate & endDate
function parseSectionDate(text, sectionDict) {
  if (text.match(/\d+/g) && text.indexOf('TBA') === -1) {
    sectionDict.days = text
    .match(/([A-Z])+/g)[0];

    sectionDict.startTime = moment(text
    .match(/[0-9]{4}/g)[0], 'HHmm')
    .format('HH:mm:ss');

    sectionDict.endTime = moment(text
    .match(/[0-9]{4}/g)[1], 'HHmm')
    .format('HH:mm:ss');

    sectionDict.startDate = text
    .match(/[0-9]{1,2}\/[0-9]{1,2}\/[0-9]{1,2}/g)[0];

    sectionDict.endDate = text
    .match(/[0-9]{1,2}\/[0-9]{1,2}\/[0-9]{1,2}/g)[1];
  }
}

/*
 * HELPER FUNCTIONS
 */

function trimNewlines(desc) {
  let n = desc.indexOf('\n');
  n = n === -1 ? desc.length : n;
  return desc.substring(0, n);
}

function formatKey(key) {
  return key.toLowerCase().replace(/[^A-z]/g, '');
}
<commit_msg>Saves raw json courses to a local rethinkdb<commit_after>'use strict';

const program   = require('commander');
const request   = require('request');
const r         = require('rethinkdb');
const Q         = require('q');

const CATALOG_API =
'http://catalog.oregonstate.edu/Services/CatalogService.svc/rest';
const COURSE_ENDPOINT =
CATALOG_API + '/courses/all';

let connection;

/*
 * Handle command line arguments
 */
program
.version('2.0.0')
.option('-n --no-save',
  'Don\'t save to a database')
.option('-i --ip [ip]',
  'Host of the RethinkDB server, default "localhost"')
.option('-p --port [port]',
  'Client port of the RethinkDB server, default 28015')
.option('-d --db [db]',
  'The RethinkDB database, default "test"')
.option('-a --auth-key',
  'Authentification key to the RethinkDB server, default ""')
.option('-v --verbose',
  'Print all output to the console')
.parse(process.argv);

function connectToRethink() {
  console.log('Connecting to RethinkDB');

  const deferred = Q.defer();
  r.connect({
    host: 'localhost',
    port: 28015,
    db: 'test',
    authKey: '',
  }, function onConnection(err, conn) {
    if (err) {
      deferred.reject(err);
    } else {
      console.log('Connected!');
      connection = conn;
      deferred.resolve();
    }
  });

  return deferred.promise;
}

function downloadCourseJSON() {
  const deferred = Q.defer();
  console.log('Downloading course catalog from ' + COURSE_ENDPOINT);
  request.get(COURSE_ENDPOINT, function handleResponse(error, res, body) {
    if (res.statusCode !== 200) {
      const err = new Error('Got response code: ' +
        res.statusCode +
        ' from server');
      deferred.reject(err);
    } else if (error) {
      deferred.reject(error);
    } else {
      deferred.resolve(body);
    }
  });

  return deferred.promise;
}

function parseJSON(rawJSON) {
  const deferred = Q.defer();
  console.log('Parsing JSON');
  deferred.resolve(JSON.parse(rawJSON));
  return deferred.promise;
}

function insertCoursesInRethink(rawParsedJSON) {
  console.log('Inserting JSON into RethinkDB');

  return Q.nfcall(
    r.table('rawJSON')
    .insert(rawParsedJSON)
    .run(connection)
  );
}

function main() {
  Q.fcall(connectToRethink)
  .then(downloadCourseJSON)
  .then(parseJSON)
  .then(insertCoursesInRethink)
  .done();
}

main();
<|endoftext|>