/*!
 * AllOrigins
 * written by Gabriel Nunes <gabriel@multiverso.me>
 * http://github.com/gnuns
 */

const express = require('express')

const {version} = require('./package.json')
const processRequest  = require('./app/process-request')

// yep, global. it's ok
// https://softwareengineering.stackexchange.com/a/47926/289420
global.AO_VERSION = version

const app = express()

app.set('case sensitive routing', false)
app.disable('x-powered-by')
app.use(enableCORS)

app.all('/:format', processRequest)

function enableCORS (req, res, next) {
  res.header('Access-Control-Allow-Origin', req.headers.origin || '*')
  res.header('Access-Control-Allow-Credentials', true)
  res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept')
  res.header('Access-Control-Allow-Methods', 'OPTIONS, GET, POST, PATCH, PUT, DELETE')
  res.header('Via', `allOrigins v${version}`)
  next()
}

module.exports = app

/*!
 * AllOrigins
 * written by Gabriel Nunes <gabriel@multiverso.me>
 * http://github.com/gnuns
 */

const express = require('express')

const {version} = require('./package.json')
// yep, global. it's ok
// https://softwareengineering.stackexchange.com/a/47926/289420
global.AO_VERSION = version

const processRequest  = require('./app/process-request')

const app = express()

app.set('case sensitive routing', false)
app.disable('x-powered-by')
app.use(enableCORS)

app.all('/:format', processRequest)

function enableCORS (req, res, next) {
  res.header('Access-Control-Allow-Origin', req.headers.origin || '*')
  res.header('Access-Control-Allow-Credentials', true)
  res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept')
  res.header('Access-Control-Allow-Methods', 'OPTIONS, GET, POST, PATCH, PUT, DELETE')
  res.header('Via', `allOrigins v${version}`)
  next()
}

module.exports = app

Define AO_VERSION before require process-request


var conference = RTC({
  // use the public google stun servers :)
  ice: [
    { url: 'stun1.l.google.com:19302' },
    { url: 'stun2.l.google.com:19302' },
    { url: 'stun3.l.google.com:19302' },
    { url: 'stun4.l.google.com:19302' }
  ],

  // we want this to work on iOS as well so we will use
  // the rtc-plugin-nicta-ios plugin so we can use the
  // build.rtc.io to create a native iOS app
  plugins: [ RTC.IOS ]
});

Create a conference variable that can be played with by forkers


var ALIVE = "alive", DEAD = "dead";

function Cell(initialState) {
    var state = initialState || DEAD;
    return {
        setNeighbours: function (neighbours) {
            var nbAliveCells = 0;
            neighbours.forEach(function(neighbor) {
                if(neighbor.state === ALIVE) {
                    nbAliveCells += 1;
                }
            });
            if(nbAliveCells < 2) {
                state = DEAD;
            }
        },
        get state() {
            return state;
        }
    };
}

describe('Live cell', function () {
    it('should die when it has fewer than two live neighbours', function () {
        var cell = new Cell(ALIVE);

        cell.setNeighbours([new Cell(ALIVE)]);

        expect(cell.state).toBe(DEAD);
    });
});

var GameOfLife = (function() {
    var ALIVE = "alive", DEAD = "dead";

    function Cell(initialState) {
        var state = initialState || DEAD;

        function getNumberOfAlive(neighbours) {
            var nbAliveCells = 0;
            neighbours.forEach(function (neighbor) {
                if (neighbor.isAlive()) {
                    nbAliveCells += 1;
                }
            });
            return nbAliveCells;
        }

        return {
            setNeighbours: function (neighbours) {
                if(getNumberOfAlive(neighbours) < 2) {
                    state = DEAD;
                }
            },
            isAlive: function() {
                return state === ALIVE;
            }
        };
    }

    function AliveCell() {
        return new Cell(ALIVE);
    }

    function DeadCell() {
        return new Cell(DEAD);
    }

    return {
        AliveCell: AliveCell,
        DeadCell: DeadCell
    };
})();

describe('Live cell', function () {
    it('should die when it has fewer than two live neighbours', function () {
        var cell = new GameOfLife.AliveCell();

        cell.setNeighbours([new GameOfLife.AliveCell()]);

        expect(cell.isAlive()).toBe(false);
    });
});

Refactor less than two neighbours then cell dies


'use strict';

/* jasmine specs for controllers go here */

describe('RootCtrl', function(){
    beforeEach(module('swiftBrowser.controllers'));

    it('should list containers', inject(function($controller, $httpBackend) {
        var containers = [
            {"count": 10, "bytes": 1234, "name": "foo"},
            {"count": 20, "bytes": 2345, "name": "bar"},
        ];
        var scope = {};
        $httpBackend.whenGET('/v1/AUTH_abc?format=json')
            .respond(200, containers);

        $controller('RootCtrl', {$scope: scope});
        expect(scope.containers).toEqual([]);
        $httpBackend.flush();
        expect(scope.containers).toEqual(containers);
    }));

    it('should set sort order', inject(function($controller) {
        var scope = {};
        $controller('RootCtrl', {$scope: scope});
        expect(scope.orderProp).toEqual('name');
    }));

});

'use strict';

/* jasmine specs for controllers go here */

describe('RootCtrl', function(){
    var scope;

    beforeEach(module('swiftBrowser.controllers'));

    beforeEach(inject(function($controller) {
        scope = {};
        $controller('RootCtrl', {$scope: scope});
    }));

    it('should list containers', inject(function($httpBackend) {
        var containers = [
            {"count": 10, "bytes": 1234, "name": "foo"},
            {"count": 20, "bytes": 2345, "name": "bar"},
        ];
        $httpBackend.whenGET('/v1/AUTH_abc?format=json')
            .respond(200, containers);

        expect(scope.containers).toEqual([]);
        $httpBackend.flush();
        expect(scope.containers).toEqual(containers);
    }));

    it('should set sort order', function() {
        expect(scope.orderProp).toEqual('name');
    });

});

Refactor common logic for setting up RootCtrl and scope


$(document).ready(function(){

  $("#new-timeslot-form").submit(function(event){
    event.preventDefault();
    var data = createDate();
    $("#modal_new_timeslot").modal('toggle');
    $.ajax({
        type: "POST",
        url: "/api/timeslots",
        data: { start: data }
      }).done(function(response) {
          swal({
                title: "Great!",
                text: "You have set up a tutoring availability!",
                confirmButtonColor: "#66BB6A",
                type: "success"
            });
          $('#tutor-cal').fullCalendar( 'refetchEvents' );
      }).fail(function(response) {
        swal({
              title: "Oops...",
              text: "Something went wrong!",
              confirmButtonColor: "#EF5350",
              type: "error"
          });
      });
  })

})

var createDate = function() {
  var date = $('.timepicker').val();
  var time = $('.datepicker').val();
  var dateTime = date + " " + time;
  return (new Date(dateTime));
}

$(document).ready(function(){

  $("#new-timeslot-form").submit(function(event){
    event.preventDefault();
    var data = createDate();
    $("#modal_new_timeslot").modal('toggle');
    $.ajax({
        type: "POST",
        url: "/api/timeslots",
        data: { start: data },
        beforeSend: customBlockUi(this)
      }).done(function(response) {
          swal({
                title: "Great!",
                text: "You have set up a tutoring availability!",
                confirmButtonColor: "#66BB6A",
                type: "success"
            });
          $('#tutor-cal').fullCalendar( 'refetchEvents' );
      }).fail(function(response) {
        swal({
              title: "Oops...",
              text: "Something went wrong!",
              confirmButtonColor: "#EF5350",
              type: "error"
          });
      });
  })

})

var createDate = function() {
  var date = $('.timepicker').val();
  var time = $('.datepicker').val();
  var dateTime = date + " " + time;
  return (new Date(dateTime));
}

Add block ui to ajax call for creating new timeslot


"use strict";

/*
    default options
*/
var defaultOptions = {
    // force additionalProperties and additionalItems to be defined on "object" and "array" types
    forceAdditional: false,
    // force items to be defined on "array" types
    forceItems: false,
    // force maxLength to be defined on "string" types
    forceMaxLength: false,
    // force properties or patternProperties to be defined on "object" types
    forceProperties: false,
    // disallow usage of keywords that this validator can't handle
    noExtraKeywords: false,
    // disallow usage of schema's without "type" defined
    noTypeless: false,
    // disallow zero length strings in validated objects
    noZeroLengthStrings: false,
    // forces "uri" format to be in fully rfc3986 compliant
    strictUris: false,
    // forces "email" format to be validated more strictly
    strictEmails: false,
    // turn on all of the above
    strict: false
};

/*
    constructor
*/
function ZSchema(options) {
    this.options = options;
}

/*
    instance methods
*/
ZSchema.prototype.compileSchema = function (schema, callback) {

};
ZSchema.prototype.validateSchema = function (schema, callback) {

};
ZSchema.prototype.validate = function (json, schema, callback) {

};

/*
    static methods
*/
ZSchema.registerFormat = function (formatName, validatorFunction) {

};

module.exports = ZSchema;

"use strict";

/*
    default options
*/
var defaultOptions = {
    // force additionalProperties and additionalItems to be defined on "object" and "array" types
    forceAdditional: false,
    // force items to be defined on "array" types
    forceItems: false,
    // force maxLength to be defined on "string" types
    forceMaxLength: false,
    // force properties or patternProperties to be defined on "object" types
    forceProperties: false,
    // disallow usage of keywords that this validator can't handle
    noExtraKeywords: false,
    // disallow usage of schema's without "type" defined
    noTypeless: false,
    // disallow zero length strings in validated objects
    noZeroLengthStrings: false,
    // forces "uri" format to be in fully rfc3986 compliant
    strictUris: false,
    // forces "email" format to be validated more strictly
    strictEmails: false,
    // turn on all of the above
    strict: false
};

/*
    constructor
*/
function ZSchema(options) {
    this.options = options;
}

/*
    instance methods
*/
ZSchema.prototype.compileSchema = function (schema, callback) {

};
ZSchema.prototype.validateSchema = function (schema, callback) {

};
ZSchema.prototype.validate = function (json, schema, callback) {

};

/*
    static methods
*/
ZSchema.registerFormat = function (formatName, validatorFunction) {

};
ZSchema.registerFormatter = function (formatterName, formatterFunction) {

};

module.exports = ZSchema;

Add an API for formatter functionality.


/* @flow */

import { isPayPalDomain } from '@paypal/sdk-client/src';
import { PopupOpenError as _PopupOpenError, destroy as zoidDestroy, destroyComponents } from 'zoid/src';

import { setupLogger, allowIframe as _allowIframe } from '../lib';
import { getCheckoutComponent } from '../checkout';
import { getButtonsComponent } from '../buttons';
import { Buttons as _ButtonsTemplate } from '../buttons/template';

export const request = {
    addHeaderBuilder: () => {
        // pass
    }
};

export const Buttons = {
    __get__: () => getButtonsComponent()
};

export const Checkout = {
    __get__: () => {
        const component = getCheckoutComponent();
        if (isPayPalDomain()) {
            return component;
        }
    }
};

export const ButtonsTemplate = {
    __get__: () => {
        if (isPayPalDomain()) {
            return _ButtonsTemplate;
        }
    }
};

export const PopupOpenError = {
    __get__: () => {
        if (isPayPalDomain()) {
            return _PopupOpenError;
        }
    }
};

export const allowIframe = {
    __get__: () => {
        if (isPayPalDomain()) {
            return _allowIframe;
        }
    }
};

export const destroyAll = {
    __get__: () => {
        if (isPayPalDomain() || __TEST__) {
            return destroyComponents;
        }
    }
};

export function setup() {
    setupLogger();
    getButtonsComponent();
    getCheckoutComponent();
}

export function destroy() {
    zoidDestroy();
}

/* @flow */

import { isPayPalDomain } from '@paypal/sdk-client/src';
import { PopupOpenError as _PopupOpenError, destroy as zoidDestroy, destroyComponents } from 'zoid/src';

