"use strict";

angular.module('arethusa.core').directive('translateLanguage', [
  '$translate',
  'translator',
  function($translate, translator) {
    return {
      restrict: 'A',
      scope: {},
      link: function(scope, element, attrs) {
        var langKey;

        function useKey(key) {
          langKey = key || $translate.use();
          $translate.use(langKey);
          scope.flag = 'images/flags/' + langKey + '.png';
        }

        var langs = ['en', 'de'];

        function toggleLang() {
          var i;
          i = langs.indexOf(langKey) + 1;
          i = i > langs.length - 1 ? 0 : i;
          useKey(langs[i]);
        }

        element.bind('click', function() {
          scope.$apply(toggleLang);
        });

        var parent = element.parent();
        translator('language', function(translation) {
          parent.attr('title', translation);
        });

        useKey();
      },
      templateUrl: 'templates/arethusa.core/translate_language.html'
    };
  }
]);

"use strict";

angular.module('arethusa.core').directive('translateLanguage', [
  '$translate',
  'translator',
  function($translate, translator) {
    return {
      restrict: 'A',
      scope: {},
      link: function(scope, element, attrs) {
        var langKey;

        function useKey(key) {
          langKey = key || $translate.use() || 'en';
          $translate.use(langKey);
          scope.flag = 'images/flags/' + langKey + '.png';
        }

        var langs = ['en', 'de'];

        function toggleLang() {
          var i;
          i = langs.indexOf(langKey) + 1;
          i = i > langs.length - 1 ? 0 : i;
          useKey(langs[i]);
        }

        element.bind('click', function() {
          scope.$apply(toggleLang);
        });

        var parent = element.parent();
        translator('language', function(translation) {
          parent.attr('title', translation);
        });

        useKey();
      },
      templateUrl: 'templates/arethusa.core/translate_language.html'
    };
  }
]);

Add a default value for the language flag


// Copyright 2014 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

__axiomRequire__('shell/chrome/background');

// Copyright 2014 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

chrome.app.runtime.onLaunched.addListener(function() {
  chrome.app.window.create('html/main_window.html', {
    'bounds': {
      'width': 2,
      'height': 2
    }
  });
});

Create a fake window to load polymer.


function getParameterByName(name) {
    var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);
    return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
}

function fixPath (path) {
    var match = RegExp('https?://[^/]*/(.*?)([?#]|$)').exec(window.location.href);
    // If a stub exists, assume secure operation, so:
    var stub =  match && match[1];
    if (stub) {
        //   (1) remove &admin= value from URL
        path = path.replace(/(\?)(?:admin=[^&]*)*(.*?)(?:&)(?:admin=[^&]*)*/,'$1$2');
        //   (2) if URL begins with '/?', append stub to '/'
        path = path.replace(/^(\/)(\?)/, '$1' + stub + '$2');
        //   (3) remove any port designation from URL
        path = path.replace(/(https?:\/\/[^\/]*):[0-9]+/, '$1');
    }
    return path;
}

function apiRequest (url, obj, returnAsString) {
    url = fixPath(url);
    if ("object" === typeof obj) {
        obj = JSON.stringify(obj);
    } else if (!obj) {
        obj = null;
    }
    var xhr = new XMLHttpRequest();
    xhr.open('POST', url, false);
    xhr.setRequestHeader("Content-type","text/plain");
    xhr.send(obj);
    var ret = xhr.responseText;
    if (!returnAsString) {
        ret = JSON.parse(ret);
    }
    return ret;
}

function getParameterByName(name) {
    var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);
    return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
}

function fixPath (path) {
    var match = RegExp('https?://[^/]*/(.*?)([?#]|$)').exec(window.location.href);
    // If a stub exists, assume secure operation, so:
    var stub =  match && match[1];
    if (stub) {
        //   (1) remove &admin= value from URL
        path = path.replace(/(\?)(?:admin=[^&]*)*(.*?)(?:&admin=[^&]*)*/,'$1$2');
        //   (2) if URL begins with '/?', append stub to '/'
        path = path.replace(/^(\/)(\?)/, '$1' + stub + '$2');
        //   (3) remove any port designation from URL
        path = path.replace(/(https?:\/\/[^\/]*):[0-9]+/, '$1');
    }
    return path;
}

function apiRequest (url, obj, returnAsString) {
    url = fixPath(url);
    if ("object" === typeof obj) {
        obj = JSON.stringify(obj);
    } else if (!obj) {
        obj = null;
    }
    var xhr = new XMLHttpRequest();
    xhr.open('POST', url, false);
    xhr.setRequestHeader("Content-type","text/plain");
    xhr.send(obj);
    var ret = xhr.responseText;
    if (!returnAsString) {
        ret = JSON.parse(ret);
    }
    return ret;
}

Fix the regexp that ate the quiz page


pref("extensions.firebug.DBG_ZIPKIN", false);
pref("extensions.zipkin.profile_site", "^((\\w+\\.)+)?(twitter)\\.com$");
pref("extensions.zipkin.zipkin_site", "https://zipkin.smf1.twitter.com");
pref("extensions.zipkin.enable_tracing", true);
pref("extensions.zipkin.trace_zipkin_ui", false);
pref("extensions.zipkin.collapse_waterfalls", false);

pref("extensions.firebug.DBG_ZIPKIN", false);
pref("extensions.zipkin.profile_site", "^((\\w+\\.)+)?(twitter)\\.com$");
pref("extensions.zipkin.zipkin_site", "");
pref("extensions.zipkin.enable_tracing", true);
pref("extensions.zipkin.trace_zipkin_ui", false);
pref("extensions.zipkin.collapse_waterfalls", false);

Fix preference default in new browser extension


import React from 'react';
import PropTypes from 'prop-types';
import TextInput from './common/TextInput';
import SelectInput from './common/SelectInput';

const SearchForm = ({ query, placeholder ,onSave, onChange, saving, errors }) => {
  const repos = [{text: 'GitHub', value: 'github'},{text: 'BitBucket', value: 'bitbucket'}];
  return (
    <form onSubmit={onSave}>

      <SelectInput
        name="repo"
        label="Repository *"
        value={query.repo}
        defaultOption="Select Repository"
        options={repos}
        onChange={onChange}
        error={errors.repo} />

      <TextInput
        name="keyword"
        label="Keyword *"
        value={query.keyword}
        placeholder={placeholder}
        onChange={onChange}
        error={errors.keyword} />

      {/* <input
        type="submit"
        disabled={saving}
        value={saving ? 'Searching...' : 'Search'}
        className="btn btn-primary"
        onClick={onSave} /> */}
    </form>
  );
};

SearchForm.propTypes = {
  query: PropTypes.object.isRequired,
  placeholder: PropTypes.string.isRequired,
  onSave: React.PropTypes.func,
  onChange: React.PropTypes.func,
  saving: React.PropTypes.bool,
  errors: React.PropTypes.object
};

export default SearchForm;

import React from 'react';
import PropTypes from 'prop-types';
import TextInput from './common/TextInput';
import SelectInput from './common/SelectInput';

const SearchForm = ({ query, placeholder ,onSave, onChange, saving, errors }) => {
  const repos = [{text: 'GitHub', value: 'github'},{text: 'BitBucket', value: 'bitbucket'}];
  return (
    <form onSubmit={onSave} disabled={saving}>

      <SelectInput
        name="repo"
        label="Repository *"
        value={query.repo}
        defaultOption="Select Repository"
        options={repos}
        onChange={onChange}
        error={errors.repo} />

      <TextInput
        name="keyword"
        label="Keyword *"
        value={query.keyword}
        placeholder={placeholder}
        onChange={onChange}
        error={errors.keyword} />

      {/* <input
        type="submit"
        disabled={saving}
        value={saving ? 'Searching...' : 'Search'}
        className="btn btn-primary"
        onClick={onSave} /> */}
    </form>
  );
};

SearchForm.propTypes = {
  query: PropTypes.object.isRequired,
  placeholder: PropTypes.string.isRequired,
  onSave: React.PropTypes.func,
  onChange: React.PropTypes.func,
  saving: React.PropTypes.bool,
  errors: React.PropTypes.object
};

export default SearchForm;

Fix search form test failure


'use strict';

angular.module('sgApp')
  .controller('MainCtrl', function ($scope, Socket) {
    // Emit ready event.
    Socket.emit('ready');

    // Listen for the talk event.
    Socket.on('update', function(data) {
      $scope.sections = data;
    });
  });

'use strict';

angular.module('sgApp')
  .controller('MainCtrl', function ($scope, Socket) {
    // Emit ready event.
    Socket.emit('ready');

    // Listen for the talk event.
    Socket.on('update', function(data) {
      $scope.sections = data;
      generateCss();
    });

    // TODO: The following is copied from kss-node, should make a better one

    function generateCss() {
      var KssStateGenerator;

      KssStateGenerator = (function() {
        var pseudo_selectors;

        pseudo_selectors = ['hover', 'enabled', 'disabled', 'active', 'visited', 'focus', 'target', 'checked', 'empty', 'first-of-type', 'last-of-type', 'first-child', 'last-child'];

        function KssStateGenerator() {
          var idx, idxs, pseudos, replaceRule, rule, stylesheet, _i, _len, _len2, _ref, _ref2;
          pseudos = new RegExp("(\\:" + (pseudo_selectors.join('|\\:')) + ")", "g");
          try {
            _ref = document.styleSheets;
            for (_i = 0, _len = _ref.length; _i < _len; _i++) {
              stylesheet = _ref[_i];
              if (stylesheet.href && stylesheet.href.indexOf(document.domain) >= 0) {
                idxs = [];
                _ref2 = stylesheet.cssRules;
                for (idx = 0, _len2 = _ref2.length; idx < _len2; idx++) {
                  rule = _ref2[idx];
                  if ((rule.type === CSSRule.STYLE_RULE) && pseudos.test(rule.selectorText)) {
                    replaceRule = function(matched, stuff) {
                      return matched.replace(/\:/g, '.pseudo-class-');
                    };
                    this.insertRule(rule.cssText.replace(pseudos, replaceRule));
                  }
                  pseudos.lastIndex = 0;
                }
              }
            }
          } catch (_error) {}
        }

        KssStateGenerator.prototype.insertRule = function(rule) {
          var headEl, styleEl;
          headEl = document.getElementsByTagName('head')[0];
          styleEl = document.createElement('style');
          styleEl.type = 'text/css';
          if (styleEl.styleSheet) {
            styleEl.styleSheet.cssText = rule;
          } else {
            styleEl.appendChild(document.createTextNode(rule));
          }
          return headEl.appendChild(styleEl);
        };

        return KssStateGenerator;

      })();

      new KssStateGenerator;
    }
  });

Add front end css parser


'use babel'

/* @flow */

import DebuggerMi2 from './debugger-mi2'
import type { EventDefs } from 'debugger'

module.exports = {
  instance: null,
  BreakpointEvent: null,
  SessionEvent: null,
  TargetEvent: null,

  activate(): void {
    this.instance = new DebuggerMi2()
  },

  consumeEventDefs(eventDefs: EventDefs): void {
    this.BreakpointEvent = eventDefs.BreakpointEvent
    this.SessionEvent    = eventDefs.SessionEvent
    this.TargetEvent     = eventDefs.TargetEvent
  },

  provideDebugger(): DebuggerMi2 {
    return this.instance
  }
}

'use babel'

/* @flow */

import DebuggerMi2 from './debugger-mi2'
import type { EventDefs } from 'debugger'

module.exports = {
  instance: null,
  BreakpointEvent: null,
  SessionEvent: null,
  TargetEvent: null,
  VariableEvent: null,

  activate(): void {
    this.instance = new DebuggerMi2()
  },

  consumeEventDefs(eventDefs: EventDefs): void {
    this.BreakpointEvent = eventDefs.BreakpointEvent
    this.SessionEvent    = eventDefs.SessionEvent
    this.TargetEvent     = eventDefs.TargetEvent
    this.VariableEvent   = eventDefs.VariableEvent
  },

  provideDebugger(): DebuggerMi2 {
    return this.instance
  }
}

