define(['backbone', 'marionette'], function(Backbone, Marionette) {
    return Backbone.View.extend({
        template: '#event-creation',

        events: {
            'click button.create-event': 'create_event'
        },

        initialize: function() {},

        render: function() {
            var rendered_template = _.template($(this.template).html())();
            this.$el.empty();
            this.$el.append(rendered_template);
            return this;
        },

        create_event: function(e) {
            e.preventDefault();
            var title = this.$el.find('.event-title').val();
            var description = this.$el.find('.event-description').val();
            this.$el.html($('#loading').html());
            this.collection.create({
                title: title,
                description: description
            }, {
                wait: true,
                success: function (model, response, options) {
                    this.render();
                }.bind(this),
                error: function (model, xhr, options) {

                }
            });
        }
    });
});

define(['backbone', 'marionette'], function(Backbone, Marionette) {
    return Backbone.View.extend({
        template: '#event-creation',

        events: {
            'click button.create-event': 'create_event'
        },

        initialize: function() {},

        render: function() {
            var rendered_template = _.template($(this.template).html())();
            this.$el.empty();
            this.$el.append(rendered_template);
            return this;
        },

        create_event: function(e) {
            e.preventDefault();
            var title = this.$el.find('.event-title').val();
            var description = this.$el.find('.event-description').val();
            this.$el.html($('#loading').html());
            this.collection.create({
                title: title,
                description: description,
                date: Date.now()
            }, {
                wait: true,
                success: function (model, response, options) {
                    this.render();
                    console.log(this.collection.models);
                }.bind(this),
                error: function (model, xhr, options) {
                    console.log('ERROR!');
                    this.render();
                }
            });
        }
    });
});

Set date when creating event, not when it's require'd


import React from 'react';

class SimpleTourItem extends React.Component {

  render() {
    const containerStyle = {
      background: 'white',
      padding: '20px',
      color: 'black',
      maxWidth: '300px'
    };

    const buttonStyle = {
      cursor: 'pointer',
      textDecoration: 'none',
      color: '#3498db',
      margin: '12px'
    };

    return (

{this.props.message}

          Skip

          Close

    );
  }
}

SimpleTourItem.propTypes = {
  message: React.PropTypes.string,
  onSkip: React.PropTypes.func,
  onDone: React.PropTypes.func
};

export default SimpleTourItem;

import React from 'react';

class SimpleTourItem extends React.Component {

  render() {
    const containerStyle = {
      background: 'white',
      padding: '20px',
      color: 'black',
      maxWidth: '300px'
    };

    const buttonStyle = {
      cursor: 'pointer',
      textDecoration: 'none',
      color: '#3498db',
      margin: '12px'
    };

    return (

{this.props.message}

          Skip

          Next

    );
  }
}

SimpleTourItem.propTypes = {
  message: React.PropTypes.string,
  onSkip: React.PropTypes.func,
  onDone: React.PropTypes.func
};

export default SimpleTourItem;

Change word from Done to Next


"use strict";

const flow    = require("lodash.flow");
const deburr  = require("lodash.deburr");
const toLower = require("lodash.tolower");

const looseMatchTransform = flow(deburr, toLower);

/**
 * Function that returns true when `needle` is found in `haystack`.
 * The main advantages of this function are that it removes accented characters and that
 * it is case-insensitive.
 *
 * Powered by lodash's {@link https://lodash.com/docs/#deburr deburr}
 *
 * @function looselyMatches
 *
 * @param haystack {String} The string to inspect.
 * @param needle   {String} The substring to look for.
 *
 * @return {Boolean} True when variations of `needle` can be found inside `haystack`.
 */
module.exports = (haystack, needle) => {
    return looseMatchTransform(haystack).indexOf(looseMatchTransform(needle)) !== -1;
};

"use strict";

const flow    = require("lodash.flow");
const deburr  = require("lodash.deburr");
const toLower = require("lodash.tolower");

const looseMatchTransform = flow(deburr, toLower);

/**
 * Function that returns true when `needle` is found in `haystack`.
 * The main advantages of this function are that it removes accented characters and that
 * it is case-insensitive.
 *
 * Powered by lodash's {@link https://lodash.com/docs/#deburr deburr}.
 *
 * @function looselyMatches
 *
 * @param haystack {String} The string to inspect.
 * @param needle   {String} The substring to look for.
 *
 * @return {Boolean} True when variations of `needle` can be found inside `haystack`.
 */
module.exports = (haystack, needle) => {
    return looseMatchTransform(haystack).indexOf(looseMatchTransform(needle)) !== -1;
};

Add terminal . at end of sentence in looselyMatches documentation string


export default class MiddlewareStack {
  constructor(handler, stack = []) {
    this._handler = handler;
    this._stack = stack.map(element => (typeof element === 'function' ? new element() : element));
    this._regenerateMiddlewareProcess();
  }

  push(middleware) {
    this._stack.push(middleware);
    this._regenerateMiddlewareProcess();
  }

  pop() {
    return this._stack.pop();
  }

  process(...data) {
    return this._middlewareProcess(...data);
  }

  _generateMiddlewareProcess(action) {
    return this._stack.reduceRight(
      (previous, current) => (...data) => current.handle(previous, ...data),
      ...data => action(...data)
    );
  }

  _regenerateMiddlewareProcess() {
    this._middlewareProcess = this._generateMiddlewareProcess(this._handler.action);
  }
}

export default class MiddlewareStack {
  constructor(handler, stack = []) {
    this._handler = handler;
    this._stack = stack.map(element => (typeof element === 'function' ? new element() : element));
    this._regenerateMiddlewareProcess();
  }

  push(middleware) {
    this._stack.push(middleware);
    this._regenerateMiddlewareProcess();
  }

  pop() {
    const popped = this._stack.pop();
    this._regenerateMiddlewareProcess();

    return popped;
  }

  process(...data) {
    return this._middlewareProcess(...data);
  }

  _generateMiddlewareProcess(action) {
    return this._stack.reduceRight(
      (previous, current) => (...data) => current.handle(previous, ...data),
      ...data => action(...data)
    );
  }

  _regenerateMiddlewareProcess() {
    this._middlewareProcess = this._generateMiddlewareProcess(this._handler.action);
  }
}

Make the middleware regen after pops.


var makeSignatureFromRoutes = require('../../src/react/router').makeSignatureFromRoutes

describe("react: makeSignatureFromRoutes", function() {
  it("should correctly join paths", function() {

    var routes = [
      {path: "/"}, {path: "/something"},
    ]

    expect(makeSignatureFromRoutes(routes)).toBe("/something")

    routes = [
      {path: "/"}, {path: "something"},
    ]

    expect(makeSignatureFromRoutes(routes)).toBe("/something")
  })

  it("should handle zero routes", function() {
    expect(makeSignatureFromRoutes([])).toBe("unknown")
  })
})

var makeSignatureFromRoutes = require('../../src/react/router').makeSignatureFromRoutes
var pushLocation = {action: 'PUSH'}, replaceLocation = {action: 'REPLACE'}

describe("react: makeSignatureFromRoutes", function() {
  it("should correctly join paths", function() {

    var routes = [
      {path: "/"}, {path: "/something"},
    ]

    var pushLocation = {
      action: 'PUSH'
    }

    expect(makeSignatureFromRoutes(routes, pushLocation)).toBe("/something")

    routes = [
      {path: "/"}, {path: "something"},
    ]

    expect(makeSignatureFromRoutes(routes, pushLocation)).toBe("/something")
  })

  it("should handle zero routes", function() {
    expect(makeSignatureFromRoutes([], pushLocation)).toBe("unknown")
  })

  it("should handle REPLACE routes", function() {
    var routes = [
      {path: "/"}, {path: "something"},
    ]
    expect(makeSignatureFromRoutes(routes, replaceLocation)).toBe("/something (REPLACE)")
  })
})

Add test for REPLACE route


import wrapper from 'atomic-fuel/libs/constants/wrapper';
import Network from 'atomic-fuel/libs/constants/network';

// Local actions
const actions = [];

// Actions that make an api request
const requests = ['SEARCH_FOR_ACCOUNT_USERS', 'GET_ACCOUNT_USER', 'UPDATE_ACCOUNT_USER'];

export const Constants = wrapper(actions, requests);

export const searchForAccountUsers = (searchTerm, page) => ({
  type: Constants.SEARCH_FOR_ACCOUNT_USERS,
  method: Network.GET,
  url: 'api/canvas_account_users',
  params: {
    search_term: searchTerm,
    page,
  },
});

export const getAccountUser = userId => ({
  type: Constants.GET_ACCOUNT_USER,
  method: Network.GET,
  url: `api/canvas_account_users/${userId}`,
});

export const updateAccountUser = (userId, userAttributes) => ({
  type: Constants.UPDATE_ACCOUNT_USER,
  method: Network.PUT,
  url: `api/canvas_account_users/${userId}`,
  body: {
    user: {
      name: userAttributes.name,
      login_id: userAttributes.loginId,
      sis_user_id: userAttributes.sisUserId,
      email: userAttributes.email,
    },
  },
});

import wrapper from 'atomic-fuel/libs/constants/wrapper';
import Network from 'atomic-fuel/libs/constants/network';

// Increasing timeout to accomodate slow environments. Default is 20_000.
Network.TIMEOUT = 60_000;

// Local actions
const actions = [];

// Actions that make an api request
const requests = ['SEARCH_FOR_ACCOUNT_USERS', 'GET_ACCOUNT_USER', 'UPDATE_ACCOUNT_USER'];

export const Constants = wrapper(actions, requests);

export const searchForAccountUsers = (searchTerm, page) => ({
  type: Constants.SEARCH_FOR_ACCOUNT_USERS,
  method: Network.GET,
  url: 'api/canvas_account_users',
  params: {
    search_term: searchTerm,
    page,
  },
});

export const getAccountUser = userId => ({
  type: Constants.GET_ACCOUNT_USER,
  method: Network.GET,
  url: `api/canvas_account_users/${userId}`,
});

export const updateAccountUser = (userId, userAttributes) => ({
  type: Constants.UPDATE_ACCOUNT_USER,
  method: Network.PUT,
  url: `api/canvas_account_users/${userId}`,
  body: {
    user: {
      name: userAttributes.name,
      login_id: userAttributes.loginId,
      sis_user_id: userAttributes.sisUserId,
      email: userAttributes.email,
    },
  },
});

Increase client-side network timeout from 20 seconds to 60


var svcMod = angular.module( "linksWeb.service-api", [] );

svcMod.factory( 'API', [ "$http", "$window", function ( $http, $window ) {

    var apiRequest = function ( method, path, requestData, callback ) {

        var headers = {
            "Content-Type": "application/json",
            "Authorization": "Token " + $window.sessionStorage.token
        };

        var options = {
            method: method,
            url: path,
            headers: headers,
            data: requestData
        };

        $http( options )
            .success( function ( data, status, headers, config ) {

                callback( null, data );

            } )
            .error( function ( data, status, headers, config ) {

                var error = {
                    data: data,
                    status: status
                };

                callback( error, null );

            } );

    };

    return {
        $get: function ( path, callback ) {
            return apiRequest( 'GET', path, {}, callback );
        },
        $post: function ( path, requestData, callback ) {
            return apiRequest( 'POST', path, requestData, callback );
        },
        $put: function ( path, requestData, callback ) {
            return apiRequest( 'PUT', path, requestData, callback );
        },
        $patch: function ( path, requestData, callback ) {
            return apiRequest( 'PATCH', path, requestData, callback );
        },
        $delete: function ( path, callback ) {
            return apiRequest( 'DELETE', path, {}, callback );
        }
    };

} ] );