import { setupLogger, allowIframe as _allowIframe } from '../lib';
import { getCheckoutComponent } from '../checkout';
import { getButtonsComponent } from '../buttons';
import { Buttons as _ButtonsTemplate } from '../buttons/template';

function protectedExport<T>(xport : T) : ?T {
    if (isPayPalDomain()) {
        return xport;
    }
}

export const request = {
    addHeaderBuilder: () => {
        // pass
    }
};

export const Buttons = {
    __get__: () => getButtonsComponent()
};

export const Checkout = {
    __get__: () => protectedExport(getCheckoutComponent())
};

export const ButtonsTemplate = {
    __get__: () => protectedExport(_ButtonsTemplate)
};

export const PopupOpenError = {
    __get__: () => protectedExport(_PopupOpenError)
};

export const allowIframe = {
    __get__: () => protectedExport(_allowIframe)
};

export const forceIframe = {
    __get__: () => protectedExport(_allowIframe)
};

export const destroyAll = {
    __get__: () => protectedExport(destroyComponents)
};

export function setup() {
    setupLogger();
    getButtonsComponent();
    getCheckoutComponent();
}

export function destroy() {
    zoidDestroy();
}

Add forceIframe as a protected export


import User from 'src/models/User';
import { auth } from 'src/config/firebase-admin';

User.prototype.login = function login(email, password) {
  return auth
    .signInWithEmailAndPassword(email, password)
    .then((firebaseUser) => {
      this.uid = firebaseUser.uid;
      this.displayName = firebaseUser.displayName;
      this.email = firebaseUser.email;
      this.isAnonymous = firebaseUser.isAnonymous;
      this.emailVerified = firebaseUser.emailVerified;
      this.photoURL = firebaseUser.photoURL;
      this.refreshToken = firebaseUser.refreshToken;

      return this;
    });
}

User.prototype.logout = () => auth.signOut();

export default User;

import User from 'src/models/User';
import { auth } from 'src/config/firebase';

User.prototype.login = function login(email, password) {
  return auth
    .signInWithEmailAndPassword(email, password)
    .then((firebaseUser) => {
      this.uid = firebaseUser.uid;
      this.displayName = firebaseUser.displayName;
      this.email = firebaseUser.email;
      this.isAnonymous = firebaseUser.isAnonymous;
      this.emailVerified = firebaseUser.emailVerified;
      this.photoURL = firebaseUser.photoURL;
      this.refreshToken = firebaseUser.refreshToken;

      return this;
    });
}

User.prototype.logout = () => auth.signOut();

export default User;

Use firebase here, server never imports this


module.exports = function sendJSON(res, next) {
  return _sendJSON(error, data) {
    if (error) {
      return next(error);
    }
    return res.json(data);
  }
};

module.exports = function sendJSON(res, next) {
  return function _sendJSON(error, data) {
    if (error) {
      return next(error);
    }
    return res.json(data);
  }
};

Fix syntax error - missing function declaration


'use strict';

angular.module('impactApp').constant('banettes', [
  {
    id: 'toutes',
    label: 'Toutes'
  },
  {
    id: 'emise',
    label: 'Émise'
  },
  {
    id: 'complet',
    label: 'Complète'
  },
  {
    id: 'incomplet',
    label: 'Incomplète'
  },
  {
    id: 'archive',
    label: 'Archivée'
  }
]);

'use strict';

angular.module('impactApp').constant('banettes', [
  {
    id: 'toutes',
    label: 'Toutes'
  },
  {
    id: 'emise',
    label: 'Émise'
  },
  {
    id: 'complet',
    label: 'Complète'
  },
  {
    id: 'incomplet',
    label: 'Incomplète'
  },
  {
    id: 'en_cours',
    label: 'En cours de saisie'
  },
  {
    id: 'archive',
    label: 'Archivée'
  }
]);

Add display of new status


import React from 'react';
import { connect } from 'react-redux';
import { Redirect } from 'react-router-dom';
import { Toolbar, ToolbarTitle } from 'material-ui/Toolbar';
import Paper from 'material-ui/Paper';

import styles from './styles.css';

import LoginForm from './LoginForm';

const LoginPage = ({ isAuthenticated, location }) => {
    const { from } = location.state || { from: { pathname: '/' } };
    return isAuthenticated
        ? <Redirect t={ from } />
        :

                    <Paper className="modal">
                        <Toolbar>
                            <ToolbarTitle
                                text="Welcome back."
                                style={{ color: 'white '}} />
                        </Toolbar>
                        <LoginForm />
                    </Paper>

};

export default connect(
    ({ root }) => ({
        isAuthenticated: root.isAuthenticated
    })
)(LoginPage);

import React from 'react';
import { connect } from 'react-redux';
import { Redirect } from 'react-router-dom';
import { Toolbar, ToolbarTitle } from 'material-ui/Toolbar';
import Paper from 'material-ui/Paper';

import styles from './styles.css';

import LoginForm from './LoginForm';

const LoginPage = ({ isAuthenticated, location }) => {
    const { from } = location.state || { from: { pathname: '/' } };
    return isAuthenticated
        ? <Redirect to={ from } />
        :

                    <Paper className="modal">
                        <Toolbar>
                            <ToolbarTitle
                                text="Welcome back."
                                style={{ color: 'white '}} />
                        </Toolbar>
                        <LoginForm />
                    </Paper>

};

export default connect(
    ({ root }) => ({
        isAuthenticated: root.isAuthenticated
    })
)(LoginPage);

Fix broken redirect after login


Template.taggedCourses.helpers({
    'courses': function () {
        return Courses.find().fetch();
    },
    'tag': function () {
      // Get reference to template instance
      var instance = this;

      // Get tag from instance
      var tag = instance.tag;

      return tag;
    }
});

Template.taggedCourses.onCreated(function(){
  // Get reference to template instance
  var instance = this;

  // Accessing the Iron.controller to invoke getParams method of Iron Router.
  var router = Router.current();

  // Getting Params of the URL
  instance.tag = router.params.tag;

  // Subscribe to courses tagged with the current tag
  instance.subscribe('taggedCourses', instance.tag);

  // Subscribe to course images
  instance.subscribe('images');
});

Template.taggedCourses.rendered = function () {
  // Get reference to template instance
  var instance = this;

  // Set the page site title for SEO
  Meta.setTitle('Courses tagged "' + instance.tag + '"');
};

Set page title on render


var util = require('util'),
    OxideClient = require('./oxide_client.js'),
    Metric = require('../metrics/metric.js');

function CarbonClient (opts) {
  OxideClient.call(this);
}
util.inherits(CarbonClient, OxideClient);

CarbonClient.prototype.record = function (path, value, timestamp) {
  var opts = { path: this._pathify(path), value: value }
  if (typeof timestamp !== 'undefined') {
    opts.timestamp = timestamp
  }

  return this.enqueue(new Metric(opts));
}

module.exports = CarbonClient;

var util = require('util'),
    OxideClient = require('./oxide_client.js'),
    Metric = require('../metrics/metric.js');

function CarbonClient (opts) {
  OxideClient.call(this, opts);
}
util.inherits(CarbonClient, OxideClient);

CarbonClient.prototype.record = function (path, value, timestamp) {
  var opts = { path: this._pathify(path), value: value }
  if (typeof timestamp !== 'undefined') {
    opts.timestamp = timestamp
  }

  return this.enqueue(new Metric(opts));
}

module.exports = CarbonClient;

Fix passing opts through carbon client to oxide client


var __path__ = '../../src/components/ArtworkImage.js';

jest.dontMock(__path__);

describe('ArtworkImage', function() {
  var React, TestUtils, ArtworkImage;
  var artworkImage;

  beforeEach(function() {
    React = require('react/addons');
    TestUtils = React.addons.TestUtils;
    ArtworkImage = require(__path__);

    artworkImage = TestUtils.renderIntoDocument(
      <ArtworkImage path='/artwork/19199193' />
    )
  });

  it('works', function() {
    expect(true).toBeTruthy();
  });
});

var __path__ = '../../src/components/ArtworkImage.js';

jest.dontMock(__path__);

describe('ArtworkImage', function() {
  var React, TestUtils, ArtworkImage;
  var domNode, artworkImage;

  beforeEach(function() {
    React = require('react/addons');
    TestUtils = React.addons.TestUtils;
    ArtworkImage = require(__path__);

    artworkImage = TestUtils.renderIntoDocument(
      <ArtworkImage path='/artwork/19199193' />
    );
    domNode = React.findDOMNode(artworkImage);
  });

  describe('in the initial state', function() {
    it('should not be visible', function() {
      var classes = domNode.getAttribute('class').split(' ');
      expect(classes).not.toContain('visible');
    });
  });

  describe('after the image has loaded', function() {
    beforeEach(function() {
      TestUtils.Simulate.load(domNode);
    });

    it('should be visible', function() {
      var classes = domNode.getAttribute('class').split(' ');
      expect(classes).toContain('visible');
    });
  });

  describe('after the image has errored', function() {
    beforeEach(function() {
      TestUtils.Simulate.error(domNode);
    });

    it('should be visible', function() {
      var classes = domNode.getAttribute('class').split(' ');
      expect(classes).not.toContain('visible');
    });
  });

});

Add *useful* tests for the ArtworkImage component


import React from 'react';

export default class Todo extends React.Component {
  static propTypes = {
    text: React.PropTypes.string.isRequired
  }

  render() {
    return (
      <li>{this.props.text}</li>
    );
  }
}

import React from 'react';

export default class Todo extends React.Component {
  static propTypes = {
    text: React.PropTypes.string.isRequired
  }

  state = {
    completed: false
  }

  render() {
    const style = {
      textDecoration: this.state.completed ? 'line-through' : undefined,
      cursor: 'default'
    };

    return (
      <li
        style={style}
        onClick={this.toggleCompleted}
      >
        {this.props.text}
      </li>
    );
  }

  toggleCompleted = (event) => {
    this.setState({ completed: !this.state.completed });
  }
}

Add the ability to cross off items on the list


"use strict";

let errors = require('../errors.js');
let Type = require('./type.js');
let Value = require('./value.js');

class NumberValue extends Value {
  constructor(type) {
    super(type);
    this.value = 0;
  }

  assign(newValue) {
    if (typeof newValue == 'number') {
      this.value = newValue;
    } else if (newValue.value !== undefined && typeof newValue.value == 'number') {
      this.value = newValue.value;
    } else {
      throw new errors.Internal(`Trying to assign ${newValue.type} to Number;`);
    }
  }

  equals(other) {
    return this.type == other.type && this.value == other.value;
  }

  innerToString() {
    return `${this.value}`;
  }

  toString() {
    return `${this.value}`;
  }
}

class NumberType extends Type {
  constructor() {
    super(null, null, 'Number');
  }
  equals(other) {
    return other === NumberType.singleton;
  }
  makeDefaultValue() {
    return new NumberValue(this);
  }
  toString() {
    return 'Number';
  }
}

NumberType.singleton = new NumberType();

module.exports = {
  Type: NumberType,
  Value: NumberValue,
};

"use strict";

let errors = require('../errors.js');
let Type = require('./type.js');
let Value = require('./value.js');

class NumberValue extends Value {
  constructor(type) {
    super(type);
    this.value = 0;
  }

  assign(newValue) {
    if (typeof newValue == 'number') {
      this.value = newValue;
    } else if (newValue.value !== undefined && typeof newValue.value == 'number') {
      this.value = newValue.value;
    } else {
      throw new errors.Internal(`Trying to assign ${newValue.type} to Number;`);
    }
  }

  equals(other) {
    return this.type == other.type && this.value == other.value;
  }

  innerToString() {
    return `${this.value}`;
  }

  assignJSON(spec) {
    this.value = spec;
  }

  toJSON() {
    return this.value;
  }

  toString() {
    return `${this.value}`;
  }
}

class NumberType extends Type {
  constructor() {
    super(null, null, 'Number');
  }
  equals(other) {
    return other === NumberType.singleton;
  }
  makeDefaultValue() {
    return new NumberValue(this);
  }
  toString() {
    return 'Number';
  }
}

NumberType.singleton = new NumberType();