Update event defs to get VariableEvent


/*
 * To use this mixin, a component needs:
 * - A computed property called "items" containing items we want to filter.
 * - A data/computed property called "filters", which is an array of strings
 *   that represent the nested props to filter by.
 *
 * To use the filter, you simply need to update the "searchInput" property.
 */
import _ from 'lodash';

export default {

	created() {
		if(!this.items || !Array.isArray(this.items)) {
			throw new Error(
				'[filterableMixin] "items" must exist in your data object and be an array'
			);
		}
	},

	data() {
		return {
			// these are just generic filters to be replaced
			filters: ['name', 'username', 'email'],
			searchInput: ''
		};
	},

	computed: {
		filteredItems() {
			return (
				this.searchInput.length > 1 ?
					this.items.filter(
						this.createFilter(
							this.filters,
							this.searchInput.toLowerCase()
						)
					) :
					this.items
			);
		}
	},

	methods: {
		createFilter(searchKeys, searchTerm) {
			return user => searchKeys.some(
				key => {
					const value = _.get(user, key);
					return value && value.toLowerCase().indexOf(searchTerm) !== -1;
				}
			);
		}
	}

};

/*
 * To use this mixin, a component needs:
 * - A computed property called "items" containing items we want to filter.
 * - A data/computed property called "filters", which is an array of strings
 *   that represent the nested props to filter by.
 *
 * To use the filter, you simply need to update the "searchInput" property.
 */
import _ from 'lodash';

export default {

	created() {
		if(!this.items || !Array.isArray(this.items)) {
			throw new Error(
				'[filterableMixin] "items" must exist in your data object and be an array'
			);
		}
	},

	data() {
		return {
			// these are just generic filters to be replaced
			filters: ['name', 'username', 'email'],
			searchInput: ''
		};
	},

	computed: {
		filteredItems() {
			return (
				this.searchInput.length > 1 ?
					this.items.filter(
						this.createFilter(
							this.filters,
							this.searchInput.toLowerCase()
						)
					) :
					this.items
			);
		}
	},

	methods: {
		createFilter(searchKeys, searchTerm) {
			const searchTerms = searchTerm.toLowerCase().split(' ');

			return item => searchKeys.some(
				key => {
					const value = _.get(item, key);

					return value && searchTerms.some(
						term => (
							term.length > 1 &&
							value.toLowerCase().indexOf(term) !== -1
						)
					)
				}
			);
		}
	}

};

Update filterable mixin so multiple terms (separated by a space) can be used to filter data.


// @flow

import React, { Component } from 'react';
import { View } from 'react-native';

import { overlayFrame as styles } from './styles';

/**
 * The type of the React {@code Component} props of {@code OverlayFrame}.
 */
type Props = {

    /**
     * The children components to be displayed into the overlay frame.
     */
    children?: React$Node,
};

/**
 * Implements a React component to act as the frame for overlays.
 */
export default class OverlayFrame extends Component<Props> {
    /**
     * Implements React's {@link Component#render()}.
     *
     * @inheritdoc
     * @returns {React$Element}
     */
    render() {
        return (
            <View style = { styles.container }>
                { this.props.children }
            </View>
        );
    }
}

// @flow

import React, { Component, type Node } from 'react';
import { SafeAreaView } from 'react-native';

import { overlayFrame as styles } from './styles';

/**
 * The type of the React {@code Component} props of {@code OverlayFrame}.
 */
type Props = {

    /**
     * The children components to be displayed into the overlay frame.
     */
    children: Node,
};

/**
 * Implements a React component to act as the frame for overlays.
 */
export default class OverlayFrame extends Component<Props> {
    /**
     * Implements React's {@link Component#render()}.
     *
     * @inheritdoc
     * @returns {ReactElement}
     */
    render() {
        return (
            <SafeAreaView style = { styles.container }>
                { this.props.children }
            </SafeAreaView>
        );
    }
}

Use a SafeAreaView for OverlayFrame


"use strict";
const gh = require("simple-github");
const jwt = require("jsonwebtoken");

const CERT = process.env.GITHUB_INTEGRATION_KEY;  // get private key
const ID = process.env.GITHUB_INTEGRATION_ID;
const GH_TOKEN = process.env.GITHUB_TOKEN;

let sign = (exp) => {
    return jwt.sign({ iss: ID }, CERT, { algorithm: 'RS256', expiresIn: exp });
};

let headers = token => {
    return {
        "Authorization": `Bearer ${ token }`,
        "Accept": "application/vnd.github.v3+json"
    };
};

let getAuthHeaders = (installation, options) => {
    if (installation) {
        return gh({
            debug: options && options.debug,
            headers: headers(sign("5m"))
        }).request("POST /installations/:installation_id/access_tokens", {
            installation_id: installation.id
        }).then(installation => headers(installation.token));
    }
    return Promise.resolve({ "Authorization": `token ${ GH_TOKEN }` });
};

module.exports = getAuthHeaders;

"use strict";
const gh = require("simple-github");
const jwt = require("jsonwebtoken");

const CERT = process.env.GITHUB_INTEGRATION_KEY;  // get private key
const ID = process.env.GITHUB_INTEGRATION_ID;
const GH_TOKEN = process.env.GITHUB_TOKEN;

let sign = (exp) => {
    return jwt.sign({ iss: ID }, CERT, { algorithm: 'RS256', expiresIn: exp });
};

let headers = token => {
    return {
        "Authorization": `Bearer ${ token }`,
        "Accept": "application/vnd.github.machine-man-preview+json"
    };
};

let getAuthHeaders = (installation, options) => {
    if (installation) {
        return gh({
            debug: options && options.debug,
            headers: headers(sign("5m"))
        }).request("POST /installations/:installation_id/access_tokens", {
            installation_id: installation.id
        }).then(installation => headers(installation.token));
    }
    return Promise.resolve({ "Authorization": `token ${ GH_TOKEN }` });
};

module.exports = getAuthHeaders;

Revert "No longer need preview header"


var replace = require("gulp-replace")
,   forEach = require("gulp-foreach")
,   loggerSpec = require("react-server-module-tagger")

// This pattern matches either of these:
//   - "__LOGGER__"
//   - "__LOGGER__({ /* options */ })"
var isWindows = ('win32' === process.platform)
,   REPLACE_TOKEN = /(?:__LOGGER__|__CHANNEL__|__CACHE__)(?:\(\s*(\{[\s\S]*?\})\s*\))?/g
,   THIS_MODULE   = isWindows
	? /(?:[^\\]+\\node_modules\\)?react-server-gulp-module-tagger\\index\.js$/
	: /(?:[^\/]+\/node_modules\/)?react-server-gulp-module-tagger\/index\.js$/

module.exports = function(config) {
	config || (config = {});
	config.basePath = module.filename.replace(THIS_MODULE,'');
	return forEach(function(stream, file){
		return stream.pipe(replace(REPLACE_TOKEN, loggerSpec.bind({file, config})));
	});
}

var replace = require("gulp-replace")
,   forEach = require("gulp-foreach")
,   loggerSpec = require("react-server-module-tagger")

// This pattern matches either of these:
//   - "__LOGGER__"
//   - "__LOGGER__({ /* options */ })"
var isWindows = ('win32' === process.platform)
,   REPLACE_TOKEN = /(?:__LOGGER__|__CHANNEL__|__CACHE__)(?:\(\s*(\{[\s\S]*?\})\s*\))?/g
,   THIS_MODULE   = isWindows
	? /(?:[^\\]+\\node_modules\\)?react-server-gulp-module-tagger\\index\.js$/
	: /(?:[^\/]+\/node_modules\/)?react-server-gulp-module-tagger\/index\.js$/

module.exports = function(config) {
	config || (config = {});
	config.basePath = module.filename.replace(THIS_MODULE,'');
	return forEach(function(stream, file){
		return stream.pipe(replace(REPLACE_TOKEN, (match, optString) => {
			return loggerSpec({
				filePath: file.path,
				basePath: config.basePath,
				trim: config.trim, optString,
			});
		}));
	});
}

Update react-server-gulp-module-tagger to use new interface


require ([
    'event_utils'
], function (
    eventUtils
) {
    eventUtils.onReady( function() {
        var l = ['e', 'd.l', 'ebi', 'b-en', 'eff', 'o', 'gar', 'tie', 'Bniem'];
        l.splice(6, 0, '@');
        var r = l.join("");
        var e = r.split("").reverse().join("");

        var beitragEmail = document.getElementById('ofbi-beitrag-email');
        if (beitragEmail) {
            beitragEmail.innerHTML =
                ''
              + e
              + '';
        }
    });
});

require ([
    'event_utils'
], function (
    eventUtils
) {
    eventUtils.onReady( function() {
        var l = ['e', 'd.l', 'ebi', 'b-en', 'eff', 'o', 'gar', 'tie', 'Bniem'];
        l.splice(6, 0, '@');
        var r = l.join("");
        var e = r.split("").reverse().join("");

        var beitragEmail = document.getElementById('ofbi-beitrag-email');
        if (beitragEmail) {
            beitragEmail.innerHTML =
                ''
              + e
              + '';
        }

        l = ['e', 'd.l', 'ebi', 'b-en', 'eff', 'o', 'mae', 'tk', 'inhcet'];
        l.splice(6, 0, '@');
        r = l.join("");
        e = r.split("").reverse().join("");

        var technikEmail = document.getElementById('ofbi-technik-email');
        if (technikEmail) {
            technikEmail.innerHTML =
                ''
              + e
              + '';
        }
    });
});

Add another email obfuscation helper.


'use strict';

var joblint = require('joblint');

module.exports = defineController;

function defineController (app) {
    app.all('/ws', function (req, res, next) {
        res.header('Access-Control-Allow-Origin', '*');
        res.header('Access-Control-Allow-Headers', 'X-Requested-With');
        next();
    });
    app.post('/ws', requireUrlEncodedPostBody, requireUnemptyJobSpec, function (req, res) {
        res.jsonp(joblint(req.body.spec));
    });
    app.all('/ws', function (req, res) {
        return res.jsonp(405, {
            error: 'Method not allowed, POST request expected'
        });
    });
}

function requireUrlEncodedPostBody (req, res, next) {
    if (req.headers['content-type'] !== 'application/x-www-form-urlencoded') {
        return res.jsonp(400, {
            error: 'Request must have a content-type of "application/x-www-form-urlencoded"'
        });
    }
    next();
}

function requireUnemptyJobSpec (req, res, next) {
    if (!req.body.spec || !req.body.spec.trim()) {
        return res.jsonp(400, {
            error: 'Spec must be a non-empty string'
        });
    }
    next();
}

'use strict';

var joblint = require('joblint');

module.exports = defineController;

function defineController (app) {
    app.all('/ws', function (req, res, next) {
        res.header('Access-Control-Allow-Origin', '*');
        res.header('Access-Control-Allow-Headers', 'X-Requested-With');
        next();
    });
    app.post('/ws', requireUrlEncodedPostBody, requireUnemptyJobSpec, function (req, res) {
        res.jsonp(joblint(req.body.spec));
    });
    app.all('/ws', function (req, res) {
        return res.jsonp(405, {
            error: 'Method not allowed, POST request expected'
        });
    });
}

function requireUrlEncodedPostBody (req, res, next) {
    if (!req.is('application/x-www-form-urlencoded')) {
        return res.jsonp(400, {
            error: 'Request must have a content-type of "application/x-www-form-urlencoded"'
        });
    }
    next();
}

function requireUnemptyJobSpec (req, res, next) {
    if (!req.body.spec || !req.body.spec.trim()) {
        return res.jsonp(400, {
            error: 'Spec must be a non-empty string'
        });
    }
    next();
}

Fix the content-type check in the webservice


class Foo {
  constructor(a = 10, ...b) {
    this.a = a
    this.b = b
  }