var svcMod = angular.module( "linksWeb.service-api", [] );

svcMod.factory( 'API', [ "$http", "$window", function ( $http, $window ) {

    var apiRequest = function ( method, path, requestData, callback ) {

        var headers = {
            "Content-Type": "application/json"
        };

        if ( $window.sessionStorage.token ) {
            headers.Authorization = "Token " + $window.sessionStorage.token;
        }

        var options = {
            method: method,
            url: path,
            headers: headers,
            data: requestData
        };

        $http( options )
            .success( function ( data, status, headers, config ) {

                callback( null, data );

            } )
            .error( function ( data, status, headers, config ) {

                var error = {
                    data: data,
                    status: status
                };

                callback( error, null );

            } );

    };

    return {
        $get: function ( path, callback ) {
            return apiRequest( 'GET', path, {}, callback );
        },
        $post: function ( path, requestData, callback ) {
            return apiRequest( 'POST', path, requestData, callback );
        },
        $put: function ( path, requestData, callback ) {
            return apiRequest( 'PUT', path, requestData, callback );
        },
        $patch: function ( path, requestData, callback ) {
            return apiRequest( 'PATCH', path, requestData, callback );
        },
        $delete: function ( path, callback ) {
            return apiRequest( 'DELETE', path, {}, callback );
        }
    };

} ] );

Set the Authorization header if a token exists


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

module.exports = function() {
    return [
        {
            request: {
                method: "GET",
                url: "http://www.example.org/xform00",
                params: {},
            },
            response: {
                code: "200",
                data: sample_xform,
            },
        },
    ];
};

module.exports = function() {
    return [];
};

Revert "Add fixture for getting an xform from an external server"


/*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */

window.matchMedia = window.matchMedia || (function( doc, undefined ) {

  "use strict";

  var bool,
      docElem = doc.documentElement,
      refNode = docElem.firstElementChild || docElem.firstChild,
      // fakeBody required for <FF4 when executed in <head>
      fakeBody = doc.createElement( "body" ),
      div = doc.createElement( "div" );

  div.id = "mq-test-1";
  div.style.cssText = "position:absolute;top:-100em";
  fakeBody.style.background = "none";
  fakeBody.appendChild(div);

  return function(q){

    div.innerHTML = "<style media=\"" + q + "\"> #mq-test-1 { width: 42px; }</style>";

    docElem.insertBefore( fakeBody, refNode );
    bool = div.offsetWidth === 42;
    docElem.removeChild( fakeBody );

    return {
      matches: bool,
      media: q
    };

  };

}( document ));

/*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */

window.matchMedia || (window.matchMedia = function() {
    "use strict";

    var styleMedia  = (window.styleMedia || window.media);

    // For those that doen't support matchMedium
    if (!styleMedia) {
        var style       = document.createElement('style'),
            info        = null,
            setStyle     = function(text) {
                if (style.styleSheet) {
                    style.styleSheet.cssText = text;
                } else {
                    style.textContent = text;
                }
            };

        style.type  = 'text/css';
        style.id    = 'matchmediajs-test';

        document.getElementsByTagName('head')[0].appendChild(style);
        info = ('getComputedStyle' in window) && window.getComputedStyle(style) || style.currentStyle;

        styleMedia = {
            matchMedium: function(media) {
                var text = '@media ' + media + '{ #matchmediajs-test { width: 1px; } }',
                    match;

                // Add css text
                setStyle(text);

                match = info.width === '1px';

                // remove css text
                setStyle('');

                return match;
            }
        };

    }

    return function(media) {
        return {
            matches: styleMedia.matchMedium(media || 'all'),
            media: media || 'all'
        };
    };
}());

Complete rewrite in an effort to gain performance


// This is our options configuration for Pa11y
// https://github.com/pa11y/pa11y#configuration

const options = {
  timeout: 60000,
  hideElements: '.skip-to, .is-visuallyhidden'
};

module.exports = options;

// This is our options configuration for Pa11y
// https://github.com/pa11y/pa11y#configuration

const options = {
  timeout: 60000,
  hideElements: '.skip-to, .is-visuallyhidden, .visuallyhidden'
};

module.exports = options;

Add .visuallyhidden class to Pa11y test excludes


const wiki = require('wikijs').default;

module.exports.commands = ['wiki', 'wikipedia'];

function prettyPage(page) {
  return page.summary()
    // Get the first "sentence" (hopefully)
    .then(str => str.substr(0, str.indexOf('.') + 1))
    // Truncate with an ellipsis if length exceeds 250 chars
    .then(str => (str.length > 250 ? `${str.substr(0, 250)}...` : str))
    // Append Wikipedia URL
    .then(str => `${str} <${page.raw.canonicalurl}>`);
}

module.exports.run = function run(remainder, parts, reply) {
  wiki().search(remainder, 1)
    .then(data => data.results[0])
    .then(wiki().page)
    .then(prettyPage)
    .then(reply)
    .catch(() => reply(`No Wikipedia page found for "${remainder}"`));
};

const wiki = require('wikijs').default;

module.exports.commands = ['wiki', 'wikipedia'];

const urlRegex = /^(?:https?:\/\/)?(?:en\.)?wikipedia\.org\/wiki\/(.+)/;

const errorMessage = term => `No Wikipedia page found for "${term}"`;

function shortSummary(page, withUrl) {
  return page.summary()
    // Get the first "sentence" (hopefully)
    .then(str => str.substr(0, str.indexOf('.') + 1))
    // Truncate with an ellipsis if length exceeds 250 chars
    .then(str => (str.length > 250 ? `${str.substr(0, 250)}...` : str))
    // Append URL if requested
    .then(str => (withUrl ? `${str} <${page.raw.canonicalurl}>` : str));
}

module.exports.run = (remainder, parts, reply) => {
  wiki().search(remainder, 1)
    .then(data => data.results[0])
    .then(wiki().page)
    .then(page => shortSummary(page, true))
    .then(reply)
    .catch(() => reply(errorMessage(remainder)));
};

module.exports.url = (url, reply) => {
  if (urlRegex.test(url)) {
    const [, match] = urlRegex.exec(url);
    const title = decodeURIComponent(match);

    wiki().page(title)
      .then(shortSummary)
      .then(reply)
      .catch(() => reply(errorMessage(title)));
  }
};

Add URL matching to Wikipedia module


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

define(([], function () {
    var DeployMenu = {
        id: "deploy",
        label: "Deploy",
        items: [
            {
                id: "deploy-to-server",
                label: "Deploy To Server",
                command: {
                    id: "deploy-to-server",
                    shortcuts: {
                        mac: {
                            key: "shift+d",
                            label: "\u21E7D"
                        },
                        other: {
                            key: "shift+d",
                            label: "Shift+D"
                        }
                    }
                },
                disabled: false
            }
        ]
    };
    return DeployMenu;
}));

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

define(([], function () {
    var DeployMenu = {
        id: "deploy",
        label: "Deploy",
        items: [
            {
                id: "deploy-to-server",
                label: "Deploy To Server",
                command: {
                    id: "deploy-to-server",
                    shortcuts: {
                        mac: {
                            key: "command+shift+d",
                            label: "\u2318\u21E7D"
                        },
                        other: {
                            key: "ctrl+shift+d",
                            label: "Ctrl+Shift+D"
                        }
                    }
                },
                disabled: false
            }
        ]
    };
    return DeployMenu;
}));

Change shortcut key for deploy to server


// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt

cur_frm.add_fetch("customer", "customer_name", "customer_name")
cur_frm.add_fetch("supplier", "supplier_name", "supplier_name")

cur_frm.add_fetch("item_code", "item_name", "item_name")
cur_frm.add_fetch("item_code", "description", "description")
cur_frm.add_fetch("item_code", "item_group", "item_group")
cur_frm.add_fetch("item_code", "brand", "brand")

cur_frm.cscript.onload = function() {
	cur_frm.set_query("item_code", function() {
		return erpnext.queries.item({"is_stock_item": "Yes", "has_serial_no": "Yes"})
	});
};

frappe.ui.form.on("Serial No", "refresh", function(frm) {
	frm.toggle_enable("item_code", frm.doc.__islocal);

	if(frm.doc.status == "Sales Returned" && frm.doc.warehouse)
		cur_frm.add_custom_button(__('Set Status as Available'), cur_frm.cscript.set_status_as_available);
});

cur_frm.cscript.set_status_as_available = function() {
	cur_frm.set_value("status", "Available");
	cur_frm.save()
}

// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt

cur_frm.add_fetch("customer", "customer_name", "customer_name")
cur_frm.add_fetch("supplier", "supplier_name", "supplier_name")

cur_frm.add_fetch("item_code", "item_name", "item_name")
cur_frm.add_fetch("item_code", "description", "description")
cur_frm.add_fetch("item_code", "item_group", "item_group")
cur_frm.add_fetch("item_code", "brand", "brand")

cur_frm.cscript.onload = function() {
	cur_frm.set_query("item_code", function() {
		return erpnext.queries.item({"is_stock_item": "Yes", "has_serial_no": "Yes"})
	});
};

frappe.ui.form.on("Serial No", "refresh", function(frm) {
	frm.toggle_enable("item_code", frm.doc.__islocal);

	if(frm.doc.status == "Sales Returned" && frm.doc.warehouse)
		cur_frm.add_custom_button(__('Set Status as Available'), function() {
			cur_frm.set_value("status", "Available");
			cur_frm.save();
		});
});

Set status button in serial no


import {DeviceEventEmitter} from 'react-native';

class KeyboardObserver {

  constructor() {
    this.keyboardHeight = 0;
    this.animationWillEndAt = 0;
    this.listeners = {};
    this.listenerId = 0;
    DeviceEventEmitter.addListener('keyboardWillShow', this.handleKeyboardWillShow.bind(this));
    DeviceEventEmitter.addListener('keyboardWillHide', this.handleKeyboardWillHide.bind(this));
  }

  addListener(callback) {
    const id = this.listenerId++;
    this.listeners[id] = callback;
    return {remove: () => delete this.listeners[id]};
  }

  notifyListeners() {
    const info = this.getKeyboardInfo();
    Object.keys(this.listeners).forEach(key => this.listeners[key](info));
  }

  handleKeyboardWillShow(frames) {
    this.keyboardHeight = frames.endCoordinates.height;
    this.animationWillEndAt = new Date().getTime() + frames.duration;
    this.notifyListeners()
  }

  handleKeyboardWillHide(frames) {
    this.animationWillEndAt = new Date().getTime() + frames.duration;
    this.keyboardHeight = 0;
    this.notifyListeners()
  }

  getKeyboardInfo() {
    return {
      keyboardHeight: this.keyboardHeight,
      animationWillEndAt: this.animationWillEndAt,
    };
  }
}

export default new KeyboardObserver();

import {Keyboard} from 'react-native';

class KeyboardObserver {

  constructor() {
    this.keyboardHeight = 0;
    this.animationWillEndAt = 0;
    this.listeners = {};
    this.listenerId = 0;
    Keyboard.addListener('keyboardWillShow', this.handleKeyboardWillShow.bind(this));
    Keyboard.addListener('keyboardWillHide', this.handleKeyboardWillHide.bind(this));
  }

  addListener(callback) {
    const id = this.listenerId++;
    this.listeners[id] = callback;
    return {remove: () => delete this.listeners[id]};
  }

  notifyListeners() {
    const info = this.getKeyboardInfo();
    Object.keys(this.listeners).forEach(key => this.listeners[key](info));
  }