module.exports = {
  Type: NumberType,
  Value: NumberValue,
};

Add assignJSON/toJSON to Number types


/*!
 * numbro.js language configuration
 * language : spanish Spain
 * author : Hernan Garcia : https://github.com/hgarcia
 */
(function () {
    var language = {
        delimiters: {
            thousands: '.',
            decimal: ','
        },
        abbreviations: {
            thousand: 'k',
            million: 'mm',
            billion: 'b',
            trillion: 't'
        },
        ordinal: function (number) {
            var b = number % 10;
            return (b === 1 || b === 3) ? 'er' :
                (b === 2) ? 'do' :
                    (b === 7 || b === 0) ? 'mo' :
                        (b === 8) ? 'vo' :
                            (b === 9) ? 'no' : 'to';
        },
        currency: {
            symbol: '€',
            position: 'postfix'
        },
        defaults: {
            currencyFormat: ',0000 a'
        },
        formats: {
            fourDigits: '0000 a',
            fullWithTwoDecimals: ',0.00 $',
            fullWithTwoDecimalsNoCurrency: ',0.00',
			fullWithNoDecimals: ',0 $'
        }
    };

    // Node
    if (typeof module !== 'undefined' && module.exports) {
        module.exports = language;
    }
    // Browser
    if (typeof window !== 'undefined' && this.numbro && this.numbro.language) {
        this.numbro.language('es', language);
    }
}());

/*!
 * numbro.js language configuration
 * language : spanish Spain
 * author : Hernan Garcia : https://github.com/hgarcia
 */
(function () {
    var language = {
        delimiters: {
            thousands: '.',
            decimal: ','
        },
        abbreviations: {
            thousand: 'k',
            million: 'mm',
            billion: 'b',
            trillion: 't'
        },
        ordinal: function (number) {
            var b = number % 10;
            return (b === 1 || b === 3) ? 'er' :
                (b === 2) ? 'do' :
                    (b === 7 || b === 0) ? 'mo' :
                        (b === 8) ? 'vo' :
                            (b === 9) ? 'no' : 'to';
        },
        currency: {
            symbol: '€',
            position: 'postfix'
        },
        defaults: {
            currencyFormat: ',0000 a'
        },
        formats: {
            fourDigits: '0000 a',
            fullWithTwoDecimals: ',0.00 $',
            fullWithTwoDecimalsNoCurrency: ',0.00',
			fullWithNoDecimals: ',0 $'
        }
    };

    // Node
    if (typeof module !== 'undefined' && module.exports) {
        module.exports = language;
    }
    // Browser
    if (typeof window !== 'undefined' && this.numbro && this.numbro.language) {
        this.numbro.language('es-ES', language);
    }
}());

Correct culture code for Español


// Common ajax-handling functions for dashboard-like pages.  Looks for <reload/>
// elements to reload the page, and reports errors or failures via alerts.

g_action_url = "action.php";

// Note that this doesn't run if the $.ajax call has a 'success:' callback that
// generates an error.
$(document).ajaxSuccess(function(event, xhr, options, xmldoc) {
  var fail = xmldoc.documentElement.getElementsByTagName("failure");
  if (fail && fail.length > 0) {
    console.log(xmldoc);
    alert("Action failed: " + fail[0].textContent);
  }
});

// <reload/> element
$(document).ajaxSuccess(function(event, xhr, options, xmldoc) {
	var reload = xmldoc.documentElement.getElementsByTagName("reload");
	if (reload && reload.length > 0) {
        console.log('ajaxSuccess event: reloading page');
		location.reload(true);
	}
});

$(document).ajaxError(function(event, jqXHR, ajaxSettings, thrownError) {
  console.log("ajaxError: " + thrownError);
  console.log(thrownError);
  console.log(jqXHR);
  console.log(jqXHR.responseText);
  console.log(event);
	alert("Ajax error: " + thrownError);
});

// Common ajax-handling functions for dashboard-like pages.  Looks for <reload/>
// elements to reload the page, and reports errors or failures via alerts.

g_action_url = "action.php";

// Note that this doesn't run if the $.ajax call has a 'success:' callback that
// generates an error.
$(document).ajaxSuccess(function(event, xhr, options, xmldoc) {
  var fail = xmldoc.documentElement.getElementsByTagName("failure");
  if (fail && fail.length > 0) {
    console.log(xmldoc);
    alert("Action failed: " + fail[0].textContent);
  }
});

// <reload/> element
$(document).ajaxSuccess(function(event, xhr, options, xmldoc) {
	var reload = xmldoc.documentElement.getElementsByTagName("reload");
	if (reload && reload.length > 0) {
        console.log('ajaxSuccess event: reloading page');
		location.reload(true);
	}
});

$(document).ajaxError(function(event, jqXHR, ajaxSettings, thrownError) {
  console.log("ajaxError: " + thrownError);
  console.log(thrownError);
  console.log(jqXHR);
  console.log("Response text: " + jqXHR.responseText);
  console.log("ReadyState: " + jqXHR.readyState);
  console.log("status: " + jqXHR.status);
  console.log(event);
	alert("Ajax error: " + thrownError);
});

Add some additional logging for ajax failures.


CommitMessages = new Mongo.Collection("CommitMessages");
RepositoryList = new Mongo.Collection("RepositoryList");
Announcements  = new Mongo.Collection("Announcements");
MentorQueue    = new Mongo.Collection("MentorQueue");
AnonReports    = new Mongo.Collection('AnonReports');
AnonUserData   = new Mongo.Collection('AnonUserData');

Initialize collection for anonymous data


function booleanEditorController($scope, $rootScope, assetsService) {

    function setupViewModel() {
        $scope.renderModel = {
            value: false
        };

        if ($scope.model.config && $scope.model.config.default && $scope.model.config.default.toString() === "1" && $scope.model && !$scope.model.value) {
            $scope.renderModel.value = true;
        }

        if ($scope.model && $scope.model.value && ($scope.model.value.toString() === "1" || angular.lowercase($scope.model.value) === "true")) {
            $scope.renderModel.value = true;
        }
    }

    setupViewModel();

    //here we declare a special method which will be called whenever the value has changed from the server
    //this is instead of doing a watch on the model.value = faster
    $scope.model.onValueChanged = function (newVal, oldVal) {
        //update the display val again if it has changed from the server
        setupViewModel();
    };

    // Update the value when the toggle is clicked
    $scope.toggle = function(){
        if($scope.renderModel.value){
            $scope.model.value = "0";
            setupViewModel();
            return;
        }

        $scope.model.value = "1";
        setupViewModel();
    };

}
angular.module("umbraco").controller("Umbraco.PropertyEditors.BooleanController", booleanEditorController);

function booleanEditorController($scope, $rootScope, assetsService) {

    function setupViewModel() {
        $scope.renderModel = {
            value: false
        };

        if ($scope.model.config && $scope.model.config.default && $scope.model.config.default.toString() === "1" && $scope.model && !$scope.model.value) {
            $scope.renderModel.value = true;
        }

        if ($scope.model && $scope.model.value && ($scope.model.value.toString() === "1" || angular.lowercase($scope.model.value) === "true")) {
            $scope.renderModel.value = true;
        }
    }

    setupViewModel();

    if( $scope.model && !$scope.model.value ) {
        $scope.model.value = ($scope.renderModel.value === true) ? '1' : '0';
    }

    //here we declare a special method which will be called whenever the value has changed from the server
    //this is instead of doing a watch on the model.value = faster
    $scope.model.onValueChanged = function (newVal, oldVal) {
        //update the display val again if it has changed from the server
        setupViewModel();
    };

    // Update the value when the toggle is clicked
    $scope.toggle = function(){
        if($scope.renderModel.value){
            $scope.model.value = "0";
            setupViewModel();
            return;
        }

        $scope.model.value = "1";
        setupViewModel();
    };

}
angular.module("umbraco").controller("Umbraco.PropertyEditors.BooleanController", booleanEditorController);

Set a initial $scope.model.value for true/false