  method() { return this.a + this.b }

  get x() { return `template${this.a}` }
}

let x = 1

const y = {
  func() { return 10 },
  get b() { return false },
  ["foo" + "bar"]: 800
}

let it = function*(n) {
  for (let i = 0; i < n; i++) yield n
}

console.log([for (a of [1, 2, ...it(10)]) a * 2])

let [a, b] = [1, 2];

let x = ([a], {b}) => a + b

let [e1,,e3] = ["5", false, 6]

const obj = {
  hi: 'hello'
};

const newObj = {
  hi,
  ...o //+
}
const {
  hi,
  ...o  //+
} = obj  //+ obj

Add simple test case for spread, rest syntax


Topics = new Mongo.Collection('topics');

TopicSchema = new SimpleSchema({
  title: {
    type: String
  },
  categoryId: {
    type: String
  },
  body: {
    type: String
  },
  author: {
    type: String
  },
  authorId: {
    type: String
  },
  slug: {
    type: String,
    index: true,
    unique: true
  },
  createdAt: {
    type: Date,
    autoValue: function () {
      return new Date();
    }
  }
});

Topics.attachSchema(TopicSchema);

Topics.simpleSchema().messages({
  notUnique: 'A duplicate slug exists.'
});

Add custom validation message for uniqueness constraint for topic slug


'use strict';

exports.green = function (str) {
  return str;
};

exports.yellow = function (str) {
  return str;
};

exports.red = function (str) {
  return str;
};

exports.green = function (str) {
  return str;
};

'use strict';

exports.green = function (str) {
  return str;
};

exports.yellow = function (str) {
  return str;
};

exports.red = function (str) {
  return str;
};

exports.magenta = function (str) {
  return str;
};

exports.green = function (str) {
  return str;
};

Fix browser colours and settings


var updateQueryStats = function(element){
    //fragment=-1 means getting all the data
    var shuffleUrl = templates.urls.sentData({
            myria: myriaConnection,
            query: queryPlan.queryId,
            fragment: -1
        });

    d3.csv(shuffleUrl, function(d) {
            d.numTuples = +d.numTuples;
            return d;
        }, function (data) {
            $(element.node()).empty();
            var div = element.append("div")
                .attr("class", "query-stats");
            var h = div.append("h4")
                .text("Query stats:");
            var totalTuple = data.reduce(function(a,b){
                return a + b.numTuples;
            }, 0);
            var items = "";
            items += templates.defItem({key: "Running time:", value: customFullTimeFormat(queryPlan.elapsedNanos, false)});
            items += templates.defItem({key: "# shuffled tuples:", value: Intl.NumberFormat().format(totalTuple)});
            var dl = templates.defList({items: items});
            $(".query-stats").append(dl);
        });
};

var updateQueryStats = function(element) {
    var shuffleUrl = templates.urls.aggregatedSentData({
            myria: myriaConnection,
            query: queryPlan.queryId
        });

    d3.csv(shuffleUrl, function(d) {
            d.numTuples = +d.numTuples;
            return d;
        }, function (data) {
            $(element.node()).empty();
            var div = element.append("div")
                .attr("class", "query-stats");
            var h = div.append("h4")
                .text("Query stats:");
            var totalTuple = data.reduce(function(a,b){
                return a + b.numTuples;
            }, 0);
            var items = "";
            items += templates.defItem({key: "Running time:", value: customFullTimeFormat(queryPlan.elapsedNanos, false)});
            items += templates.defItem({key: "# shuffled tuples:", value: Intl.NumberFormat().format(totalTuple)});
            var dl = templates.defList({items: items});
            $(".query-stats").append(dl);
        });
};

Use aggregated data in stats cause it’s smaller and we can use the cached version


const gulp = require("gulp");
const del = require("del");
const browserify = require("browserify");
const babelify = require("babelify");
const src = require("vinyl-source-stream");

const source = "src";
const target = "build";

gulp.task("js", function () {
    return browserify({entries: `${source}/js/app.js`, debug: true})
        .transform(babelify)
        .bundle()
        .pipe(src("app.js"))
        .pipe(gulp.dest(`${target}/js`));
});

gulp.task("files", function () {
    return gulp.src(`${source}/files/**/*`)
        .pipe(gulp.dest(target));
});

gulp.task("clean", function (cb) {
    del(`${target}/*`, cb);
});

gulp.task("default", ["clean"], function () {
    gulp.start("js", "files");
});

gulp.task("watch", ["default"], function () {
    gulp.watch(`${source}/js/**/*.js`, ["js"]);
    gulp.watch(`${source}/files/**/*`, ["files"]);
});

const gulp = require("gulp");
const del = require("del");
const browserify = require("browserify");
const babelify = require("babelify");
const src = require("vinyl-source-stream");

const source = "src";
const target = "build";

gulp.task("js", function () {
    return browserify({entries: `${source}/js/app.js`, debug: true})
        .transform(babelify.configure({sourceMapRelative: `${__dirname}/`}))
        .bundle()
        .pipe(src("app.js"))
        .pipe(gulp.dest(`${target}/js`));
});

gulp.task("files", function () {
    return gulp.src(`${source}/files/**/*`)
        .pipe(gulp.dest(target));
});

gulp.task("clean", function (cb) {
    del(`${target}/*`, cb);
});

gulp.task("default", ["clean"], function () {
    gulp.start("js", "files");
});

gulp.task("watch", ["default"], function () {
    gulp.watch(`${source}/js/**/*.js`, ["js"]);
    gulp.watch(`${source}/files/**/*`, ["files"]);
});

Make babelify generate source maps with relative paths


import Expression from '../Expression';
import Sequence from '../dataTypes/Sequence';
import Specificity from '../Specificity';

/**
 * @extends {Expression}
 */
class ContextItemExpression extends Expression {
	constructor () {
		super(
			new Specificity({}),
			[]
		);
	}

	evaluate (dynamicContext, _executionParameters) {
		if (dynamicContext.contextItem === null) {
			throw new Error('XPDY0002: context is absent, it needs to be present to use the "." operator');
		}
		return Sequence.singleton(dynamicContext.contextItem);
	}

}
export default ContextItemExpression;

import Expression from '../Expression';
import Sequence from '../dataTypes/Sequence';
import Specificity from '../Specificity';

/**
 * @extends {Expression}
 */
class ContextItemExpression extends Expression {
	constructor () {
		super(
			new Specificity({}),
			[],
			{
				resultOrder: Expression.RESULT_ORDERINGS.SORTED
			}
		);
	}

	evaluate (dynamicContext, _executionParameters) {
		if (dynamicContext.contextItem === null) {
			throw new Error('XPDY0002: context is absent, it needs to be present to use the "." operator');
		}
		return Sequence.singleton(dynamicContext.contextItem);
	}

}
export default ContextItemExpression;

Make the context item expression correctly predict the result order


if(window.jQuery) {
  (function ( $ ) {
    $.fn.snabbt = function(arg1, arg2) {

      return snabbt(this.get(), arg1, arg2);
    };
  }( jQuery ));
}

if(window.jQuery) {
  (function ( $ ) {
    $.fn.snabbt = function(arg1, arg2) {
      return snabbtjs.snabbt(this.get(), arg1, arg2);
    };
  }( jQuery ));
}

Fix jquery bug after umd fixes


/**
 * Copyright (c) 2014, CKSource - Frederico Knabben. All rights reserved.
 * Licensed under the terms of the MIT License (see LICENSE.md).
 */

'use strict';

var path = require( 'path' ),
	_ = require( 'lodash' ),
	files = [
		path.join( __dirname, '/../node_modules/requirejs/require.js' ),
		path.join( __dirname, '/adapter.js' )
	],
	requireConfig = {};

module.exports = {
	name: 'bender-pagebuilder-amd',
	files: files,
	include: files,

	build: function( data ) {
		data.parts.push( '<head><script>require.config(' + JSON.stringify( requireConfig ) + ');</script></head>' );
		return data;
	},

	init: function( done ) {
		var config = this.conf.amd || this.conf[ 'bender-amd' ];

		if ( this.conf[ 'bender-amd' ] ) {
			this.logger.warn( '"bender-amd" config property is deprecated, please use "amd" instead' );
		}

		if ( config ) {
			_.extend( requireConfig, config );
		}

		done();
	}
};

/**
 * Copyright (c) 2014, CKSource - Frederico Knabben. All rights reserved.
 * Licensed under the terms of the MIT License (see LICENSE.md).
 */

'use strict';

var path = require( 'path' ),
	_ = require( 'lodash' ),
	files = [
		path.join( require.resolve( 'requirejs' ), '../../require.js' ),
		path.join( __dirname, '/adapter.js' )
	],
	requireConfig = {};

module.exports = {
	name: 'bender-pagebuilder-amd',
	files: files,
	include: files,

	build: function( data ) {
		data.parts.push( '<head><script>require.config(' + JSON.stringify( requireConfig ) + ');</script></head>' );
		return data;
	},

	init: function( done ) {
		var config = this.conf.amd || this.conf[ 'bender-amd' ];

		if ( this.conf[ 'bender-amd' ] ) {
			this.logger.warn( '"bender-amd" config property is deprecated, please use "amd" instead' );
		}

		if ( config ) {
			_.extend( requireConfig, config );
		}

		done();
	}
};

Resolve the path to the RequireJS instead of hardcoding it.


const auth = require('@feathersjs/authentication');
const hooks = require('feathers-hooks-common');
const globalHooks = require('../../../hooks');
const lookupSchool = require('./lookupSchool');

module.exports = {
	before: {
		all: [
			globalHooks.ifNotLocal(auth.hooks.authenticate('jwt')),
			globalHooks.ifNotLocal(globalHooks.restrictToCurrentSchool),
			lookupSchool,
		],
		find: [],
		create: [
			globalHooks.ifNotLocal(globalHooks.hasPermission('SCHOOL_EDIT')),
		],
		update: [hooks.disable()],
		get: [hooks.disallow()],
		patch: [hooks.disallow()],
		remove: [hooks.disallow()],
	},
};

const auth = require('@feathersjs/authentication');
const hooks = require('feathers-hooks-common');
const globalHooks = require('../../../hooks');
const lookupSchool = require('./lookupSchool');

module.exports = {
	before: {
		all: [
			globalHooks.ifNotLocal(auth.hooks.authenticate('jwt')),
			globalHooks.ifNotLocal(globalHooks.restrictToCurrentSchool),
			lookupSchool,
		],
		find: [],
		create: [
			globalHooks.ifNotLocal(globalHooks.hasPermission('SCHOOL_EDIT')),
		],
		update: [hooks.disallow()],
		get: [hooks.disallow()],
		patch: [hooks.disallow()],
		remove: [hooks.disallow()],
	},
};

Use disallow instead of disable


'use strict';

var postal = require('postal');
var raf = require('raf');
var FluzoStore = require('fluzo-store')(postal);

var fluzo_channel = postal.channel('fluzo');

var render_pending = false;
var render_suscriptions = [];
var updating = false;

var Fluzo = {

   Store: FluzoStore,
   action: FluzoStore.action,

   clearRenderRequests: function () {
      render_pending = false;
   },

   requestRender: function () {
      render_pending = true;
   },

   onRender: function (cb) {
      render_suscriptions.push(cb);
      var index = render_suscriptions.length - 1;
      return function () {
         render_suscriptions.splice(index, 1);
      };
   },

   renderIfRequested: function (delta) {
      if (render_pending) {
         var now = Date.now();
         render_suscriptions.forEach(function (cb) {
            cb(now, delta);
         });
         this.clearRenderRequests();
      }
   },

   startUpdating: function () {
      updating = true;
      (function tick(delta) {
         if (updating) {
            raf(tick);
         }
         Fluzo.renderIfRequested(delta);
      })(0);
   },

   stopUpdating: function () {
      updating = false;
   }

};

fluzo_channel.subscribe('store.changed.*', Fluzo.requestRender);

module.exports = Fluzo;

'use strict';