  handleKeyboardWillShow(frames) {
    this.keyboardHeight = frames.endCoordinates.height;
    this.animationWillEndAt = new Date().getTime() + frames.duration;
    this.notifyListeners()
  }

  handleKeyboardWillHide(frames) {
    this.animationWillEndAt = new Date().getTime() + frames.duration;
    this.keyboardHeight = 0;
    this.notifyListeners()
  }

  getKeyboardInfo() {
    return {
      keyboardHeight: this.keyboardHeight,
      animationWillEndAt: this.animationWillEndAt,
    };
  }
}

export default new KeyboardObserver();

Fix React Native Keyboard warnings


'use strict';

/**
* Computes the inverse of the lower incomplete gamma function.
*
* @module @stdlib/math/base/special/gammaincinv
*
* @example
* var gammaincinv = require( '@stdlib/math/base/special/gammaincinv' );
*
* var val = gammaincinv( 0.5, 2.0 );
* // returns ~1.678
*
* val = gammaincinv( 0.1, 10.0 );
* // returns ~6.221
*
* val = gammaincinv( 0.75, 3.0 );
* // returns ~3.92
*
* val = gammaincinv( 0.75, 3.0, true );
* // returns ~1.727
*
* val = gammaincinv( 0.75, NaN );
* // returns NaN
*
* val = gammaincinv( NaN, 3.0 );
* // returns NaN
*/

// MODULES //

var gammaincinv = require( './gammaincinv.js' );

// EXPORTS //

module.exports = gammaincinv;

'use strict';

/**
* Compute the inverse of the lower incomplete gamma function.
*
* @module @stdlib/math/base/special/gammaincinv
*
* @example
* var gammaincinv = require( '@stdlib/math/base/special/gammaincinv' );
*
* var val = gammaincinv( 0.5, 2.0 );
* // returns ~1.678
*
* val = gammaincinv( 0.1, 10.0 );
* // returns ~6.221
*
* val = gammaincinv( 0.75, 3.0 );
* // returns ~3.92
*
* val = gammaincinv( 0.75, 3.0, true );
* // returns ~1.727
*
* val = gammaincinv( 0.75, NaN );
* // returns NaN
*
* val = gammaincinv( NaN, 3.0 );
* // returns NaN
*/

// MODULES //

var gammaincinv = require( './gammaincinv.js' );

// EXPORTS //

module.exports = gammaincinv;

Update description to imperative mood


Meteor.methods({
  fetchInstaApi: function(){
    var instaDebounce = _.debounce(Meteor.bindEnvironment(function(){
      var ig = Meteor.npmRequire('instagram-node').instagram();

      ig.use({ access_token: Meteor.settings.instagram.access_token });
      ig.user_self_media_recent(Meteor.bindEnvironment(function(err, medias, pagination, remaining, limit) {
        if(!err){
          medias.forEach(function(entry){
            if(entry.tags.indexOf("webonaut") > -1 ||  entry.tags.indexOf("Webonaut") > -1){
              Meteor.call('addInsta', entry) ;
            }
          })
        }
      }, 60 * 1000));
    }));
    instaDebounce();
  }
});

Meteor.methods({
  fetchInstaApi: function(){
    /*var instaDebounce = _.debounce(Meteor.bindEnvironment(function(){
      var ig = Meteor.npmRequire('instagram-node').instagram();

      ig.use({ access_token: Meteor.settings.instagram.access_token });
      ig.user_self_media_recent(Meteor.bindEnvironment(function(err, medias, pagination, remaining, limit) {
        if(!err){
          medias.forEach(function(entry){
            if(entry.tags.indexOf("webonaut") > -1 ||  entry.tags.indexOf("Webonaut") > -1){
              Meteor.call('addInsta', entry) ;
            }
          })
        }
      }, 60 * 1000));
    }));
    instaDebounce();*/
  }
});

Revert "Scaling crash testing twitFeed"


(function (){
"use strict";

var BBM = require("./BBM.js");
var ENUM = BBM.ENUM;
var REGSTR =
[
  "\"{3,}"
, "--"
, "\\*{2}"
, "\\^{2}"
, ",,"
, "__"
, "''"
, "\\?<"
, "!<"
, "#<"
, "#\\["
, "-\\["
, "\\]"
];

var REGEX = new RegExp(REGSTR.join("|"), "g");

//Serializes this subtree into BBM.
function toBBM()
{

}

BBM.prototype.toBBM = toBBM;

}());

(function (){
"use strict";

var BBM = require("./BBM.js");
var ENUM = BBM.ENUM;
var REGSTR =
[
  "\\\\[\\s\\S]"
, "\"{3,}"
, "--"
, "\\*{2}"
, "\\^{2}"
, ",,"
, "__"
, "''"
, "\\?<"
, "!<"
, "#<"
, "#\\["
, "-\\["
, "\\]"
];

var REGEX = new RegExp(REGSTR.join("|"), "g");

//Serializes this subtree into BBM.
function toBBM()
{

}

BBM.prototype.toBBM = toBBM;

}());

Add backslash tokens to the escape list.


var prevWindowOnError = window.onerror;
window.onerror = function(message, url, line, stack) {
  var now = Date.now();
  stack = stack || 'window@'+url+':'+line+':0';
  Kadira.sendErrors([{
    appId : Kadira.options.appId,
    name : message,
    source : 'client',
    startTime : now,
    type : 'window.onerror',
    info : getBrowserInfo(),
    stack : [{at: now, events: [], stack: stack}],
  }]);

  if(prevWindowOnError && typeof prevWindowOnError === 'function') {
    prevWindowOnError(message, url, line);
  }
}

var prevWindowOnError = window.onerror;
window.onerror = function(message, url, line, stack) {
  var now = Date.now();
  stack = stack || 'window@'+url+':'+line+':0';
  Kadira.sendErrors([{
    appId : Kadira.options.appId,
    name : 'Error: ' + message,
    source : 'client',
    startTime : now,
    type : 'window.onerror',
    info : getBrowserInfo(),
    stack : [{at: now, events: [], stack: stack}],
  }]);

  if(prevWindowOnError && typeof prevWindowOnError === 'function') {
    prevWindowOnError(message, url, line);
  }
}

Use 'ErrorName: message' format for name


import tinymce from 'tinymce/tinymce';
window.tinymce = tinymce;
import 'tinymce/themes/silver';
import 'tinymce/plugins/link';
import 'tinymce/plugins/lists';
import 'tinymce/plugins/image';
import 'tinymce/plugins/table';
import 'tinymce/plugins/code';
import 'tinymce/plugins/paste';
import 'tinymce/plugins/media';
import 'tinymce/plugins/noneditable';
import {getInitConfig} from '../libs/tinymce_extensions';

const ENHANCED = window.VERIFIED || window.SUP;

function initRichTextEditor(element, code=false) {
  // Vue rebuilds the whole dom in between the call to init and tinymce actually doing the init
  // so we use a selector here until we use vue to init tinymce
  const selector=`${element.type}#${element.id}`;
  let config = getInitConfig(selector, ENHANCED, code);

  return tinymce.init(config);
}

for (const textArea of document.querySelectorAll('.richtext-editor'))
  initRichTextEditor(textArea);

for (const textArea of document.querySelectorAll('.richtext-editor-src'))
  initRichTextEditor(textArea, true);

global.initRichTextEditor = initRichTextEditor;

import tinymce from 'tinymce/tinymce';
window.tinymce = tinymce;
import 'tinymce/themes/silver';
import 'tinymce/icons/default';
import 'tinymce/plugins/link';
import 'tinymce/plugins/lists';
import 'tinymce/plugins/image';
import 'tinymce/plugins/table';
import 'tinymce/plugins/code';
import 'tinymce/plugins/paste';
import 'tinymce/plugins/media';
import 'tinymce/plugins/noneditable';
import {getInitConfig} from '../libs/tinymce_extensions';

const ENHANCED = window.VERIFIED || window.SUP;

function initRichTextEditor(element, code=false) {
  // Vue rebuilds the whole dom in between the call to init and tinymce actually doing the init
  // so we use a selector here until we use vue to init tinymce
  const selector=`${element.type}#${element.id}`;
  let config = getInitConfig(selector, ENHANCED, code);

  return tinymce.init(config);
}

for (const textArea of document.querySelectorAll('.richtext-editor'))
  initRichTextEditor(textArea);

for (const textArea of document.querySelectorAll('.richtext-editor-src'))
  initRichTextEditor(textArea, true);

global.initRichTextEditor = initRichTextEditor;

Include TinyMCE icons after update


/**
 * Modules datastore fixtures.
 *
 * Site Kit by Google, Copyright 2020 Google LLC
 *
 * 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
 *
 *     https://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.
 */

/**
 * Internal dependencies
 */
import modules from '../fixtures.json'; // TODO: move into this directory.

// Only Search Console and Site Verification are always active.
const alwaysActive = [ 'search-console', 'site-verification' ];

/**
 * Makes a copy of the modules with the given module activation set.
 *
 * @param {...string} slugs Active module slugs.
 * @return {Object[]} Array of module objects.
 */
export const withActive = ( ...slugs ) => {
	const activeSlugs = alwaysActive.concat( slugs );
	return modules.map( ( module ) => {
		return { ...module, active: activeSlugs.includes( module.slug ) };
	} );
};

export default withActive();

/**
 * Modules datastore fixtures.
 *
 * Site Kit by Google, Copyright 2020 Google LLC
 *
 * 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
 *
 *     https://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.
 */

/**
 * Internal dependencies
 */
import modules from '../fixtures.json'; // TODO: move into this directory.

// Only Search Console and Site Verification are always active.
const alwaysActive = [ 'search-console', 'site-verification' ];

/**
 * Makes a copy of the modules with the given module activation set.
 *
 * @since n.e.x.t
 *
 * @param {...string} slugs Active module slugs.
 * @return {Object[]} Array of module objects.
 */
export const withActive = ( ...slugs ) => {
	const activeSlugs = alwaysActive.concat( slugs );
	return modules.map( ( module ) => {
		return { ...module, active: activeSlugs.includes( module.slug ) };
	} );
};

export default withActive();

Fix new docs ESLint rule violation.


module.exports = import('ifrau').then(function(ifrau) {
	return ifrau.Client;
});

module.exports = import('ifrau').then(function(ifrau) {
	return ifrau.SlimClient;
});

Return slim client instead of full client


import Ember from 'ember';

import countries from './countries';

export default Ember.Component.extend({
    i18n: Ember.inject.service(),
    countries: countries,
    languages: Ember.computed('countries', function() {
      var langs = [];
      this.get('countries').forEach((country) => {
        country.languages.forEach((lang) => {
          langs.push({
            countryCode: country.code,
            name: lang.name,
            code: lang.code
          });
        });
      });
      langs.sort(function(a, b) {
        if (a.name > b.name) {
          return 1;
        }
        if (a.name < b.name) {
          return -1;
        }
        return 0;
      });
      return langs;
    }),
    onPick: null,
    country: null,
    actions: {
      pickLanguage(language, code) {
        this.get('onPick')(language, code);
        this.set('i18n.locale', code);
      }
    }
});

import Ember from 'ember';

import countries from './countries';

export default Ember.Component.extend({
    i18n: Ember.inject.service(),
    countries: countries,
    languages: Ember.computed('countries', function() {
      var langs = [];
      this.get('countries').forEach((country) => {
        country.languages.forEach((lang) => {
          langs.push({
            countryCode: country.code,
            name: lang.name,
            code: lang.code
          });
        });
      });
      langs.sort(function(a, b) {
        if (a.countryCode > b.countryCode) {
          return 1;
        }
        if (a.countryCode < b.countryCode) {
          return -1;
        }
        return 0;
      });
      return langs;
    }),
    onPick: null,
    country: null,
    actions: {
      pickLanguage(language, code) {
        this.get('onPick')(language, code);
        this.set('i18n.locale', code);
      }
    }
});