( function( global, chrome, d )
{
    // I know, UGLY, but it's just a prototype.
    function View( $element, manager )
    {
        this.manager  = manager;
        this.$left    = $element.find( '.left' );
        this.$right   = $element.find( '.right' );
    }

    View.prototype.display = function()
    {
        var self = this;

        self.manager.getSections( function( sections )
        {
            sections.forEach( function( section )
            {
                var $column = ( section.title.substring( 0, 1 ) === '+' ) ? self.$left : self.$right;

                $s = $( '

' );

                $s.append( '
' + section.title.substring( 1 ) + '
' );
                $column.append( $s );

                ( function( $section )
                {
                    self.manager.getBookmarks(
                        section,
                        function( bookmarks )
                        {
                            bookmarks.forEach( function( bookmark )
                            {
                                var $link = $( '' + bookmark.title + '' );

                                $section.append( $link );
                            } );
                        }
                    );

                } )( $s );
            } );
        } );
    }

    d.View = View;

} )( this, chrome, this.Dashboard = this.Dashboard || {} );

var $c = $( '#container' ), $s;

( function( global, chrome, d )
{
    // I know, UGLY, but it's just a prototype.
    // FIXME: Refactor, improve, rewrite from scrath, whatever but make it pretty and efficient !
    function View( $element, manager )
    {
        this.manager  = manager;
        this.$left    = $element.find( '.left' );
        this.$right   = $element.find( '.right' );
    }

    View.prototype.display = function()
    {
        var self = this;

        self.manager.getSections( function( sections )
        {
            sections.forEach( function( section )
            {
                var $column    = ( section.title.substring( 0, 1 ) === '+' ) ? self.$left : self.$right,
                    $s         = $( '

' );
                    $s[ 0 ].id = section.id;

                $s.append( '
' + section.title.substring( 1 ) + '
' );
                $column.append( $s );

                ( function( $section )
                {
                    self.manager.getBookmarks(
                        section,
                        function( bookmarks )
                        {
                            bookmarks.forEach( function( bookmark )
                            {
                                var $link           = $( '' + bookmark.title + '' );
                                    $link[ 0 ].id   = bookmark.id;
                                    $link[ 0 ].href = bookmark.url;

                                $section.append( $link );
                            } );
                        }
                    );

                } )( $s );
            } );
        } );
    }

    d.View = View;

} )( this, chrome, this.Dashboard = this.Dashboard || {} );

var $c = $( '#container' ), $s;

Set each bookmark's identifier on its DOM element 'id' attribute.


var gulp = require('gulp');
var browserSync = require('browser-sync');

gulp.task('default', ['serve']);

gulp.task('serve', function() {
  browserSync.init(null, {
    server: {
      baseDir: './'
    },
    notify: false
  });

  gulp.watch([
    './*.html',
    './css/**/*.css',
    './js/**/*.js'
  ], browserSync.reload);
});

var gulp = require('gulp');
var browserSync = require('browser-sync');

gulp.task('default', ['serve']);

gulp.task('serve', function() {
  browserSync.init(null, {
    server: {
      baseDir: './'
    },
    notify: false
  });

  gulp.watch([
    './*.html',
    './css/**/*.css',
    './js/**/*.js'
  ], function() {
    browserSync.reload({ once: true });
  });
});

Make sure that the reload only triggers once for multiple file updates.


var gulp = require( 'gulp' ),
	bower = require( 'gulp-bower' ),
	del = require( 'del' ),
	vui = require( 'vui-helpers' );

gulp.task( 'clean', function( cb ) {
	del([ 'accordion.css' ], cb);
} );

gulp.task( 'lib', function() {
	return bower( 'lib/' );
} );

gulp.task( 'css', function () {
	return vui.makeCss(
		'accordion.css.less',
		'accordion.css',
		{ 'lintOpts' : '.csslintrc' }
	);
} );

gulp.task( 'default', [ 'clean' ], function() {
	gulp.start( 'css' );
} );

gulp.task( 'test', [ 'lib' ], function () {
	return vui.test(
			'test/unit/karma.conf.js',
			[
				'lib/jquery/jquery.min.js',
				'lib/jquery.ui/ui/jquery.ui.core.js',
				'lib//jquery.ui/ui/jquery.ui.widget.js',
				'test/unit/**/*Spec.js'
			],
			'accordion.css'
		);
} );

var gulp = require( 'gulp' ),
	bower = require( 'gulp-bower' ),
	del = require( 'del' ),
	vui = require( 'vui-helpers' );

gulp.task( 'clean', function( cb ) {
	del([ 'accordion.css' ], cb);
} );

gulp.task( 'lib', function() {
	return bower( 'lib/' );
} );

gulp.task( 'css', function () {
	return vui.makeCss(
		'accordion.css.less',
		'accordion.css',
		{ 'lintOpts' : '.csslintrc' }
	);
} );

gulp.task( 'default', [ 'clean' ], function() {
	gulp.start( 'css' );
} );

gulp.task( 'test', [ 'lib' ], function () {
	return vui.test(
			'test/unit/karma.conf.js',
			[
				'lib/jquery/jquery.min.js',
				'lib/jquery.ui/ui/jquery.ui.core.js',
				'lib//jquery.ui/ui/jquery.ui.widget.js',
				'accordion.js',
				'test/unit/**/*Spec.js'
			],
			'accordion.css'
		);
} );

Include accordion.js in list of JavaScript files for testing.


var gulp = require('gulp');
var webpack = require('webpack');

var clientConfig = require('./config/webpack.prod.client.js')
var serverConfig = require('./config/webpack.prod.server.js')

gulp.task('bundle-client', function(done) {
    webpack( clientConfig ).run(onBundle(done))
});

gulp.task('bundle-server', function(done) {
    webpack( serverConfig ).run(onBundle(done))
});

gulp.task('move-index', function() {
	gulp.src('./src/index.html').pipe(gulp.dest('./app'))
});

gulp.task('move-assets', ['move-index'], function() {
	gulp.src('./src/assets/**/*', {base: './src'}).pipe(gulp.dest('./app/'))
});

gulp.task('bundle', ['bundle-client', 'bundle-server']);
gulp.task('build', ['bundle', 'move-assets'])

function onBundle(done) {
    return function(err, stats) {
        if (err) console.log('Error', err);
        else console.log(stats.toString());
        done()
    }
}

var gulp = require('gulp');
var webpack = require('webpack');

var clientConfig = require('./config/webpack.prod.client.js')
var serverConfig = require('./config/webpack.prod.server.js')

gulp.task('bundle-client', function(done) {
    webpack( clientConfig ).run(onBundle(done))
});

gulp.task('bundle-server', function(done) {
    webpack( serverConfig ).run(onBundle(done))
});

gulp.task('move-assets', function() {
	gulp.src('./src/assets/**/*', {base: './src'}).pipe(gulp.dest('./app/'))
});

gulp.task('bundle', ['bundle-client', 'bundle-server']);
gulp.task('build', ['bundle', 'move-assets'])  // TODO - elctron build

function onBundle(done) {
    return function(err, stats) {
        if (err) console.log('Error', err);
        else console.log(stats.toString());
        done()
    }
}

Remove gulp html task; done by webpack


'use strict';

var Db       = require('dbjs')
  , relation = module.exports = require('dbjs/lib/_relation');

require('./base');

relation.set('toDOMInputBox', function (document/*, options*/) {
	var box, options = arguments[1];
	box = this.ns.toDOMInputBox(document, options);
	box.set(this.objectValue);
	box.setAttribute('name', this._id_);
	if (this.required && (!options || (options.type !== 'checkbox'))) {
		box.setAttribute('required', true);
	}
	this.on('change', function () { box.set(this.objectValue); });
	return box;
});

relation.set('toDOMInput', Db.Base.prototype.toDOMInput);
relation._getRel_('fieldHint').ns = Db.String;
relation.set('DOMId', function () {
	return this._id_.replace(/:/g, '-');
});

'use strict';

var Db       = require('dbjs')
  , relation = module.exports = require('dbjs/lib/_relation');

require('./base');

relation.set('toDOMInputBox', function (document/*, options*/) {
	var box, options = arguments[1];
	box = this.ns.toDOMInputBox(document, options);
	box.set(this.objectValue);
	box.setAttribute('name', this._id_);
	if (this.required && (!options || (options.type !== 'checkbox'))) {
		box.setAttribute('required', true);
	}
	this.on('change', function () { box.set(this.objectValue); });
	return box;
});

relation.set('toDOMInput', Db.Base.prototype.toDOMInput);
relation.get('fieldHint').ns = Db.String;
relation.set('DOMId', function () {
	return this._id_.replace(/:/g, '-');
});

Update up to changes in dbjs


'use strict';

/* global TbplJob, Notification */

setTimeout(checkResult, 5000);

function checkResult() {
  var tbpl = new TbplJob();
  var result = 'Fail!';
  if (tbpl.isDone()) {
    if (tbpl.isSuccessful()) {
      result = 'Success!';
    }
    Notification.requestPermission(function() {
      var notification = new Notification(tbpl.description, { body: result });
      notification.onclick = function() {
        window.focus();
      };
    });
  } else {
    setTimeout(checkResult, 5000);
  }
}

'use strict';

/* global TbplJob, Notification */

(function() {
  var container = document.querySelector('#container');

  addTrackingSign();
  setTimeout(checkResult, 5000);

  function checkResult() {
    var tbpl = new TbplJob();
    var result = 'Fail!';
    if (tbpl.isDone()) {
      if (tbpl.isSuccessful()) {
        result = 'Success!';
      }

      Notification.requestPermission(function() {
        var notification = new Notification(tbpl.description, { body: result });
        notification.onclick = function() {
          window.focus();
        };
      });

      removeTrackingSign();
    } else {
      setTimeout(checkResult, 5000);
    }
  }

  function addTrackingSign() {
    container.style.borderStyle = 'dashed';
    // Firefox light orange, https://www.mozilla.org/en-US/styleguide/identity/firefox/color/.
    container.style.borderColor = '#FF9500';
  }

  function removeTrackingSign() {
    container.style.borderStyle = null;
    container.style.borderColor = null;
  }
})();

Add tracking sign as a border of the container element


(function(){
  var cleanNumber = function(i) {
    return i.replace(/[^\-?0-9.]/g, '');
  },

  compareNumberReverse = function(a, b) {
    a = parseFloat(a);
    b = parseFloat(b);

    a = isNaN(a) ? 0 : a;
    b = isNaN(b) ? 0 : b;

    return b - a;
  };

  Tablesort.extend('reverse_number', function(item) {
    return item.match(/^-?[£\x24Û¢´€]?\d+\s*([,\.]\d{0,2})/) || // Prefixed currency
      item.match(/^-?\d+\s*([,\.]\d{0,2})?[£\x24Û¢´€]/) || // Suffixed currency
      item.match(/^-?(\d)*-?([,\.]){0,1}-?(\d)+([E,e][\-+][\d]+)?%?$/); // Number
  }, function(a, b) {
    a = cleanNumber(a);
    b = cleanNumber(b);

    return compareNumberReverse(b, a);
  });
}());

(function(){
  var cleanNumber = function(i) {
      var n = parseFloat(i.replace(/[^\-?0-9.]/g, ''));

      return isNaN(n) ? null : n;
  },

  compareNumberReverse = function(a, b) {
      // Treat null as always greater
      if (a === null) return 1;
      if (b === null) return -1;

      return a - b;
  };

  Tablesort.extend('reverse_number', function(item) {
    return item.match(/^-?[£\x24Û¢´€]?\d+\s*([,\.]\d{0,2})/) || // Prefixed currency
      item.match(/^-?\d+\s*([,\.]\d{0,2})?[£\x24Û¢´€]/) || // Suffixed currency
      item.match(/^-?(\d)*-?([,\.]){0,1}-?(\d)+([E,e][\-+][\d]+)?%?$/); // Number
  }, function(a, b) {
      return compareNumberReverse(cleanNumber(a), cleanNumber(b));
  });
}());

Sort null risk levels correctly


(function registerStackProcessor() {

  widget.parser.register( 'stack', {
    prefix: '$',
    doc: {
      name: "${<i>variable</i>}"
    },
    recursive: true,
    processor: decode
  });

  function decode( token, context )
  {
    var sep = token.indexOf( ',' );
    if( sep > -1 )
    {
      var dbName = token.substring( 0, sep );
      var path = token.substring( sep+1 ).split( '|', 2 );
      if( path.length > 1 )
        return widget.util.get( dbName, path[0], path[1] );
      else
        return widget.util.get( dbName, path[0] );
    }
    else
    {
      context = context || (widget.util.getStack() || [])[0];
      return widget.get( context, token ) || "${" + token + "}";
    }
  }

})();

(function registerStackProcessor() {

  widget.parser.register( 'stack', {
    prefix: '$',
    doc: {
      name: "${<i>variable</i>}"
    },
    recursive: true,
    processor: decode
  });

  function decode( token, context )
  {
    var sep = token.indexOf( ',' );
    var path;
    if( sep > -1 )
    {
      var dbName = token.substring( 0, sep );
      path = token.substring( sep+1 ).split( '|', 2 );
      return widget.util.get( dbName, path[0], path[1] );
    }
    else
    {
      context = context || (widget.util.getStack() || [])[0];
      path = token.split( '|', 2 );
      return widget.get( context, path[0], path[1] ) || "${" + token + "}";
    }
  }

})();

Fix bug not letting default values through in ${} templates


L.RotatedMarker = L.Marker.extend({
    options: {
        angle: 0
    },
	statics: {
		// determine the best and only CSS transform rule to use for this browser
		bestTransform: L.DomUtil.testProp([
			'transform',
			'WebkitTransform',
			'msTransform',
			'MozTransform',
			'OTransform'
		])
	},
    _setPos: function (pos) {
        L.Marker.prototype._setPos.call(this, pos);

		var rotate = ' rotate(' + this.options.angle + 'deg)';
		if (L.RotatedMarker.bestTransform) {
			// use the CSS transform rule if available
			this._icon.style[L.RotatedMarker.bestTransform] += rotate;
		} else if(L.Browser.ie) {
			// fallback for IE6, IE7, IE8
			var rad = this.options.angle * L.LatLng.DEG_TO_RAD,
				costheta = Math.cos(rad),
				sintheta = Math.sin(rad);
			this._icon.style.filter += ' progid:DXImageTransform.Microsoft.Matrix(sizingMethod=\'auto expand\', M11=' +
				costheta + ', M12=' + (-sintheta) + ', M21=' + sintheta + ', M22=' + costheta + ')';
		}
    }
});

Replace tabs coming from bad editor conf with spaces


var test = require('tap').test;
var browserify = require('browserify');
var vm = require('vm');

function bundle (file) {
    test('bundle transform', function (t) {
        t.plan(1);

        var b = browserify();
        b.add(__dirname + file);
        b.transform(__dirname + '/..');
        b.bundle(function (err, src) {
            if (err) t.fail(err);
            testBundle(src, t);
        });
    });
}

bundle('/../example/bar.js');

function testBundle(src, t) {
    function log (msg) {
        t.equal(msg, 555);
    }
    vm.runInNewContext(src, {
        console: { log: log }
    });
}

var test = require('tap').test;
var browserify = require('browserify');
var vm = require('vm');

test('no options bundle', function(t) {
    t.plan(1);
    var b = browserify();
    b.add(__dirname + '/../example/bar.js');
    b.transform(__dirname + '/..');
    b.bundle(function (err, src) {
        if (err) t.fail(err);
        testBundle(src, t);
    });
});

test('options bundle', function(t) {
    t.plan(1);
    var b = browserify();
    b.add(__dirname + '/../example/bar.js');
    b.transform(require('../index.js').jade({
        pretty: false
    }));
    b.bundle(function (err, src) {
        if (err) t.fail(err);
        testBundle(src, t);
    });
});

function testBundle(src, t) {
    function log (msg) {
        t.equal(msg, 555);
    }
    vm.runInNewContext(src, {
        console: { log: log }
    });
}

Add test for options transfomr


var tests = Object.keys(window.__karma__.files).filter(function (file) {
    return (/Spec\.js$/).test(file);
});

requirejs.config({
    // Karma serves files from '/base'
    baseUrl: "/base",

    paths: {
        "sugar-web": ".",
        "mustache": "lib/mustache",
        "text": "lib/text"
    },

    // ask Require.js to load these files (all our tests)
    deps: tests,

    // start test run, once Require.js is done
    callback: window.__karma__.start
});

var tests = Object.keys(window.__karma__.files).filter(function (file) {
    return (/Spec\.js$/).test(file);
});

requirejs.config({
    // Karma serves files from '/base'
    baseUrl: "/base",

    paths: {
        "sugar-web": ".",
        "mustache": "lib/mustache",
        "text": "lib/text",
        "webL10n": "lib/webL10n"
    },

    // ask Require.js to load these files (all our tests)
    deps: tests,

    // start test run, once Require.js is done
    callback: window.__karma__.start
});

Add webL10n missing definition path


define([
    'rivets',
    'jquery',

    'rivets-backbone-adapter'
], function (rivets, $) {
    'use strict';

    rivets.formatters.join = function (array, separator) {
        return $.isArray(array) ? array.join(separator) : array;
    };

    rivets.formatters.contains = function (array, needle) {
        return $.isArray(array) ? $.inArray(needle, array) > -1 : false;
    };

    rivets.formatters.not = function (value) {
        return !value;
    };

    rivets.formatters.unit = function(value, unit) {
        return value || value === 0 ? value + unit : value;
    };

    return rivets;
});

define([
    'rivets',
    'underscore',

    'rivets-backbone-adapter'
], function (rivets, _) {
    'use strict';

    /* Make sure Array.isArray is available (should be). */
    if (!Array.isArray) {
        Array.isArray = function(arg) {
            return Object.prototype.toString.call(arg) === '[object Array]';
        };
    }

    rivets.formatters.join = function (array, separator) {
        return isArray(array) ? array.join(separator) : array;
    };

    rivets.formatters.contains = function (array, needle) {
        return isArray(array) ? _.indexOf(array, needle) > -1 : false;
    };

    rivets.formatters.not = function (value) {
        return !value;
    };

    rivets.formatters.unit = function(value, unit) {
        return value || value === 0 ? value + unit : value;
    };

    return rivets;
});

Use underscore rather than jQuery for array utils


import fs from 'fs';
import path from 'path';

import { logFatal } from '../log';

const configPath = path.resolve(__dirname, 'config.js');

if (!fs.existsSync(configPath)) {
    logFatal(`No config file called config.js found at ${configPath}`);
}

const config = require('./config.js');
const keys = Object.keys(config);
const missingKeys = ['restroomBotToken', 'kitchenBotToken', 'livingRoomBotToken'].filter(key => {
    return !keys.includes(key);
})

console.log(missingKeys)

if (missingKeys.length != 0) {
    logFatal(`Missing configuration variables: ${missingKeys.join()}`);
}

export default config

import fs from 'fs';
import path from 'path';

import { logFatal } from '../log';

const configPath = path.resolve(__dirname, 'config.js');

if (!fs.existsSync(configPath)) {
    logFatal(`No config file called config.js found at ${configPath}`);
}

// Use require syntax here because ES2015 module declarations are hoisted
const config = require('./config.js');

const keys = Object.keys(config);

const missingKeys = ['restroomBotToken', 'kitchenBotToken', 'livingRoomBotToken'].filter(key => {
    return !keys.includes(key);
});

if (missingKeys.length != 0) {
    logFatal(`Missing configuration variables: ${missingKeys.join()}`);
}

export default config

Remove some debug statements in config parsing


var React = require('react'),
  {Link} = require('react-router');

module.exports = React.createClass({
  render: function() {
    let channel = '';
    if (this.props.params && this.props.params.name) {
      channel = `@${this.props.params.name}`;
    } else if (this.props.params && this.props.params.category) {
      channel = this.props.params.category
    }

    return (

          {this.props.edit && <Link to="/profile/edit" className="trigger">format_paint</Link>}
          {this.props.likes && thumb_up}
          {this.props.replies && reply}
          {this.props.messages && chat_bubble_outline}
          {this.props.add && <Link to="/write" className="trigger">add</Link>}

    );
  }
});

var React = require('react'),
    {Link} = require('react-router');

module.exports = React.createClass({
  render: function() {
    let channel = '';
    if (this.props.params && this.props.params.name) {
      channel = `@${this.props.params.name}`;
    } else if (this.props.params && this.props.params.category) {
      channel = this.props.params.category
    }

    return (

          {this.props.edit && <Link to="/profile/edit" className="trigger">format_paint</Link>}
          {this.props.likes && thumb_up}
          {this.props.replies && reply}
          {this.props.messages && <Link to={`/messages/${channel}`} className="trigger">chat_bubble_outline</Link>}
          {this.props.add && <Link to="/write" className="trigger">add</Link>}

    );
  }
});

Add messages link to the floating actions bubble


module.exports = {
  facebook: {
    appID: 'FACEBOOK_APP_ID',
    appSecret: 'FACEBOOK_APP_SECRET',
    callbackUrl: 'http://localhost:3000/auth/facebook/callback',
    profileFields: ['id', 'displayName', 'name', 'email', 'gender', 'age_range', 'link', 'picture', 'locale', 'timezone', 'updated_time', 'verified']
  },
  amazon: {
    appID: 'AMAZON_APP_ID',
    appSecret: 'AMAZON_APP_SECRET',
    callbackUrl: 'http://127.0.0.1:3000/auth/amazon/callback'
  }
}

module.exports = {
  facebook: {
    appID: 'FACEBOOK_APP_ID',
    appSecret: 'FACEBOOK_APP_SECRET',
    callbackUrl: 'http://localhost:3000/auth/facebook/callback',
    profileFields: ['id', 'displayName', 'name', 'email', 'gender', 'age_range', 'link', 'picture', 'locale', 'timezone', 'updated_time', 'verified']
  },
  amazon: {
    appID: 'AMAZON_APP_ID',
    appSecret: 'AMAZON_APP_SECRET',
    callbackUrl: 'http://127.0.0.1:3000/auth/amazon/callback'
  },
  google: {
    type: 'service_account',
    project_id: 'GOOGLE_PROJECT_ID',
    private_key_id: 'GOOGLE_PRIVATE_KEY_ID',
    private_key: 'GOOGLE_PRIVATE_KEY',
    client_email: 'GOOGLE_CLIENT_EMAIL',
    client_id: 'GOOGLE_CLIENT_ID',
    auth_uri: 'https://accounts.google.com/o/oauth2/auth',
    token_uri: 'https://accounts.google.com/o/oauth2/token',
    auth_provider_x509_cert_url: 'https://www.googleapis.com/oauth2/v1/certs',
    client_x509_cert_url: 'GOOGLE_CLIENT_x509_CERT_URL'
  }
}

Add template for google cloud api keys


const autoUpdater = require('electron').autoUpdater
const Menu = require('electron').Menu

var state = 'checking'

exports.initialize = function () {
  autoUpdater.on('checking-for-update', function () {
    state = 'checking'
    exports.updateMenu()
  })

  autoUpdater.on('update-available', function () {
    state = 'checking'
    exports.updateMenu()
  })

  autoUpdater.on('update-downloaded', function () {
    state = 'installed'
    exports.updateMenu()
  })

  autoUpdater.on('update-not-available', function () {
    state = 'no-update'
    exports.updateMenu()
  })

  autoUpdater.on('error', function () {
    state = 'no-update'
    exports.updateMenu()
  })

  autoUpdater.setFeedURL('https://electron-api-demos.githubapp.com/mac')
  autoUpdater.checkForUpdates()
}

exports.updateMenu = function () {
  var menu = Menu.getApplicationMenu()
  if (!menu) return

  menu.items.forEach(function (item) {
    if (item.submenu) {
      item.submenu.items.forEach(function (item) {
        switch (item.key) {
          case 'checkForUpdate':
            item.visible = state === 'no-update'
            break
          case 'checkingForUpdate':
            item.visible = state === 'checking'
            break
          case 'restartToUpdate':
            item.visible = state === 'installed'
            break
        }
      })
    }
  })
}

const autoUpdater = require('electron').autoUpdater
const Menu = require('electron').Menu

var state = 'checking'

exports.initialize = function () {
  autoUpdater.on('checking-for-update', function () {
    state = 'checking'
    exports.updateMenu()
  })

  autoUpdater.on('update-available', function () {
    state = 'checking'
    exports.updateMenu()
  })

  autoUpdater.on('update-downloaded', function () {
    state = 'installed'
    exports.updateMenu()
  })

  autoUpdater.on('update-not-available', function () {
    state = 'no-update'
    exports.updateMenu()
  })

  autoUpdater.on('error', function () {
    state = 'no-update'
    exports.updateMenu()
  })

  autoUpdater.setFeedURL('https://electron-api-demos.githubapp.com/updates')
  autoUpdater.checkForUpdates()
}

exports.updateMenu = function () {
  var menu = Menu.getApplicationMenu()
  if (!menu) return

  menu.items.forEach(function (item) {
    if (item.submenu) {
      item.submenu.items.forEach(function (item) {
        switch (item.key) {
          case 'checkForUpdate':
            item.visible = state === 'no-update'
            break
          case 'checkingForUpdate':
            item.visible = state === 'checking'
            break
          case 'restartToUpdate':
            item.visible = state === 'installed'
            break
        }
      })
    }
  })
}

Update URL for new cross-platform endpoint


jQuery(function ($) {
    setup_search_box();
    $('.tablesorter').tablesorter({
        sortList: [[1,0]],
        headers: {
            1: { sorter: 'text'},
            2: { sorter: false }
        }
    });
});

function setup_search_box() {
    var el = $('#my_search_box .search');
    if ( ! el.length ) { return; }

    // Here we have logic for handling cases where user views a repo
    // and then presses "Back" to go back to the list. We want to
    //     (a) NOT scroll to search input if scroll position is offset.
    //          This is a "Back" after scrolling and viewing a repo
    //     (b) Refresh 'search' if search input has some text.
    //          This is a "Back" after clicking on search results
    if ( $(window).scrollTop() == 0 ) {
        el.focus();
    }

    if ( el.val().length ) {
        userList.search(el.val());
    }
}

jQuery(function ($) {
    setup_search_box();
    $('.tablesorter').tablesorter({
        sortList: [[0,0]],
        headers: {
            1: { sorter: 'text'},
            2: { sorter: false }
        }
    });
});

function setup_search_box() {
    var el = $('#my_search_box .search');
    if ( ! el.length ) { return; }

    // Here we have logic for handling cases where user views a repo
    // and then presses "Back" to go back to the list. We want to
    //     (a) NOT scroll to search input if scroll position is offset.
    //          This is a "Back" after scrolling and viewing a repo
    //     (b) Refresh 'search' if search input has some text.
    //          This is a "Back" after clicking on search results
    if ( $(window).scrollTop() == 0 ) {
        el.focus();
    }

    if ( el.val().length ) {
        userList.search(el.val());
    }
}

Fix default sort order. azawawi--


'use strict';

function formatMoney(value) {
  var rem = value - ~~value;
  var arr = String(~~value).split('');
  var result = [];

  while (arr.length) {
    var from = Math.max(0, arr.length - 3);
    result.unshift(arr.splice(from, 3));
  }

  return result.reduce(function (a, b) {
    return a + ' ' + b.join('');
  }) + rem.toFixed(2).slice(1);
}

module.exports = function (value) {
  var remaining = value - ~~value,
      string    = '' + ~~value,
      length    = string.length,
      places    = 0;

  while (--length) {
    places += 1;
    // At every third position we want to insert a comma
    if (places % 3 === 0) {
      string = string.substr(0, length) + ',' + string.substr(length);
    }
  }

  return '$' + string + remaining.toFixed(2).slice(1);
}

Apply functionality from the number format to the money format solution.


import React from 'react'
import { getSiteProps } from 'react-static'
//
import logoImg from '../logo.png'

export default getSiteProps(() => (

Hi, I'm Tanay PrabhuDesai.

I'm a software engineer based in Pune, India.

This website is generated using Frozen Flask

))

import React from 'react'
import { getSiteProps } from 'react-static'
//
import logoImg from '../logo.png'

export default getSiteProps(() => (

Hi, I'm Tanay PrabhuDesai.

I'm a software engineer based in Pune, India.

))

Remove unnessary line from home page


if (!global) {
  global = self;
}

let { fetch, Request, Response, Headers, URL, URLSearchParams } = global;
fetch = fetch.bind(global);
export { fetch, Request, Response, Headers, URL, URLSearchParams };

if (!global.process) {
  global.process = {};
}

if (!global.process.env) {
  global.process.env = {
    NODE_ENV: 'production',
  };
}

if (!global.process.version) {
  global.process.version = '';
}

if (!global.process.hrtime) {
  // Adapted from https://github.com/kumavis/browser-process-hrtime
  global.process.hrtime = function hrtime(previousTimestamp) {
    var clocktime = Date.now() * 1e-3;
    var seconds = Math.floor(clocktime);
    var nanoseconds = Math.floor((clocktime % 1) * 1e9);
    if (previousTimestamp) {
      seconds = seconds - previousTimestamp[0];
      nanoseconds = nanoseconds - previousTimestamp[1];
      if (nanoseconds < 0) {
        seconds--;
        nanoseconds += 1e9;
      }
    }
    return [seconds, nanoseconds];
  };
}

if (!global.os) {
  // FIXME: Add some sensible values
  global.os = {};
}

if (!global) {
  global = self;
}

let { fetch, Request, Response, Headers, URL, URLSearchParams } = global;
fetch = fetch.bind(global);
export { fetch, Request, Response, Headers, URL, URLSearchParams };

if (!global.process) {
  global.process = {};
}

if (!global.process.env) {
  global.process.env = {
    // app is a global available on fly.io
    NODE_ENV: app ? app.env : 'production',
  };
}

if (!global.process.version) {
  global.process.version = '';
}

if (!global.process.hrtime) {
  // Adapted from https://github.com/kumavis/browser-process-hrtime
  global.process.hrtime = function hrtime(previousTimestamp) {
    var clocktime = Date.now() * 1e-3;
    var seconds = Math.floor(clocktime);
    var nanoseconds = Math.floor((clocktime % 1) * 1e9);
    if (previousTimestamp) {
      seconds = seconds - previousTimestamp[0];
      nanoseconds = nanoseconds - previousTimestamp[1];
      if (nanoseconds < 0) {
        seconds--;
        nanoseconds += 1e9;
      }
    }
    return [seconds, nanoseconds];
  };
}

if (!global.os) {
  // FIXME: Add some sensible values
  global.os = {};
}

Use fly.io app env when available


import loggerSpec from 'react-server-module-tagger';
import path from 'path';

module.exports = function() {
	return {
		visitor: {
			Identifier(p, state) {
				const {node} = p;
				const {name} = node;

				const config = { trim: state.opts.trim };
				const parent = path.resolve(path.join(process.cwd(), '..')) + path.sep;
				const fp = this.file.opts.filename.replace(parent, '');
				const file =  { path: fp };

				//TODO: Support labels
				const moduleTag = loggerSpec.bind({ file, config })(fp);

				let tokens;
				if (state.opts.tokens) {
					tokens = new Set(state.opts.tokens);
				} else {
					tokens = new Set(["__LOGGER__", "__CHANNEL__", "__CACHE__"]);
				}

				if (tokens.has(name)) {
					// this strikes me as a dirty, nasty hack.  I think it would be better
					// to parse the object as json and coerce it to an array of
					// ObjectProperties to construct an ObjectExpression
					p.node.name = moduleTag;
				}
			},
		},
	};
}

import loggerSpec from 'react-server-module-tagger';
import path from 'path';

module.exports = function() {
	return {
		visitor: {
			Identifier(p, state) {
				const {node} = p;
				const {name} = node;

				const trim = state.opts.trim;
				const parent = path.resolve(path.join(process.cwd(), '..')) + path.sep;
				const filePath = this.file.opts.filename.replace(parent, '');

				//TODO: Support labels
				const moduleTag = loggerSpec({ filePath, trim });

				let tokens;
				if (state.opts.tokens) {
					tokens = new Set(state.opts.tokens);
				} else {
					tokens = new Set(["__LOGGER__", "__CHANNEL__", "__CACHE__"]);
				}

				if (tokens.has(name)) {
					// this strikes me as a dirty, nasty hack.  I think it would be better
					// to parse the object as json and coerce it to an array of
					// ObjectProperties to construct an ObjectExpression
					p.node.name = moduleTag;
				}
			},
		},
	};
}

Update babel-plugin-react-server to use new react-server-module-tagger interface


'use strict';

import JobClient from '../client';

let apiClient;

class DrushIORun {

  constructor(client, project, job, identifier, data = {}) {
    apiClient = client;
    this.project = project;
    this.job = job || new JobClient(client, project, data.job.id);
    this.identifier = identifier;
    this.data = data;
  }

  /**
   * Retrieves run details for this job run.
   * @return {Promise}
   */
  get() {
    return new Promise((resolve, reject) => {
      apiClient.get(`/projects/${this.project.identifier}/${this.job.identifier}/run/${this.identifier}`).then((response) => {
        this.data = response.body;
        resolve(this)
      }).catch((err) => {
        reject(err);
      });
    });
  }

}

export default DrushIORun;

'use strict';

import JobClient from '../client';

let apiClient;

class DrushIORun {

  constructor(client, project, job, identifier, data = {}) {
    apiClient = client;
    this.project = project;
    this.job = job || new JobClient(client, project, data.job.id);
    this.identifier = identifier;
    this.data = data;
  }

  /**
   * Retrieves run details for this job run.
   * @return {Promise}
   */
  get() {
    return new Promise((resolve, reject) => {
      apiClient.get(`/projects/${this.project.identifier}/jobs/${this.job.identifier}/runs/${this.identifier}`).then((response) => {
        this.data = response.body;
        resolve(this)
      }).catch((err) => {
        reject(err);
      });
    });
  }

}

export default DrushIORun;

Fix bug in job run get() method.


#!/usr/bin/env node

var parseArgs = require('minimist');
var gonzales = require('..');
var fs = require('fs');
var path = require('path');

var options = getOptions();
process.stdin.isTTY ? processFile(options._[0]) : processSTDIN();

function getOptions() {
    var parserOptions = {
        boolean: ['silent'],
        alias: {
            syntax: 's',
            context: 'c'
        }
    };
    return parseArgs(process.argv.slice(2), parserOptions);
}

function processSTDIN() {
    var input = '';
    process.stdin.resume();
    process.stdin.setEncoding('utf8');
    process.stdin.on('data', function (data) {
        input += data;
    });
    process.stdin.on('end', function () {
        processInputData(input);
    });
}

function processFile(file) {
    if (!file) process.exit(0);
    if (!options.syntax) options.syntax = path.extname(file).substring(1);
    var css = fs.readFileSync(file, 'utf-8').trim();
    processInputData(css);
}

function processInputData(input) {
    try {
        var ast = gonzales.parse(input, {
            syntax: options.syntax,
            rule: options.context
        });
        process.stdout.write(ast.toJson());
        process.exit(0);
    } catch (e) {
        if (!options.silent) process.stderr.write(e.message);
        process.exit(1);
    }
}

#!/usr/bin/env node

var parseArgs = require('minimist');
var gonzales = require('..');
var fs = require('fs');
var path = require('path');

var options = getOptions();
process.stdin.isTTY ? processFile(options._[0]) : processSTDIN();

function getOptions() {
    var parserOptions = {
        boolean: ['silent'],
        alias: {
            syntax: 's',
            context: 'c'
        }
    };
    return parseArgs(process.argv.slice(2), parserOptions);
}

function processSTDIN() {
    var input = '';
    process.stdin.resume();
    process.stdin.setEncoding('utf8');
    process.stdin.on('data', function (data) {
        input += data;
    });
    process.stdin.on('end', function () {
        processInputData(input);
    });
}

function processFile(file) {
    if (!file) process.exit(0);
    if (!options.syntax) options.syntax = path.extname(file).substring(1);
    var css = fs.readFileSync(file, 'utf-8').trim();
    processInputData(css);
}

function processInputData(input) {
    try {
        var ast = gonzales.parse(input, {
            syntax: options.syntax,
            rule: options.context
        });
        process.stdout.write(ast.toJson());
        process.exit(0);
    } catch (e) {
        if (!options.silent) process.stderr.write(e.toString());
        process.exit(1);
    }
}

Print more complete error message


// An example configuration file.
exports.config = {
  sauceUser: process.env.SAUCE_USERNAME,
  sauceKey: process.env.SAUCE_ACCESS_KEY,
  allScriptsTimeout: 11000,

  specs: [
    'e2e/specs/*.js'
  ],

  capabilities: {
    'browserName': 'chrome'
  },

  chromeOnly: true,

  baseUrl: 'http://localhost:8000/',

  framework: 'jasmine',

  jasmineNodeOpts: {
    showColors: true,
    defaultTimeoutInterval: 30000
  }
};

// Docs for protractor 0.24:
// https://github.com/angular/protractor/blob/8582b195ed0f4d48a0d9513017b21e99f8feb2fe/docs/api.md
exports.config = {
  sauceUser: process.env.SAUCE_USERNAME,
  sauceKey: process.env.SAUCE_ACCESS_KEY,
  allScriptsTimeout: 11000,

  specs: [
    'e2e/specs/*.js'
  ],

  capabilities: {
    'browserName': 'chrome'
  },

  chromeOnly: true,

  baseUrl: 'http://localhost:8000/',

  framework: 'jasmine',

  jasmineNodeOpts: {
    showColors: true,
    defaultTimeoutInterval: 30000
  }
};

Add comment pointing to appropriate version of protractor API


var app = require('app');
var BrowserWindow = require('browser-window');

var menu = module.exports = [];
var isDarwin = process.platform === 'darwin';

menu.push(
	{
		label: '&File',
		submenu: [
			{
				label: '&Quit',
				accelerator: 'CmdOrCtrl+Q',
				click: function () {
					app.quit();
				}
			}
		]
	}
);

menu.push(
    {
        label: '&Debug',
        submenu: [
            {
                label: '&Reload',
                accelerator: 'CmdOrCtrl+R',
                click: function () {
                    BrowserWindow.getFocusedWindow().reloadIgnoringCache();
                }
            },
            {
                label: 'Toggle &Developer Tools',
                accelerator: isDarwin ? 'Alt+Cmd+I' : 'Ctrl+Shift+I',
                click: function () {
                    BrowserWindow.getFocusedWindow().toggleDevTools();
                }
            }
        ]
    }
);

menu.push(
  {
    label: 'Edit',
    submenu: [
      {
        label: 'Undo',
        accelerator: 'Command+Z',
        selector: 'undo:'
      },
      {
        label: 'Redo',
        accelerator: 'Shift+Command+Z',
        selector: 'redo:'
      },
      {
        type: 'separator'
      },
      {
        label: 'Cut',
        accelerator: 'Command+X',
        selector: 'cut:'
      },
      {
        label: 'Copy',
        accelerator: 'Command+C',
        selector: 'copy:'
      },
      {
        label: 'Paste',
        accelerator: 'Command+V',
        selector: 'paste:'
      },
      {
        label: 'Select All',
        accelerator: 'Command+A',
        selector: 'selectAll:'
      },
    ]
  }
);

Allow copy and paste in electron app


/**
 * Controller to bring functionality for the modal.
 *
 * @author Claire Wilgar
 */

/**
 * Module export of controller function.
 *
 * @ngInject
 * @param {angular.Service} $scope
 * @param {angular.Service} $mdDialog
 * @param {service} ApiService
 * @param {var} item
 */
module.exports = function($scope, $mdDialog, ApiService, item) {

    var self = this;

    self.item = item;

    if (!angular.isUndefined(self.item.available)) {
        console.log(self.item.available);
        self.item.available = new Date(self.item.available);
    }

    self.hide = function() {
        $mdDialog.hide();
    };

    self.cancel = function() {
        $mdDialog.cancel();
    };

    self.submitForm = function(data) {
        ApiService.submitForm(data)
            .then(function() {
                $mdDialog.hide(data);
            }, function(response) {
                $mdDialog.cancel(response);
            });
    };
};

/**
 * Controller to bring functionality for the modal.
 *
 * @author Claire Wilgar
 */

/**
 * Module export of controller function.
 *
 * @ngInject
 * @param {angular.Service} $scope
 * @param {angular.Service} $mdDialog
 * @param {service} ApiService
 * @param {var} item
 */
module.exports = function($scope, $mdDialog, ApiService, item) {

    var self = this;

    self.item = item;

    if (!angular.isUndefined(self.item) && self.item != null) {
        console.log(self.item.available);
        self.item.available = new Date(self.item.available);
    }

    self.hide = function() {
        $mdDialog.hide();
    };

    self.cancel = function() {
        $mdDialog.cancel();
    };

    self.submitForm = function(data) {
        ApiService.submitForm(data)
            .then(function() {
                $mdDialog.hide(data);
            }, function(response) {
                $mdDialog.cancel(response);
            });
    };
};

Fix issue checking date when creating new item


var keystone = require('../../../');
var session = require('../../../lib/session');

module.exports = function (req, res) {
	session.signout(req, res, function () {
		if (typeof keystone.get('signout redirect') === 'string') {
			return res.redirect(keystone.get('signout redirect'));
		} else if (typeof keystone.get('signout redirect') === 'function') {
			return keystone.get('signout redirect')(req, res);
		} else {
			return res.redirect('/' + keystone.get('admin path') + '/signin?signedout');
		}
	});
};

var keystone = require('../../../');
var session = require('../../../lib/session');

module.exports = function (req, res) {
	session.signout(req, res, function () {
		if (typeof keystone.get('signout redirect') === 'string') {
			return res.redirect(keystone.get('signout redirect'));
		} else if (typeof keystone.get('signout redirect') === 'function') {
			return keystone.get('signout redirect')(req, res);
		} else {
			return res.redirect('/' + keystone.get('admin path') + '/signin?signedout');
			// After logging out, the user will be redirected to /signin?signedout
			// It shows a bar on top of the sign in panel saying "You have been signed out".
		}
	});
};

Comment for signin?signedout version added


// All actions, used by actions or reducers, go here

export const CLOSE_CARD = 'CLOSE_CARD'
export const LOAD_CARD_PAGE = 'LOAD_CARD_PAGE'
export const NAVIGATE = 'NAVIGATE'
export const OPEN_CARD = 'OPEN_CARD'

export const RECEIVE_ALL_REGIONS = 'RECEIVE_ALL_REGIONS'
export const RECEIVE_CARD_PAGE_DATA = 'RECEIVE_CARD_PAGE_DATA'
export const RECEIVE_CARD_PAGES = 'RECEIVE_CARD_PAGES'
export const RECEIVE_CARD_QUERY = 'RECEIVE_CARD_QUERY'
export const RECEIVE_CHART_DATA = 'RECEIVE_CHART_DATA'
export const RECEIVE_QUERY_RESULT = 'RECEIVE_QUERY_RESULT'
export const RECEIVE_REGION = 'RECEIVE_REGION'
export const RECEIVE_TABLE_HEADERS = 'RECEIVE_TABLE_HEADERS'
export const RECEIVE_TABLES = 'RECEIVE_TABLES'

export const REQUEST_CARD_QUERY = 'REQUEST_CARD_QUERY'
export const REQUEST_CHART_DATA = 'REQUEST_CHART_DATA'
export const REQUEST_REGION = 'REQUEST_REGION'

export const SELECT_DATA_VIEW = 'SELECT_DATA_VIEW'
export const UPDATE_CARD_QUERY = 'UPDATE_CARD_QUERY'
export const SET_TABLE_VISIBILITY = 'SET_TABLE_VISIBILITY'

Add action for fiddling with the show table button


import Vue from 'vue'
import distanceInWords from 'date-fns/distance_in_words'

export default new Vue({
  data: {
    locale: 'en',
    loadedLocale: 'en',
    locales: {},
    now: new Date(),
  },
  created () {
    setInterval(() => { this.now = new Date() }, 10 * 1000)
  },
  watch: {
    async locale (locale) {
      if (locale === 'zh') locale = 'zh_tw' // https://date-fns.org/v1.29.0/docs/I18n
      Vue.set(this.locales, this.locale, await import(`date-fns/locale/${locale}`))
      this.loadedLocale = this.locale
    },
  },
  methods: {
    distanceInWordsToNow (date, options = {}) {
      if (options.disallowFuture && date > this.now) date = this.now
      return distanceInWords(this.now, date, { locale: this.locales[this.loadedLocale], ...options })
    },
  },
})

import Vue from 'vue'
import distanceInWords from 'date-fns/distance_in_words'

// https://date-fns.org/v1.29.0/docs/I18n
const localeMap = {
  zh: 'zh_tw',
}

export default new Vue({
  data: {
    locale: 'en',
    localeData: null,
    now: new Date(),
  },
  created () {
    setInterval(() => { this.now = new Date() }, 10 * 1000)
  },
  watch: {
    async locale (locale) {
      this.localeData = await import(`date-fns/locale/${localeMap[locale] || locale}`)
    },
  },
  methods: {
    distanceInWordsToNow (date, options = {}) {
      if (options.disallowFuture && date > this.now) date = this.now
      return distanceInWords(this.now, date, { locale: this.localeData, ...options })
    },
  },
})

Simplify dateFns locale data handling + add map


/* See license.txt for terms of usage */

"use strict";

const { Trace, TraceError } = require("../core/trace.js").get(module.id);
const { once } = require("sdk/dom/events.js");
const { getMostRecentBrowserWindow } = require("sdk/window/utils");
const { openTab } = require("sdk/tabs/utils");

// Module implementation
var Win = {};

/**
 * Returns a promise that is resolved as soon as the window is loaded
 * or immediately if the window is already loaded.
 *
 * @param {Window} The window object we need use when loaded.
 */
Win.loaded = win => new Promise(resolve => {
  if (win.document.readyState === "complete") {
    resolve(win.document);
  }
  else {
    once(win, "load", event => resolve(event.target));
  }
});

// xxxHonza: we might want to merge with Win.loaded
Win.domContentLoaded = win => new Promise(resolve => {
  if (win.document.readyState === "complete") {
    resolve(win.document);
  }
  else {
    once(win, "DOMContentLoaded", event => resolve(event.target));
  }
});

Win.openNewTab = function(url, options) {
  let browser = getMostRecentBrowserWindow();
  openTab(browser, url, options);
}

// Exports from this module
exports.Win = Win;

/* See license.txt for terms of usage */

"use strict";

const { Cu } = require("chrome");

const { Trace, TraceError } = require("../core/trace.js").get(module.id);
const { once } = require("sdk/dom/events.js");
const { getMostRecentBrowserWindow } = require("sdk/window/utils");
const { openTab } = require("sdk/tabs/utils");

const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
const { makeInfallible } = devtools["require"]("devtools/toolkit/DevToolsUtils.js");

// Module implementation
var Win = {};

/**
 * Returns a promise that is resolved as soon as the window is loaded
 * or immediately if the window is already loaded.
 *
 * @param {Window} The window object we need use when loaded.
 */
Win.loaded = makeInfallible(win => new Promise(resolve => {
  if (win.document.readyState === "complete") {
    resolve(win.document);
  }
  else {
    once(win, "load", event => resolve(event.target));
  }
}));

// xxxHonza: we might want to merge with Win.loaded
Win.domContentLoaded = makeInfallible(win => new Promise(resolve => {
  if (win.document.readyState === "complete") {
    resolve(win.document);
  }
  else {
    once(win, "DOMContentLoaded", event => resolve(event.target));
  }
}));

Win.openNewTab = function(url, options) {
  let browser = getMostRecentBrowserWindow();
  openTab(browser, url, options);
}

// Exports from this module
exports.Win = Win;

Use make infallible for win load handlers


import { PLUGIN_TYPE } from 'rotonde-plugin';

export const TYPE = PLUGIN_TYPE.ISOMORPHIC;
export { default as ChatClientPlugin } from './client';
export { default as ChatServerPlugin } from './server';

import { PLUGIN_TYPE } from 'rotonde-plugin';
import ChatClientPlugin from './client';
import ChatServerPlugin from './server';

export {
  ChatClientPlugin,
  ChatServerPlugin
};

// Plugin export.
export const TYPE = PLUGIN_TYPE.ISOMORPHIC;
export const ClientPlugin = ChatClientPlugin;
export const ServerPlugin = ChatServerPlugin;

Use consistent exports for plugins


!function(){

  window.attachSpinnerEvents = function () {

    var spinners = document.querySelectorAll('.toggle-spinner'),
      i = spinners.length;

    while (i--) {
      console.log('Attaching spinners');
      var s = spinners[i];

      s.addEventListener('click', function (e) {

        e.preventDefault();
        e.stopPropagation();

        var target = e.target;

        if (e.target.nodeName === 'I') {
          target = e.target.parentNode;
        }

        var spinner = target.parentNode.querySelectorAll('.spinner')[0];
        spinner.classList.toggle('active');
      });
    }

    // Close spinner event handler
    document.addEventListener('click', function (e) {
      var els = document.querySelectorAll('.spinner'),
      o = els.length;
      while (o--) els[o].classList.remove('active');
    });
  }

  attachSpinnerEvents();

}();

!function(){

  window.attachSpinnerEvents = function () {

    var spinners = document.querySelectorAll('.toggle-spinner'),
      i = spinners.length;

    while (i--) {

      var s = spinners[i];

      s.addEventListener('click', function (e) {

        e.preventDefault();
        e.stopPropagation();

        var target = e.target;

        if (e.target.nodeName === 'I') {
          target = e.target.parentNode;
        }

        var spinner = target.parentNode.querySelectorAll('.spinner')[0];
        spinner.classList.toggle('active');
      });
    }

    // Close spinner event handler
    document.addEventListener('click', function (e) {
      var els = document.querySelectorAll('.spinner'),
      o = els.length;
      while (o--) els[o].classList.remove('active');
    });
  }

  attachSpinnerEvents();

}();

Remove comment in the while loop


Package.describe({
  name: 'meteortesting:mocha',
  summary: 'Run Meteor package or app tests with Mocha',
  git: 'https://github.com/meteortesting/meteor-mocha.git',
  documentation: '../README.md',
  version: '0.5.1',
  testOnly: true,
});

Package.onUse(function onUse(api) {
  api.use([
    'practicalmeteor:mocha-core@1.0.0',
    'ecmascript@0.3.0',
    'lmieulet:meteor-coverage@1.1.4',
  ]);

  api.use([
    'browser-policy@1.0.0',
    'http@1.1.1',
    'meteortesting:browser-tests@0.2.0'
  ], 'server');

  api.mainModule('client.js', 'client');
  api.mainModule('server.js', 'server');
});

Package.describe({
  name: 'meteortesting:mocha',
  summary: 'Run Meteor package or app tests with Mocha',
  git: 'https://github.com/meteortesting/meteor-mocha.git',
  documentation: '../README.md',
  version: '0.5.1',
  testOnly: true,
});

Package.onUse(function onUse(api) {
  api.use([
    'practicalmeteor:mocha-core@1.0.0',
    'ecmascript@0.3.0',
    'lmieulet:meteor-coverage@1.1.4 || 2.0.1',
  ]);

  api.use([
    'browser-policy@1.0.0',
    'http@1.1.1',
    'meteortesting:browser-tests@0.2.0'
  ], 'server');

  api.mainModule('client.js', 'client');
  api.mainModule('server.js', 'server');
});

Allow using meteor-coverage 2.x.x, needed for code-coverage support from meteor 1.6.0 and onwards


var env = process.env.NODE_ENV || 'development'
  , config = require('../config/config')[env]
  , nodemailer = require('nodemailer')
  ;

var transporter = nodemailer.createTransport({
    host: process.env.EMAIL_HOST || config.EMAIL_HOST,
    port: process.env.EMAIL_PORT || config.EMAIL_PORT,
    auth: {
        user: process.env.EMAIL || config.EMAIL,
        pass: process.env.EMAIL_PASS || config.EMAIL_PASS
    }
});

module.exports = transporter;

var env = process.env.NODE_ENV || 'development'
  , config = require('../config/config')[env] || {}
  , nodemailer = require('nodemailer')
  ;

var transporter = nodemailer.createTransport({
    host: process.env.EMAIL_HOST || config.EMAIL_HOST,
    port: process.env.EMAIL_PORT || config.EMAIL_PORT,
    auth: {
        user: process.env.EMAIL || config.EMAIL,
        pass: process.env.EMAIL_PASS || config.EMAIL_PASS
    }
});

module.exports = transporter;

Make sure config isn't undefined for any env


"use strict";

module.exports = function(sequelize, DataTypes) {
  var Task = sequelize.define("Task", {
    taskID:       { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true },
    name:         { type: DataTypes.STRING, allowNull: false },
    taskType:     { type: DataTypes.ENUM('todo', 'exercise', 'meal', 'meeting', 'other'), allowNull: false, defaultValue: 'other' },
    date:         { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
    time:         { type: DataTypes.STRING, allowNull: true, defaultValue: null },
    moveable:     { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: true },
    autoMoveable: { type: DataTypes.BOOLEAN, allowNull: true, defaultValue: null },
    description:  { type: DataTypes.TEXT, allowNull: false, defaultValue: '' },
    stravaID:     { type: DataTypes.INTEGER, allowNull: true, defaultValue: null },
    isRecurring:  { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: false },
    recPeriod:    { type: DataTypes.ENUM('weekly', 'monthly'), allowNull: true, defaultValue: null },
    recRange:     { type: DataTypes.INTEGER, allowNull: true, defaultValue: null }
  }, {
    classMethods: {
      associate: function(models) {
        Task.belongsTo(models.User, {
          onDelete: "CASCADE",
          foreignKey: {
            allowNull: false
          }
        });
        Task.hasMany(models.TaskRecurrence);
      }
    }
  });

  return Task;
}

"use strict";

module.exports = function(sequelize, DataTypes) {
  var Task = sequelize.define("Task", {
    taskID:       { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true },
    name:         { type: DataTypes.STRING, allowNull: false },
    taskType:     { type: DataTypes.ENUM('todo', 'exercise', 'meal', 'meeting', 'other'), allowNull: false, defaultValue: 'other' },
    date:         { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
    time:         { type: DataTypes.STRING, allowNull: true, defaultValue: null },
    moveable:     { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: true },
    autoMoveable: { type: DataTypes.BOOLEAN, allowNull: true, defaultValue: null },
    description:  { type: DataTypes.TEXT, allowNull: false, defaultValue: '' },
    stravaID:     { type: DataTypes.INTEGER, allowNull: true, defaultValue: null },
    isRecurring:  { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: false },
    recPeriod:    { type: DataTypes.ENUM('week', 'month'), allowNull: true, defaultValue: null },
    recRange:     { type: DataTypes.INTEGER, allowNull: true, defaultValue: null }
  }, {
    classMethods: {
      associate: function(models) {
        Task.belongsTo(models.User, {
          onDelete: "CASCADE",
          foreignKey: {
            allowNull: false
          }
        });
        Task.hasMany(models.TaskRecurrence);
      }
    }
  });

  return Task;
}

Fix enum (how did this work?)


function Game(){
  this.$track = $('#racetrack');
}

function Player(id){
  this.player = $('.player')[id - 1];
  this.position = 10;
}

Player.prototype.move = function(){
  this.position += 10;
};

Player.prototype.updatePosition = function(){
  $player = $(this.player);
  $player.css('margin-left', this.position);
};

$(document).ready(function() {
    var game = new Game();
    var player1 = new Player(1);
    var player2 = new Player(2);

    $(document).on('keyup', function(keyPress){
      if(keyPress.keyCode === 80) {
        player1.move();
        player1.updatePosition();
      } else if (keyPress.keyCode === 81) {
        player2.move();
        player2.updatePosition();
      }
    });
  });

function Game(){
  this.$track = $('#racetrack');
  this.finishLine = this.$track.width() - 54;
}

function Player(id){
  this.player = $('.player')[id - 1];
  this.position = 10;
}

Player.prototype.move = function(){
  this.position += 10;
};

Player.prototype.updatePosition = function(){
  $player = $(this.player);
  $player.css('margin-left', this.position);
};

$(document).ready(function() {
    var game = new Game();
    var player1 = new Player(1);
    var player2 = new Player(2);

    $(document).on('keyup', function(keyPress){
      if(keyPress.keyCode === 80) {
        player1.move();
        player1.updatePosition();
      } else if (keyPress.keyCode === 81) {
        player2.move();
        player2.updatePosition();
      }
    });
  });

Add finishLine state to Game object


import Ember from 'ember';

export default Ember.Route.extend({
    beforeModel: function(transition) {
        var win = window.open('/github_login', 'Authorization',
                              'width=1000,height=450,' +
                              'toolbar=0,scrollbars=1,status=1,resizable=1,' +
                              'location=1,menuBar=0');
        if (!win) { return; }

        // For the life of me I cannot figure out how to do this other than
        // polling
        var self = this;
        var oauthInterval = window.setInterval(function(){
            if (!win.closed) { return; }
            window.clearInterval(oauthInterval);
            var response = JSON.parse(localStorage.github_response);

            if (!response.ok) {
                self.controllerFor('application').set('flashError',
                                                      'Failed to log in');
                return;
            }
            var data = response.data;
            if (data.errors) {
                var error = "Failed to log in: " + data.errors[0];
                self.controllerFor('application').set('flashError', error);
                return;
            }

            var user = self.store.push('user', data.user);
            var transition = self.session.get('savedTransition');
            self.session.loginUser(user);
            if (transition) {
                transition.retry();
            }
        }, 200);
        transition.abort();
    }
});

import Ember from 'ember';

export default Ember.Route.extend({
    beforeModel: function(transition) {
        var win = window.open('/github_login', 'Authorization',
                              'width=1000,height=450,' +
                              'toolbar=0,scrollbars=1,status=1,resizable=1,' +
                              'location=1,menuBar=0');
        if (!win) { return; }

        // For the life of me I cannot figure out how to do this other than
        // polling
        var self = this;
        var oauthInterval = window.setInterval(function(){
            if (!win.closed) { return; }
            window.clearInterval(oauthInterval);
            var response = JSON.parse(localStorage.github_response);

            if (!response.ok) {
                self.controllerFor('application').set('flashError',
                                                      'Failed to log in');
                return;
            }
            var data = response.data;
            if (data.errors) {
                var error = "Failed to log in: " + data.errors[0];
                self.controllerFor('application').set('flashError', error);
                return;
            }

            var user = self.store.push('user', data.user);
            user.set('api_token', data.api_token);
            var transition = self.session.get('savedTransition');
            self.session.loginUser(user);
            if (transition) {
                transition.retry();
            }
        }, 200);
        transition.abort();
    }
});

Set the api token from a successful authorization


/**
 * Created by Leandro Luque on 24/07/17.
 */

/* JSHint configurations */
/* jshint esversion: 6 */
/* jshint -W097 */

'use strict';

// A.
const ATTRIBUTE = "A1";

// C.
const CLASS = "C1";

// D.
const DEFAULT = "~";

// I.
const INITIAL_VALUE = "I1";
const INTERFACE     = "I2";
const IS_ABSTRACT   = "I3";
const IS_LEAF       = "I4";
const IS_READ_ONLY  = "I5";
const IS_STATIC     = "I6";

// M.
const MODEL = "M1";

// O.
const OPERATION = "O1";

// P.
const PRIVATE   = "-";
const PROTECTED = "#";
const PUBLIC    = "+";

// R.
const RETURN_TYPE = "R1";

// S.
const STEREOTYPE = "S1";

// T.
const TYPE = "T1";

// V.
const VISIBILITY = "V1";

/**
 * Created by Leandro Luque on 24/07/17.
 */

/* JSHint configurations */
/* jshint esversion: 6 */
/* jshint -W097 */

'use strict';

// A.
const ATTRIBUTE = "A1";

// C.
const CLASS = "C1";

// D.
const DEFAULT = "~";

// I.
const INITIAL_VALUE = "I1";
const INTERFACE     = "I2";
const IS_ABSTRACT   = "I3";
const IS_LEAF       = "I4";
const IS_READ_ONLY  = "I5";
const IS_STATIC     = "I6";

// M.
const MODEL = "M1";

// O.
const OPERATION = "O1";

// P.
const PRIVATE   = "-";
const PROTECTED = "#";
const PUBLIC    = "+";
const PARAMETER = "P1"

// R.
const RETURN_TYPE = "R1";

// S.
const STEREOTYPE = "S1";

// T.
const TYPE = "T1";

// V.
const VISIBILITY = "V1";

Add parameter to UML constants


(function () {
    'use strict';
    // TODO: This should likely be a directive, something like nih-navbar once
    //       we have profiles to link to

    /* ngInject */
    function NavbarController($scope, $timeout, $state, NavbarConfig) {
        var ctl = this;
        var defaultAlertHeight = 50;
        var alertTimeout = null;
        var history = [];
        initialize();

        function initialize() {
            ctl.config = NavbarConfig.config;
            ctl.alert = {};
            ctl.alertHeight = 0;

            ctl.back = back;
            ctl.hideAlert = hideAlert;

            $scope.$on('nih.notifications.hide', hideAlert);
            $scope.$on('nih.notifications.show', showAlert);
            $scope.$on('$stateChangeSuccess', function (event, toState) {
                history.push(toState);
            });
        }

        function back() {
            var stateName = history.length > 1 ? history.splice(-2)[0].name : 'profiles';
            $state.go(stateName);
        }

        function showAlert(event, alert) {
            ctl.alert = alert;
            ctl.alertHeight = defaultAlertHeight;
            if (alert.timeout) {
                alertTimeout = $timeout(hideAlert, alert.timeout);
            }
        }

        function hideAlert() {
            ctl.alertHeight = 0;
            if (alertTimeout) {
                $timeout.cancel(alertTimeout);
                alertTimeout = null;
            }
        }
    }

    angular.module('nih.views.navbar')
    .controller('NavbarController', NavbarController);

})();

(function () {
    'use strict';
    // TODO: This should likely be a directive, something like nih-navbar once
    //       we have profiles to link to

    /* ngInject */
    function NavbarController($scope, $timeout, $state, NavbarConfig) {
        var ctl = this;
        var defaultAlertHeight = 50;
        var defaultBackState = 'locations';
        var alertTimeout = null;
        var history = [];
        initialize();

        function initialize() {
            ctl.config = NavbarConfig.config;
            ctl.alert = {};
            ctl.alertHeight = 0;

            ctl.back = back;
            ctl.hideAlert = hideAlert;

            $scope.$on('nih.notifications.hide', hideAlert);
            $scope.$on('nih.notifications.show', showAlert);
            $scope.$on('$stateChangeSuccess', function (event, toState) {
                history.push(toState);
            });
        }

        function back() {
            // Get the last two states from the history array
            //  [0] is last state, [1] is current state
            // and return the state name
            var stateName = history.length > 1 ? history.splice(-2)[0].name : defaultBackState;
            $state.go(stateName);
        }

        function showAlert(event, alert) {
            ctl.alert = alert;
            ctl.alertHeight = defaultAlertHeight;
            if (alert.timeout) {
                alertTimeout = $timeout(hideAlert, alert.timeout);
            }
        }

        function hideAlert() {
            ctl.alertHeight = 0;
            if (alertTimeout) {
                $timeout.cancel(alertTimeout);
                alertTimeout = null;
            }
        }
    }

    angular.module('nih.views.navbar')
    .controller('NavbarController', NavbarController);

})();

Comment NavbarController.back() and update default state