var postal = require('postal');
var raf = require('raf');
var FluzoStore = require('fluzo-store')(postal);

var fluzo_channel = postal.channel('fluzo');

var render_pending = false;
var render_suscriptions = [];
var updating = false;

var Fluzo = {

   Store: FluzoStore,
   action: FluzoStore.action,

   clearRenderRequests: function () {
      render_pending = false;
   },

   requestRender: function () {
      render_pending = true;
   },

   onRender: function (cb) {
      render_suscriptions.push(cb);
      var index = render_suscriptions.length - 1;
      return function () {
         render_suscriptions.splice(index, 1);
      };
   },

   renderIfRequested: function (delta) {
      if (render_pending) {
         var now = Date.now();
         render_suscriptions.forEach(function (cb) {
            cb(now, delta);
         });
         this.clearRenderRequests();
      }
   },

   startUpdating: function () {
      if (!updating) {
         updating = true;
         (function tick(delta) {
            if (updating) {
               raf(tick);
            }
            Fluzo.renderIfRequested(delta);
         })(0);
      }
   },

   stopUpdating: function () {
      updating = false;
   }

};

fluzo_channel.subscribe('store.changed.*', Fluzo.requestRender);

module.exports = Fluzo;

Check if not already started before starting updates


'use strict';

var gulp = require('gulp');
var env = require('gulp-env');
var fs = require('fs');

var projectPath = 'app/';
var outputPath = 'public/build/';
var configPath = 'config/';

var envFile = configPath + 'env.prod.json';
if( fs.existsSync(configPath + 'env.local.json') ) {
  envFile = configPath + 'env.local.json';
}
console.log('Using env file: ' + envFile);

/**
 *  Check the SASS style.
 */
gulp.task('styles:ci', function () {
  gulp.src(projectPath + '**/*.s+(a|c)ss')
  .pipe(sassLint())
  .pipe(sassLint.format())
  .pipe(sassLint.failOnError())
});

Remove not usedn gulp task any more


var gulp = require('gulp')
var uglify = require('gulp-uglify')
var rename = require('gulp-rename')
var header = require('gulp-header')
var ghPages = require('gulp-gh-pages')

var pkg = require('./package.json')
var banner = [
  '/**',
  ' * <%= pkg.name %> - <%= pkg.description %>',
  ' *',
  ' * @version v<%= pkg.version %>',
  ' * @link <%= pkg.homepage %>',
  ' * @author <%= pkg.author %>',
  ' * @license <%= pkg.license %>',
  ' */',
  ''].join('\n')

gulp.task('build', function () {
  return gulp.src('src/*')
    .pipe(uglify())
    .pipe(header(banner, {pkg: pkg}))
    .pipe(rename('blooming-menu.min.js'))
    .pipe(gulp.dest('build'))
})

gulp.task('deploy:ghpages', function () {
  gulp
    .src('./build/blooming-menu.min.js')
    .pipe(gulp.dest('./example/js'))

  return gulp
    .src('./example/**/*')
    .pipe(ghPages())
})

var gulp = require('gulp')
var uglify = require('gulp-uglify')
var rename = require('gulp-rename')
var header = require('gulp-header')
var ghPages = require('gulp-gh-pages')

var pkg = require('./package.json')
var banner = [
  '/**',
  ' * <%= pkg.name %> - <%= pkg.description %>',
  ' *',
  ' * @version v<%= pkg.version %>',
  ' * @link <%= pkg.homepage %>',
  ' * @author <%= pkg.author %>',
  ' * @license <%= pkg.license %>',
  ' */',
  ''].join('\n')

gulp.task('build', function () {
  return gulp.src('src/*')
    .pipe(uglify())
    .pipe(header(banner, {pkg: pkg}))
    .pipe(rename('blooming-menu.min.js'))
    .pipe(gulp.dest('build'))
    .pipe(gulp.dest('example/js'))
})

gulp.task('deploy:ghpages', function () {
  gulp
    .src('./build/blooming-menu.min.js')
    .pipe(gulp.dest('./example/js'))

  return gulp
    .src('./example/**/*')
    .pipe(ghPages())
})

Copy builded file to example/js/ on build


var gulp   = require('gulp');
var jshint = require('gulp-jshint');
var jscs   = require('gulp-jscs');
var babel  = require('gulp-babel');
var rename = require('gulp-rename');
var uglify = require('gulp-uglify');
var header = require('gulp-header');
var pkg    = require('./package.json');

var banner = '/*! <%= pkg.name %> - v<%= pkg.version %> | <%= new Date().getFullYear() %> */\n';

gulp.task('build', function() {
    return gulp.src('./src/d3-funnel/d3-funnel.js')
        .pipe(jshint())
        .pipe(jshint.reporter('default'))
        .pipe(jshint.reporter('fail'))
        .pipe(jscs({
            esnext: true,
            verbose: true
        }))
        .pipe(babel())
        .pipe(gulp.dest('./dist/'))
        .pipe(rename({
            extname: '.min.js'}
        ))
        .pipe(uglify())
        .pipe(header(banner, {
            pkg: pkg
        }))
        .pipe(gulp.dest('./dist/'));
});

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

var gulp   = require('gulp');
var jshint = require('gulp-jshint');
var jscs   = require('gulp-jscs');
var babel  = require('gulp-babel');
var rename = require('gulp-rename');
var uglify = require('gulp-uglify');
var header = require('gulp-header');
var pkg    = require('./package.json');

var banner = '/*! <%= pkg.name %> - v<%= pkg.version %> | <%= new Date().getFullYear() %> */\n';

gulp.task('build', function() {
	return gulp.src('./src/d3-funnel/d3-funnel.js')
		.pipe(jshint())
		.pipe(jshint.reporter('default'))
		.pipe(jshint.reporter('fail'))
		.pipe(jscs({
			configPath: './.jscsrc'
		}))
		.pipe(babel())
		.pipe(gulp.dest('./dist/'))
		.pipe(rename({
				extname: '.min.js'}
		))
		.pipe(uglify())
		.pipe(header(banner, {
			pkg: pkg
		}))
		.pipe(gulp.dest('./dist/'));
});

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

Fix issue with gulp-jscs not using options in .jscsrc


import styled from 'styled-components';
import get from 'extensions/themeGet';

const NavigationBar = styled.header.withConfig({
  displayName: 'NavigationBar',
})`
  background: ${get('colors.primary.default')};
  color: ${get('colors.text.inverted')};
  border-bottom: ${get('thicknesses.normal')} solid
    ${get('colors.shadow.default')};
  padding: 0 ${get('spacing.large')};
  display: flex;
  flex-shrink: 0;
  justify-content: space-between;
  z-index: 1000;

  /* we don't want the nav to expand or collapse, just keep its natural size */
  flex: 0 0 auto;

  & > * {
    margin: auto 0;
  }
`;

NavigationBar.Sub = NavigationBar.extend`
  background: ${get('colors.gray.light')};
  color: ${get('colors.text.default')};
`;

NavigationBar.Dark = NavigationBar.extend`
  background: ${get('colors.primary.dark')};
`;

export default NavigationBar;

import styled from 'styled-components';
import get from 'extensions/themeGet';

const NavigationBar = styled.header.withConfig({
  displayName: 'NavigationBar',
})`
  background: ${get('colors.primary.default')};
  color: ${get('colors.text.inverted')};
  border-bottom: ${get('thicknesses.normal')} solid
    ${get('colors.shadow.default')};
  padding: 0 ${get('spacing.large')};
  display: flex;
  flex-shrink: 0;
  justify-content: space-between;

  /* we don't want the nav to expand or collapse, just keep its natural size */
  flex: 0 0 auto;

  & > * {
    margin: auto 0;
  }
`;

NavigationBar.Sub = NavigationBar.extend`
  background: ${get('colors.gray.light')};
  color: ${get('colors.text.default')};
`;

NavigationBar.Dark = NavigationBar.extend`
  background: ${get('colors.primary.dark')};
`;

export default NavigationBar;

Remove z-index from navigation bar, since it's not fixed by default


import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { observer } from 'mobx-react';
import ElectronWebView from 'react-electron-web-view';

import ServiceModel from '../../../models/Service';

@observer
class ServiceWebview extends Component {
  static propTypes = {
    service: PropTypes.instanceOf(ServiceModel).isRequired,
    setWebviewReference: PropTypes.func.isRequired,
    detachService: PropTypes.func.isRequired,
  };

  webview = null;

  componentWillUnmount() {
    const { service, detachService } = this.props;
    detachService({ service });
  }

  render() {
    const {
      service,
      setWebviewReference,
    } = this.props;

    return (
      <ElectronWebView
        ref={(webview) => { this.webview = webview; }}
        autosize
        src={service.url}
        preload="./webview/recipe.js"
        partition={`persist:service-${service.id}`}
        onDidAttach={() => {
          setWebviewReference({
            serviceId: service.id,
            webview: this.webview.view,
          });
        }}
        onUpdateTargetUrl={this.updateTargetUrl}
        useragent={service.userAgent}
        disablewebsecurity={service.recipe.disablewebsecurity}
        allowpopups
      />
    );
  }
}

export default ServiceWebview;

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { observer } from 'mobx-react';
import ElectronWebView from 'react-electron-web-view';

import ServiceModel from '../../../models/Service';

@observer
class ServiceWebview extends Component {
  static propTypes = {
    service: PropTypes.instanceOf(ServiceModel).isRequired,
    setWebviewReference: PropTypes.func.isRequired,
    detachService: PropTypes.func.isRequired,
  };

  webview = null;

  componentWillUnmount() {
    const { service, detachService } = this.props;
    detachService({ service });
  }

  refocusWebview = () => {
    const { webview } = this;
    if (!webview) return;
    webview.view.blur();
    webview.view.focus();
  };

  render() {
    const {
      service,
      setWebviewReference,
    } = this.props;

    return (
      <ElectronWebView
        ref={(webview) => {
          this.webview = webview;
          webview.view.addEventListener('did-stop-loading', this.refocusWebview);
        }}
        autosize
        src={service.url}
        preload="./webview/recipe.js"
        partition={`persist:service-${service.id}`}
        onDidAttach={() => {
          setWebviewReference({
            serviceId: service.id,
            webview: this.webview.view,
          });
        }}
        onUpdateTargetUrl={this.updateTargetUrl}
        useragent={service.userAgent}
        disablewebsecurity={service.recipe.disablewebsecurity}
        allowpopups
      />
    );
  }
}

export default ServiceWebview;

Add workaround for electron webview issue with disappearing cursors


module.exports = WatchIgnorePlugin

function WatchIgnorePlugin(paths) {
    this.paths = paths
}

WatchIgnorePlugin.prototype.apply = function (compiler) {
    compiler.plugin('after-environment', function () {
        compiler.watchFileSystem = new IgnoringWatchFileSystem(compiler.watchFileSystem, this.paths)
    }.bind(this))
}

function IgnoringWatchFileSystem(wfs, paths) {
    this.wfs = wfs
    this.paths = paths
}

IgnoringWatchFileSystem.prototype.watch = function (files, dirs, missing, startTime, delay, callback, callbackUndelayed) {
    var ignored = function (path) {
        return this.paths.some(function (p) {
            return path.match(p)
        })
    }.bind(this)
    var notIgnored = function (path) { return !ignored(path) }
    var ignoredFiles = files.filter(ignored)
    var ignoredDirs = dirs.filter(ignored)
    this.wfs.watch(files.filter(notIgnored), dirs.filter(notIgnored), missing, startTime, delay, function (err, filesModified, dirsModified, fileTimestamps, dirTimestamps) {
        if (err) return callback(err)
        ignoredFiles.forEach(function (path) {
            fileTimestamps[path] = 1
        })
        ignoredDirs.forEach(function (path) {
            dirTimestamps[path] = 1
        })
        callback(err, filesModified, dirsModified, fileTimestamps, dirTimestamps)
    }, callbackUndelayed)
}

module.exports = WatchIgnorePlugin

