<commit_before>var popup = require('../lib/popup'),
    customHash = require('../lib/custom_hash.js'),
    qs = require('qs-hash');
    writable = false;

module.exports = function(context, readonly) {

    writable = !readonly;

    function map(selection) {

        context.map = L.mapbox.map(selection.node(), null, {
                infoControl: false,
                attributionControl: true
            })
            .setView([20, 0], 2)
            .addControl(L.mapbox.geocoderControl('tmcw.map-u4ca5hnt', {
                position: 'topright'
            }));

        context.map.zoomControl.setPosition('topright');

        L.hash(context.map);

        context.mapLayer = L.featureGroup().addTo(context.map);

        if (writable) {
          context.drawControl = new L.Control.Draw({
              position: 'topright',
              edit: { featureGroup: context.mapLayer },
              draw: {
                  circle: false,
                  polyline: { metric: navigator.language !== 'en-US' },
                  polygon: { metric: navigator.language !== 'en-US' },
                  marker: {
                      icon: L.mapbox.marker.icon({})
                  }
              }
          }).addTo(context.map);

          context.map
            .on('draw:edited', update)
            .on('draw:deleted', update);
        }

        context.map
            .on('draw:created', created)
            .on('popupopen', popup(context));

        context.map.attributionControl.addAttribution('Feedback');
        context.map.attributionControl.addAttribution('About');

        function update() {
            geojsonToLayer(context.mapLayer.toGeoJSON(), context.mapLayer);
            context.data.set({map: layerToGeoJSON(context.mapLayer)}, 'map');
        }

        context.dispatch.on('change.map', function() {
            geojsonToLayer(context.data.get('map'), context.mapLayer);
        });

        function created(e) {
            context.mapLayer.addLayer(e.layer);
            update();
        }
    }

    function layerToGeoJSON(layer) {
        var features = [];
        layer.eachLayer(collect);
        function collect(l) { if ('toGeoJSON' in l) features.push(l.toGeoJSON()); }
        return {
            type: 'FeatureCollection',
            features: features
        };
    }

    return map;
};

function geojsonToLayer(geojson, layer) {
    layer.clearLayers();
    L.geoJson(geojson, {
        style: L.mapbox.simplestyle.style,
        pointToLayer: function(feature, latlon) {
            if (!feature.properties) feature.properties = {};
            return L.mapbox.marker.style(feature, latlon);
        }
    }).eachLayer(add);
    function add(l) {
        bindPopup(l);
        l.addTo(layer);
    }
}