Sort flags by country code


import React from 'react';
import TeaserGrid from 'components/TeaserGrid';
import Container from 'components/Container';
import BlockContainer from 'components/BlockContainer';
import Section from 'components/Section';
import Heading2 from 'components/Heading2';
import styles from './homepage-petitions.scss';
import Link from 'components/Link';

const HomepagePetitions = ({ groupedPetitions, title, text, linkText }) => (

      <Container>
        <BlockContainer>

              <Heading2 text={title} />
              {text}

            <Link href='/petitions'>{linkText}</Link>

        </BlockContainer>
        <TeaserGrid
          petitions={groupedPetitions.trending.data}
          isLoading={groupedPetitions.trending.isLoading}
        />
      </Container>

);

export default HomepagePetitions;

import React from 'react';
import TeaserGrid from 'components/TeaserGrid';
import Container from 'components/Container';
import BlockContainer from 'components/BlockContainer';
import Section from 'components/Section';
import Heading2 from 'components/Heading2';
import styles from './homepage-petitions.scss';
import Link from 'components/Link';

const HomepagePetitions = ({ groupedPetitions, title, text, linkText }) => (

      <Container>
        <BlockContainer>

              <Heading2 text={title} />
              {text}

            <Link href='/petitions'>{linkText}</Link>

        </BlockContainer>
        <TeaserGrid
          petitions={groupedPetitions.latest.data}
          isLoading={groupedPetitions.latest.isLoading}
        />
      </Container>

);

export default HomepagePetitions;

Use latest petitions on HP instead...


/* globals module */
module.exports = {
  afterInstall: function() {
    var _this = this;
    return _this.addAddonsToProject({
      packages: [
        { name: 'ember-flexberry', target: '0.12.3' }
      ]
    }).then(function () {
      return _this.addPackagesToProject([
      { name: 'npm:jointjs', target: '^2.1.2' },
      ]);
    });
  },

  normalizeEntityName: function() {}
};

/* globals module */
module.exports = {
  afterInstall: function() {
    var _this = this;
    return _this.addAddonsToProject({
      packages: [
        { name: 'ember-cli-jstree', target: '1.0.9' },
        { name: 'ember-flexberry', target: '0.12.3' },
        { name: 'ember-promise-helpers', target: '1.0.3' }
      ]
    }).then(function () {
      return _this.addPackagesToProject([
        { name: 'jointjs', target: '^2.1.2' }
      ]);
    });
  },

  normalizeEntityName: function() {}
};

Fix default blueprint add missing dependencies


var config = require('./webpack.config.js');
var webpack = require('webpack');

config.devtool = 'source-map';

config.output.publicPath = './';

config.plugins.push(
  new webpack.DefinePlugin({
    'process.env': {
      'NODE_ENV': JSON.stringify('production')
    }
  })
);

config.plugins.push(
  new webpack.optimize.UglifyJsPlugin({
    compressor: {
      warnings: false
    }
  })
);

config.eslint.emitError = true;

module.exports = config;

var config = require('./webpack.config.js');
var webpack = require('webpack');
var pkg = require('./package.json');

var banner = '@license minigrid ';
banner = banner + pkg.version;
banner = banner + ' – minimal cascading grid layout http://alves.im/minigrid';

config.devtool = 'source-map';

config.output.publicPath = './';

config.plugins.push(
  new webpack.DefinePlugin({
    'process.env': {
      'NODE_ENV': JSON.stringify('production')
    }
  })
);

config.plugins.push(
  new webpack.optimize.UglifyJsPlugin({
    compressor: {
      warnings: false
    }
  }),
  new webpack.BannerPlugin(banner)
);

config.eslint.emitError = true;

module.exports = config;

Add banner comment to dis version


function TileMap(w, h) {
  this.width = w;
  this.height = h;
  this.reset();
}

TileMap.prototype.reset = function() {
  this.map = [];
  for(var y = 0; y < this.height; y++) {
    this.map[y] = [];
    for(var x = 0; x < this.width; x++) {
      this.map[y][x] = new Tile();
    }
  }
}

function Tile() {

}

TileMap.Tile = Tile;

if(typeof module != 'undefined') {
  module.exports = TileMap;
}

function TileMap(w, h) {
  this.width = w;
  this.height = h;
  this.reset();
}

TileMap.prototype.reset = function() {
  this.map = [];
  for(var y = 0; y < this.height; y++) {
    this.map[y] = [];
    for(var x = 0; x < this.width; x++) {
      this.map[y][x] = new Tile(x, y);
    }
  }
}

function Tile(x, y) {
  this.x = x;
  this.y = y;
}

TileMap.Tile = Tile;

if(typeof module != 'undefined') {
  module.exports = TileMap;
}

Change Tile to store its own coordinates


module.exports = {
  options: {
    require: ['sass-css-importer'],
    loadPath: ['<%%= bowerOpts.directory || "bower_components" %>'],
    bundleExec: true,
  },
  debug: {
    src: '<%%= dirs.tmp %>/prepare/assets/styles/main.scss',
    dest: '<%%= dirs.tmp %>/public/assets/styles/main.css',
    options: {
      style: 'expanded'
    }
  },
  "public": {
    src: '<%%= dirs.tmp %>/prepare/assets/styles/main.scss',
    dest: '<%%= dirs.tmp %>/public/assets/styles/main.css',
    options: {
      style: 'compressed'
    }
  }
};

module.exports = {
  options: {
    require: ['sass-css-importer'],
    loadPath: ['<%%= bowerOpts.directory || "bower_components" %>'],
    bundleExec: true
  },
  debug: {
    src: '<%%= dirs.tmp %>/prepare/assets/styles/main.scss',
    dest: '<%%= dirs.tmp %>/public/assets/styles/main.css',
    options: {
      style: 'expanded',
      sourcemaps: 'true'
    }
  },
  "public": {
    src: '<%%= dirs.tmp %>/prepare/assets/styles/main.scss',
    dest: '<%%= dirs.tmp %>/public/assets/styles/main.css',
    options: {
      style: 'compressed'
    }
  }
};

Add sourcemaps option to Sass task


(function() {
	"use strict";
	function initPage() {

		var fileTypes = [
			"csv",
			"gif",
			"jpeg",
			"jpg",
			"mp3",
			"mp4",
			"pdf",
			"png",
			"tif",
			"tiff",
			"txt",
			"xml"
		];

		var list = fileTypes.join(", ").toUpperCase();
		var filter = fileTypes.join("|");

		// Match any file that doesn't contain the above extensions.
		var regex = new RegExp("(\.|\/)(" + filter + ")$", "i");
		var acceptableFileTypeList = $(".acceptable-file-type-list");
		acceptableFileTypeList.text(list);

		var fileUploadButton = $('#fileupload');
		if (fileUploadButton.length > 0) { // If we are on a page with file upload.
			// Override curation_concern's file filter. We wait until we are sure that the curation_concern has initialized to make sure this gets called last.
			setTimeout(function() {
				fileUploadButton.fileupload(
					'option',
					'acceptFileTypes',
					regex
				);
			}, 1000);
		}
	}

	$(window).bind('page:change', function() {
		initPage();
	});
})();

(function() {
	"use strict";
	function initPage() {

		var fileTypes = [
			"csv",
			"gif",
			"htm",
			"html",
			"jpeg",
			"jpg",
			"mp3",
			"mp4",
			"pdf",
			"png",
			"tif",
			"tiff",
			"txt",
			"xml"
		];

		var list = fileTypes.join(", ").toUpperCase();
		var filter = fileTypes.join("|");

		// Match any file that doesn't contain the above extensions.
		var regex = new RegExp("(\.|\/)(" + filter + ")$", "i");
		var acceptableFileTypeList = $(".acceptable-file-type-list");
		acceptableFileTypeList.text(list);

		var fileUploadButton = $('#fileupload');
		if (fileUploadButton.length > 0) { // If we are on a page with file upload.
			// Override curation_concern's file filter. We wait until we are sure that the curation_concern has initialized to make sure this gets called last.
			setTimeout(function() {
				fileUploadButton.fileupload(
					'option',
					'acceptFileTypes',
					regex
				);
			}, 1000);
		}
	}

	$(window).bind('page:change', function() {
		initPage();
	});
})();

Add html and htm to list of acceptable files


/*! http://mths.be/repeat v0.1.0 by @mathias */
if (!String.prototype.repeat) {
	(function() {
		'use strict'; // needed to support `apply`/`call` with `undefined`/`null`
		var repeat = function(count) {
			if (this == null) {
				throw TypeError();
			}
			var string = String(this);
			// `ToInteger`
			var n = count ? Number(count) : 0;
			if (n != n) { // better `isNaN`
				n = 0;
			}
			// Account for out-of-bounds indices
			if (n < 0 || n == Infinity) {
				throw RangeError();
			}
			if (n == 0) {
				return '';
			}
			var result = '';
			while (n--) {
				result += string;
			}
			return result;
		};
		if (Object.defineProperty) {
			Object.defineProperty(String.prototype, 'repeat', {
				'value': repeat,
				'configurable': true,
				'writable': true
			});
		} else {
			String.prototype.repeat = repeat;
		}
	}());
}

/*! http://mths.be/repeat v0.1.0 by @mathias */
if (!String.prototype.repeat) {
	(function() {
		'use strict'; // needed to support `apply`/`call` with `undefined`/`null`
		var repeat = function(count) {
			if (this == null) {
				throw TypeError();
			}
			var string = String(this);
			// `ToInteger`
			var n = count ? Number(count) : 0;
			if (n != n) { // better `isNaN`
				n = 0;
			}
			// Account for out-of-bounds indices
			if (n < 0 || n == Infinity) {
				throw RangeError();
			}
			var result = '';
			while (n--) {
				result += string;
			}
			return result;
		};
		if (Object.defineProperty) {
			Object.defineProperty(String.prototype, 'repeat', {
				'value': repeat,
				'configurable': true,
				'writable': true
			});
		} else {
			String.prototype.repeat = repeat;
		}
	}());
}

Remove unnecessary check for `n == 0`


var requirejs = require('requirejs');

var applicationConfig = {
    baseUrl: './public/js',
    name: 'app',
    out: './public/app.js',
    mainConfigFile: './public/js/app.js',
    paths: {
        'socket.io': 'empty:',
        'requireLib': 'require'
    },
    include: [
    	'requireLib'
    ]
};

var mainConfig = {
    baseUrl: './public/js',
    name: 'main',
    out: './public/main.js',
    mainConfigFile: './public/js/main.js',
    paths: {
        'socket.io': 'empty:',
        'requireLib': 'require'
    },
    include: [
    	'requireLib'
    ]
};

requirejs.optimize(applicationConfig, function (log) {
	console.log("Application javascript optimisation complete".green);
	console.log((log).cyan);
}, function(error) {
    console.log(("Error optimizing javascript: " + error).red);
});

requirejs.optimize(mainConfig, function (log) {
	console.log("Main javascript optimisation complete".green);
	console.log((log).cyan);
}, function(err) {
    console.log(("Error optimizing javascript: " + err).red);
});

var requirejs = require('requirejs');