function WatchIgnorePlugin(paths) {
    this.paths = paths
}

WatchIgnorePlugin.prototype.apply = function (compiler) {
    compiler.plugin('after-environment', function () {
        compiler.watchFileSystem = new IgnoringWatchFileSystem(compiler.watchFileSystem, this.paths)
    }.bind(this))
}

function IgnoringWatchFileSystem(wfs, paths) {
    this.wfs = wfs
    this.paths = paths
}

IgnoringWatchFileSystem.prototype.watch = function (files, dirs, missing, startTime, delay, callback, callbackUndelayed) {
    var ignored = function (path) {
        return this.paths.some(function (p) {
            return path.search(p) >= 0
        })
    }.bind(this)
    var notIgnored = function (path) { return !ignored(path) }
    var ignoredFiles = files.filter(ignored)
    var ignoredDirs = dirs.filter(ignored)
    this.wfs.watch(files.filter(notIgnored), dirs.filter(notIgnored), missing, startTime, delay, function (err, filesModified, dirsModified, fileTimestamps, dirTimestamps) {
        if (err) return callback(err)
        ignoredFiles.forEach(function (path) {
            fileTimestamps[path] = 1
        })
        ignoredDirs.forEach(function (path) {
            dirTimestamps[path] = 1
        })
        callback(err, filesModified, dirsModified, fileTimestamps, dirTimestamps)
    }, callbackUndelayed)
}

Use .search for browser compatibility.


'use strict';

var browserify = require('browserify');
var gulp = require('gulp');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var uglify = require('gulp-uglify');
var sourcemaps = require('gulp-sourcemaps');
var gutil = require('gulp-util');

gulp.task('js', function () {
  // set up the browserify instance on a task basis
  var b = browserify({
    entries: './public/js/app.js',
    debug: true
  });

  return b.bundle()
    .pipe(source('bundle.js'))
    .pipe(buffer())
    .pipe(sourcemaps.init({loadMaps: true}))
        // Add transformation tasks to the pipeline here.
        .pipe(uglify())
        .on('error', gutil.log)
    .pipe(sourcemaps.write('./'))
    .pipe(gulp.dest('./public/js/'));
});

'use strict';

var browserify = require('browserify');
var gulp = require('gulp');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var uglify = require('gulp-uglify');
var sourcemaps = require('gulp-sourcemaps');
var gutil = require('gulp-util');

var config = {
  js: ['src/**/*.js']
};

gulp.task('js', function () {
  // set up the browserify instance on a task basis
  var b = browserify({
    entries: './public/js/app.js',
    debug: true
  });

  return b.bundle()
    .pipe(source('bundle.js'))
    .pipe(buffer())
    .pipe(sourcemaps.init({loadMaps: true}))
        // Add transformation tasks to the pipeline here.
        .pipe(uglify())
        .on('error', gutil.log)
    .pipe(sourcemaps.write('./'))
    .pipe(gulp.dest('./public/js/'));
});

gulp.task('develop', function() {
  gulp.watch(config.js, ['js']);
});

Add auto JS building on source change


/**
 * @license
 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
 * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
 * Code distributed by Google as part of the polymer project is also
 * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
 */

(function(scope) {

  'use strict';

  HTMLImports.whenReady(function() {
    requestAnimationFrame(function() {
      window.dispatchEvent(new CustomEvent('WebComponentsReady'));
    });
  });

  if (customElements && customElements.polyfillWrapFlushCallback) {
    // Here we ensure that the public `HTMLImports.whenReady`
    // always comes *after* custom elements have upgraded.
    let flushCallback;
    function runAndClearCallback() {
      if (flushCallback) {
        let cb = flushCallback;
        flushCallback = null;
        cb();
      }
    }
    let origWhenReady = HTMLImports.whenReady;
    customElements.polyfillWrapFlushCallback(function(cb) {
      flushCallback = cb;
      origWhenReady(runAndClearCallback);
    });

    HTMLImports.whenReady = function(cb) {
      origWhenReady(function() {
        runAndClearCallback();
        cb();
      });
    }

  }

})(window.WebComponents);

/**
 * @license
 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
 * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
 * Code distributed by Google as part of the polymer project is also
 * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
 */

(function(scope) {

  'use strict';

  if (customElements && customElements.polyfillWrapFlushCallback) {
    // Here we ensure that the public `HTMLImports.whenReady`
    // always comes *after* custom elements have upgraded.
    let flushCallback;
    function runAndClearCallback() {
      if (flushCallback) {
        let cb = flushCallback;
        flushCallback = null;
        cb();
      }
    }
    let origWhenReady = HTMLImports.whenReady;
    customElements.polyfillWrapFlushCallback(function(cb) {
      flushCallback = cb;
      origWhenReady(runAndClearCallback);
    });

    HTMLImports.whenReady = function(cb) {
      origWhenReady(function() {
        runAndClearCallback();
        cb();
      });
    }

  }

  HTMLImports.whenReady(function() {
    requestAnimationFrame(function() {
      window.dispatchEvent(new CustomEvent('WebComponentsReady'));
    });
  });

})(window.WebComponents);

Move dispatching WebComponentsReady until after HTMLImports.whenReady is patched.


var winston = require('winston'),
    path    = require('path');

var logLevel = 'warn';

var sharedLogger = function(filename) {
  return new winston.Logger({
    level: logLevel,
    transports: [
      new winston.transports.Console({
        prettyPrint: true,
        timestamp: true,
        level: logLevel,
        label: path.basename(filename, '.js')
      })
    ]
  });
};

sharedLogger.setLevel = function (val) {
  logLevel = val;
}

function failedPromiseHandler(logger) {
  logger = logger || sharedLogger;

  return function(err) {
    logger.error(err.stack);
  };
}

function uuid() {
	return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
    var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
    return v.toString(16);
  });
}

module.exports = {
  logger: sharedLogger,
  failedPromiseHandler: failedPromiseHandler,
  uuid: uuid
}

var winston = require('winston'),
    path    = require('path');

var logLevel = 'warn';

var sharedLogger = function(filename) {
  return new winston.Logger({
    level: logLevel,
    transports: [
      new winston.transports.Console({
        prettyPrint: true,
        timestamp: true,
        level: logLevel,
        label: path.basename(filename, '.js')
      })
    ]
  });
};

sharedLogger.setLevel = function (val) {
  logLevel = val;
}

function failedPromiseHandler(logger) {
  logger = logger || sharedLogger(__filename);

  return function(err) {
    logger.error(err.stack);
  };
}

function uuid() {
	return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
    var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
    return v.toString(16);
  });
}

module.exports = {
  logger: sharedLogger,
  failedPromiseHandler: failedPromiseHandler,
  uuid: uuid
}

Create default logger object if one is not sent


// Taken from https://muffinresearch.co.uk/removing-leading-whitespace-in-es6-template-strings/
export const singleLineString = (strings, ...values) => {
  let output = '';
  for (let i = 0; i < values.length; i++) {
    output += strings[i] + values[i];
  }
  output += strings[values.length];

  let lines = output.split(/(?:\r\n|\n|\r)/);

  return lines.map((line) => {
    return line.replace(/^\s+/gm, '');
  }).join(' ').trim();
};

export const print = (target, name, descriptor) => {
  const method = descriptor.value;

  descriptor.value = function (...args) {
    const msg = method.apply(this, args);
    console.log(singleLineString`${msg}`);
    args[0] ? console.log(err) : void(0);
    return this;
  }
}

export const exit = (target, name, descriptor) => {
  const method = descriptor.value;

  descriptor.value = function (...args) {
    method.apply(this, args);
    process.exit(1);
    return this;
  }
}

export const promiseWrapper = (fn, ...args) => {
  return new Promise((resolve, reject) => {
    try {
      fn.apply(this, args);
      resolve();
    } catch (err) {
      reject(err);
    }
  });
};

// Taken from https://muffinresearch.co.uk/removing-leading-whitespace-in-es6-template-strings/
export const singleLineString = (strings, ...values) => {
  let output = '';
  for (let i = 0; i < values.length; i++) {
    output += strings[i] + values[i];
  }
  output += strings[values.length];

  let lines = output.split(/(?:\r\n|\n|\r)/);

  return lines.map((line) => {
    return line.replace(/^\s+/gm, '');
  }).join(' ').trim();
};

export const print = (target, name, descriptor) => {
  const method = descriptor.value;

  descriptor.value = function (...args) {
    const msg = method.apply(this, args);
    console.log(singleLineString`${msg}`);
    if (args[0]) console.error(args[0]);
    return this;
  }
}

export const exit = (target, name, descriptor) => {
  const method = descriptor.value;

  descriptor.value = function (...args) {
    method.apply(this, args);
    process.exit(1);
    return this;
  }
}

export const promiseWrapper = (fn, ...args) => {
  return new Promise((resolve, reject) => {
    try {
      fn.apply(this, args);
      resolve();
    } catch (err) {
      reject(err);
    }
  });
};

Print out errors now uses right variable


import Client from '../../client.js';
import { enterJob } from '../../../utils/buildmessage.js';
import { ensureDependencies } from '../../../cli/dev-bundle-helpers.js';

const NPM_DEPENDENCIES = {
  puppeteer: '1.3.0'
};

export default class PuppeteerClient extends Client {
  constructor(options) {
    super(options);

    enterJob(
      {
        title: 'Installing Puppeteer in Meteor tool'
      },
      () => {
        ensureDependencies(NPM_DEPENDENCIES);
      }
    );

    this.npmPackageExports = require('puppeteer');

    this.name = 'Puppeteer';
  }

  async connect() {
    // Note for Travis and CircleCI to run sandbox must be turned off.
    // From a security perspective this is not ideal, in the future would be worthwhile
    // to configure to include only for CI based setups
    this.browser = await this.npmPackageExports.launch({ args: ['--no-sandbox', '--disable-setuid-sandbox'] });
    this.page = await this.browser.newPage();
    this.page.goto(`http://${this.host}:${this.port}`);
  }

  async stop() {
    this.page && await this.page.close();
    this.page = null;

    this.browser && await this.browser.close();
    this.browser = null;
  }

  static pushClients(clients, appConfig) {
    clients.push(new PuppeteerClient(appConfig));
  }
}

import Client from '../../client.js';
import { enterJob } from '../../../utils/buildmessage.js';
import { ensureDependencies } from '../../../cli/dev-bundle-helpers.js';

const NPM_DEPENDENCIES = {
  puppeteer: '8.0.0'
};

export default class PuppeteerClient extends Client {
  constructor(options) {
    super(options);

    enterJob(
      {
        title: 'Installing Puppeteer in Meteor tool'
      },
      () => {
        ensureDependencies(NPM_DEPENDENCIES);
      }
    );

    this.npmPackageExports = require('puppeteer');

    this.name = 'Puppeteer';
  }

  async connect() {
    // Note for Travis and CircleCI to run sandbox must be turned off.
    // From a security perspective this is not ideal, in the future would be worthwhile
    // to configure to include only for CI based setups
    this.browser = await this.npmPackageExports.launch({ args: ['--no-sandbox', '--disable-setuid-sandbox'] });
    this.page = await this.browser.newPage();
    this.page.goto(`http://${this.host}:${this.port}`);
  }

  async stop() {
    this.page && await this.page.close();
    this.page = null;

    this.browser && await this.browser.close();
    this.browser = null;
  }

  static pushClients(clients, appConfig) {
    clients.push(new PuppeteerClient(appConfig));
  }
}

Fix browserstack test failures. Old puppeteer is not supported anymore.


import { spawn } from 'child_process'
import electron from 'electron-prebuilt'
import browserSync from 'browser-sync'
import browserSyncConnectUtils from 'browser-sync/lib/connect-utils'

const bsync = browserSync.create()

const getRootUrl = (options) => {
  const port = options.get('port')
  return `http://localhost:${port}`
}

const getClientUrl = (options) => {
  const pathname = browserSyncConnectUtils.clientScript(options)
  return getRootUrl(options) + pathname
}