function bindPopup(l) {

    var properties = l.toGeoJSON().properties,
        table = '',
        info = '';

    if (!properties) return;

    if (!Object.keys(properties).length) properties = { '': '' };

    for (var key in properties) {
        table += '
<input type="text" value="' + key + '"' + (!writable ? ' readonly' : '') + ' />	' +
            '<input type="text" value="' + properties[key] + '"' + (!writable ? ' readonly' : '') + ' />
';
    }

    if (l.feature && l.feature.geometry) {
        if (l.feature.geometry.type === 'LineString') {
            var total = d3.pairs(l.feature.geometry.coordinates).reduce(function(total, pair) {
                return total + L.latLng(pair[0][1], pair[0][0])
                    .distanceTo(L.latLng(pair[1][1], pair[1][0]));
            }, 0);
            info += '
Length: ' + total.toFixed(2) + ' m
';
        } else if (l.feature.geometry.type === 'Point') {
            info += '
Latitude: ' + l.feature.geometry.coordinates[1].toFixed(2) + '
' +
                '
Longitude: ' + l.feature.geometry.coordinates[0].toFixed(2) + '
';
        } else if (l.feature.geometry.type === 'Polygon') {
            info += '
Area: ' + (LGeo.area(l) / 1000000).toFixed(2) + ' km2'+ '
'
        }
    }

    var content = '
' +
        '
' + info + '
' +
        '

' + table + '

' +
        (writable ? '

' +
            '
<button class="add major">add row</button> ' +
            '<button class="save major">save</button> ' +
            '<button class="major cancel">cancel</button>
' +
            '
<button class="delete-invert"> remove</button>

' : '') +
        '
';

    l.bindPopup(L.popup({
        maxWidth: 500,
        maxHeight: 400
    }, l).setContent(content));
}
<commit_msg>Missing ; thanks jshint<commit_after>var popup = require('../lib/popup'),
    customHash = require('../lib/custom_hash.js'),
    qs = require('qs-hash');
    writable = false;

module.exports = function(context, readonly) {

    writable = !readonly;

    function map(selection) {

        context.map = L.mapbox.map(selection.node(), null, {
                infoControl: false,
                attributionControl: true
            })
            .setView([20, 0], 2)
            .addControl(L.mapbox.geocoderControl('tmcw.map-u4ca5hnt', {
                position: 'topright'
            }));

        context.map.zoomControl.setPosition('topright');

        L.hash(context.map);

        context.mapLayer = L.featureGroup().addTo(context.map);

        if (writable) {
          context.drawControl = new L.Control.Draw({
              position: 'topright',
              edit: { featureGroup: context.mapLayer },
              draw: {
                  circle: false,
                  polyline: { metric: navigator.language !== 'en-US' },
                  polygon: { metric: navigator.language !== 'en-US' },
                  marker: {
                      icon: L.mapbox.marker.icon({})
                  }
              }
          }).addTo(context.map);

          context.map
            .on('draw:edited', update)
            .on('draw:deleted', update);
        }

        context.map
            .on('draw:created', created)
            .on('popupopen', popup(context));

        context.map.attributionControl.addAttribution('Feedback');
        context.map.attributionControl.addAttribution('About');

        function update() {
            geojsonToLayer(context.mapLayer.toGeoJSON(), context.mapLayer);
            context.data.set({map: layerToGeoJSON(context.mapLayer)}, 'map');
        }

        context.dispatch.on('change.map', function() {
            geojsonToLayer(context.data.get('map'), context.mapLayer);
        });

        function created(e) {
            context.mapLayer.addLayer(e.layer);
            update();
        }
    }

    function layerToGeoJSON(layer) {
        var features = [];
        layer.eachLayer(collect);
        function collect(l) { if ('toGeoJSON' in l) features.push(l.toGeoJSON()); }
        return {
            type: 'FeatureCollection',
            features: features
        };
    }

    return map;
};

function geojsonToLayer(geojson, layer) {
    layer.clearLayers();
    L.geoJson(geojson, {
        style: L.mapbox.simplestyle.style,
        pointToLayer: function(feature, latlon) {
            if (!feature.properties) feature.properties = {};
            return L.mapbox.marker.style(feature, latlon);
        }
    }).eachLayer(add);
    function add(l) {
        bindPopup(l);
        l.addTo(layer);
    }
}

function bindPopup(l) {

    var properties = l.toGeoJSON().properties,
        table = '',
        info = '';

    if (!properties) return;

    if (!Object.keys(properties).length) properties = { '': '' };

    for (var key in properties) {
        table += '
<input type="text" value="' + key + '"' + (!writable ? ' readonly' : '') + ' />	' +
            '<input type="text" value="' + properties[key] + '"' + (!writable ? ' readonly' : '') + ' />
';
    }

    if (l.feature && l.feature.geometry) {
        if (l.feature.geometry.type === 'LineString') {
            var total = d3.pairs(l.feature.geometry.coordinates).reduce(function(total, pair) {
                return total + L.latLng(pair[0][1], pair[0][0])
                    .distanceTo(L.latLng(pair[1][1], pair[1][0]));
            }, 0);
            info += '
Length: ' + total.toFixed(2) + ' m
';
        } else if (l.feature.geometry.type === 'Point') {
            info += '
Latitude: ' + l.feature.geometry.coordinates[1].toFixed(2) + '
' +
                '
Longitude: ' + l.feature.geometry.coordinates[0].toFixed(2) + '
';
        } else if (l.feature.geometry.type === 'Polygon') {
            info += '
Area: ' + (LGeo.area(l) / 1000000).toFixed(2) + ' km2'+ '
';
        }
    }

    var content = '
' +
        '
' + info + '
' +
        '

' + table + '

' +
        (writable ? '

' +
            '
<button class="add major">add row</button> ' +
            '<button class="save major">save</button> ' +
            '<button class="major cancel">cancel</button>
' +
            '
<button class="delete-invert"> remove</button>

' : '') +
        '
';

    l.bindPopup(L.popup({
        maxWidth: 500,
        maxHeight: 400
    }, l).setContent(content));
}
<|endoftext|>


<commit_before>const fetchMiddleware = store => next => action => {

  if (!action.url || !Array.isArray(action.types)) {
    return next(action)
  }

  const [LOADING, SUCCESS, ERROR] = action.types

  next({
    type: LOADING,
    loading: true,
    ...action
  })

  fetch(action.url, {params: action.params})
    .then(result => result.json().then(result => {
      next({
        type: SUCCESS,
        loading: false,
        payload: result
      })
    }))
    .catch(err => {
      next({
        type: ERROR,
        loading: false,
        err: err
      })
    })
}

export default fetchMiddleware<commit_msg>修改中间件，使其可以处理多个url<commit_after>const fetchMiddleware = store => next => action => {

  if (!(action.url || action.urls) || !Array.isArray(action.types)) {
    return next(action)
  }

  const [LOADING, SUCCESS, ERROR] = action.types

  next({
    type: LOADING,
    loading: true,
    ...action
  })

  if (action.urls) {
    Promise.all(action.urls.map(e => fetch(e)))
      .then(result => {
        Promise.all(result.map(e=>e.json()))
          .then(result => {
            next({
              type: SUCCESS,
              loading: false,
              payload: result
            })
          })
      }).catch(err => {
        next({
          type: ERROR,
          loading: false,
          err: err
        })
      })

  } else if (action.url) {
    fetch(action.url).then(result => result.json().then(result => {
      next({
        type: SUCCESS,
        loading: false,
        payload: result
      })
    })).catch(err => {
      next({
        type: ERROR,
        loading: false,
        err: err
      })
    })
  }
}

export default fetchMiddleware<|endoftext|>


<commit_before>console.log( "==== simpread options page load ====" )

import '../assets/css/options_page.css';
import '../assets/css/option.css';
import '../vender/notify/notify.css';

import Velocity   from 'velocity';
import Notify     from 'notify';

import Tabs       from 'tabs';
import * as waves from 'waves';
import * as tt    from 'tooltip';
import Button     from 'button';

import { storage, STORAGE_MODE as mode } from 'storage';

import FocusOpt   from 'focusopt';
import ReadOpt    from 'readopt';

let tabsItemID = 0;

const tabsItem = [{
        name: "共通",
        value: "common",
        active : true,
        route: "#common",
    },{
        name: "聚焦模式",
        value: "focus",
        route: "#focus",
    },{
        name: "阅读模式",
        value: "read",
        route: "#read",
    },{
        name: "稍后读",
        value: "later",
        route: "#later",
}],
    headerColors = [
        "#64B5F6",
        "#81C784",
        "#9575CD",
        "#BA68C8"
],
    topColors = [
        "#2196F3",
        "#4CAF50",
        "#673AB7",
        "#9C27B0"
];

/**
 * Add parallax scroll
 */
$( window ).scroll( (event) => {
    const $target = $( event.target ),
          offset  = ( 0 - $target.scrollTop() ) / 2;
    $( ".top" ).css( "transform", `translate3d(0px, ${offset}px, 0px)` )
});

/**
 * Save simpread data
 *
 * @param {string} simpread mode
 */
function save( mode ) {
    storage.Write( ()=> {
        new Notify().Render( 0, "保存成功，页面刷新后生效！" );
    });
}

/**
 * Tabs onChange event handler
 *
 * @param {jquery} prev jquery object
 * @param {event} current event
 */
function tabsOnChange( $prev, event ) {
    let $target = $( event.target );
    while ( !$target.is( "tab-label" ) ) { $target = $target.parent(); }
    $target = $target.find( "a" );
    Render( $target.attr( "id" ) );
}

/**
 * Tabs render
 *
 * @param {string} header background color
 */
function tabsRender( color ) {
    const tabs = <Tabs waves="sr-tabs waves-effect waves-light"
                    headerStyle={{ transition: 'all 1000ms cubic-bezier(0.23, 1, 0.32, 1) 0ms' }}
                    bgColor={ color }
                    items={ tabsItem }
                    onChange={ ( $p, evt )=>tabsOnChange( $p, evt ) }>

aaa

                        <FocusOpt option={ storage.focus } />
                        <Button type="raised" backgroundColor={ topColors[1] } width="100%" waves="sr-button waves-effect waves-button" text="保 存" onClick={ ()=>save( mode.focus ) } />

                        <ReadOpt option={ storage.read } />
                        <Button type="raised" backgroundColor={ topColors[2] } width="100%" waves="sr-button waves-effect waves-button" text="保 存" onClick={ ()=>save( mode.read ) } />

ddd

                </Tabs>;
    ReactDOM.render( tabs, $( ".tabscontainer" )[0] );
}

/**
 * Set banner and tabs.Render()
 *
 * @param {number} headerColors index
 */
function Render( idx ) {
    $( ".banner" ).css( "background-image", `url(../assets/images/banner-${idx}.png)` );
    $( ".top" ).css( "background-color", topColors[idx] );
    tabsRender( headerColors[ idx ] );
}

/**
 * Get tabsItemID from window.location.hash exist
 */
window.location.hash && ( tabsItemID = tabsItem.findIndex( item => item.route == window.location.hash ) );
tabsItemID == -1 || tabsItemID == 0 ? tabsItemID = 0 : tabsItem.forEach( ( item, index ) => item.active = tabsItemID == index ? true : false );

/**
 * Entry:
 * - get data from chrome storage
 * - waves.Render()
 * - tooltip.Render()
 */
storage.Get( function() {
    console.log( "simpread storage get success!", storage.focus, storage.read );
    Render( tabsItemID );
    tt.Render( "body" );
    waves.Render({ root: "main", name: "sr-tabs" });
});
<commit_msg>Moidfy options page UI.<commit_after>console.log( "==== simpread options page load ====" )

import '../assets/css/options_page.css';
import '../assets/css/option.css';
import '../vender/notify/notify.css';

import Velocity   from 'velocity';
import Notify     from 'notify';

import Tabs       from 'tabs';
import * as waves from 'waves';
import * as tt    from 'tooltip';
import Button     from 'button';

import { storage, STORAGE_MODE as mode } from 'storage';

import FocusOpt   from 'focusopt';
import ReadOpt    from 'readopt';

let tabsItemID = 0;

const tabsItem = [{
        name: "共通",
        value: "common",
        active : true,
        route: "#common",
    },{
        name: "聚焦模式",
        value: "focus",
        route: "#focus",
    },{
        name: "阅读模式",
        value: "read",
        route: "#read",
    },{
        name: "稍后读",
        value: "later",
        route: "#later",
}],
    headerColors = [
        "#64B5F6",
        "#81C784",
        "#9575CD",
        "#BA68C8"
],
    topColors = [
        "#2196F3",
        "#4CAF50",
        "#673AB7",
        "#9C27B0"
];

/**
 * Add parallax scroll
 */
$( window ).scroll( (event) => {
    const $target = $( event.target ),
          offset  = ( 0 - $target.scrollTop() ) / 2;
    $( ".top" ).css( "transform", `translate3d(0px, ${offset}px, 0px)` )
});

/**
 * Save simpread data
 *
 * @param {string} simpread mode
 */
function save( mode ) {
    storage.Write( ()=> {
        new Notify().Render( 0, "保存成功，页面刷新后生效！" );
    });
}

/**
 * Tabs onChange event handler
 *
 * @param {jquery} prev jquery object
 * @param {event} current event
 */
function tabsOnChange( $prev, event ) {
    let $target = $( event.target );
    while ( !$target.is( "tab-label" ) ) { $target = $target.parent(); }
    $target = $target.find( "a" );
    Render( $target.attr( "id" ) );
}

/**
 * Tabs render
 *
 * @param {string} header background color
 */
function tabsRender( color ) {
    const tabs = <Tabs waves="sr-tabs waves-effect waves-light"
                    headerStyle={{ transition: 'all 1000ms cubic-bezier(0.23, 1, 0.32, 1) 0ms' }}
                    bgColor={ color }
                    items={ tabsItem }
                    onChange={ ( $p, evt )=>tabsOnChange( $p, evt ) }>

aaa

                        <FocusOpt option={ storage.focus } />
                        <Button type="raised" backgroundColor={ topColors[1] } width="100%" waves="sr-button waves-effect waves-button" text="保 存" onClick={ ()=>save( mode.focus ) } />

                        <ReadOpt option={ storage.read } />
                        <Button type="raised" backgroundColor={ topColors[2] } width="100%" waves="sr-button waves-effect waves-button" text="保 存" onClick={ ()=>save( mode.read ) } />

ddd

                </Tabs>;
    ReactDOM.render( tabs, $( ".tabscontainer" )[0] );
}

/**
 * Set banner and tabs.Render()
 *
 * @param {number} headerColors index
 */
function Render( idx ) {
    //$( ".banner" ).css( "background-image", `url(../assets/images/banner-${idx}.png)` );
    $( ".top" ).css( "background-color", topColors[idx] );
    tabsRender( headerColors[ idx ] );
}

/**
 * Get tabsItemID from window.location.hash exist
 */
window.location.hash && ( tabsItemID = tabsItem.findIndex( item => item.route == window.location.hash ) );
tabsItemID == -1 || tabsItemID == 0 ? tabsItemID = 0 : tabsItem.forEach( ( item, index ) => item.active = tabsItemID == index ? true : false );

/**
 * Entry:
 * - get data from chrome storage
 * - waves.Render()
 * - tooltip.Render()
 */
storage.Get( function() {
    console.log( "simpread storage get success!", storage.focus, storage.read );
    Render( tabsItemID );
    tt.Render( "body" );
    waves.Render({ root: "main", name: "sr-tabs" });
});
<|endoftext|>


<commit_before>#!/usr/bin/env node
//
// Alacon - Command line interface for Alasql
// Version: 0.2.1
// Date: 27.07.2015
// (c) 2014-2015, Andrey Gershun & M. Rangel Wulff
//

var alasql = require('alasql');
var path = require('path');
var fs = require('fs');

/**
 * Is a Directory
 *
 * @param {String} filePath
 * @returns {Boolean}
 */

function isDirectory(filePath) {
  var isDir = false;
  try {
    var absolutePath = path.resolve(filePath);
    isDir = fs.lstatSync(absolutePath).isDirectory();
  } catch (e) {
    isDir = e.code === 'ENOENT';
  }
  return isDir;
}

if(process.argv.length <= 2) {
	console.log('AlaSQL command-line utility (version '+alasql.version+') ');
	console.log();
	console.log('Usage:');
	console.log('  alasql "sql-statement" [ params ]        - Run SQL statement');
	console.log('  alasql --file file.sql [ params ]        - (or -f) Run SQL from file');
	console.log('  alasql --version                         - (or -v) Echo AlaSQL version');
	console.log();
	console.log('Samples:');
	console.log('  alasql \'select 2+2\'');
	console.log('  alasql \'select count(*) from txt()\' < city.txt');
	console.log('  alasql \'select * into xlsx("city.xlsx") from txt("city.txt")\'');
	console.log();
	process.exit(0);
}

var sql = process.argv[2];

var parami = 3;

if(sql === '-v' || sql === '--version' ) {
	console.log(alasql.version); // Issue #373
	process.exit(0);
}

if(sql === '-f' || sql === '--file' ) {
	if(process.argv.length<=3){
		console.log('Error: filename missing');
		process.exit(1);
	}

	var filePath = path.resolve(process.argv[3]);

	if (!fs.existsSync(filePath)) {
		console.log('Error: file not found');
		process.exit(1);
	}

	if (isDirectory(filePath)) {
		console.log('Error: file expected but directory found');
		process.exit(1);
	}

	sql = fs.readFileSync('/Users/mrw/git/slet/file.sql', 'utf8').toString();
	parami++;
}

var params = [];
for(var i=parami;i<process.argv.length;i++) {
	var a = process.argv[i];
	if(a[0] !== '"' && a[0] !== "'") {
		if(+a == a){					// jshint ignore:line
			a = +a;
		}
	}
	params.push(a);
}

 alasql
 	.promise(sql,params)
    .then(function(res){
      	if(!alasql.options.stdout){
			console.log(res);
		}
		process.exit(0);
    }).catch(function(err){
        console.log(err);
        process.exit(1);
    });

<commit_msg>Removed hardcoded value<commit_after>#!/usr/bin/env node
//
// Alacon - Command line interface for Alasql
// Version: 0.2.1
// Date: 27.07.2015
// (c) 2014-2015, Andrey Gershun & M. Rangel Wulff
//

var alasql = require('alasql');
var path = require('path');
var fs = require('fs');

/**
 * Is a Directory
 *
 * @param {String} filePath
 * @returns {Boolean}
 */

function isDirectory(filePath){
      	var isDir = false;
      	try {
      	      	var absolutePath = path.resolve(filePath);
      	      	isDir = fs.lstatSync(absolutePath).isDirectory();
      	} catch (e) {
      	      	isDir = e.code === 'ENOENT';
      	}
      	return isDir;
}

if(process.argv.length <= 2) {
	console.log('AlaSQL command-line utility (version '+alasql.version+')');
	console.log();
	console.log('Usage:');
	console.log('  alasql "sql-statement" [ params ]        - Run SQL statement');
	console.log('  alasql --file file.sql [ params ]        - (or -f) Run SQL from file');
	console.log('  alasql --version                         - (or -v) Echo AlaSQL version');
	console.log();
	console.log('Samples:');
	console.log('  alasql \'select 2+2\'');
	console.log('  alasql \'select count(*) from txt()\' < city.txt');
	console.log('  alasql \'select * into xlsx("city.xlsx") from txt("city.txt")\'');
	console.log();
	process.exit(0);
}

var sql = process.argv[2];

var parami = 3;

if(sql === '-v' || sql === '--version' ) {
	console.log(alasql.version); // Issue #373
	process.exit(0);
}

if(sql === '-f' || sql === '--file' ) {
	if(process.argv.length<=3){
		console.log('Error: filename missing');
		process.exit(1);
	}

	var filePath = path.resolve(process.argv[3]);

	if (!fs.existsSync(filePath)) {
		console.log('Error: file not found');
		process.exit(1);
	}

	if (isDirectory(filePath)) {
		console.log('Error: file expected but directory found');
		process.exit(1);
	}

	sql = fs.readFileSync(filePath, 'utf8').toString();
	parami++;
}

var params = [];
for(var i=parami;i<process.argv.length;i++) {
	var a = process.argv[i];
	if(a[0] !== '"' && a[0] !== "'") {
		if(+a == a){					// jshint ignore:line
			a = +a;
		}
	}
	params.push(a);
}

 alasql.promise(sql,params)
      	.then(function(res){
      	      	if(!alasql.options.stdout){
			console.log(res);
		}
		process.exit(0);
      	}).catch(function(err){
      	      	console.log(err);
      	      	process.exit(1);
      	});

<|endoftext|>


<commit_before>var _; //globals

describe("About Applying What We Have Learnt", function() {

  var products;

  beforeEach(function () {
    products = [
       { name: "Sonoma", ingredients: ["artichoke", "sundried tomatoes", "mushrooms"], containsNuts: false },
       { name: "Pizza Primavera", ingredients: ["roma", "sundried tomatoes", "goats cheese", "rosemary"], containsNuts: false },
       { name: "South Of The Border", ingredients: ["black beans", "jalapenos", "mushrooms"], containsNuts: false },
       { name: "Blue Moon", ingredients: ["blue cheese", "garlic", "walnuts"], containsNuts: true },
       { name: "Taste Of Athens", ingredients: ["spinach", "kalamata olives", "sesame seeds"], containsNuts: true }
    ];
  });

  /*********************************************************************************/

  it("given I'm allergic to nuts and hate mushrooms, it should find a pizza I can eat (imperative)", function () {

    var i,j,hasMushrooms, productsICanEat = [];

    for (i = 0; i < products.length; i+=1) {
        if (products[i].containsNuts === false) {
            hasMushrooms = false;
            for (j = 0; j < products[i].ingredients.length; j+=1) {
               if (products[i].ingredients[j] === "mushrooms") {
                  hasMushrooms = true;
               }
            }
            if (!hasMushrooms) productsICanEat.push(products[i]);
        }
    }

    expect(productsICanEat.length).toBe(FILL_ME_IN);
  });

  it("given I'm allergic to nuts and hate mushrooms, it should find a pizza I can eat (functional)", function () {

      var productsICanEat = [];

      /* solve using filter() & all() / any() */

      expect(productsICanEat.length).toBe(FILL_ME_IN);
  });

  /*********************************************************************************/

  it("should add all the natural numbers below 1000 that are multiples of 3 or 5 (imperative)", function () {

    var sum = 0;
    for(var i=1; i<1000; i+=1) {
      if (i % 3 === 0 || i % 5 === 0) {
        sum += i;
      }
    }

    expect(sum).toBe(FILL_ME_IN);
  });

  it("should add all the natural numbers below 1000 that are multiples of 3 or 5 (functional)", function () {

    var sum = FILL_ME_IN;    /* try chaining range() and reduce() */

    expect(233168).toBe(FILL_ME_IN);
  });

  /*********************************************************************************/
   it("should count the ingredient occurrence (imperative)", function () {
    var ingredientCount = { "{ingredient name}": 0 };

    for (i = 0; i < products.length; i+=1) {
        for (j = 0; j < products[i].ingredients.length; j+=1) {
            ingredientCount[products[i].ingredients[j]] = (ingredientCount[products[i].ingredients[j]] || 0) + 1;
        }
    }

    expect(ingredientCount['mushrooms']).toBe(FILL_ME_IN);
  });

  it("should count the ingredient occurrence (functional)", function () {
    var ingredientCount = { "{ingredient name}": 0 };

    /* chain() together map(), flatten() and reduce() */

    expect(ingredientCount['mushrooms']).toBe(FILL_ME_IN);
  });

  /*********************************************************************************/
  /* UNCOMMENT FOR EXTRA CREDIT */
  /*
  it("should find the largest prime factor of a composite number", function () {

  });

  it("should find the largest palindrome made from the product of two 3 digit numbers", function () {

  });

  it("should find the smallest number divisible by each of the numbers 1 to 20", function () {

  });

  it("should find the difference between the sum of the squares and the square of the sums", function () {

  });

  it("should find the 10001st prime", function () {

  });
  */
});
<commit_msg>Complete koans 48-50 in 9.AboutApplyingWhatWeHaveLearned.js<commit_after>var _; //globals

describe("About Applying What We Have Learnt", function() {

  var products;

  beforeEach(function () {
    products = [
       { name: "Sonoma", ingredients: ["artichoke", "sundried tomatoes", "mushrooms"], containsNuts: false },
       { name: "Pizza Primavera", ingredients: ["roma", "sundried tomatoes", "goats cheese", "rosemary"], containsNuts: false },
       { name: "South Of The Border", ingredients: ["black beans", "jalapenos", "mushrooms"], containsNuts: false },
       { name: "Blue Moon", ingredients: ["blue cheese", "garlic", "walnuts"], containsNuts: true },
       { name: "Taste Of Athens", ingredients: ["spinach", "kalamata olives", "sesame seeds"], containsNuts: true }
    ];
  });

  /*********************************************************************************/

  it("given I'm allergic to nuts and hate mushrooms, it should find a pizza I can eat (imperative)", function () {

    var i,j,hasMushrooms, productsICanEat = [];

    for (i = 0; i < products.length; i+=1) {
        if (products[i].containsNuts === false) {
            hasMushrooms = false;
            for (j = 0; j < products[i].ingredients.length; j+=1) {
               if (products[i].ingredients[j] === "mushrooms") {
                  hasMushrooms = true;
               }
            }
            if (!hasMushrooms) productsICanEat.push(products[i]);
        }
    }

    expect(productsICanEat.length).toBe(1);
  });

  it("given I'm allergic to nuts and hate mushrooms, it should find a pizza I can eat (functional)", function () {

      var productsICanEat = [];

      /* solve using filter() & all() / any() */
      productsICanEat = _.filter(products, function(product){
             return (!product.containsNuts &&
                      _.all(product.ingredients, function(ingredient){
                        return !(ingredient === "mushrooms")
                      })
                    )
        });

      expect(productsICanEat.length).toBe(1);
  });

  /*********************************************************************************/

  it("should add all the natural numbers below 1000 that are multiples of 3 or 5 (imperative)", function () {

    var sum = 0;
    for(var i=1; i<1000; i+=1) {
      if (i % 3 === 0 || i % 5 === 0) {
        sum += i;
      }
    }

    expect(sum).toBe(233168);
  });

  it("should add all the natural numbers below 1000 that are multiples of 3 or 5 (functional)", function () {

    var sum = FILL_ME_IN;    /* try chaining range() and reduce() */

    expect(233168).toBe(FILL_ME_IN);
  });

  /*********************************************************************************/
   it("should count the ingredient occurrence (imperative)", function () {
    var ingredientCount = { "{ingredient name}": 0 };

    for (i = 0; i < products.length; i+=1) {
        for (j = 0; j < products[i].ingredients.length; j+=1) {
            ingredientCount[products[i].ingredients[j]] = (ingredientCount[products[i].ingredients[j]] || 0) + 1;
        }
    }

    expect(ingredientCount['mushrooms']).toBe(FILL_ME_IN);
  });

  it("should count the ingredient occurrence (functional)", function () {
    var ingredientCount = { "{ingredient name}": 0 };

    /* chain() together map(), flatten() and reduce() */

    expect(ingredientCount['mushrooms']).toBe(FILL_ME_IN);
  });

  /*********************************************************************************/
  /* UNCOMMENT FOR EXTRA CREDIT */
  /*
  it("should find the largest prime factor of a composite number", function () {

  });

  it("should find the largest palindrome made from the product of two 3 digit numbers", function () {

  });

  it("should find the smallest number divisible by each of the numbers 1 to 20", function () {

  });

  it("should find the difference between the sum of the squares and the square of the sums", function () {

  });

  it("should find the 10001st prime", function () {

  });
  */
});
<|endoftext|>


<commit_before><commit_msg>StreetName: add test<commit_after>/* eslint-env jest */
import React from 'react'
import { shallow } from 'enzyme'
import { mountWithIntl } from '../../../../test/helpers/intl-enzyme-test-helper.js'
import StreetName from '../StreetName'

describe('StreetName', () => {
  it('renders without crashing', () => {
    const wrapper = shallow(<StreetName />)
    expect(wrapper.exists()).toEqual(true)
  })

  it('renders a name', () => {
    const wrapper = shallow(<StreetName name="foo" />)
    expect(wrapper.text()).toEqual('foo')
  })

  it('truncates very long names', () => {
    const wrapper = shallow(<StreetName name="foobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobar" />)
    // We don't care what the actual length of string is, just that it's been truncated
    expect(wrapper.text().endsWith('…')).toEqual(true)
  })

  it('renders a placeholder if there is no name', () => {
    // Requires the react-intl test helper to retrieve the correct content of <FormattedMessage />
    const wrapper = mountWithIntl(<StreetName />)
    expect(wrapper.text()).toEqual('Unnamed St')
  })

  it('shows a "Click to edit" message when mouse is hovering over it')
  it('does not do that if the street name is not editable')
  it('responds to an onClick handler')
})
<|endoftext|>


<commit_before>'use strict';

var path = require('path'),
    browserSync = require('browser-sync'),
    cluster = require('cluster'),
    extend = require('extend');

if (cluster.isWorker) {
    console.error('Workers are not supported yet, check the following brunch issue: https://github.com/brunch/brunch/issues/879');
}

function BrowserSync(config) {
    var cfg,
        self = this,
        bsConfig = {
            logSnippet: false
        };

    if (config && config.plugins && config.plugins.browserSync) {
        cfg = config.plugins.browserSync;
    } else {
        cfg = {};
    }
    if (config && config.persistent) {
        self.enabled = cfg.enabled ? cfg.enabled : true;
    }

    if (self.enabled && !browserSync.active) {
        extend(bsConfig, cfg);
        browserSync(bsConfig, function(err, bs) {
            if (err) {
                console.error("browserSync " + err);
            } else {
                self.server = bs;
                if (self.onServerSnippet) { // if compile before server launched
                    self.onServerSnippet();
                }
            }
        });
    }
}

BrowserSync.prototype.brunchPlugin = true;
BrowserSync.prototype.type = 'javascript';
BrowserSync.prototype.extension = 'js';

// after each compilation
BrowserSync.prototype.onCompile = function onBrowserSyncCompile(changedFiles) {
    if (!this.enabled) {
        return;
    }

    var files = changedFiles.map(function(obj) {
        return obj.path;
    });

    // only reload for files enabled
    if ('[object Object]' === Object.prototype.toString.call(this.enabled)) {
        if (!(changedFiles.length > 0 || this.enabled.assets)) {
            return;
        }

        var changedExts = files.map(function(f) {
            return path.extname(f).slice(1);
        });
        var wasChanged = Object.keys(this.enabled).some(function(k) {
            return this.enabled[k] && changedExts.indexOf(k) !== -1;
        });
        if (!wasChanged) {
            return;
        }
    }

    // give files array when available
    browserSync.reload(files.length > 0 ? files : null);
};

// file to include
var incPath = path.join(__dirname, 'vendor', 'browser-sync-injector.js');
BrowserSync.prototype.include = function browserSyncInclude() {
    return this.enabled ? [incPath] : [];
};

// stop the server on brunch stop
BrowserSync.prototype.teardown = function browserSyncTeardown() {
    if (browserSync.active) {
        browserSync.exit();
    }
};

BrowserSync.prototype.compile = function browserSyncCompile(params, callback) {
    if (!this.enabled || 'browser-sync-injector.js' !== path.basename(params.path)) {
        return callback(null, params);
    }

    var self = this;
    this.onServerSnippet = function browserSyncOnServerSnippet() {
        var injectedJS = [
            '(function(/* BrowserSync-Brunch */) {',
            '  var url = "//" + location.hostname + ":PORT' +
                '/browser-sync/browser-sync-client.VERSION.js";',
            '  var bs = document.createElement("script");',
            '  bs.type = "text/javascript"; bs.async = true; bs.src = url;',
            '  var s = document.getElementsByTagName("script")[0];',
            '  s.parentNode.insertBefore(bs, s);',
            '})();'
        ]
        .join("\n")
        .replace(/PORT/g, self.server.options.getIn(['port']))
        .replace(/VERSION/g, self.server.options.getIn(['version']));

        params.data = injectedJS;
        callback(null, params);
    };

    if (this.server) { // if server launched before compile
        this.onServerSnippet();
    }
};

module.exports = BrowserSync;
<commit_msg>Make the config `enabled: false` to work properly<commit_after>'use strict';

var path = require('path'),
    browserSync = require('browser-sync'),
    cluster = require('cluster'),
    extend = require('extend');

if (cluster.isWorker) {
    console.error('Workers are not supported yet, check the following brunch issue: https://github.com/brunch/brunch/issues/879');
}

function BrowserSync(config) {
    var cfg,
        self = this,
        bsConfig = {
            logSnippet: false
        };

    if (config && config.plugins && config.plugins.browserSync) {
        cfg = config.plugins.browserSync;
    } else {
        cfg = {};
    }
    if (config && config.persistent) {
        self.enabled = (cfg.enabled == null) ? true : cfg.enabled;
    }

    if (self.enabled && !browserSync.active) {
        extend(bsConfig, cfg);
        browserSync(bsConfig, function(err, bs) {
            if (err) {
                console.error("browserSync " + err);
            } else {
                self.server = bs;
                if (self.onServerSnippet) { // if compile before server launched
                    self.onServerSnippet();
                }
            }
        });
    }
}

BrowserSync.prototype.brunchPlugin = true;
BrowserSync.prototype.type = 'javascript';
BrowserSync.prototype.extension = 'js';

// after each compilation
BrowserSync.prototype.onCompile = function onBrowserSyncCompile(changedFiles) {
    if (!this.enabled) {
        return;
    }

    var files = changedFiles.map(function(obj) {
        return obj.path;
    });

    // only reload for files enabled
    if ('[object Object]' === Object.prototype.toString.call(this.enabled)) {
        if (!(changedFiles.length > 0 || this.enabled.assets)) {
            return;
        }

        var changedExts = files.map(function(f) {
            return path.extname(f).slice(1);
        });
        var wasChanged = Object.keys(this.enabled).some(function(k) {
            return this.enabled[k] && changedExts.indexOf(k) !== -1;
        });
        if (!wasChanged) {
            return;
        }
    }

    // give files array when available
    browserSync.reload(files.length > 0 ? files : null);
};

// file to include
var incPath = path.join(__dirname, 'vendor', 'browser-sync-injector.js');
BrowserSync.prototype.include = function browserSyncInclude() {
    return this.enabled ? [incPath] : [];
};

// stop the server on brunch stop
BrowserSync.prototype.teardown = function browserSyncTeardown() {
    if (browserSync.active) {
        browserSync.exit();
    }
};

BrowserSync.prototype.compile = function browserSyncCompile(params, callback) {
    if (!this.enabled || 'browser-sync-injector.js' !== path.basename(params.path)) {
        return callback(null, params);
    }

    var self = this;
    this.onServerSnippet = function browserSyncOnServerSnippet() {
        var injectedJS = [
            '(function(/* BrowserSync-Brunch */) {',
            '  var url = "//" + location.hostname + ":PORT' +
                '/browser-sync/browser-sync-client.VERSION.js";',
            '  var bs = document.createElement("script");',
            '  bs.type = "text/javascript"; bs.async = true; bs.src = url;',
            '  var s = document.getElementsByTagName("script")[0];',
            '  s.parentNode.insertBefore(bs, s);',
            '})();'
        ]
        .join("\n")
        .replace(/PORT/g, self.server.options.getIn(['port']))
        .replace(/VERSION/g, self.server.options.getIn(['version']));

        params.data = injectedJS;
        callback(null, params);
    };

    if (this.server) { // if server launched before compile
        this.onServerSnippet();
    }
};

module.exports = BrowserSync;
<|endoftext|>


<commit_before>
var get     = require('./get')
var resolve = require('url').resolve
var join    = require('path').join
var funders = require('./funders')

function ghApi() {
  return resolve('https://api.github.com', join.apply(null, arguments))
}

function bcApi() {
  return resolve('https://blockchain.info', join.apply(null, arguments) + '?format=json')
}

exports.funders = function (user, repo, issue, wallet, cb) {
  var project, wallet
  get.all([
    ghApi('repos', user, repo, 'issues', issue, 'comments'),
    bcApi('address', wallet),
  ], function (err, args) {
    if(err) return cb(err)
    cb(null, funders.apply(null, args))
  })

}

if(!module.parent) {
  exports.funders('dominictarr', 'feedopensource', '4', '1PTAwipYpP63uNrcxfm5FewxRdZyar6ceu',
  function (err, data) {
    if(err) throw err
    console.log(JSON.stringify(data, null, 2))
    process.exit()
  })
}
<commit_msg>add api for getting iterations<commit_after>
var get     = require('./get')
var resolve = require('url').resolve
var join    = require('path').join
var funders = require('./funders')
var iterations = require('./iterations')

function ghApi() {
  return resolve('https://api.github.com', join.apply(null, arguments))
}

function bcApi() {
  return resolve('https://blockchain.info', join.apply(null, arguments) + '?format=json')
}

exports.funders = function (user, repo, issue, wallet, cb) {
  var project, wallet
  get.all([
    ghApi('repos', user, repo, 'issues', issue, 'comments'),
    bcApi('address', wallet),
  ], function (err, args) {
    if(err) return cb(err)
    cb(null, funders.apply(null, args))
  })
}

exports.iterations = function (user, repo, cb) {
  get.all([
    ghApi('repos', user, repo, 'issues'),
    ghApi('repos', user, repo, 'collaborators'),
  ], function (err, data) {
    if(err) return cb(err)
    cb(null, iterations(data[0], data[1], user, repo))
  })
}

if(!module.parent) {
  if(process.argv[2] === 'funders')
    exports.funders('dominictarr', 'feedopensource', '4', '1PTAwipYpP63uNrcxfm5FewxRdZyar6ceu',
    function (err, data) {
      if(err) throw err
      console.log(JSON.stringify(data, null, 2))
      process.exit()
    })
  else
  exports.iterations('dominictarr', 'feedopensource', function (err, data) {
    if(err) throw err
    console.log(JSON.stringify(data, null, 2))
    process.exit()
  })
}
<|endoftext|>


<commit_before>var _; //globals

describe("About Applying What We Have Learnt", function() {

  var products;

  beforeEach(function () {
    products = [
       { name: "Sonoma", ingredients: ["artichoke", "sundried tomatoes", "mushrooms"], containsNuts: false },
       { name: "Pizza Primavera", ingredients: ["roma", "sundried tomatoes", "goats cheese", "rosemary"], containsNuts: false },
       { name: "South Of The Border", ingredients: ["black beans", "jalapenos", "mushrooms"], containsNuts: false },
       { name: "Blue Moon", ingredients: ["blue cheese", "garlic", "walnuts"], containsNuts: true },
       { name: "Taste Of Athens", ingredients: ["spinach", "kalamata olives", "sesame seeds"], containsNuts: true }
    ];
  });

  /*********************************************************************************/

  it("given I'm allergic to nuts and hate mushrooms, it should find a pizza I can eat (imperative)", function () {

    var i,j,hasMushrooms, productsICanEat = [];

    for (i = 0; i < products.length; i+=1) {
        if (products[i].containsNuts === false) {
            hasMushrooms = false;
            for (j = 0; j < products[i].ingredients.length; j+=1) {
               if (products[i].ingredients[j] === "mushrooms") {
                  hasMushrooms = true;
               }
            }
            if (!hasMushrooms) productsICanEat.push(products[i]);
        }
    }

    expect(productsICanEat.length).toBe(FILL_ME_IN);
  });

  it("given I'm allergic to nuts and hate mushrooms, it should find a pizza I can eat (functional)", function () {

      var productsICanEat = [];

      /* solve using filter() & all() / any() */

      expect(productsICanEat.length).toBe(FILL_ME_IN);
  });

  /*********************************************************************************/

  it("should add all the natural numbers below 1000 that are multiples of 3 or 5 (imperative)", function () {

    var sum = 0;
    for(var i=1; i<1000; i+=1) {
      if (i % 3 === 0 || i % 5 === 0) {
        sum += i;
      }
    }

    expect(sum).toBe(FILL_ME_IN);
  });

  it("should add all the natural numbers below 1000 that are multiples of 3 or 5 (functional)", function () {

    var sum = FILL_ME_IN;    /* try chaining range() and reduce() */

    expect(233168).toBe(FILL_ME_IN);
  });

  /*********************************************************************************/
   it("should count the ingredient occurrence (imperative)", function () {
    var ingredientCount = { "{ingredient name}": 0 };

    for (i = 0; i < products.length; i+=1) {
        for (j = 0; j < products[i].ingredients.length; j+=1) {
            ingredientCount[products[i].ingredients[j]] = (ingredientCount[products[i].ingredients[j]] || 0) + 1;
        }
    }

    expect(ingredientCount['mushrooms']).toBe(FILL_ME_IN);
  });

  it("should count the ingredient occurrence (functional)", function () {
    var ingredientCount = { "{ingredient name}": 0 };

    /* chain() together map(), flatten() and reduce() */

    expect(ingredientCount['mushrooms']).toBe(FILL_ME_IN);
  });

  /*********************************************************************************/
  /* UNCOMMENT FOR EXTRA CREDIT */
  /*
  it("should find the largest prime factor of a composite number", function () {

  });

  it("should find the largest palindrome made from the product of two 3 digit numbers", function () {

  });

  it("should find the smallest number divisible by each of the numbers 1 to 20", function () {

  });

  it("should find the difference between the sum of the squares and the square of the sums", function () {

  });

  it("should find the 10001st prime", function () {

  });
  */
});
<commit_msg>Added AboutApplyingWhatWeHaveLearnt<commit_after>var _; //globals

describe("About Applying What We Have Learnt", function() {

  var products;

  beforeEach(function () {
    products = [
       { name: "Sonoma", ingredients: ["artichoke", "sundried tomatoes", "mushrooms"], containsNuts: false },
       { name: "Pizza Primavera", ingredients: ["roma", "sundried tomatoes", "goats cheese", "rosemary"], containsNuts: false },
       { name: "South Of The Border", ingredients: ["black beans", "jalapenos", "mushrooms"], containsNuts: false },
       { name: "Blue Moon", ingredients: ["blue cheese", "garlic", "walnuts"], containsNuts: true },
       { name: "Taste Of Athens", ingredients: ["spinach", "kalamata olives", "sesame seeds"], containsNuts: true }
    ];
  });

  /*********************************************************************************/

  it("given I'm allergic to nuts and hate mushrooms, it should find a pizza I can eat (imperative)", function () {

    var i,j,hasMushrooms, productsICanEat = [];

    for (i = 0; i < products.length; i+=1) {
        if (products[i].containsNuts === false) {
            hasMushrooms = false;
            for (j = 0; j < products[i].ingredients.length; j+=1) {
               if (products[i].ingredients[j] === "mushrooms") {
                  hasMushrooms = true;
               }
            }
            if (!hasMushrooms) productsICanEat.push(products[i]);
        }
    }

    expect(productsICanEat.length).toBe(1);
  });

  it("given I'm allergic to nuts and hate mushrooms, it should find a pizza I can eat (functional)", function () {

      var productsICanEat = [];

      /* solve using filter() & all() / any() */
      products.filter(function() {return products.containsNuts: false;})
      expect(productsICanEat.length).toBe(1);
  });

  /*********************************************************************************/

  it("should add all the natural numbers below 1000 that are multiples of 3 or 5 (imperative)", function () {

    var sum = 0;
    for(var i=1; i<1000; i+=1) {
      if (i % 3 === 0 || i % 5 === 0) {
        sum += i;
      }
    }

    expect(sum).toBe(233168);
  });

  it("should add all the natural numbers below 1000 that are multiples of 3 or 5 (functional)", function () {

    var sum = FILL_ME_IN;    /* try chaining range() and reduce() */

    expect(233168).toBe(FILL_ME_IN);
  });

  /*********************************************************************************/
   it("should count the ingredient occurrence (imperative)", function () {
    var ingredientCount = { "{ingredient name}": 0 };

    for (i = 0; i < products.length; i+=1) {
        for (j = 0; j < products[i].ingredients.length; j+=1) {
            ingredientCount[products[i].ingredients[j]] = (ingredientCount[products[i].ingredients[j]] || 0) + 1;
        }
    }

    expect(ingredientCount['mushrooms']).toBe(FILL_ME_IN);
  });

  it("should count the ingredient occurrence (functional)", function () {
    var ingredientCount = { "{ingredient name}": 0 };

    /* chain() together map(), flatten() and reduce() */

    expect(ingredientCount['mushrooms']).toBe(FILL_ME_IN);
  });

  /*********************************************************************************/
  /* UNCOMMENT FOR EXTRA CREDIT */
  /*
  it("should find the largest prime factor of a composite number", function () {

  });

  it("should find the largest palindrome made from the product of two 3 digit numbers", function () {

  });

  it("should find the smallest number divisible by each of the numbers 1 to 20", function () {

  });

  it("should find the difference between the sum of the squares and the square of the sums", function () {

  });

  it("should find the 10001st prime", function () {

  });
  */
});

<|endoftext|>


<commit_before><commit_msg>Log highlighting<commit_after>$(function() {

    function highlightLine(lineNum) {
        lineNum -= 1;
        $(".logs--line[data-line-num='" + lineNum + "']").addClass("highlighted");
    }

    var hash = window.location.hash;
    var remainingPart = hash.substr(0, 2);
    if (remainingPart == "#L") {
        console.log("Got request to highlight lines!");
        if ($("div.logs").length() == 0) {
            console.log("Not on a logs page!");
        } else {
            var remainingParts = remainingPart.substr(2).split(",");
            for (var i = 0; i < remainingParts.length; i++) {
                var part = remainingParts[i];
                if (part.indexOf("-") !== -1) {
                    var re = /([0-9]+)-([0-9]+)/;
                    var match = re.exec(part);
                    if (match.length !== 3) {
                        console.log("Invalid fragment! " + part);
                    } else {
                        console.log("Min: " + match[1] + ", Max: " + match[2]);
                        var imin = Number(match[1]);
                        var imax = Number(match[1]);
                        for (var j = imin; j < imax; j++) {
                            highlightLine(j);
                        }
                    }
                } else {
                    var ival = Number(part);
                    if (isNaN(ival)) {
                        console.log("Invalid fragment! " + part)
                    } else {
                        highlightLine(ival);
                    }
                }
            }
        }
    }
});
<|endoftext|>


<commit_before>/*
       Licensed to the Apache Software Foundation (ASF) under one
       or more contributor license agreements.  See the NOTICE file
       distributed with this work for additional information
       regarding copyright ownership.  The ASF licenses this file
       to you under the Apache License, Version 2.0 (the
       "License"); you may not use this file except in compliance
       with the License.  You may obtain a copy of the License at

         http://www.apache.org/licenses/LICENSE-2.0

       Unless required by applicable law or agreed to in writing,
       software distributed under the License is distributed on an
       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
       KIND, either express or implied.  See the License for the
       specific language governing permissions and limitations
       under the License.
*/

var Q = require('q');
var fs = require('fs');
var util = require('util');
var path = require('path');
var shell = require('shelljs');
var spawn = require('cordova-common').superspawn.spawn;
var CordovaError = require('cordova-common').CordovaError;
var check_reqs = require('../check_reqs');

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

var MARKER = 'YOUR CHANGES WILL BE ERASED!';
var SIGNING_PROPERTIES = '-signing.properties';
var TEMPLATE =
    '# This file is automatically generated.\n' +
    '# Do not modify this file -- ' + MARKER + '\n';

function StudioBuilder (projectRoot) {
    GenericBuilder.call(this, projectRoot);

    this.binDirs = {gradle: this.binDirs.studio};
}

util.inherits(StudioBuilder, GenericBuilder);

StudioBuilder.prototype.getArgs = function (cmd, opts) {
    if (cmd === 'release') {
        cmd = 'cdvBuildRelease';
    } else if (cmd === 'debug') {
        cmd = 'cdvBuildDebug';
    }
    var args = [cmd, '-b', path.join(this.root, 'build.gradle')];
    if (opts.arch) {
        args.push('-PcdvBuildArch=' + opts.arch);
    }

    // 10 seconds -> 6 seconds
    args.push('-Dorg.gradle.daemon=true');
    // to allow dex in process
    args.push('-Dorg.gradle.jvmargs=-Xmx2048m');
    // allow NDK to be used - required by Gradle 1.5 plugin
    args.push('-Pandroid.useDeprecatedNdk=true');
    args.push.apply(args, opts.extraArgs);
    // Shaves another 100ms, but produces a "try at own risk" warning. Not worth it (yet):
    // args.push('-Dorg.gradle.parallel=true');
    return args;
};

/*
 * This returns a promise
 */

StudioBuilder.prototype.runGradleWrapper = function (gradle_cmd) {
    var gradlePath = path.join(this.root, 'gradlew');
    var wrapperGradle = path.join(this.root, 'wrapper.gradle');
    if (fs.existsSync(gradlePath)) {
        // Literally do nothing, for some reason this works, while !fs.existsSync didn't on Windows
    } else {
        return spawn(gradle_cmd, ['-p', this.root, 'wrapper', '-b', wrapperGradle], {stdio: 'inherit'});
    }
};

StudioBuilder.prototype.readProjectProperties = function () {

    function findAllUniq (data, r) {
        var s = {};
        var m;
        while ((m = r.exec(data))) {
            s[m[1]] = 1;
        }
        return Object.keys(s);
    }

    var data = fs.readFileSync(path.join(this.root, 'project.properties'), 'utf8');
    return {
        libs: findAllUniq(data, /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg),
        gradleIncludes: findAllUniq(data, /^\s*cordova\.gradle\.include\.\d+=(.*)(?:\s|$)/mg),
        systemLibs: findAllUniq(data, /^\s*cordova\.system\.library\.\d+=(.*)(?:\s|$)/mg)
    };
};

StudioBuilder.prototype.extractRealProjectNameFromManifest = function () {
    var manifestPath = path.join(this.root, 'app', 'src', 'main', 'AndroidManifest.xml');
    var manifestData = fs.readFileSync(manifestPath, 'utf8');
    var m = /<manifest[\s\S]*?package\s*=\s*"(.*?)"/i.exec(manifestData);
    if (!m) {
        throw new CordovaError('Could not find package name in ' + manifestPath);
    }

    var packageName = m[1];
    var lastDotIndex = packageName.lastIndexOf('.');
    return packageName.substring(lastDotIndex + 1);
};

// Makes the project buildable, minus the gradle wrapper.
StudioBuilder.prototype.prepBuildFiles = function () {
    // Update the version of build.gradle in each dependent library.
    var pluginBuildGradle = path.join(this.root, 'cordova', 'lib', 'plugin-build.gradle');
    var propertiesObj = this.readProjectProperties();
    var subProjects = propertiesObj.libs;

    // Check and copy the gradle file into the subproject
    // Called by the loop before this function def

    var checkAndCopy = function (subProject, root) {
        var subProjectGradle = path.join(root, subProject, 'build.gradle');
        // This is the future-proof way of checking if a file exists
        // This must be synchronous to satisfy a Travis test
        try {
            fs.accessSync(subProjectGradle, fs.F_OK);
        } catch (e) {
            shell.cp('-f', pluginBuildGradle, subProjectGradle);
        }
    };

    for (var i = 0; i < subProjects.length; ++i) {
        if (subProjects[i] !== 'CordovaLib') {
            checkAndCopy(subProjects[i], this.root);
        }
    }
    var name = this.extractRealProjectNameFromManifest();
    // Remove the proj.id/name- prefix from projects: https://issues.apache.org/jira/browse/CB-9149
    var settingsGradlePaths = subProjects.map(function (p) {
        var realDir = p.replace(/[/\\]/g, ':');
        var libName = realDir.replace(name + '-', '');
        var str = 'include ":' + libName + '"\n';
        if (realDir.indexOf(name + '-') !== -1) {
            str += 'project(":' + libName + '").projectDir = new File("' + p + '")\n';
        }
        return str;
    });

    fs.writeFileSync(path.join(this.root, 'settings.gradle'),
        '// GENERATED FILE - DO NOT EDIT\n' +
        'include ":"\n' + settingsGradlePaths.join(''));

    // Update dependencies within build.gradle.
    var buildGradle = fs.readFileSync(path.join(this.root, 'app', 'build.gradle'), 'utf8');
    var depsList = '';
    var root = this.root;
    var insertExclude = function (p) {
        var gradlePath = path.join(root, p, 'build.gradle');
        var projectGradleFile = fs.readFileSync(gradlePath, 'utf-8');
        if (projectGradleFile.indexOf('CordovaLib') !== -1) {
            depsList += '{\n        exclude module:("CordovaLib")\n    }\n';
        } else {
            depsList += '\n';
        }
    };
    subProjects.forEach(function (p) {
        console.log('Subproject Path: ' + p);
        var libName = p.replace(/[/\\]/g, ':').replace(name + '-', '');
        if (libName !== 'app') {
            depsList += '    implementation(project(path: ":' + libName + '"))';
            insertExclude(p);
        }
    });
    // For why we do this mapping: https://issues.apache.org/jira/browse/CB-8390
    var SYSTEM_LIBRARY_MAPPINGS = [
        [/^\/?extras\/android\/support\/(.*)$/, 'com.android.support:support-$1:+'],
        [/^\/?google\/google_play_services\/libproject\/google-play-services_lib\/?$/, 'com.google.android.gms:play-services:+']
    ];

    propertiesObj.systemLibs.forEach(function (p) {
        var mavenRef;
        // It's already in gradle form if it has two ':'s
        if (/:.*:/.exec(p)) {
            mavenRef = p;
        } else {
            for (var i = 0; i < SYSTEM_LIBRARY_MAPPINGS.length; ++i) {
                var pair = SYSTEM_LIBRARY_MAPPINGS[i];
                if (pair[0].exec(p)) {
                    mavenRef = p.replace(pair[0], pair[1]);
                    break;
                }
            }
            if (!mavenRef) {
                throw new CordovaError('Unsupported system library (does not work with gradle): ' + p);
            }
        }
        depsList += '    compile "' + mavenRef + '"\n';
    });

    buildGradle = buildGradle.replace(/(SUB-PROJECT DEPENDENCIES START)[\s\S]*(\/\/ SUB-PROJECT DEPENDENCIES END)/, '$1\n' + depsList + '    $2');
    var includeList = '';

    propertiesObj.gradleIncludes.forEach(function (includePath) {
        includeList += 'apply from: "../' + includePath + '"\n';
    });
    buildGradle = buildGradle.replace(/(PLUGIN GRADLE EXTENSIONS START)[\s\S]*(\/\/ PLUGIN GRADLE EXTENSIONS END)/, '$1\n' + includeList + '$2');
    // This needs to be stored in the app gradle, not the root grade
    fs.writeFileSync(path.join(this.root, 'app', 'build.gradle'), buildGradle);
};

StudioBuilder.prototype.prepEnv = function (opts) {
    var self = this;
    return check_reqs.check_gradle()
      .then(function (gradlePath) {
          return self.runGradleWrapper(gradlePath);
      }).then(function () {
          return self.prepBuildFiles();
      }).then(function () {
          // If the gradle distribution URL is set, make sure it points to version we want.
          // If it's not set, do nothing, assuming that we're using a future version of gradle that we don't want to mess with.
          // For some reason, using ^ and $ don't work.  This does the job, though.
          var distributionUrlRegex = /distributionUrl.*zip/;
          var distributionUrl = process.env['CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL'] || 'https\\://services.gradle.org/distributions/gradle-4.1-all.zip';
          var gradleWrapperPropertiesPath = path.join(self.root, 'gradle', 'wrapper', 'gradle-wrapper.properties');
          shell.chmod('u+w', gradleWrapperPropertiesPath);
          shell.sed('-i', distributionUrlRegex, 'distributionUrl=' + distributionUrl, gradleWrapperPropertiesPath);

          var propertiesFile = opts.buildType + SIGNING_PROPERTIES;
          var propertiesFilePath = path.join(self.root, propertiesFile);
          if (opts.packageInfo) {
              fs.writeFileSync(propertiesFilePath, TEMPLATE + opts.packageInfo.toProperties());
          } else if (isAutoGenerated(propertiesFilePath)) {
              shell.rm('-f', propertiesFilePath);
          }
      });
};

/*
 * Builds the project with gradle.
 * Returns a promise.
 */
StudioBuilder.prototype.build = function (opts) {
    var wrapper = path.join(this.root, 'gradlew');
    var args = this.getArgs(opts.buildType === 'debug' ? 'debug' : 'release', opts);

    return spawn(wrapper, args, {stdio: 'pipe'})
    .progress(function (stdio) {
        if (stdio.stderr) {
            /*
             * Workaround for the issue with Java printing some unwanted information to
             * stderr instead of stdout.
             * This function suppresses 'Picked up _JAVA_OPTIONS' message from being
             * printed to stderr. See https://issues.apache.org/jira/browse/CB-9971 for
             * explanation.
             */
            var suppressThisLine = /^Picked up _JAVA_OPTIONS: /i.test(stdio.stderr.toString());
            if (suppressThisLine) {
                return;
            }
            process.stderr.write(stdio.stderr);
        } else {
            process.stdout.write(stdio.stdout);
        }
    }).catch(function (error) {
        if (error.toString().indexOf('failed to find target with hash string') >= 0) {
            return check_reqs.check_android_target(error).then(function () {
                // If due to some odd reason - check_android_target succeeds
                // we should still fail here.
                return Q.reject(error);
            });
        }
        return Q.reject(error);
    });
};

StudioBuilder.prototype.clean = function (opts) {
    var builder = this;
    var wrapper = path.join(this.root, 'gradlew');
    var args = builder.getArgs('clean', opts);
    return Q().then(function () {
        return spawn(wrapper, args, {stdio: 'inherit'});
    })
    .then(function () {
        shell.rm('-rf', path.join(builder.root, 'out'));

        ['debug', 'release'].forEach(function (config) {
            var propertiesFilePath = path.join(builder.root, config + SIGNING_PROPERTIES);
            if (isAutoGenerated(propertiesFilePath)) {
                shell.rm('-f', propertiesFilePath);
            }
        });
    });
};

module.exports = StudioBuilder;

function isAutoGenerated (file) {
    return fs.existsSync(file) && fs.readFileSync(file, 'utf8').indexOf(MARKER) > 0;
}
<commit_msg>CB-13646: End of an era.  Using the deprecated NDK by default breaks the build.  Crosswalk users need to specify the Gradle parameters to keep it working.<commit_after>/*
       Licensed to the Apache Software Foundation (ASF) under one
       or more contributor license agreements.  See the NOTICE file
       distributed with this work for additional information
       regarding copyright ownership.  The ASF licenses this file
       to you under the Apache License, Version 2.0 (the
       "License"); you may not use this file except in compliance
       with the License.  You may obtain a copy of the License at

         http://www.apache.org/licenses/LICENSE-2.0

       Unless required by applicable law or agreed to in writing,
       software distributed under the License is distributed on an
       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
       KIND, either express or implied.  See the License for the
       specific language governing permissions and limitations
       under the License.
*/

var Q = require('q');
var fs = require('fs');
var util = require('util');
var path = require('path');
var shell = require('shelljs');
var spawn = require('cordova-common').superspawn.spawn;
var CordovaError = require('cordova-common').CordovaError;
var check_reqs = require('../check_reqs');

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

var MARKER = 'YOUR CHANGES WILL BE ERASED!';
var SIGNING_PROPERTIES = '-signing.properties';
var TEMPLATE =
    '# This file is automatically generated.\n' +
    '# Do not modify this file -- ' + MARKER + '\n';

function StudioBuilder (projectRoot) {
    GenericBuilder.call(this, projectRoot);

    this.binDirs = {gradle: this.binDirs.studio};
}

util.inherits(StudioBuilder, GenericBuilder);

StudioBuilder.prototype.getArgs = function (cmd, opts) {
    if (cmd === 'release') {
        cmd = 'cdvBuildRelease';
    } else if (cmd === 'debug') {
        cmd = 'cdvBuildDebug';
    }
    var args = [cmd, '-b', path.join(this.root, 'build.gradle')];
    if (opts.arch) {
        args.push('-PcdvBuildArch=' + opts.arch);
    }

    // 10 seconds -> 6 seconds
    args.push('-Dorg.gradle.daemon=true');
    // to allow dex in process
    args.push('-Dorg.gradle.jvmargs=-Xmx2048m');
    // allow NDK to be used - required by Gradle 1.5 plugin
    // args.push('-Pandroid.useDeprecatedNdk=true');
    args.push.apply(args, opts.extraArgs);
    // Shaves another 100ms, but produces a "try at own risk" warning. Not worth it (yet):
    // args.push('-Dorg.gradle.parallel=true');
    return args;
};

/*
 * This returns a promise
 */

StudioBuilder.prototype.runGradleWrapper = function (gradle_cmd) {
    var gradlePath = path.join(this.root, 'gradlew');
    var wrapperGradle = path.join(this.root, 'wrapper.gradle');
    if (fs.existsSync(gradlePath)) {
        // Literally do nothing, for some reason this works, while !fs.existsSync didn't on Windows
    } else {
        return spawn(gradle_cmd, ['-p', this.root, 'wrapper', '-b', wrapperGradle], {stdio: 'inherit'});
    }
};

StudioBuilder.prototype.readProjectProperties = function () {

    function findAllUniq (data, r) {
        var s = {};
        var m;
        while ((m = r.exec(data))) {
            s[m[1]] = 1;
        }
        return Object.keys(s);
    }

    var data = fs.readFileSync(path.join(this.root, 'project.properties'), 'utf8');
    return {
        libs: findAllUniq(data, /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg),
        gradleIncludes: findAllUniq(data, /^\s*cordova\.gradle\.include\.\d+=(.*)(?:\s|$)/mg),
        systemLibs: findAllUniq(data, /^\s*cordova\.system\.library\.\d+=(.*)(?:\s|$)/mg)
    };
};

StudioBuilder.prototype.extractRealProjectNameFromManifest = function () {
    var manifestPath = path.join(this.root, 'app', 'src', 'main', 'AndroidManifest.xml');
    var manifestData = fs.readFileSync(manifestPath, 'utf8');
    var m = /<manifest[\s\S]*?package\s*=\s*"(.*?)"/i.exec(manifestData);
    if (!m) {
        throw new CordovaError('Could not find package name in ' + manifestPath);
    }

    var packageName = m[1];
    var lastDotIndex = packageName.lastIndexOf('.');
    return packageName.substring(lastDotIndex + 1);
};

// Makes the project buildable, minus the gradle wrapper.
StudioBuilder.prototype.prepBuildFiles = function () {
    // Update the version of build.gradle in each dependent library.
    var pluginBuildGradle = path.join(this.root, 'cordova', 'lib', 'plugin-build.gradle');
    var propertiesObj = this.readProjectProperties();
    var subProjects = propertiesObj.libs;

    // Check and copy the gradle file into the subproject
    // Called by the loop before this function def

    var checkAndCopy = function (subProject, root) {
        var subProjectGradle = path.join(root, subProject, 'build.gradle');
        // This is the future-proof way of checking if a file exists
        // This must be synchronous to satisfy a Travis test
        try {
            fs.accessSync(subProjectGradle, fs.F_OK);
        } catch (e) {
            shell.cp('-f', pluginBuildGradle, subProjectGradle);
        }
    };

    for (var i = 0; i < subProjects.length; ++i) {
        if (subProjects[i] !== 'CordovaLib') {
            checkAndCopy(subProjects[i], this.root);
        }
    }
    var name = this.extractRealProjectNameFromManifest();
    // Remove the proj.id/name- prefix from projects: https://issues.apache.org/jira/browse/CB-9149
    var settingsGradlePaths = subProjects.map(function (p) {
        var realDir = p.replace(/[/\\]/g, ':');
        var libName = realDir.replace(name + '-', '');
        var str = 'include ":' + libName + '"\n';
        if (realDir.indexOf(name + '-') !== -1) {
            str += 'project(":' + libName + '").projectDir = new File("' + p + '")\n';
        }
        return str;
    });

    fs.writeFileSync(path.join(this.root, 'settings.gradle'),
        '// GENERATED FILE - DO NOT EDIT\n' +
        'include ":"\n' + settingsGradlePaths.join(''));

    // Update dependencies within build.gradle.
    var buildGradle = fs.readFileSync(path.join(this.root, 'app', 'build.gradle'), 'utf8');
    var depsList = '';
    var root = this.root;
    var insertExclude = function (p) {
        var gradlePath = path.join(root, p, 'build.gradle');
        var projectGradleFile = fs.readFileSync(gradlePath, 'utf-8');
        if (projectGradleFile.indexOf('CordovaLib') !== -1) {
            depsList += '{\n        exclude module:("CordovaLib")\n    }\n';
        } else {
            depsList += '\n';
        }
    };
    subProjects.forEach(function (p) {
        console.log('Subproject Path: ' + p);
        var libName = p.replace(/[/\\]/g, ':').replace(name + '-', '');
        if (libName !== 'app') {
            depsList += '    implementation(project(path: ":' + libName + '"))';
            insertExclude(p);
        }
    });
    // For why we do this mapping: https://issues.apache.org/jira/browse/CB-8390
    var SYSTEM_LIBRARY_MAPPINGS = [
        [/^\/?extras\/android\/support\/(.*)$/, 'com.android.support:support-$1:+'],
        [/^\/?google\/google_play_services\/libproject\/google-play-services_lib\/?$/, 'com.google.android.gms:play-services:+']
    ];

    propertiesObj.systemLibs.forEach(function (p) {
        var mavenRef;
        // It's already in gradle form if it has two ':'s
        if (/:.*:/.exec(p)) {
            mavenRef = p;
        } else {
            for (var i = 0; i < SYSTEM_LIBRARY_MAPPINGS.length; ++i) {
                var pair = SYSTEM_LIBRARY_MAPPINGS[i];
                if (pair[0].exec(p)) {
                    mavenRef = p.replace(pair[0], pair[1]);
                    break;
                }
            }
            if (!mavenRef) {
                throw new CordovaError('Unsupported system library (does not work with gradle): ' + p);
            }
        }
        depsList += '    compile "' + mavenRef + '"\n';
    });

    buildGradle = buildGradle.replace(/(SUB-PROJECT DEPENDENCIES START)[\s\S]*(\/\/ SUB-PROJECT DEPENDENCIES END)/, '$1\n' + depsList + '    $2');
    var includeList = '';

    propertiesObj.gradleIncludes.forEach(function (includePath) {
        includeList += 'apply from: "../' + includePath + '"\n';
    });
    buildGradle = buildGradle.replace(/(PLUGIN GRADLE EXTENSIONS START)[\s\S]*(\/\/ PLUGIN GRADLE EXTENSIONS END)/, '$1\n' + includeList + '$2');
    // This needs to be stored in the app gradle, not the root grade
    fs.writeFileSync(path.join(this.root, 'app', 'build.gradle'), buildGradle);
};

StudioBuilder.prototype.prepEnv = function (opts) {
    var self = this;
    return check_reqs.check_gradle()
      .then(function (gradlePath) {
          return self.runGradleWrapper(gradlePath);
      }).then(function () {
          return self.prepBuildFiles();
      }).then(function () {
          // If the gradle distribution URL is set, make sure it points to version we want.
          // If it's not set, do nothing, assuming that we're using a future version of gradle that we don't want to mess with.
          // For some reason, using ^ and $ don't work.  This does the job, though.
          var distributionUrlRegex = /distributionUrl.*zip/;
          var distributionUrl = process.env['CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL'] || 'https\\://services.gradle.org/distributions/gradle-4.1-all.zip';
          var gradleWrapperPropertiesPath = path.join(self.root, 'gradle', 'wrapper', 'gradle-wrapper.properties');
          shell.chmod('u+w', gradleWrapperPropertiesPath);
          shell.sed('-i', distributionUrlRegex, 'distributionUrl=' + distributionUrl, gradleWrapperPropertiesPath);

          var propertiesFile = opts.buildType + SIGNING_PROPERTIES;
          var propertiesFilePath = path.join(self.root, propertiesFile);
          if (opts.packageInfo) {
              fs.writeFileSync(propertiesFilePath, TEMPLATE + opts.packageInfo.toProperties());
          } else if (isAutoGenerated(propertiesFilePath)) {
              shell.rm('-f', propertiesFilePath);
          }
      });
};

/*
 * Builds the project with gradle.
 * Returns a promise.
 */
StudioBuilder.prototype.build = function (opts) {
    var wrapper = path.join(this.root, 'gradlew');
    var args = this.getArgs(opts.buildType === 'debug' ? 'debug' : 'release', opts);

    return spawn(wrapper, args, {stdio: 'pipe'})
    .progress(function (stdio) {
        if (stdio.stderr) {
            /*
             * Workaround for the issue with Java printing some unwanted information to
             * stderr instead of stdout.
             * This function suppresses 'Picked up _JAVA_OPTIONS' message from being
             * printed to stderr. See https://issues.apache.org/jira/browse/CB-9971 for
             * explanation.
             */
            var suppressThisLine = /^Picked up _JAVA_OPTIONS: /i.test(stdio.stderr.toString());
            if (suppressThisLine) {
                return;
            }
            process.stderr.write(stdio.stderr);
        } else {
            process.stdout.write(stdio.stdout);
        }
    }).catch(function (error) {
        if (error.toString().indexOf('failed to find target with hash string') >= 0) {
            return check_reqs.check_android_target(error).then(function () {
                // If due to some odd reason - check_android_target succeeds
                // we should still fail here.
                return Q.reject(error);
            });
        }
        return Q.reject(error);
    });
};

StudioBuilder.prototype.clean = function (opts) {
    var builder = this;
    var wrapper = path.join(this.root, 'gradlew');
    var args = builder.getArgs('clean', opts);
    return Q().then(function () {
        return spawn(wrapper, args, {stdio: 'inherit'});
    })
    .then(function () {
        shell.rm('-rf', path.join(builder.root, 'out'));

        ['debug', 'release'].forEach(function (config) {
            var propertiesFilePath = path.join(builder.root, config + SIGNING_PROPERTIES);
            if (isAutoGenerated(propertiesFilePath)) {
                shell.rm('-f', propertiesFilePath);
            }
        });
    });
};

module.exports = StudioBuilder;

function isAutoGenerated (file) {
    return fs.existsSync(file) && fs.readFileSync(file, 'utf8').indexOf(MARKER) > 0;
}
<|endoftext|>


<commit_before>const cheerio = require('cheerio');
const moment = require('moment');

function proxerDateToTimestamp(time) {
    if(time === 'Jetzt') {
        return moment().unix();
    }
    const match = /(\d+) (\w+)/.exec(time);
    time = parseInt(match[1]);
    const unit = toEnglishUnit(match[2]);
    return moment().subtract(time, unit).unix();
}

function toEnglishUnit(unit) {
    switch(unit.toLowerCase()) {
        case "tag":
            return "days";
        case "minut":
            return "minutes";
        case "stund":
            return "hours";
        case "sekund":
            return "seconds";
        case "monat":
            return "months";
        case "jahr":
            return "years";
        default:
            return "";
    }
}

const parser = {
    parseSelf: function(top) {
        return /dein Profil/.test(top.text());
    },

    parseProfileTable: function(table) {
        const rows = table.find('tr');
        let current = rows.first();
        const info = {
            ranking: parser.parseRankingColumn(current),
            online: parser.parseOnlineColumn((current = current.next())),
            member_since: parser.parseMemberSinceColumn((current = current.next())),
            last_online: parser.parseLastOnlineColumn((current = current.next())),
            last_update: parser.parseLastUpdateColumn((current = current.next()))
        };

        const status = parser.parseStatusColumn(current.next());
        if(typeof(status) === 'undefined' || status == null) {
            info.allow_friends = parser.parseContactColumn(current.next());
            info.status = parser.parseStatusColumn(current.next().next());
        } else {
            info.allow_friends = true;
            info.status = status;
        }

        return info;
    },

    parseUserInfo: function(title) {
        const elem = title.find('a');
        return {
            name: elem.text(),
            id: parseInt(/(\d+)/.exec(elem.attr('href'))[1])
        };
    },

    parseMemberSinceColumn: function(row) {
        const date = row.children().next().text();
        return moment(date, 'DD.MM.YYYY').unix();
    },

    parseRankingColumn: function(row) {
        const text = row.children().next().text();
        const match = /Anime: (\d+).+Manga: (\d+).*Uploads: (\d+).*Forum: (\d+).*Info: (\d+).*Zsp\.: (\d+).*Summe: (\d+).*- ([\w -]+)\[\?\]/.exec(text)
        return {
            anime: parseInt(match[1]),
            manga: parseInt(match[2]),
            uploads: parseInt(match[3]),
            forum: parseInt(match[4]),
            wiki: parseInt(match[5]),
            additional: parseInt(match[6]),
            total: parseInt(match[7]),
            title: match[8]
        };
    },

    parseOnlineColumn: function(row) {
        return /online/.test(row.find('img').attr('src'));
    },

    parseLastOnlineColumn: function(row) {
        return proxerDateToTimestamp(row.children().next().text());
    },

    parseLastUpdateColumn: function(row) {
        return proxerDateToTimestamp(row.children().next().text());
    },

    parseStatusColumn: function(row) {
        const status = row.find('td[align=center]');
        var text = "";
        if(status.find('#whatrustatus').length > 0) {
            text = status.find('#whatrustatus').text();
        } else {
            text = status.find('div').text();
        }

        const lastDashIndex = text.lastIndexOf(' - ');
        if(lastDashIndex < 0) {
            return null;
        }
        const message = text.substring(0, lastDashIndex);
        const time = text.substring(lastDashIndex + 1);
        return {
            message: message,
            written: proxerDateToTimestamp(time)
        }
    },

    parseContactColumn: function(row) {
        return row.find('#addFriend').length == 1;
    },

    parseProfilePicture: function(table) {
        return table.find('tr').next().children().next().find('img').attr('src');
    }
};

parser.parseProfile = function(page) {
    return Promise.resolve(page).then(cheerio.load).then(function($) {
        const userinfo = parser.parseUserInfo($('.inner h3'));
        const self = parser.parseSelf($('#profileTop'));
        const info = parser.parseProfileTable($('.profile'));
        const image = parser.parseProfilePicture($('.inner table').first());
        info.name = userinfo.name;
        info.id = userinfo.id;
        info.picture = image;
        info.self = self;
        return info;
    });
};

module.exports = parser;
<commit_msg>Make profile parser a little mor robust<commit_after>const cheerio = require('cheerio');
const moment = require('moment');

function proxerDateToTimestamp(time) {
    if(time === 'Jetzt') {
        return moment().unix();
    }
    const match = /(\d+) (\w+)/.exec(time);
    time = parseInt(match[1]);
    const unit = toEnglishUnit(match[2]);
    return moment().subtract(time, unit).unix();
}

function toEnglishUnit(unit) {
    switch(unit.toLowerCase()) {
        case "tag":
            return "days";
        case "minut":
            return "minutes";
        case "stund":
            return "hours";
        case "sekund":
            return "seconds";
        case "monat":
            return "months";
        case "jahr":
            return "years";
        default:
            return "";
    }
}

const parser = {
    parseSelf: function(top) {
        return /dein Profil/.test(top.text());
    },

    parseProfileTable: function(table) {
        const rows = table.find('tr');
        let current = rows.first();
        const info = {
            ranking: parser.parseRankingColumn(current),
            online: parser.parseOnlineColumn((current = current.next())),
            member_since: parser.parseMemberSinceColumn((current = current.next())),
            last_online: parser.parseLastOnlineColumn((current = current.next())),
            last_update: parser.parseLastUpdateColumn((current = current.next()))
        };

        const status = parser.parseStatusColumn(current.next());
        if(typeof(status) === 'undefined' || status == null) {
            info.allow_friends = parser.parseContactColumn(current.next());
            info.status = parser.parseStatusColumn(current.next().next());
        } else {
            info.allow_friends = true;
            info.status = status;
        }

        return info;
    },

    parseUserInfo: function(title) {
        const elem = title.find('a');
        return {
            name: elem.text(),
            id: parseInt(/(\d+)/.exec(elem.attr('href'))[1])
        };
    },

    parseMemberSinceColumn: function(row) {
        const date = row.children().next().text();
        return moment(date, 'DD.MM.YYYY').unix();
    },

    parseRankingColumn: function(row) {
        const text = row.children().next().text();
        const split = text.split('|').map(s => s.trim());
        const first = split.slice(0, 5);
        const last = split[5];
        const generalPoints = first.map(s => /[\w\.]+: (\d+)/.exec(s)).map(match => parseInt(match[1]));
        const lastColumn = /[\w\.]+: (\d+)\w+: (\d+) - ([\w -]+).*/.exec(last);
        return {
            anime: generalPoints[0],
            manga: generalPoints[1],
            uploads: generalPoints[2],
            forum: generalPoints[3],
            wiki: generalPoints[4],
            additional: parseInt(lastColumn[1]),
            total: parseInt(lastColumn[2]),
            title: lastColumn[3]
        };
    },

    parseOnlineColumn: function(row) {
        return /online/.test(row.find('img').attr('src'));
    },

    parseLastOnlineColumn: function(row) {
        return proxerDateToTimestamp(row.children().next().text());
    },

    parseLastUpdateColumn: function(row) {
        return proxerDateToTimestamp(row.children().next().text());
    },

    parseStatusColumn: function(row) {
        const status = row.find('td[align=center]');
        var text = "";
        if(status.find('#whatrustatus').length > 0) {
            text = status.find('#whatrustatus').text();
        } else {
            text = status.find('div').text();
        }

        const lastDashIndex = text.lastIndexOf(' - ');
        if(lastDashIndex < 0) {
            return null;
        }
        const message = text.substring(0, lastDashIndex);
        const time = text.substring(lastDashIndex + 1);
        return {
            message: message,
            written: proxerDateToTimestamp(time)
        }
    },

    parseContactColumn: function(row) {
        return row.find('#addFriend').length == 1;
    },

    parseProfilePicture: function(table) {
        return table.find('tr').next().children().next().find('img').attr('src');
    }
};

parser.parseProfile = function(page) {
    return Promise.resolve(page).then(cheerio.load).then(function($) {
        const userinfo = parser.parseUserInfo($('.inner h3'));
        const self = parser.parseSelf($('#profileTop'));
        const info = parser.parseProfileTable($('.profile'));
        const image = parser.parseProfilePicture($('.inner table').first());
        info.name = userinfo.name;
        info.id = userinfo.id;
        info.picture = image;
        info.self = self;
        return info;
    });
};

module.exports = parser;
<|endoftext|>


<commit_before>/**
 * Created by steph on 2017-11-18.
 */
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import PivotTableUI from 'react-pivottable/PivotTableUI';
import createPlotlyComponent from 'react-plotly.js/factory';
import createPlotlyRenderers from 'react-pivottable/PlotlyRenderers';
import TableRenderers from 'react-pivottable/TableRenderers';
import { isLoading, hasStoppedLoading } from '../../actions/main-actions';
import { viewReportData } from '../../actions/report-info-actions';

const mapStateToProps = state => {
  return {
    user: state.user,
    tables: state.tables,
    reportData: state.reportData
  };
};

const Plot = createPlotlyComponent(window.Plotly);

const mapDispatchToProps = dispatch => {
  return {
    getData: (token) => {
      dispatch(isLoading());
      dispatch(viewReportData(token));
    },
    stopLoading: () => {
      dispatch(hasStoppedLoading());
    }
  };
};

class ReportBuilderContainer extends React.Component {
  constructor(props) {
    super(props);
    this.state = { pivotState: props };
  }

  componentWillReceiveProps(nextProps) {
    this.setState({pivotState: nextProps});
    if (nextProps.reportData.data && nextProps.reportData.data.length > 0) {
      this.setState({pivotState: {
        data: nextProps.reportData.data,
        cols:["Working Month"],
        aggregatorName: "Count Unique Values", vals: ["Contractor Name"],
        rendererName: "Stacked Column Chart",
        plotlyOptions: {width: 900, height: 500}
      }});
      this.props.stopLoading();
    }
  }

  componentDidMount() {
    this.props.getData(this.props.user.token);
  }

  componentWillMount() {
    /**
     * set data for report here
     */
    this.setState({
      mode: "demo",
      filename: "Trending Reports",
      pivotState: {
        data: this.state.pivotState.data,
        cols: ["Working Month"],
        aggregatorName: "Count Unique Values", vals: ["Contractor Name"],
        rendererName: "Stacked Column Chart",
        plotlyOptions: {width: 900, height: 500}
      }
    });
  }

  render() {
    return <PivotTableUI
      data={this.state.pivotState.data} onChange={s => this.setState({pivotState: s})}
      renderers={Object.assign({}, TableRenderers, createPlotlyRenderers(Plot))}
      {...this.state.pivotState} unusedOrientationCutoff={Infinity}
    />;
  }
}

ReportBuilderContainer.propTypes = {
  user: PropTypes.object.isRequired,
  tables: PropTypes.object.isRequired,
  reportData: PropTypes.object.isRequired
};

const ReportBuilder = connect(
  mapStateToProps,
  mapDispatchToProps
)(ReportBuilderContainer);

export default ReportBuilder;<commit_msg>Fixed charts<commit_after>/**
 * Created by steph on 2017-11-18.
 */
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import PivotTableUI from 'react-pivottable/PivotTableUI';
import createPlotlyComponent from 'react-plotly.js/factory';
import createPlotlyRenderers from 'react-pivottable/PlotlyRenderers';
import TableRenderers from 'react-pivottable/TableRenderers';
import { isLoading, hasStoppedLoading } from '../../actions/main-actions';
import { viewReportData } from '../../actions/report-info-actions';

const mapStateToProps = state => {
  return {
    user: state.user,
    tables: state.tables,
    reportData: state.reportData
  };
};

const Plot = createPlotlyComponent(window.Plotly);

const mapDispatchToProps = dispatch => {
  return {
    getData: (token) => {
      dispatch(isLoading());
      dispatch(viewReportData(token));
    },
    stopLoading: () => {
      dispatch(hasStoppedLoading());
    }
  };
};

const data = [];

class ReportBuilderContainer extends React.Component {
  constructor(props) {
    super(props);
    this.state = { pivotState: props };
  }

  componentWillReceiveProps(nextProps) {
    this.setState({pivotState: nextProps});
    if (nextProps.reportData.data && nextProps.reportData.data.length > 0) {
      this.setState({pivotState: {
        data: nextProps.reportData.data,
        cols:["Working Month"],
        aggregatorName: "Count Unique Values", vals: ["Contractor Name"],
        rendererName: "Stacked Column Chart",
        plotlyOptions: {width: 900, height: 500}
      }});
      this.props.stopLoading();
    }
  }

  componentDidMount() {
    this.props.getData(this.props.user.token);
  }

  componentWillMount() {
    /**
     * set data for report here
     */
    this.setState({
      mode: "demo",
      filename: "Trending Reports",
      pivotState: {
        data: data,
        cols: ["Working Month"],
        aggregatorName: "Count Unique Values", vals: ["Contractor Name"],
        rendererName: "Stacked Column Chart",
        plotlyOptions: {width: 900, height: 500}
      }
    });
  }

  render() {
    return <PivotTableUI
      data={this.state.pivotState.data} onChange={s => this.setState({pivotState: s})}
      renderers={Object.assign({}, TableRenderers, createPlotlyRenderers(Plot))}
      {...this.state.pivotState} unusedOrientationCutoff={Infinity}
    />;
  }
}

ReportBuilderContainer.propTypes = {
  user: PropTypes.object.isRequired,
  tables: PropTypes.object.isRequired,
  reportData: PropTypes.object.isRequired
};

const ReportBuilder = connect(
  mapStateToProps,
  mapDispatchToProps
)(ReportBuilderContainer);

export default ReportBuilder;<|endoftext|>


<commit_before>/*
* @Author: Jiyun
* @Date:   2015-06-25 03:35:03
* @Last Modified by:   Jiyun
* @Last Modified time: 2015-06-25 04:12:42
*/

// jshint ignore:start

var express = require('express');

var request = require('request');
var schedule = require('node-schedule');
var session = require('express-session');
var everyauthCN = require('everyauth-cn');
everyauthCN.douban.scope('douban_basic_common,shuo_basic_w,shuo_basic_r');
var authSettings = require('./config/auth-settings');

var app = express();
app.set('views','cloud/views');// 设置模板目录
app.set('view engine', 'jade');

app.use(express.bodyParser());// 读取请求 body 的中间件

app.use(session({
    secret: 'everyauth-cn',
    resave: false,
    saveUninitialized: true,
    // cookie: { secure: true }
}));
app.use(everyauthCN.middleware());

everyauthCN.debug = false;

// 为字符串添加 repeat 方法:
// 判断是否存在这个方法
if (!String.repeat) {
    // 创建repeat方法
    String.prototype.repeat = function (l) {
        // 创建元素值为空、个数为重复次数+1的数组，用字符串自身做为分隔符连接起来，返回连接后的值。
        return new Array(l + 1).join(this);
    }
}

app.get('/', function (req, res) {

    // 判断是不是拿到了 token
    if (typeof req.session.auth !== 'undefined') {

        // 判断是不是豆瓣大笨鸡的 uid
        // todo 通过 config 增加多个帐号
        if (req.session.auth.douban.user.id === '67736974') {
            // 取得 token
            var accessToken = req.session.auth.douban.accessToken;

            // 定义自动定时任务的规则
            var rule = new schedule.RecurrenceRule();
            rule.minute = [0];

            // 调整时区
            var d = new Date(); //创建一个Date对象
            var localTime = d.getTime();
            var localOffset = d.getTimezoneOffset() * 60000; //获得当地时间偏移的毫秒数
            var utc = localTime + localOffset; //utc即GMT时间
            var offset = 8; //以北京时间为例，东8区
            var beijing = utc + (3600000 * offset);
            var date = new Date(beijing); // 得到最终的准确时间
            var now = date.getHours(); // 得到当前小时

            if (now === 0) {
                now = 24;
            }

            // console.log('now = ', now);

            var string = '咯~'; // todo: 多种文字形式 或者 支持前端页面中 input 传值？

            // 生成重复的字符串儿
            var text = '我是豆瓣大笨鸡，\r\n我正在练习报时，\r\n' + string.repeat(now);

            // 开始自动定时任务
            var autoTask = schedule.scheduleJob(rule, function () {
                postToDouban(accessToken, text, function (err, httpResponse, body) {
                    // todo: mail to me
                    // if (!err) {
                    //     console.log('豆瓣广播发布成功！偶也');
                    // } else {
                    //     console.error(error, body);
                    // }
                });
            });

            res.render('hello', {message: '欢迎豆瓣大笨鸡！'});
            return;

        } else {
            res.render('hello', {message: '骗谁呢？你根本不是豆瓣大笨鸡！'});
            return;
        }

    } else {
        res.render('hello', {message: '请问，你是豆瓣大笨鸡吗？'});
    }
});

// 发送豆瓣广播
function postToDouban (accessToken, text, callback) {
    var r = request.post('https://api.douban.com/shuo/v2/statuses/', {
            method: 'POST',
            headers: {'Authorization': 'Bearer ' + accessToken},
            timeout: 3000
        }, function (err, httpResponse, body) {
            if (callback && typeof callback === 'function') {
                callback(err, httpResponse, body);
            }
        });

    var form = r.form();
    form.append('text', text);
    // todo: 可增加配图
}

app.listen(8080);

// jshint ignore:end<commit_msg>fix bug<commit_after>/*
* @Author: Jiyun
* @Date:   2015-06-25 03:35:03
* @Last Modified by:   Jiyun
* @Last Modified time: 2015-06-25 11:29:26
*/

// jshint ignore:start

var express = require('express');

var request = require('request');
var schedule = require('node-schedule');
var session = require('express-session');
var everyauthCN = require('everyauth-cn');
everyauthCN.douban.scope('douban_basic_common,shuo_basic_w,shuo_basic_r');
var authSettings = require('./config/auth-settings');

var app = express();
app.set('views','cloud/views');// 设置模板目录
app.set('view engine', 'jade');

app.use(express.bodyParser());// 读取请求 body 的中间件

app.use(session({
    secret: 'everyauth-cn',
    resave: false,
    saveUninitialized: true,
    // cookie: { secure: true }
}));
app.use(everyauthCN.middleware());

everyauthCN.debug = false;

// 为字符串添加 repeat 方法:
// 判断是否存在这个方法
if (!String.repeat) {
    // 创建repeat方法
    String.prototype.repeat = function (l) {
        // 创建元素值为空、个数为重复次数+1的数组，用字符串自身做为分隔符连接起来，返回连接后的值。
        return new Array(l + 1).join(this);
    }
}

app.get('/', function (req, res) {

    // 判断是不是拿到了 token
    if (typeof req.session.auth !== 'undefined') {

        // 判断是不是豆瓣大笨鸡的 uid
        // todo 通过 config 增加多个帐号
        if (req.session.auth.douban.user.id === '67736974') {
            // 取得 token
            var accessToken = req.session.auth.douban.accessToken;

            // 定义自动定时任务的规则
            var rule = new schedule.RecurrenceRule();
            rule.minute = [0];

            var string = '咯~'; // todo: 多种文字形式 或者 支持前端页面中 input 传值？

            // 开始自动定时任务
            var autoTask = schedule.scheduleJob(rule, function () {

                // 调整时区
                var d = new Date(); //创建一个Date对象
                var localTime = d.getTime();
                var localOffset = d.getTimezoneOffset() * 60000; //获得当地时间偏移的毫秒数
                var utc = localTime + localOffset; //utc即GMT时间
                var offset = 8; //以北京时间为例，东8区
                var beijing = utc + (3600000 * offset);
                var date = new Date(beijing); // 得到最终的准确时间
                var now = date.getHours(); // 得到当前小时

                if (now === 0) {
                    now = 24;
                }

                // 生成重复的字符串儿
                var text = '我是豆瓣大笨鸡，\r\n我正在练习报时，\r\n' + string.repeat(now);

                postToDouban(accessToken, text, function (err, httpResponse, body) {
                    // todo: mail to me
                    // if (!err) {
                    //     console.log('豆瓣广播发布成功！偶也');
                    // } else {
                    //     console.error(error, body);
                    // }
                });
            });

            res.render('hello', {message: '欢迎豆瓣大笨鸡！'});
            return;

        } else {
            res.render('hello', {message: '骗谁呢？你根本不是豆瓣大笨鸡！'});
            return;
        }

    } else {
        res.render('hello', {message: '请问，你是豆瓣大笨鸡吗？'});
    }
});

// 发送豆瓣广播
function postToDouban (accessToken, text, callback) {
    var r = request.post('https://api.douban.com/shuo/v2/statuses/', {
            method: 'POST',
            headers: {'Authorization': 'Bearer ' + accessToken},
            timeout: 3000
        }, function (err, httpResponse, body) {
            if (callback && typeof callback === 'function') {
                callback(err, httpResponse, body);
            }
        });

    var form = r.form();
    form.append('text', text);
    // todo: 可增加配图
}

app.listen(8080);

// jshint ignore:end<|endoftext|>