var applicationConfig = {
    baseUrl: './public/js',
    name: 'app',
    out: './public/app.js',
    mainConfigFile: './public/js/app.js',
    paths: {
        'socket.io': 'empty:',
        'requireLib': 'require'
    },
    include: [
    	'requireLib'
    ]
};

var mainConfig = {
    baseUrl: './public/js',
    name: 'main',
    out: './public/main.js',
    mainConfigFile: './public/js/main.js',
    paths: {
        'socket.io': 'empty:',
        'requireLib': 'require'
    },
    include: [
    	'requireLib'
    ]
};

requirejs.optimize(applicationConfig, function (log) {
	console.log("Application javascript optimisation complete".green);
	console.log((log).cyan);
}, function(error) {
    console.log(("Error optimizing javascript: " + error).red);
});

requirejs.optimize(mainConfig, function (log) {
	console.log("Main javascript optimisation complete".green);
    console.log((log).cyan);
}, function(error) {
    console.log(("Error optimizing javascript: " + error).red);
});

var cssConfig = {
	cssIn: './public/css/main.css',
	out: './public/app.css',
	optimizeCss: 'standard'
};
requirejs.optimize(cssConfig, function(log) {
	console.log(("Application CSS optimisation complete").green);
	console.log((log).cyan);
}, function(error) {
    console.log(("Error optimizing CSS: " + error).red);
});

Compress CSS into a single file


import { constant, includes } from 'lodash';
import { element } from 'angular';
import uiModules from 'ui/modules';
import registry from 'ui/registry/chrome_nav_controls';
import '../components/notification_center';
import template from './nav_control.html';
import 'ui/angular-bootstrap';

registry.register(constant({
  name: 'notification_center',
  order: 1000,
  template
}));

const module = uiModules.get('notification_center', []);
module.controller('notificationCenterNavController', ($scope, $compile, $document, NotificationCenter) => {
  function initNotificationCenter() {
    const $elem = $scope.$notificationCenter = $compile('<notification-center></notification-center>')($scope).appendTo('.app-wrapper');
    $document.on('click', ({ target }) => {
      if (target !== $elem[0] && !$elem[0].contains(target)) {
        $elem.hide();
      }
    });
  };

  $scope.openNotificationCenter = event => {
    event.preventDefault();
    if (!$scope.$notificationCenter) {
      initNotificationCenter();
    } else {
      $scope.$notificationCenter.toggle();
    }
    event.stopPropagation();
  };

  $scope.formatTooltip = () =>{
    return `${NotificationCenter.notifications.length} new notifications`;
  };
});

import { constant, includes } from 'lodash';
import { element } from 'angular';
import uiModules from 'ui/modules';
import registry from 'ui/registry/chrome_nav_controls';
import '../components/notification_center';
import template from './nav_control.html';
import 'ui/angular-bootstrap';

registry.register(constant({
  name: 'notification_center',
  order: 1000,
  template
}));

const module = uiModules.get('notification_center', []);
module.controller('notificationCenterNavController', ($scope, $compile, $document, NotificationCenter) => {
  function initNotificationCenter() {
    const $elem = $scope.$notificationCenter = $compile('<notification-center/>')($scope).appendTo('.app-wrapper');
    $document.on('click', () => $elem.hide());
    $elem.on('click', e => e.stopPropagation());
  };

  $scope.openNotificationCenter = event => {
    event.preventDefault();
    if (!$scope.$notificationCenter) {
      initNotificationCenter();
    } else {
      $scope.$notificationCenter.toggle();
    }
    event.stopPropagation();
  };

  $scope.formatTooltip = () =>{
    return `${NotificationCenter.notifications.length} new notifications`;
  };
});

Fix notification-center not to close when button clicked.


/*
 * A reusable component to display a styled heading.
 */

import PropTypes from 'prop-types';
import React, {Component} from 'react';
import {StyleSheet, css} from 'aphrodite/no-important';

export default class Heading extends Component {

    static propTypes = {
        level: PropTypes.oneOf([1, 2]).isRequired,
    }

    render() {
        const Tag = `h${this.props.level}`;
        return <Tag className={css(styles[Tag])}>
            {this.props.children}
        </Tag>;
    }

}

const styles = StyleSheet.create({
    h1: {
        marginTop: 18,
        fontSize: 32,
        fontWeight: 'normal',
    },
    h2: {
        marginTop: 14,
        fontSize: 24,
        fontWeight: 'normal',
    },
});

/*
 * A reusable component to display a styled heading.
 */

import PropTypes from 'prop-types';
import React, {Component} from 'react';
import {StyleSheet, css} from 'aphrodite/no-important';

export default class Heading extends Component {

    static propTypes = {
        level: PropTypes.oneOf([1, 2, 3]).isRequired,
    }

    render() {
        const Tag = `h${this.props.level}`;
        return <Tag className={css(styles[Tag])}>
            {this.props.children}
        </Tag>;
    }

}

const styles = StyleSheet.create({
    h1: {
        marginTop: 18,
        fontSize: 32,
        fontWeight: 'normal',
    },
    h2: {
        marginTop: 14,
        fontSize: 24,
        fontWeight: 'normal',
    },
    h3: {
        marginTop: 12,
        fontSize: 20,
        fontWeight: 'normal',
    },
});

Add support for level-3 headings


var httpProxy = require('http-proxy');
var http = require('http');

var proxy = httpProxy.createProxy();

var routes = {
        // Router
        'hook.labeli.org' : 'http://localhost:9000',
        // Api
        'api.labeli.org' : 'http://localhost:9010',
        'hook.api.labeli.org' : 'http://localhost:9011',
        // Website
        'next.labeli.org' : 'http://localhost:9020',
        'hook.next.labeli.org' : 'http://localhost:9021',
};

var router = http.createServer(function(req, res)
{
    proxy.web(req, res, {target : routes[req.headers.host]});
});

router.listen(80);

var httpProxy = require('http-proxy');
var http = require('http');

var proxy = httpProxy.createProxyServer({});

var routes = {
    // Router
    'hook.labeli.org' : 'http://localhost:9000',
    // Api
    'api.labeli.org' : 'http://localhost:9010',
    'hook.api.labeli.org' : 'http://localhost:9011',
    // Website
    'next.labeli.org' : 'http://localhost:9020',
    'hook.next.labeli.org' : 'http://localhost:9021',
};

function getRouteFor(host){
    if (routes[host] == undefined)
        return "http://localhost:9020";

    return routes[host];
}

var router = http.createServer(function(req, res)
{
    proxy.web(req, res, {target : getRouteFor(req.headers.host)});
});

router.listen(80);

Add method getRouteFor() to create default route


var merge = require('webpack-merge')
var prodEnv = require('./prod.env')

module.exports = merge(prodEnv, {
  NODE_ENV: '"development"',
  API_HOST: '"http://localhost:3000"'
})

var merge = require('webpack-merge')
var prodEnv = require('./prod.env')

module.exports = merge(prodEnv, {
  NODE_ENV: '"development"',
  API_HOST: '"http://localhost:5100"'
})

Fix local api server path because of Foreman


// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
// GO AFTER THE REQUIRES BELOW.
//
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require leaflet
//= require mapbox
//= require bootstrap
//= require ./typeahead.min
//= require_tree .

// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
// GO AFTER THE REQUIRES BELOW.
//
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require leaflet
//= require ./leaflet.markercluster
//= require mapbox
//= require bootstrap
//= require ./typeahead.min
//= require_tree .

Add explicit include (early in order) for the leaflet.markercluster js file


$(function () {
  $("[class='switch-checkbox']").bootstrapSwitch();

  $('input[class="switch-checkbox"]').on('switchChange.bootstrapSwitch', function(event, state) {
    var url = $(this).attr('url') + state;
    var method = $(this).attr('method');

    $.ajax({
      url: url,
      type: method,
      dataType: 'script'
    });
  });

  $("[class='switch-checkbox-schedule']").bootstrapSwitch();

  $('input[class="switch-checkbox-schedule"]').on('switchChange.bootstrapSwitch', function(event, state) {
    var url = $(this).attr('url');
    var method = $(this).attr('method');

    if(state){
      url += $(this).attr('value');
    }

    var callback = function(data) {
      showError($.parseJSON(data.responseText).errors);
    }
    $.ajax({
      url: url,
      type: method,
      error: callback,
      dataType: 'json'
    });
  });
});

function checkboxSwitch(selector){
  $(selector).bootstrapSwitch(

  );

  $(selector).on('switchChange.bootstrapSwitch', function(event, state) {
    var url = $(this).attr('url') + state;
    var method = $(this).attr('method');

    $.ajax({
      url: url,
      type: method,
      dataType: 'script'
    });
  });
}

$(function () {
  checkboxSwitch("[class='switch-checkbox']");

  $("[class='switch-checkbox-schedule']").bootstrapSwitch();

  $('input[class="switch-checkbox-schedule"]').on('switchChange.bootstrapSwitch', function(event, state) {
    var url = $(this).attr('url');
    var method = $(this).attr('method');

    if(state){
      url += $(this).attr('value');
    }

    var callback = function(data) {
      showError($.parseJSON(data.responseText).errors);
    }
    $.ajax({
      url: url,
      type: method,
      error: callback,
      dataType: 'json'
    });
  });
});

Add a function for initing boostrapSwitch outside of $()


const fs = require('fs');
const path = require('path');
const { infer } = require('bayesjs');
const { json, send } = require('micro');

const htmlContent = fs
  .readFileSync(path.join(__dirname, 'index.html'))
  .toString();

module.exports = async (req, res) => {
  if (req.url !== '/') {
    return send(res, 404);
  }

  switch (req.method.toUpperCase()) {
    case 'GET': {
      return htmlContent;
    }
    case 'POST': {
      const payload = await json(req);
      return infer(payload.network, payload.nodes, payload.given);
    }
  }

  return send(res, 404);
};

const fs = require('fs');
const path = require('path');
const { infer } = require('bayesjs');
const { json, send } = require('micro');

const htmlContent = fs
  .readFileSync(path.join(__dirname, 'index.html'))
  .toString();

module.exports = async (req, res) => {
  if (req.url !== '/') {
    return send(res, 404);
  }

  switch (req.method.toUpperCase()) {
    case 'GET': {
      return htmlContent;
    }
    case 'POST': {
      const payload = await json(req);
      return infer(payload.network, payload.nodes, payload.given);
    }
    default: {
      return send(res, 405);
    }
  }
};

Change 'not found' to 'method not allowed'


const Discord = require('discord.js');
const config = require('./config.json');
const manager = new Discord.ShardingManager('./bot.js');

//Spawn with a relevant number of shards automatically
manager.spawn();

const Discord = require('discord.js');
const config = require('./config.json');
const API = require("./api.json");
const manager = new Discord.ShardingManager('./bot.js', {
	token: API.discord
});

//Spawn with a relevant number of shards automatically
manager.spawn();

Add token in sharding manager


/* Listen for messages and get modResource meta tag*/
chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
  if (msg.command && (msg.command == "getResource")) {

    function getMetaContentByName(name,content){
        var content = (content==null)?'content':content;
        var meta = document.querySelector("meta[name='" + name + "']");
        if (meta) {
            return meta.getAttribute(content);
        }
    }
    sendResponse(getMetaContentByName("modResource"));
  }
});

/* Listen for messages and get modResource meta tag*/
chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
  if (msg.command && (msg.command == "getResource")) {

    function getMetaContentByName(name,content){
        var content = (content==null)?'content':content;
        var meta = document.querySelector("meta[name='" + name + "']");
        if (meta) {
            return meta.getAttribute(content);
        }
    }
    sendResponse(getMetaContentByName('application-name','data-id'));
  }
});