bsync.init({
  ui: false,
  // Port 35829 = LiveReload's default port 35729 + 100.
  // If the port is occupied, Browsersync uses next free port automatically.
  port: 35829,
  ghostMode: false,
  open: false,
  notify: false,
  logSnippet: false,
  socket: {
    // Use the actual port here.
    domain: getRootUrl
  }
}, (err, bs) => {
  if (err) return console.error(err)

  spawn(electron, ['.'], {
    env: {
      ...{
        NODE_ENV: 'development',
        BROWSER_SYNC_CLIENT_URL: getClientUrl(bs.options)
      },
      ...process.env
    },
    stdio: 'inherit'
  })

  bsync
    .watch('../react-mobile/react/**/*')
    .on('change', bsync.reload)
})

import { spawn } from 'child_process'
import electron from 'electron-prebuilt'
import browserSync from 'browser-sync'
import browserSyncConnectUtils from 'browser-sync/lib/connect-utils'

const bsync = browserSync.create()

const getRootUrl = (options) => {
  const port = options.get('port')
  return `http://localhost:${port}`
}

const getClientUrl = (options) => {
  const pathname = browserSyncConnectUtils.clientScript(options)
  return getRootUrl(options) + pathname
}

bsync.init({
  ui: false,
  // Port 35829 = LiveReload's default port 35729 + 100.
  // If the port is occupied, Browsersync uses next free port automatically.
  port: 35829,
  ghostMode: false,
  open: false,
  notify: false,
  logSnippet: false,
  socket: {
    // Use the actual port here.
    domain: getRootUrl
  }
}, (err, bs) => {
  if (err) return console.error(err)

  spawn(electron, ['.'], {
    env: {
      NODE_ENV: 'development',
      BROWSER_SYNC_CLIENT_URL: getClientUrl(bs.options),
      ...process.env
    },
    stdio: 'inherit'
  })

  bsync
    .watch('../react-mobile/react/**/*')
    .on('change', bsync.reload)
})

Fix up use of spread in env


'use strict';

const request = require('superagent');
const querystring = require('querystring');

const config = require('../../config/lib/bertly');

/**
 * executePost - sends the POST request to the Bertly service
 *
 * @param  {Object} data
 * @return {Promise}
 */
async function executePost(data) {
  return request
    .post(config.baseUri)
    .type('form')
    .set(config.apiKeyHeader, config.apiKey)
    .send(data)
    .then(res => res.body);
}

/**
 * createRedirect - Receives an URL. It is URI encoded and sent
 *              as the value of the url= property to Bertly.
 *
 * @see https://github.com/DoSomething/bertly#create-redirect
 * @param  {string} url
 * @return {Promise}
 */
async function createRedirect(url) {
  const encodedUrl = querystring.escape(url);
  return executePost({ url: encodedUrl });
}

module.exports = {
  createRedirect,
};

'use strict';

const request = require('superagent');

const config = require('../../config/lib/bertly');

/**
 * executePost - sends the POST request to the Bertly service
 *
 * @param  {Object} data
 * @return {Promise}
 */
async function executePost(data) {
  return request
    .post(config.baseUri)
    .type('form')
    .set(config.apiKeyHeader, config.apiKey)
    .send(data)
    .then(res => res.body);
}

/**
 * createRedirect - Receives an URL. It is URI encoded and sent
 *              as the value of the url= property to Bertly.
 *
 * @see https://github.com/DoSomething/bertly#create-redirect
 * @param  {string} url
 * @return {Promise}
 */
async function createRedirect(url) {
  return executePost({ url });
}

module.exports = {
  createRedirect,
};

Fix double encoding that would bork Bertly redirect


// Command-line arguments passed to npm when rebuilding binary packages.
var args = [
  "rebuild",

  // The --no-bin-links flag tells npm not to create symlinks in the
  // node_modules/.bin/ directory when rebuilding packages, which helps
  // avoid problems like https://github.com/meteor/meteor/issues/7401.
  "--no-bin-links",

  // The --update-binary flag tells node-pre-gyp to replace previously
  // installed local binaries with remote binaries:
  // https://github.com/mapbox/node-pre-gyp#options
  "--update-binary"
];

exports.get = function () {
  // Make a defensive copy.
  return args.slice(0);
};

// Command-line arguments passed to npm when rebuilding binary packages.
var args = [
  "rebuild",

  // The --no-bin-links flag tells npm not to create symlinks in the
  // node_modules/.bin/ directory when rebuilding packages, which helps
  // avoid problems like https://github.com/meteor/meteor/issues/7401.
  "--no-bin-links",

  // The --update-binary flag tells node-pre-gyp to replace previously
  // installed local binaries with remote binaries:
  // https://github.com/mapbox/node-pre-gyp#options
  "--update-binary"
];

// Allow additional flags to be passed via the $METEOR_NPM_REBUILD_FLAGS
// environment variable.
var flags = process.env.METEOR_NPM_REBUILD_FLAGS;
if (flags) {
  args.push.apply(args, flags.split(/\s+/g));
}

exports.get = function () {
  // Make a defensive copy.
  return args.slice(0);
};

Support additional `meteor rebuild` flags via environment variable.


import React from 'react';
import { storiesOf } from '@kadira/storybook';
import AspectRatio from '../index';

import '../../aspect-ratio.css';

storiesOf('AspectRatio', module)
  .add('Image', () => (

Image with Aspect Ratio

      <AspectRatio ratio="3/4" style={{ maxWidth: '400px' }}>

      </AspectRatio>

  ))
  .add('Background Image', () => (

Background image with aspect ratio

      <AspectRatio
        ratio="3/4"
        style={{
          maxWidth: '300px',
          backgroundImage: 'url(https://c1.staticflickr.com/4/3896/14550191836_cc0675d906.jpg)',
          backgroundSize: 'cover',
        }}
      />

  ))
  .add('Iframe', () => (

Iframe with aspect ratio

      <AspectRatio ratio="560/315" style={{ maxWidth: '560px' }}>

      </AspectRatio>

  ));

Update iframe url (the poor video was removed)


'use strict';

import debug from 'gulp-debug';
import gulp from 'gulp';
// import phantom from 'phantomjs2';
import phantom from 'phantomjs';
import plumber from 'gulp-plumber';
import webshot from 'gulp-webshot';

import config from './_config.babel.js';
import reportError from './_report-error.babel.js';

let sourceFiles = config.files.source.markup;

gulp.task('screenshots', () => {
  return gulp.src(sourceFiles)
    .pipe(plumber({
      errorHandler: reportError
    }))
    .pipe(debug({
      title: 'screenshots:'
    }))
    .pipe(webshot({
      phantomPath: phantom.path,
      dest: config.path.destination.screenshots,
      root: config.path.source.base
    }))
    .pipe(plumber.stop())
    .on('error', reportError);
});

gulp.task('screenshots:watch', function() {
  gulp.watch(sourceFiles, ['screenshots']);
});

'use strict';

import debug from 'gulp-debug';
import gulp from 'gulp';
// import phantom from 'phantomjs2';
// import phantom from 'phantomjs';
import plumber from 'gulp-plumber';
import webshot from 'gulp-webshot';

import config from './_config.babel.js';
import reportError from './_report-error.babel.js';

let sourceFiles = config.files.source.markup;

gulp.task('screenshots', () => {
  return gulp.src(sourceFiles)
    .pipe(plumber({
      errorHandler: reportError
    }))
    .pipe(debug({
      title: 'screenshots:'
    }))
    .pipe(webshot({
      phantomPath: phantom.path,
      dest: config.path.destination.screenshots,
      root: config.path.source.base
    }))
    .pipe(plumber.stop())
    .on('error', reportError);
});

gulp.task('screenshots:watch', function() {
  gulp.watch(sourceFiles, ['screenshots']);
});

Tidy gulp screenshots import directives


$(document).ready(function() {
    $("#design ul").hide();
    $("#design").on('click', function(event) {
        event.preventDefault();
        /* Act on the event */
        $(this).children('ul').slideToggle('slow', function() {

        });
    });
});

Remove click on nav event 1.move the event to hover in css


var Algorithm = require("./abstract")("RSA-OAEP")
  , RSA       = require("./shared/RSA")
  , forge     = require("node-forge")
  , types     = Algorithm.types
  , public    = types.public.usage
  , private   = types.private.usage;

//attached shared RSA
RSA(Algorithm);

Algorithm.checkParams = checkParams;

public.encrypt = createEncrypt;
private.decrypt = createDecrypt;

module.exports = Algorithm;

function createEncrypt(Key){
  return function RSA_OAEP_ENCRYPT(alg,buf){
    return new Buffer(Key.publicKey.encrypt(buf.toString("binary"), "RSA-OAEP"),"binary");
  };
}

function createDecrypt(Key){
  return function RSA_OAEP_DECRYPT(alg,buf){
    return new Buffer(Key.privateKey.decrypt(buf.toString("binary"), "RSA-OAEP"),"binary");
  };
}

function checkParams(format, algorithm, usages){
  //if (!(algorithm.hash && (algorithm.hash.name === "SHA-256")))
    //throw new Error("Unsupported or missing hash name");
  //if (!Buffer.isBuffer(algorithm.publicExponent))
    //throw new Error("algorithm.publicExponent not a Buffer source");

}

var Algorithm = require("./abstract")("RSA-OAEP")
  , RSA       = require("./shared/RSA")
  , forge     = require("node-forge")
  , types     = Algorithm.types
  , public    = types.public.usage
  , private   = types.private.usage;

//attached shared RSA
RSA(Algorithm);

Algorithm.checkParams = checkParams;

public.encrypt = createEncrypt;
private.decrypt = createDecrypt;

module.exports = Algorithm;

function createEncrypt(Key){
  return function RSA_OAEP_ENCRYPT(alg,buf){
    return new Buffer(Key.publicKey.encrypt(buf.toString("binary"), "RSA-OAEP"),"binary");
  };
}

function createDecrypt(Key){
  return function RSA_OAEP_DECRYPT(alg,buf){
    return new Buffer(Key.privateKey.decrypt(buf.toString("binary"), "RSA-OAEP", {
      md: forge.md[alg.hash.name.toLowerCase().replace('-','')].create()
    }),"binary");
  };
}

function checkParams(format, algorithm, usages){
  const ALLOWED_HASH_ALGORITHMS = ['SHA-1', 'SHA-256', 'SHA-384', 'SHA-512'];

  if (!algorithm.hash) {
    algorith.hash = {name: 'SHA-1'}
  } else if(!ALLOWED_HASH_ALGORITHMS.indexOf(algorithm.hash.name)){
    throw new Error("Unsupported or missing hash name");
  }

  if (!Buffer.isBuffer(algorithm.publicExponent))
    throw new Error("algorithm.publicExponent not a Buffer source");

}

Add hash parameter to forge decrypt.


// Load modules

var Crypto = require('crypto');

// Declare internals

var internals = {};

exports = module.exports = function (options) {

    return {
        protocol: 'oauth2',
        useParamsAuth: true,
        auth: 'https://www.facebook.com/v2.3/dialog/oauth',
        token: 'https://graph.facebook.com/oauth/access_token',
        scope: ['email'],
        scopeSeparator: ',',
        profile: function (credentials, params, get, callback) {

            var query = {
                appsecret_proof: Crypto.createHmac('sha256', this.clientSecret).update(credentials.token).digest('hex')
            };

            get('https://graph.facebook.com/v2.3/me', query, function (profile) {

                credentials.profile = {
                    id: profile.id,
                    username: profile.username,
                    displayName: profile.name,
                    name: {
                        first: profile.first_name,
                        last: profile.last_name,
                        middle: profile.middle_name
                    },
                    email: profile.email,
                    raw: profile
                };

                return callback();
            });
        }
    };
};

// Load modules

var Crypto = require('crypto');

// Declare internals

var internals = {};

exports = module.exports = function (options) {

    return {
        protocol: 'oauth2',
        useParamsAuth: true,
        auth: 'https://www.facebook.com/v2.3/dialog/oauth',
        token: 'https://graph.facebook.com/oauth/access_token',
        scope: ['email'],
        scopeSeparator: ',',
        profile: function (credentials, params, get, callback) {

            var query = {
                appsecret_proof: Crypto.createHmac('sha256', this.clientSecret).update(credentials.token).digest('hex'),
                fields: 'id,name,email,first_name,last_name,middle_name'
            };

            get('https://graph.facebook.com/v2.3/me', query, function (profile) {

                credentials.profile = {
                    id: profile.id,
                    username: profile.username,
                    displayName: profile.name,
                    name: {
                        first: profile.first_name,
                        last: profile.last_name,
                        middle: profile.middle_name
                    },
                    email: profile.email,
                    raw: profile
                };

                return callback();
            });
        }
    };
};

Add fields to the query


import React from 'react'
import {Link} from "react-static";

class NavBar extends React.Component {
  constructor (props) {
    super(props)
  }

  compareCurrentLocationWith(link) {
    return link === '/' + window.location.pathname.split('/')[1] ? 'active disabled' : ''
  }

  render () {
    return (

          Tanay PrabhuDesai

              <Link to='/'>Home</Link>

              <Link to='/resume'>Résumé</Link>

              <Link to='/blog'>Blog</Link>

              <Link to='/about'>About Me</Link>

    )
  }
}
export default NavBar

import React from 'react'
import {Link, withRouter} from 'react-static'

class NavBar extends React.Component {
  constructor (props) {
    super(props)
  }

  compareCurrentLocationWith(link) {
    return link === '/' + this.props.location.pathname.split('/')[1] ? 'active disabled' : ''
  }

  render () {
    return (

          Tanay PrabhuDesai

              <Link to='/'>Home</Link>

              <Link to='/resume'>Résumé</Link>

              <Link to='/blog'>Blog</Link>

              <Link to='/about'>About Me</Link>

    )
  }
}
export default withRouter(NavBar)

Mark selection on the nav bar


import Raven from 'raven-js';
import config from 'config';

if (config.sentry) {
    const options = {
        logger: 'admin',
        release: config.sentry.release,
        tags: config.sentry.tags,
    };

    Raven.config(config.sentry.dsn, options).install();
}

export default Raven;

import Raven from 'raven-js';
import config from 'config';

if (config.sentry) {
    const options = {
        logger: 'udata.js',  // Allow filtering in Sentry
        release: config.sentry.release,
        tags: config.sentry.tags,
    };

    Raven.config(config.sentry.dsn, options).install();
}

export default Raven;

Rename JS logger from admin to udata.js


'use strict';

/**
 * Module dependencies.
 */
var _ = require('lodash'),
	errorHandler = require('../errors.server.controller'),
	mongoose = require('mongoose'),
	passport = require('passport'),
	User = mongoose.model('User');

/**
 * View all users
 */
exports.all = function(req, res) {
  User.find({_id: {$ne: req.user._id}}, {displayName: 1}, function(err, docs) {
    return res.status(200).send(docs);
  });
};

exports.sendMessage = function(req, res) {
	var newMessage = {text: req.body.message, from: req.user.displayName};
	User.findOne({_id: req.body.id}, function(err, doc) {
    doc.messages.push(newMessage);
		doc.save();
  });
	return res.status(200);
};

'use strict';

/**
 * Module dependencies.
 */
var _ = require('lodash'),
	errorHandler = require('../errors.server.controller'),
	mongoose = require('mongoose'),
	passport = require('passport'),
	User = mongoose.model('User');

/**
 * View all users
 */
exports.all = function(req, res) {
  User.find({_id: {$ne: req.user._id}}, {displayName: 1}, function(err, docs) {
    return res.status(200).send(docs);
  });
};

exports.sendMessage = function(req, res) {
	var newMessage = {text: req.body.message, from: req.user.displayName};
	User.findOne({_id: req.body.id}, function(err, doc) {
    doc.messages.push(newMessage);
		doc.save();
  });
	return res.status(200).send('Message sent');
};

Fix bug with message about sending message to user


import State from './src/State';
import Observer from './src/Observer';

export {
  State,
  Observer
};

export { default as State } from './src/State';
export { default as Observer } from './src/Observer';

IMPROVE change re-export to proper es6 syntax


var vui = vui || {};
vui.matchers = vui.matchers || {};

vui.matchers.jasmine = {
	toMatchRecordedObjectAt: function() {
		return {
			compare: function ( actual, recordedObjectPath, exceptions ) {

				var expectedResult;

				//@if !RECORDING
				expectedResult = vui.records.getRecord(recordedObjectPath);
				//@endif

				//@if RECORDING
				expectedResult = actual
				vui.records.setRecord(recordedObjectPath, expectedResult);
				//@endif

				var diff = vui.differs.json.diffLogs( actual, expectedResult, exceptions );

	       		var retStr = "";
				for( d in diff ) {
					retStr = "Expected " + d + " to be " + diff[d].expected + " but got " + diff[d].actual;
				}

				return {
					pass: retStr == "",
					message: retStr
				};

			}
		};
	}
};

var vui = vui || {};
vui.matchers = vui.matchers || {};

vui.matchers.jasmine = {
	toMatchRecordedObjectAt: function() {
		return {
			compare: function ( actual, recordedObjectPath, exceptions ) {

				var expectedResult;

				//@if !RECORDING
				expectedResult = vui.records.getRecord(recordedObjectPath);
				//@endif

				//@if RECORDING
				expectedResult = actual
				vui.records.setRecord(recordedObjectPath, expectedResult);
				//@endif

				var diff = vui.differs.json.diffLogs( actual, expectedResult, exceptions );

	       		var retStr = "";
				for( d in diff ) {
					retStr = retStr + "Expected " + d + " to be " + diff[d].expected + " but got " + diff[d].actual + "\n";
				}

				return {
					pass: retStr == "",
					message: retStr
				};

			}
		};
	}
};

Fix an issue with the Jasmine log not outputting the full string.


"use strict";

const Task = require('../Task'),
    gulp = require('gulp'),
    mocha = require('gulp-mocha'),
    istanbul = require('gulp-istanbul'),
    isparta = require('isparta');

class TestTask extends Task {

    constructor(buildManager) {
        super(buildManager);
        this.command = "test";
        let extension = null;
        if (this._buildManager.options.typescript) {
            require('ts-node/register');
            extension = 'ts';
        } else {
            require('babel-core/register');
            extension = 'js';
        }
        this.dependsOn = [() => {
            return gulp.src([this._buildManager.options.scripts + '*/*.' + extension])
                .pipe(istanbul({
                    instrumenter: isparta.Instrumenter,
                    includeUntested: true
                }))
                .pipe(istanbul.hookRequire());
        }];
    }

    action() {
        return gulp.src(this._buildManager.options.test, {read: false})
            .pipe(mocha({
                reporter: 'spec',
                compilers: {
                    ts: require('ts-node/register'),
                    js: require('babel-core/register')
                }
            }))
            .pipe(istanbul.writeReports({
                reporters: ['lcov', 'json', 'text', 'text-summary', 'cobertura']
            }))
    }
}

module.exports = TestTask;

"use strict";

const Task = require('../Task'),
    gulp = require('gulp'),
    mocha = require('gulp-mocha'),
    istanbul = require('gulp-istanbul'),
    isparta = require('isparta'),
    Promise = require('bluebird');

class TestTask extends Task {

    constructor(buildManager) {
        super(buildManager);
        this.command = "test";
        let extension = null;
        if (this._buildManager.options.typescript) {
            require('ts-node/register');
            extension = 'ts';
        } else {
            require('babel-core/register');
            extension = 'js';
        }
        this.dependsOn = [() => {
            if (this._buildManager.options.typescript) return Promise.resolve();
            return gulp.src([this._buildManager.options.scripts + '*/*.' + extension])
                .pipe(istanbul({
                    instrumenter: isparta.Instrumenter,
                    includeUntested: true
                }))
                .pipe(istanbul.hookRequire());
        }];
    }

    action() {
        return gulp.src(this._buildManager.options.test, {read: false})
            .pipe(mocha({
                reporter: 'spec',
                compilers: {
                    ts: require('ts-node/register'),
                    js: require('babel-core/register')
                }
            }))
            .pipe(istanbul.writeReports({
                reporters: ['lcov', 'json', 'text', 'text-summary', 'cobertura']
            }))
    }
}

module.exports = TestTask;

Disable typescript instrument on tests


function change_chat_tabs() {
  console.log("Event");
  inject_image('._d97');
  jq('._d97').each(function(){
    for (var i = 0; i < image_decoder.length; i++) {
      console.log('Checking for ' + image_decoder[i][0])
      jq(this).html(jq(this).html().replace(
        image_decoder[i][0],
        "<img class='emoticon' style='height: 30px; width: 30px;' src='" + image_decoder[i][1] + "'/>"
      ));
    }
  });
}

jq('.fbNubFlyout').on('DOMNodeInserted DOMNodeRemoved change load', function() {  // Something changes inside the chat tab
  change_chat_tabs();
});

jq('._d97').on('change load', function(){  // Chat Bubble messages
  change_chat_tabs();
});

jq('.fbNub').on('click', function() {  // Facebook tab button is clicked
  change_chat_tabs();
});

jq('._55ln').on('click', function() {  //Facebook sidebar person is clicked
  change_chat_tabs();
})

jq('._552m').on('input paste', function() {  // Text change in textbox. Mainly used to cover all bases
  change_chat_tabs();
});

change_chat_tabs();

function change_chat_tabs() {
  console.log("Event");
  inject_image('._d97', 'emoticon');
}

jq('.fbNubFlyout').on('DOMNodeInserted DOMNodeRemoved change load', function() {  // Something changes inside the chat tab
  change_chat_tabs();
});

jq('._d97').on('change load', function(){  // Chat Bubble messages
  change_chat_tabs();
});

jq('.fbNub').on('click', function() {  // Facebook tab button is clicked
  change_chat_tabs();
});

jq('._55ln').on('click', function() {  //Facebook sidebar person is clicked
  change_chat_tabs();
})

change_chat_tabs();

Stop message event fireing as much


var sum = 1;
var currentNumber = 1;

for (var increment = 2; currentNumber < 1002001; increment += 2) {
  for (var counter = 0; counter < 4; counter++) {
    currentNumber += increment;
    sum += currentNumber;
  }
}

console.log(sum);

diagonalSum = function(sideLength) {
  var sum = 1;
  var currentNumber = 1;

  for (var increment = 2; currentNumber < sideLength * sideLength; increment += 2) {
    for (var counter = 0; counter < 4; counter++) {
      currentNumber += increment;
      sum += currentNumber;
    }
  }

  return sum;
}

console.log(diagonalSum(1001));

Refactor for more general usability


function precommit_noop(object)
{
    return object;
}

function precommit_append_value(object)
{
    upd_data = object.values[[0]].data + "_precommit_hook_was_here";
    object.values[[0]].data = upd_data;
    return object;
}

function precommit_nonobj(object)
{
    return "not_an_obj";
}

function precommit_fail(object)
{
    return "fail";
}

function precommit_fail_reason(object)
{
    return {"fail":"the hook says no"};
}

function precommit_crash(object)
{
    throw "wobbler";
}

Add missing newline at EOF


var expect = require('chai').expect;
var count = require('count-substring');

module.exports = function (browser) {
    describe("The tests written in Elm", function () {
        it('should pass', function () {
            return browser
                .url('http://localhost:8080/elm.html')
                .waitUntil(function () {
                    return this.getText("#results").then(function (text) {
                        return text.indexOf("suites run") > 0;
                    });
                }, 30000, 500)
                .getText("#results")
                .then(function (text) {
                    // Always log the test results
                    console.log(text);

                    var failedCount = count(text, "FAILED");
                    expect(failedCount).to.equal(0);
                });
        });
    });
};