Change meta name for valid html output


import TB from '../../src/tadaboard-node-sdk';

describe('TB', () => {
});

import TB from '../../src/tadaboard-node-sdk';

describe('TB', () => {
  describe('response', () => {
    it('is a function', () => {
      expect(TB.response).to.be.a('function');
    });
  });
});

Add simple test for TB.response


'use strict';

const {
  remote: { app }
} = require('electron');

const DefaultNotification = require('./default-notification');
const { appId, loadBots } = require('./helpers');

// From https://github.com/felixrieseberg/electron-windows-notifications#appusermodelid
// On windows 8 we have to explicitly set the appUserModelId otherwise notification won't work.
app.setAppUserModelId(appId);

window.Notification = DefaultNotification;
if (process.platform === 'darwin') {
	const DarwinNotification = require('./darwin-notifications');
	window.Notification = DarwinNotification;
}

window.addEventListener('load', () => {
	loadBots();
});

'use strict';

const {
  remote: { app }
} = require('electron');

const DefaultNotification = require('./default-notification');
const { appId, loadBots } = require('./helpers');

// From https://github.com/felixrieseberg/electron-windows-notifications#appusermodelid
// On windows 8 we have to explicitly set the appUserModelId otherwise notification won't work.
app.setAppUserModelId(appId);

window.Notification = DefaultNotification;

if (process.platform === 'darwin') {
	const DarwinNotification = require('./darwin-notifications');
	window.Notification = DarwinNotification;
}

window.addEventListener('load', () => {
	// Call this function only when user is logged in
	// eslint-disable-next-line no-undef, camelcase
	if (page_params.realm_uri) {
		loadBots();
	}
});

Refactor code for bot mention in reply.


(attr, bindingPath, model) {
    let cellComponent = this._super(...arguments);
    if (attr.kind === 'belongsTo') {
      switch (<% '`${model.modelName}+${bindingPath}`' %>) {
<%= bodySwitchBindingPath %>
      }
    }

    return cellComponent;
  }

(attr, bindingPath, model) {
    let cellComponent = this._super(...arguments);
    if (attr.kind === 'belongsTo') {
      switch (<%= '`${model.modelName}+${bindingPath}`' %>) {
<%= bodySwitchBindingPath %>
      }
    }

    return cellComponent;
  }

Fix flexberry-edit-form blueprint getCellComponent template


$( document ).ready(function() {
    Dropzone.autoDiscover = false;
    var myDropzone = new Dropzone(".dropzone", {
        url: '/api/file',
        autoProcessQueue: true,
        maxFilesize: maxFilesize,
        acceptedFiles : acceptedFiles
    });
    myDropzone.on("success", function(file, http) {
        window.setTimeout(function(){
            location.reload();
        }, 1000);
    });
    myDropzone.on("sending", function(file, fromData) {
        if ($('.alert-danger').length > 0) {
            $('.alert-danger').remove();
        }
    });
    myDropzone.on("error", function(file, errorMessage) {
        var html = '
' + errorMessage.file[0] + '
';
        $('.col-md-12').first().prepend(html);
        setTimeout(function() {
            myDropzone.removeFile(file);
        }, 2000);
    });
});

$( document ).ready(function() {
    Dropzone.autoDiscover = false;
    var myDropzone = new Dropzone(".dropzone", {
        url: '/api/file',
        autoProcessQueue: true,
        maxFilesize: maxFilesize,
        acceptedFiles : acceptedFiles
    });
    myDropzone.on("queuecomplete", function(file, http) {
        window.setTimeout(function(){
            location.reload();
        }, 1000);
    });
    myDropzone.on("sending", function(file, fromData) {
        if ($('.alert-danger').length > 0) {
            $('.alert-danger').remove();
        }
    });
    myDropzone.on("error", function(file, errorMessage) {
        var html = '
' + errorMessage.file[0] + '
';
        $('.col-md-12').first().prepend(html);
        setTimeout(function() {
            myDropzone.removeFile(file);
        }, 2000);
    });
});

Fix early redirect when uploading multiple files


module.exports = {
  app: {
    src: ['tmp/transpiled/app/**/*.js'],
    dest: 'tmp/public/assets/app.js'
  },

  test: {
    src: 'tmp/transpiled/tests/**/*.js',
    dest: 'tmp/public/tests/tests.js'
  }
};

module.exports = {
  app: {
    src: ['tmp/transpiled/app/**/*.js'],
    dest: 'tmp/public/assets/app.js',
    options: {
      sourcesContent: true
    },
  },

  test: {
    src: 'tmp/transpiled/tests/**/*.js',
    dest: 'tmp/public/tests/tests.js',
    options: {
      sourcesContent: true
    }
  }
};

Revert "sourceMappingURL instead of sourceContent for now"


define(['app', 'controllers/Today', 'controllers/History', 'services/History'], function (app) {
  "use strict";

  return app.config(['$routeProvider', function (routes) {
    routes
      .when('/', {
        controller: 'TodayController',
        templateUrl: '/partials/today.html'
      })
      .when('/history', {
        controller: 'HistoryController',
        templateUrl: '/partials/history.html'
      })
      .when('/signedin', {
        controller: function (location, historyService) {
          historyService.sync();
          location.url('/');
        },
        resolve: {
          location:       '$location',
          historyService: 'historyService'
        },
        // A view is required in order for the Controller to
        // get called.
        template: 'Hello!'
      })
      .otherwise({ redirectTo: '/' });
  }]);
});

define(['app', 'controllers/Today', 'controllers/History', 'services/History'], function (app) {
  "use strict";

  return app.config(['$routeProvider', function (routes) {
    routes
      .when('/', {
        controller: 'TodayController',
        templateUrl: '/partials/today.html'
      })
      .when('/history', {
        controller: 'HistoryController',
        templateUrl: '/partials/history.html'
      })
      .when('/signedin', {
        controller: ['$location', 'historyService', function (location, historyService) {
          historyService.sync();
          location.url('/');
        }],
        // A view is required in order for the Controller to
        // get called.
        template: 'Hello!'
      })
      .otherwise({ redirectTo: '/' });
  }]);
});

Fix resolve for minified route controller


"use strict";

angular.module("hikeio").
	directive("fancybox", ["$rootScope", function($rootScope) {
		return {
			link: function (scope, element, attrs) {
				scope.$on("$routeChangeStart", function () {
					$.fancybox.close();
				});
				scope.$on("fancyboxClose", function () {
					$.fancybox.close();
				});
				$(element).find(attrs.fancybox).fancybox({
					afterLoad: function(current, previous) {
						$rootScope.$broadcast("fancyboxLoaded");
					},
					afterClose: function(current, previous) {
						$rootScope.$broadcast("fancyboxClosed");
					},
					padding : 2,
					nextEffect : "none",
					prevEffect : "none",
					closeEffect : "none",
					closeBtn : true,
					arrows : false,
					keys : true,
					nextClick : true
				});
			}
		};
	}]);

"use strict";

angular.module("hikeio").
	directive("fancybox", ["$rootScope", function($rootScope) {
		return {
			link: function (scope, element, attrs) {
				var context = {
					afterLoad: function(current, previous) {
						$rootScope.$broadcast("fancyboxLoaded");
					},
					afterClose: function(current, previous) {
						$rootScope.$broadcast("fancyboxClosed");
					},
					padding : 2,
					nextEffect : "none",
					prevEffect : "none",
					closeEffect : "none",
					closeBtn : true,
					arrows : false,
					keys : true,
					nextClick : true
				};

				scope.$on("$routeChangeStart", function () {
					$.fancybox.close();
				});
				scope.$on("fancyboxClose", function () {
					$.fancybox.close();
				});
				scope.$on("$destroy", function () {
					context.afterLoad = null;
					context.afterClose = null;
				});

				$(element).find(attrs.fancybox).fancybox(context);
			}
		};
	}]);

Remove fancybox afterLoad / afterClose event on destroy.


"use strict";

const http = require("http");
const express = require("express");

let app = express();

// Use Cross-Origin headers so browsers allow SharedArrayBuffer
app.use(function(req, res, next) {
	res.header("Cross-Origin-Opener-Policy", "same-origin");
	res.header("Cross-Origin-Embedder-Policy", "require-corp");
	next();
});

// TODO - Add some logging on each request

// Serve all static files in this folder
app.use(express.static("."));

let server = http.createServer(app);
server.listen(8000, "0.0.0.0");

"use strict";

const http = require("http");
const express = require("express");

let app = express();

// Add some logging on each request
app.use(function(req, res, next) {
	const date = new Date().toISOString();
	console.log(
		`[${date}] "${cyan(req.method)} ${cyan(req.url)}" "${req.headers["user-agent"]}"`
	);
	next();
});

// Use Cross-Origin headers so browsers allow SharedArrayBuffer
app.use(function(req, res, next) {
	res.header("Cross-Origin-Opener-Policy", "same-origin");
	res.header("Cross-Origin-Embedder-Policy", "require-corp");
	next();
});

// Serve all static files in this folder
app.use(express.static(".", { fallthrough: false }));

// Add logging on failed requests.
app.use(function (error, req, res, next) {
	const date = new Date().toISOString();
	console.error(
		`[${date}] "${red(req.method)} ${red(req.url)}" Error (${red(error.status.toString())}): "${red(error.message)}"`
	);
	next(error);
});

let server = http.createServer(app);
server.listen(8000, "0.0.0.0");

function red(string) {
	return `\x1b[31m${string}\x1b[0m`;
}

function cyan(string) {
	return `\x1b[36m${string}\x1b[0m`;
}

Add logging to test server


var path = require('path');
var chai = require('chai');

chai.use(require('chai-fs'));
chai.should();

const ROOT_DIR = path.join(process.cwd(), '..');

describe('As a dev', function() {

    describe('When testing generator directory structure', function() {

        it('then _config folder should exist', function() {
            var pathToTest = path.join(ROOT_DIR, '_config');
            pathToTest.should.be.a.directory();
        })

        it('then routes folder should exist', function() {
            var pathToTest = path.join(ROOT_DIR, 'routes');
            pathToTest.should.be.a.directory();
        })

        it('then views folder should exist', function() {
            var pathToTest = path.join(ROOT_DIR, 'views');
            pathToTest.should.be.a.directory();
        })
    });

});

var path = require('path');
var chai = require('chai');

chai.use(require('chai-fs'));
chai.should();

const ROOT_DIR = path.join(process.cwd(), '..');

describe('As a dev', function() {

    describe('When testing generator directory structure', function() {

        var pathToTest;

        it('then _config folder should exist', function() {
            pathToTest = path.join(ROOT_DIR, '_config');
            pathToTest.should.be.a.directory();
        })

        it('then _source folder should exist', function() {
            pathToTest = path.join(ROOT_DIR, '_source');
            pathToTest.should.be.a.directory();
        })

        it('then gulpTasks folder should exist', function() {
            pathToTest = path.join(ROOT_DIR, 'gulpTasks');
            pathToTest.should.be.a.directory();
        })

        it('then routes folder should exist', function() {
            pathToTest = path.join(ROOT_DIR, 'routes');
            pathToTest.should.be.a.directory();
        })

        it('then test folder should exist', function() {
            pathToTest = path.join(ROOT_DIR, 'views');
            pathToTest.should.be.a.directory();
        })

        it('then views folder should exist', function() {
            pathToTest = path.join(ROOT_DIR, 'views');
            pathToTest.should.be.a.directory();
        })

    });

});

Update directoryStructure test to include all base folders. Refactor order to mirror folder order, minor refactor