var expect = require('chai').expect;
var count = require('count-substring');
var Q = require('q');

module.exports = function (browser) {
    describe("The tests written in Elm", function () {
        var falsy = function () {
            return Q.when(false);
        };

        it('should pass', function () {
            return browser
                .url('http://localhost:8080/elm.html')
                .waitUntil(function () {
                    return this.getText("#results").then(function (text) {
                        return text.indexOf("suites run") > 0;
                    }, falsy);
                }, 30000, 500)
                .getText("#results")
                .then(function (text) {
                    // Always log the test results
                    console.log(text);

                    var failedCount = count(text, "FAILED");
                    expect(failedCount).to.equal(0);
                });
        });
    });
};

Fix another timing issue in tests.


import RailsRanger from '../../src/rails-ranger'

describe('railsRanger', () => {
  describe('.get', () => {
    beforeEach(() => {
      spy(RailsRanger.client, 'get')
    })

    it('triggers a get request through the client', () => {
      let api = new RailsRanger()
      api.get('/users')
      expect(RailsRanger.client.get).to.have.been.calledOnce
    })

    it('interpolates parameters into the path', () => {
      let api = new RailsRanger()
      api.get('/users/:id', { id: 1 })
      expect(RailsRanger.client.get).to.have.been.calledOnce
    })

    it('transforms remaining parameters into query params', () => {
      expect(RailsRanger.greet).to.have.always.returned('hello')
    })
  })
})

import RailsRanger from '../../src/rails-ranger'

describe('RailsRanger', () => {
  let ranger = null

  beforeEach(() => {
    // Defines a new RailsRanger instance to be used in each test
    ranger = new RailsRanger()
  })

  describe('.get', () => {
    // Spy on client.get function
    beforeEach(() => { spy(ranger.client, 'get') })

    it('triggers a get request through the client', () => {
      ranger.get('/users')
      expect(ranger.client.get).to.have.been.calledOnce
    })

    it('interpolates parameters into the path', () => {
      ranger.get('/users/:id', { id: 1 })
      expect(ranger.client.get).to.have.been.calledWith('/users/1')
    })

    it('interpolates parameters into the query', () => {
      ranger.get('/users/', { id: 1 })
      expect(ranger.client.get).to.have.been.calledWith('/users/?id=1')
    })

    it('transforms remaining parameters from path into query', () => {
      ranger.get('/users/:id', { id: 1, only: 'logged' })
      expect(ranger.client.get).to.have.been.calledWith('/users/1?only=logged')
    })
  })

  describe('.post', () => {
    // Spy on client.post function
    beforeEach(() => { spy(ranger.client, 'post') })

    it('triggers a post request through the client', () => {
      ranger.post('/users')
      expect(ranger.client.post).to.have.been.calledOnce
    })

    it('interpolates parameters into the path', () => {
      ranger.post('/users/:id', { id: 1 })
      expect(ranger.client.post).to.have.been.calledWith('/users/1')
    })
  })

  describe('.delete', () => {
    // Spy on client.delete function
    beforeEach(() => { spy(ranger.client, 'delete') })

    it('triggers a delete request through the client', () => {
      ranger.delete('/users')
      expect(ranger.client.delete).to.have.been.calledOnce
    })

    it('interpolates parameters into the path', () => {
      ranger.delete('/users/:id', { id: 1 })
      expect(ranger.client.delete).to.have.been.calledWith('/users/1')
    })
  })
})

Add tests for get, post and delete


/**
 * Copyright 2014 Ian Davies
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 *
 * You may obtain a copy of the License at
 * 		http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
define([], function() {

	var PageController = ['$scope', 'auth', 'api', function($scope, auth, api) {
		$scope.contentVersion = api.contentVersion.get();

		$scope.setVersion = function() {
			api.admin.synchroniseDatastores().then(function() {
				api.contentVersion.set({version: $scope.contentVersion.liveVersion}, {}).then(function(data) {
					$scope.contentVersion = api.contentVersion.get();
				});
			})
		}
	}]

	return {
		PageController: PageController,
	};
})

/**
 * Copyright 2014 Ian Davies
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 *
 * You may obtain a copy of the License at
 * 		http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
define([], function() {

	var PageController = ['$scope', 'auth', 'api', function($scope, auth, api) {
		$scope.contentVersion = api.contentVersion.get();

		$scope.setVersion = function() {
			api.admin.synchroniseDatastores().then(function() {
				api.contentVersion.set({version: $scope.contentVersion.liveVersion}, {}).$promise.then(function(data) {
					$scope.contentVersion = api.contentVersion.get();
				});
			})
		}
	}]

	return {
		PageController: PageController,
	};
})

Fix setting content version on admin page.


(function() {
  var BackgroundGeo = function($q) {
    var from;

    current()
      .then(function(data){
        from = new google.maps.LatLng(data.latitude, data.longitude);
      });

    instance = {
      current: current,
      distance: distance
    };

    ///////////////

    function current(){
      var locator = $q.defer();
      locator.resolve(window.currLocation.coords);
      return locator.promise;
    }
    function distance(lat, lng){
      var dist, to;
      to = new google.maps.LatLng(lat, lng);
      dist = google.maps.geometry.spherical.computeDistanceBetween(from, to) * 0.000621371192;
      return Math.floor((dist + 0.005)*100) / 100;

    }

    return instance;
  };
  BackgroundGeo
    .$inject = ['$q'];
  angular
    .module('app.services.BackgroundGeo', [])
    .factory('BackgroundGeo', BackgroundGeo);
})();

(function() {
  var BackgroundGeo = function($q) {
    var from;

    current()
      .then(function(data){
        from = new google.maps.LatLng(data.latitude, data.longitude);
      });

    var instance = {
      current: current,
      distance: distance
    };

    ///////////////

    function current(){
      var locator = $q.defer();
      locator.resolve(window.currLocation.coords);
      return locator.promise;
    };

    function distance(lat, lng){
      var dist, to;
      to = new google.maps.LatLng(lat, lng);
      from = new google.maps.LatLng(window.currLocation.coords.latitude, window.currLocation.coords.longitude);
      dist = google.maps.geometry.spherical.computeDistanceBetween(from, to) * 0.000621371192;
      return Math.floor((dist + 0.005)*100) / 100;
    };

    return instance;
  };
  BackgroundGeo
    .$inject = ['$q'];
  angular
    .module('app.services.BackgroundGeo', [])
    .factory('BackgroundGeo', BackgroundGeo);
})();

Fix bug in from location


var state = { };

const setAppState = newState => {
  console.log("Pre merge app state is", state, newState);
  state = { ...state, ...newState };
  console.log("App state is", state);
};

const getAppState = () => {
  return state;
};

export {
  getAppState,
  setAppState,
};

var state = { };

const setAppState = newState => {
  state = { ...state, ...newState };
};

const getAppState = () => {
  return state;
};

export {
  getAppState,
  setAppState,
};

Remove logging statements from global state.


/// <binding ProjectOpened='watch' />
module.exports = function (grunt) {

    grunt.loadNpmTasks("grunt-browserify");
    grunt.loadNpmTasks("grunt-contrib-watch");

    grunt.initConfig({
        browserify: {
            dist: {
                files: {
                    "./App/Build/bundle.js": ["./App/**/*.es6.js"]
                },
                options: {
                    transform: [
                       ["babelify", {
                           loose: "all"
                       }]
                    ]
                }
            }
        },
        watch: {
            scripts: {
                files: ["Gruntfile.js", "./App/**/*.es6.js"],
                tasks: ["browserify"]
            }
        }
    });

    grunt.registerTask("default", ["build", "watch"]);
    grunt.registerTask("build", ["browserify"]);
};

Add bindings for vs extension. if you open projcet, it should auto run watch command


var locastyle = locastyle || {};

locastyle.charCounter = (function() {
  'use strict';

  function updateCounter(index, count) {
    $('.ls-number-counter-'+index).text(count);
  }

  function countText() {
    $('[data-ls-module="charCounter"]').each(function(index, field) {
      var limit = $(field).attr('maxlength');
      var html = '
' + limit +
        ' caracteres restantes
';
      var prefixGroup = $(field).closest('.ls-prefix-group');

      $(field).removeAttr('maxlength').data().maxlength = limit;

      if (prefixGroup.length) {
        prefixGroup.after(html);
      } else {
        $(field).after(html);
      }

      $(field).keyup(function() {
        var count = $(this).val().length;
        var limit = $(this).data().maxlength;

        if(count > limit) {
          $(this).val($(this).val().substring(0, limit));
          updateCounter(index, 0);
        } else {
          updateCounter(index, limit - count);
        }
      });

      $(field).trigger('keyup');
    });
  }

  function init() {
    countText();
  }

  return {
    init: init
  };

}());

var locastyle = locastyle || {};

locastyle.charCounter = (function() {
  'use strict';

  function updateCounter(index, count) {
    $('.ls-number-counter-'+index).text(count);
  }

  function countText() {
    $('[data-ls-module="charCounter"]').each(function(index, field) {
      $('.ls-number-counter-' + index).parent().remove();

      var limit = $(field).attr('maxlength');
      var html = '
' + limit +
        ' caracteres restantes
';
      var prefixGroup = $(field).closest('.ls-prefix-group');

      $(field).data().maxlength = limit;

      if (prefixGroup.length) {
        prefixGroup.after(html);
      } else {
        $(field).after(html);
      }

      $(field).keyup(function() {
        var count = $(this).val().length;
        var limit = $(this).data().maxlength;

        if(count > limit) {
          $(this).val($(this).val().substring(0, limit));
          updateCounter(index, 0);
        } else {
          updateCounter(index, limit - count);
        }
      });

      $(field).trigger('keyup');
    });
  }

  function init() {
    countText();
  }

  return {
    init: init
  };

}());

Fix char counter when init one more time


$(document).ready(function () {
    var locale = 'en' !== LOCALE ? LOCALE : '';
    var options = $.extend({}, $.datepicker.regional[locale], $.timepicker.regional[locale], {
        dateFormat: 'dd.mm.yy'
    });

    var init;
    (init = function () {
        ['date', 'datetime', 'time'].map(function (type) {
            $('input.' + type)[type + 'picker'](options);
        });
    })();

    $(document).bind('ajaxSuccess', init);
});

$(document).ready(function () {
    var locale = 'en' !== LOCALE ? LOCALE : '';
    var translations = {
        datepicker: $.datepicker.regional['undefined' !== typeof $.datepicker.regional[locale] ? locale : ''],
        timepicker: $.timepicker.regional['undefined' !== typeof $.timepicker.regional[locale] ? locale : '']
    };

    var options = $.extend({}, translations.datepicker, translations.timepicker, {
        dateFormat: 'dd.mm.yy'
    });

    var init;
    (init = function () {
        ['date', 'datetime', 'time'].map(function (type) {
            $('input.' + type)[type + 'picker'](options);
        });
    })();

    $(document).bind('ajaxSuccess', init);
});

Use english locale as fallback for date-time-pickers.


$(document).ready(function() {
    var id = location.pathname.split('/').slice(-1)[0];
    if (id) {
        $('#paste').show();
        $.get('get/' + id, function(entry) {
            $('#paste').text(entry.content);
            $('#content').val(entry.content);
        }, 'json');
    }

    $('#entry form').bind('submit', function() {
        var entry = {
            content: $('#content').val()
        };
        $.post("post", JSON.stringify(entry), function(id) {
            location = id;
        }, 'text');
        return false;
    });
});

$(document).ready(function() {
    var id = location.pathname.split('/').slice(-1)[0];
    if (id) {
        $.get('get/' + id, function(entry) {
            $('#paste').show().text(entry.content);
            $('#content').val(entry.content);
        }, 'json');
    }

    $('#entry form').bind('submit', function() {
        var entry = {
            content: $('#content').val()
        };
        $.post("post", JSON.stringify(entry), function(id) {
            location = id;
        }, 'text');
        return false;
    });
});

Use jQuery chaining, you dolt.