var Backbone = require('backbone');
var url = require('url');

"use strict";

exports.HistoryUpdate = Backbone.View.extend({

    initialize : function () {
        console.log('HistoryUpdate:initialize');
        this.listenTo(this.model, "change:asset", this.assetChanged);
        this.listenTo(this.model, "change:activeTemplate", this.assetChanged);
        // note that we don't listen for a change in the collection as
        // this could lead to an invalid URL (e.g. change the collection to
        // something else, URL immediately changes, user saves before asset
        // loads)
    },

    assetChanged: function () {
        var u = url.parse(window.location.href.replace('#', '?'), true);
        u.search = null;
        if (this.model.activeTemplate() == undefined ||
            this.model.activeCollection() == undefined ||
            this.model.assetIndex() == undefined) {
            // only want to set full valid states.
            return
        }
        u.query.t = this.model.activeTemplate();
        u.query.c = this.model.activeCollection();
        u.query.i = this.model.assetIndex() + 1;
        history.replaceState(null, null, url.format(u).replace('?', '#'));
    }
});

var Backbone = require('backbone');
var url = require('url');

"use strict";

exports.HistoryUpdate = Backbone.View.extend({

    initialize : function () {
        console.log('HistoryUpdate:initialize');
        this.listenTo(this.model, "change:asset", this.assetChanged);
        this.listenTo(this.model, "change:activeTemplate", this.assetChanged);
        // note that we don't listen for a change in the collection as
        // this could lead to an invalid URL (e.g. change the collection to
        // something else, URL immediately changes, user saves before asset
        // loads)
    },

    assetChanged: function () {
        var u = url.parse(window.location.href.replace('#', '?'), true);
        u.search = null;

        if (this.model.activeTemplate()) {
            u.query.t = this.model.activeTemplate();
        }

        if (this.model.activeCollection()) {
            u.query.c = this.model.activeCollection();
        }

        if (this.model.assetIndex() !== undefined) {
            u.query.i = this.model.assetIndex() + 1;
        }

        history.replaceState(null, null, url.format(u).replace('?', '#'));
    }
});

Allow for more flexible url pattern


var url = require("url");
var packageJSON = require("../../../package.json");

var config = {
  // @@ENV gets replaced by build system
  environment: "@@ENV",
  // If the UI is served through a proxied URL, this can be set here.
  rootUrl: "",
  // Defines the Marathon API URL,
  // leave empty to use the same as the UI is served.
  apiURL: "../",
  // Intervall of API request in ms
  updateInterval: 5000,
  // Local http server URI while tests run
  localTestserverURI: {
    address: "localhost",
    port: 8181
  },
  version: ("@@TEAMCITY_UI_VERSION".indexOf("@@TEAMCITY") === -1) ?
    "@@TEAMCITY_UI_VERSION" :
    `${packageJSON.version}-SNAPSHOT`
};

if (process.env.GULP_ENV === "development") {
  try {
    var configDev = require("./config.dev");
    config = Object.assign(config, configDev);
  } catch (e) {
    console.info("You could copy config.template.js to config.dev.js " +
      "to enable a development configuration.");
  }
}

// Convert relative URLs to absolute URLs for node-fetch
if (global.document != null) {
  if (config.apiURL.indexOf(":") === -1) {
    config.apiURL = url.resolve(
      document.location.origin,
      document.location.pathname + config.apiURL
    );
  }
}

module.exports = config;

var packageJSON = require("../../../package.json");

var config = {
  // @@ENV gets replaced by build system
  environment: "@@ENV",
  // If the UI is served through a proxied URL, this can be set here.
  rootUrl: "",
  // Defines the Marathon API URL,
  // leave empty to use the same as the UI is served.
  apiURL: "../",
  // Intervall of API request in ms
  updateInterval: 5000,
  // Local http server URI while tests run
  localTestserverURI: {
    address: "localhost",
    port: 8181
  },
  version: ("@@TEAMCITY_UI_VERSION".indexOf("@@TEAMCITY") === -1) ?
    "@@TEAMCITY_UI_VERSION" :
    `${packageJSON.version}-SNAPSHOT`
};

if (process.env.GULP_ENV === "development") {
  try {
    var configDev = require("./config.dev");
    config = Object.assign(config, configDev);
  } catch (e) {
    console.info("You could copy config.template.js to config.dev.js " +
      "to enable a development configuration.");
  }
}
module.exports = config;

Revert "Convert relative apiURL paths to absolute paths"


angular.module('soundmist').directive('player', function (Player) {
  return {
    restrict: 'E',
    scope: false,
    replace: false,
    templateUrl: 'directives/player/player.html',
    link: function (scope, element) {
      scope.Player = Player

      let slider = angular.element(document.querySelector('#progress'))[0]
      let blocking = false

      scope.$watch('Player.getActive()', function (track) {
        if (track == undefined) return
        scope.track = track

        // Use the high-res artwork instead of the downscaled one provided
        var url = scope.track.artwork_url
        if (url) {
          url = url.replace('large.jpg', 't500x500.jpg')
        }

        scope.wallpaper = {
          'background': 'linear-gradient(rgba(255, 126, 0, 0.90),rgba(255, 119, 0, 0.75)), url(' + url + ')'
        }
      })

      scope.$watch('Player.getProgress(track)', function (progress) {
        if (!blocking) scope.progress = progress * 1000
      })

      slider.addEventListener('mousedown', event => {
        blocking = true
      })

      slider.addEventListener('mouseup', function (event) {
        if (blocking) {
          let x = event.pageX - this.offsetLeft
          let progress = x / slider.offsetWidth

          Player.setProgress(scope.track, progress)
          blocking = false
        }
      })
    }
  }
})

angular.module('soundmist').directive('player', function (Player) {
  return {
    restrict: 'E',
    scope: false,
    replace: false,
    templateUrl: 'directives/player/player.html',
    link: function (scope, element) {
      scope.Player = Player

      let slider = angular.element(document.querySelector('#progress'))[0]
      let blocking = false

      scope.$watch('Player.getActive()', function (track) {
        if (track == undefined) return
        scope.track = track

        // Use the high-res artwork instead of the downscaled one provided
        var url = scope.track.artwork_url
        if (url) {
          url = url.replace('large.jpg', 't500x500.jpg')
        }

        scope.wallpaper = {
          'background': 'linear-gradient(rgba(255, 126, 0, 0.90),rgba(255, 119, 0, 0.95)), url(' + url + ')'
        }
      })

      scope.$watch('Player.getProgress(track)', function (progress) {
        if (!blocking) scope.progress = progress * 1000
      })

      slider.addEventListener('mousedown', event => {
        blocking = true
      })

      slider.addEventListener('mouseup', function (event) {
        if (blocking) {
          let x = event.pageX - this.offsetLeft
          let progress = x / slider.offsetWidth

          Player.setProgress(scope.track, progress)
          blocking = false
        }
      })
    }
  }
})

Increase headers background color opacity


'use strict';

(function() {
  angular.module('ncsaas')
    .service('invitationService', ['baseServiceClass', '$http', 'ENV', invitationService]);

  function invitationService(baseServiceClass, $http, ENV) {
    /*jshint validthis: true */
    var vm = this;
    var ServiceClass = baseServiceClass.extend({
      init: function() {
        this._super();
        this.endpoint = '/user-invitations/';
      },
      accept: function(invitation_uuid) {
        return vm.executeAction(invitation_uuid, 'accept');
      },
      cancel: function(invitation_uuid) {
        return vm.executeAction(invitation_uuid, 'cancel');
      },
      resend: function(invitation_uuid) {
        return vm.executeAction(invitation_uuid, 'resend');
      },
      executeAction: function(invitation_uuid, action) {
        var url = ENV.apiEndpoint + '/api/user-invitations/' + invitation_uuid + '/' + action + '/';
        return $http.post(url);
      }
    });
    return new ServiceClass();
  }

})();

'use strict';

(function() {
  angular.module('ncsaas')
    .service('invitationService', ['baseServiceClass', '$http', 'ENV', invitationService]);

  function invitationService(baseServiceClass, $http, ENV) {
    /*jshint validthis: true */
    var ServiceClass = baseServiceClass.extend({
      init: function() {
        this._super();
        this.endpoint = '/user-invitations/';
      },
      accept: function(invitation_uuid) {
        return this.executeAction(invitation_uuid, 'accept');
      },
      cancel: function(invitation_uuid) {
        return this.executeAction(invitation_uuid, 'cancel');
      },
      resend: function(invitation_uuid) {
        return this.executeAction(invitation_uuid, 'resend');
      },
      executeAction: function(invitation_uuid, action) {
        var url = ENV.apiEndpoint + '/api/user-invitations/' + invitation_uuid + '/' + action + '/';
        return $http.post(url);
      }
    });
    return new ServiceClass();
  }

})();

Implement invitation data service (WAL-32)


"use strict";

const fs = require("fs-extra-promise");
const path = require("path");
const log = require("log");

const findDirsWithEntry = async (baseDir, entryFile = "index.js") => {
    const res = [];
    const dirContent = await fs.readdirAsync(baseDir);
    for (const dirName of dirContent) {
        const dirPath = path.join(baseDir, dirName);
        const entryPath = path.join(dirPath, entryFile);
        try {
            fs.accessAsync(entryPath, fs.constants.R_OK);
            res.push({
                path: entryPath,
                name: dirName,
                dir: dirPath
            });
        } catch (error) {
            log.warn(`Path ${entryPath} isn't readable, skipping...`, error);
        }
    }

    return res;
};

module.exports = findDirsWithEntry;

"use strict";

const fs = require("fs-extra-promise");
const path = require("path");
const log = require("log");

const findDirsWithEntry = async (baseDir, entryFile = "index.js") => {
    const res = [];
    const dirContent = await fs.readdirAsync(baseDir);
    for (const dirName of dirContent) {
        const dirPath = path.join(baseDir, dirName);
        const entryPath = path.join(dirPath, entryFile);

        try {
            const dirStat = await fs.statAsync(dirPath);
            if (dirStat.isDirectory()) {
                await fs.accessAsync(entryPath, fs.constants.R_OK);
                res.push({
                    path: entryPath,
                    name: dirName,
                    dir: dirPath
                });
            }
        } catch (error) {
            log.warn(`Path ${entryPath} isn't accessible, skipping...`, error);
        }
    }

    return res;
};

module.exports = findDirsWithEntry;

Fix problem with regular files in search paths


/**
 * Cell used for showing and allowing the editing of an AmountEntry
 *
 * Copyright 2015 Ethan Smith
 */

var $ = require('jquery'),
    AmountEntryCell = require('./AmountEntryCell');

var EditableAmountCell = AmountEntryCell.extend({

   render: function() {
      var self = this;
      this._super();

      this.$el.on('blur', 'input', function() {
         self.trigger('editing:ended', $(this));
      });
   },

   _renderContent: function() {
      var elem = $('<input />').attr({
         type: 'text',
         value: this.model.get('amount').readable(),
         class: 'jsCurrency'
      });

      return $('<div>').append(elem.clone()).html();
   }
});

module.exports = EditableAmountCell;

/**
 * Cell used for showing and allowing the editing of an AmountEntry
 *
 * Copyright 2015 Ethan Smith
 */

var $ = require('jquery'),
    AmountEntryCell = require('./AmountEntryCell');

var EditableAmountCell = AmountEntryCell.extend({

   render: function() {
      var self = this;
      this._super();

      this.$el.on('blur', 'input', function() {
         var originalHash = self.model.hash();
         self.model.get('amount').set($(this).val());
         self.trigger('editing:ended', originalHash, self.model);
      });
   },

   _renderContent: function() {
      var elem = $('<input />').attr({
         type: 'text',
         value: this.model.get('amount').readable(),
         class: 'jsCurrency'
      });

      return $('<div>').append(elem.clone()).html();
   }
});

module.exports = EditableAmountCell;

Update model and trigger edited event on blur


// @flow

import type { Children } from 'react';
import React from 'react';

import { addTransitionListener } from 'yubaba-core';

export default class TransitionContainer extends React.Component {
  _detatch: Function;

  props: {
    pair: string,
    children?: Children,
    style?: any,
  };

  state = {
    visible: false,
  };

  componentWillMount () {
    // We need to have this be attached before
    // everything else is mounted, but we don't want to run this on the server.
    // How?
    this._detatch = addTransitionListener(this.props.pair, this.setVisibility);
  }

  componentWillUnmount () {
    this._detatch();
  }

  setVisibility = (visible: boolean) => {
    this.setState({
      visible,
    });
  };

  render () {
    const { pair, style, ...props } = this.props;

    return (
      <div {...props} style={{ ...style, opacity: this.state.visible ? 1 : 0 }}>
        {this.props.children}
      </div>
    );
  }
}

// @flow

import type { Children } from 'react';
import React from 'react';

import { addTransitionListener } from 'yubaba-core';

export default class TransitionContainer extends React.Component {
  _detatch: Function;

  props: {
    pair: string,
    children?: Children,
    style?: any,
  };

  state = {
    visible: false,
  };

  componentWillMount () {
    if (document) {
      this._detatch = addTransitionListener(this.props.pair, this.setVisibility);
    }
  }

  componentWillUnmount () {
    this._detatch();
  }

  setVisibility = (visible: boolean) => {
    this.setState({
      visible,
    });
  };

  render () {
    const { pair, style, ...props } = this.props;

    return (
      <div {...props} style={{ ...style, opacity: this.state.visible ? 1 : 0 }}>
        {this.props.children}
      </div>
    );
  }
}

Check for documents existance in transition container


require('dotenv').config({silent: true});

const process = require('process');
const http = require('http');
const createHandler = require('github-webhook-handler');
const GitHubApi = require('github');
const Configuration = require('./lib/configuration');
const Dispatcher = require('./lib/dispatcher');
const installations = require('./lib/installations');

const PORT = process.env.PORT || 3000;
const webhook = createHandler({path: '/', secret: process.env.WEBHOOK_SECRET || 'development'});

// Cache installations
installations.load();
// Listen for new installations
installations.listen(webhook);

const github = new GitHubApi({
  debug: true
});

github.authenticate({
  type: 'oauth',
  token: process.env.GITHUB_TOKEN
});

http.createServer((req, res) => {
  webhook(req, res, err => {
    if (err) {
      console.log('ERROR', err);
      res.statusCode = 500;
      res.end('Something has gone terribly wrong.');
    } else {
      res.statusCode = 404;
      res.end('no such location');
    }
  });
}).listen(PORT);

webhook.on('*', event => {
  if (event.payload.repository) {
    const installation = installations.for(event.payload.repository.owner.login);
    installations.authAs(installation).then(github => {
      const dispatcher = new Dispatcher(github, event);
      return Configuration.load(github, event.payload.repository).then(config => {
        dispatcher.call(config);
      });
    });
  }
});

console.log('Listening on http://localhost:' + PORT);

require('dotenv').config({silent: true});

const process = require('process');
const http = require('http');
const createHandler = require('github-webhook-handler');
const Configuration = require('./lib/configuration');
const Dispatcher = require('./lib/dispatcher');
const installations = require('./lib/installations');

const PORT = process.env.PORT || 3000;
const webhook = createHandler({path: '/', secret: process.env.WEBHOOK_SECRET || 'development'});

// Cache installations
installations.load();
// Listen for new installations
installations.listen(webhook);

http.createServer((req, res) => {
  webhook(req, res, err => {
    if (err) {
      console.log('ERROR', err);
      res.statusCode = 500;
      res.end('Something has gone terribly wrong.');
    } else {
      res.statusCode = 404;
      res.end('no such location');
    }
  });
}).listen(PORT);

webhook.on('*', event => {
  if (event.payload.repository) {
    const installation = installations.for(event.payload.repository.owner.login);
    installations.authAs(installation).then(github => {
      const dispatcher = new Dispatcher(github, event);
      return Configuration.load(github, event.payload.repository).then(config => {
        dispatcher.call(config);
      });
    });
  }
});

console.log('Listening on http://localhost:' + PORT);

Remove unused github api instance


var queue = require('queue-async');
var express = require('express');
var request = require('request');

var app = express();
app.use(express.static(__dirname + '/static'));

app.get('/:owner/:repo', function(req, res, next) {
  var q = queue();
  var repoName = req.params.owner + '/' + req.params.repo;
  res.locals.repoName = repoName;
  for (var i=1; i<=10; i++) {
    q.defer(request,{
      url:'https://api.github.com/repos/' + repoName + '/events',
      qs: {page: i},
      json: true
      });
  }
  q.awaitAll(function(err, pages) {
    if (err) return next(err);
    res.locals.stargazes = [].concat.apply([], pages).filter(function(v) {
      return v.type == 'WatchEvent';
    });
    res.set('content-type','text/html');
    res.render('starwatchcats.jade');
  });
});

var server = require('http').createServer(app);

server.listen(process.env.PORT || 5000, process.env.IP);

var queue = require('queue-async');
var express = require('express');
var request = require('request');

var app = express();
app.use(express.static(__dirname + '/static'));

function renderStargazes(req, res, next) {
  var q = queue();
  for (var i=1; i<=10; i++) {
    q.defer(request,{
      url:'https://api.github.com/repos/' + res.local.repoName + '/events',
      qs: {page: i},
      json: true
      });
  }
  q.awaitAll(function(err, pages) {
    if (err) return next(err);
    res.locals.stargazes = [].concat.apply([], pages).filter(function(v) {
      return v.type == 'WatchEvent';
    });
    res.set('content-type','text/html');
    res.render('starwatchcats.jade');
  });
}

app.get('/:owner/:repo', function(req, res, next) {
  res.locals.repoName = req.params.owner + '/' + req.params.repo;
  renderStargazes(req, res, next);
});

if (process.env.DEFAULT_REPO) {
  app.get('/', function(req, res, next) {
    res.locals.repoName = process.env.DEFAULT_REPO;
    renderStargazes(req, res, next);
  });
}

var server = require('http').createServer(app);

server.listen(process.env.PORT || 5000, process.env.IP);

Add environmental config for root repo


var _ = require('lodash'),
    path = require('path'),
    webpackConfig = require('./webpack.config.js');

_.merge(webpackConfig, {
    devtool: 'inline-source-map',
    module: {
      preLoaders: [
        {
          test: /\.(js|jsx)?$/,
          include: path.resolve('src/'),
          loader: 'babel-istanbul-instrumenter'
        }
      ]
    }
});

module.exports = function(config) {
    config.set({
        frameworks: ['jasmine'],
        browsers: ['PhantomJS'],
        plugins: [
            'karma-coveralls',
            'karma-coverage',
            'karma-jasmine',
            'karma-phantomjs-launcher',
            'karma-sourcemap-loader',
            require('karma-webpack')
        ],
        reporters: ['progress', 'coverage', 'coveralls'],
        coverageReporter: {
          type : 'lcov',
          dir: 'coverage/'
        },
        preprocessors: {
            'webpack.tests.js': [
                'webpack',
                'sourcemap'
            ]
        },
        singleRun: false,
        webpack: webpackConfig,
        webpackServer: {
            watchOptions: {
                aggregateTimeout: 500,
                poll: 1000
            },
            stats: {
                colors: true
            },
            noInfo: true
        },
        files: [
            './node_modules/phantomjs-polyfill/bind-polyfill.js',
            'webpack.tests.js'
        ]
    });
};

var _ = require('lodash'),
    path = require('path'),
    webpackConfig = require('./webpack.config.js');

_.merge(webpackConfig, {
    devtool: 'inline-source-map',
    module: {
      postLoaders: [
        {
          test: /\.(js|jsx)?$/,
          include: path.resolve('src/'),
          loader: 'babel-istanbul-instrumenter'
        }
      ]
    }
});

module.exports = function(config) {
    config.set({
        frameworks: ['jasmine'],
        browsers: ['PhantomJS'],
        plugins: [
            'karma-jasmine',
            'karma-phantomjs-launcher',
            'karma-sourcemap-loader',
            require('karma-webpack')
        ],
        reporters: ['progress'],
        coverageReporter: {
          type : 'lcov',
          dir: 'coverage/'
        },
        preprocessors: {
            'webpack.tests.js': [
                'webpack',
                'sourcemap'
            ]
        },
        singleRun: false,
        webpack: webpackConfig,
        webpackServer: {
            watchOptions: {
                aggregateTimeout: 500,
                poll: 1000
            },
            stats: {
                colors: true
            },
            noInfo: true
        },
        files: [
            './node_modules/phantomjs-polyfill/bind-polyfill.js',
            'webpack.tests.js'
        ]
    });
};

Change from `preLoaders` to `postLoaders`


/*globals $, GOVUK, suchi */
/*jslint
 white: true,
 browser: true */

$(function() {
  "use strict";
  function browserWarning() {
    var container = $('

'),
        text = $('
Upgrade your web browser (the software you use to access the internet), it’s out of date
'),
        closeLink = $('Close');

    return container.append(text.append(closeLink));
  }

  // we don't show the message when the cookie warning is also there
  if (GOVUK.cookie('seen_cookie_message')) {
    if (suchi.isOld(navigator.userAgent)) {
      if(GOVUK.cookie('govuk_not_first_visit') !== null && GOVUK.cookie('govuk_browser_upgrade_dismissed') === null){
        var $prompt = browserWarning();
        $('#global-cookie-message').after($prompt);
        $prompt.show();
        $prompt.on("click", ".dismiss", function(e) {
          $prompt.hide();
          // the warning is dismissable for 2 weeks
          GOVUK.cookie('govuk_browser_upgrade_dismissed', 'yes', { days: 14 });
        });
      }
    }

    // We're not showing the message on first visit
    GOVUK.cookie('govuk_not_first_visit', 'yes', { days: 28 });
  }
});

/*globals $, GOVUK, suchi */
/*jslint
 white: true,
 browser: true */

$(function() {
  "use strict";
  function browserWarning() {
    var container = $('

'),
        text = $('
Upgrade your web browser (the software you use to access the internet), it’s out of date
'),
        closeLink = $('Close');

    return container.append(text.append(closeLink));
  }

  // we don't show the message when the cookie warning is also there
  if (GOVUK.cookie('seen_cookie_message')) {
    if (suchi.isOld(navigator.userAgent)) {
      if(GOVUK.cookie('govuk_not_first_visit') !== null && GOVUK.cookie('govuk_browser_upgrade_dismissed') === null){
        var $prompt = browserWarning();
        $('#global-cookie-message').after($prompt);
        $prompt.show();
        $prompt.on("click", ".dismiss", function(e) {
          $prompt.hide();
          // the warning is dismissable for 4 weeks, for users who are not in a
          // position to upgrade right now or unable to (no control of browser)
          GOVUK.cookie('govuk_browser_upgrade_dismissed', 'yes', { days: 28 });
        });
      }
    }

    // We're not showing the message on first visit
    GOVUK.cookie('govuk_not_first_visit', 'yes', { days: 28 });
  }
});

Increase the browser upgrade dismissal duration