(function () {
    'use strict';

    function RelatedLinksAppController($scope) {
        var vm = this;

        vm.relations = $scope.model.viewModel;
        var currentVariant = _.find($scope.content.variants, function (v) { return v.active });

        if (currentVariant.language) {
            vm.culture = currentVariant.language.culture;
            vm.cultureRelations = _.filter(vm.relations,
                function(r) { return r.Culture.toLowerCase() === vm.culture.toLowerCase() });
        } else {
            vm.cultureRelations = vm.relations;
        }

        vm.ungrouped = [];

        for (var i = 0; i < vm.cultureRelations.length; i++) {
            var relation = vm.cultureRelations[i];

            for (var j = 0; j < relation.Properties.length; j++) {
                vm.ungrouped.push({
                    id: relation.Id,
                    name: relation.Name,
                    propertyname: relation.Properties[j].PropertyName,
                    tabname: relation.Properties[j].TabName
                });
            }
        }

    }

    angular.module('umbraco').controller('Our.Umbraco.Nexu.Controllers.RelatedLinksAppController',
        [
            '$scope',
            'editorState',
            'localizationService',
            RelatedLinksAppController
        ]);

})();

(function () {
    'use strict';

    function RelatedLinksAppController($scope) {
        var vm = this;

        vm.relations = $scope.model.viewModel;
        var currentVariant = _.find($scope.content.variants, function (v) { return v.active });

        if (currentVariant.language) {
            vm.culture = currentVariant.language.culture;
            vm.cultureRelations = _.filter(vm.relations,
                function(r) { return r.Culture.toLowerCase() === vm.culture.toLowerCase() });
        } else {
            vm.cultureRelations = vm.relations;
        }

        vm.ungrouped = [];

        for (var i = 0; i < vm.cultureRelations.length; i++) {
            var relation = vm.cultureRelations[i];

            for (var j = 0; j < relation.Properties.length; j++) {
                vm.ungrouped.push({
                    id: relation.Id,
                    name: relation.Name,
                    propertyname: relation.Properties[j].PropertyName,
                    tabname: relation.Properties[j].TabName,
                    published: relation.IsPublished,
                    trashed: relation.IsTrashed
                });
            }
        }

    }

    angular.module('umbraco').controller('Our.Umbraco.Nexu.Controllers.RelatedLinksAppController',
        [
            '$scope',
            'editorState',
            'localizationService',
            RelatedLinksAppController
        ]);

})();

Set publish and trashed state


/**
 * Patterns gallery - A simple gallery
 *
 * Copyright 2013 Simplon B.V. - Wichert Akkerman
 */
define([
    "jquery",
    "../registry",
    "photoswipe"
], function($, patterns) {
    var gallery = {
        name: "gallery",
        trigger: ".pat-gallery",

        init: function($el) {
            return $el;
        }
    };

    patterns.register(gallery);
    return gallery;
});

/**
 * Patterns gallery - A simple gallery
 *
 * Copyright 2013 Simplon B.V. - Wichert Akkerman
 */
define([
    "jquery",
    "../registry",
    "klass", // Workaround because bungle is ignoring dependencies currently
    "photoswipe"
], function($, patterns) {
    var gallery = {
        name: "gallery",
        trigger: ".pat-gallery",

        init: function($el) {
            return $el;
        }
    };

    patterns.register(gallery);
    return gallery;
});

Work around bungle bug (it ignores dependencies).


/**
 * @title Slack Notification on User Signup
 * @overview Slack notification on user signup.
 * @gallery true
 * @category webhook
 *
 * This rule sends a message to a slack channel on every user signup.
 *
 */

function slackNotificationOnUserSignup(user, context, callback) {
  // short-circuit if the user signed up already or is using a refresh token
  if (
    context.stats.loginsCount > 1 ||
    context.protocol === 'oauth2-refresh-token'
  ) {
    return callback(null, user, context);
  }

  // get your slack's hook url from: https://slack.com/services/10525858050
  const SLACK_HOOK = configuration.SLACK_HOOK_URL;

  const slack = require('slack-notify')(SLACK_HOOK);
  const message =
    'New User: ' + (user.name || user.email) + ' (' + user.email + ')';
  const channel = '#some_channel';

  slack.success({
    text: message,
    channel: channel
  });

  // don’t wait for the Slack API call to finish, return right away (the request will continue on the sandbox)`
  callback(null, user, context);
}

/**
 * @title Slack Notification on User Signup
 * @overview Slack notification on user signup.
 * @gallery true
 * @category webhook
 *
 * This rule sends a message to a slack channel on every user signup.
 *
 */

function slackNotificationOnUserSignup(user, context, callback) {
  // short-circuit if the user signed up already or is using a refresh token
  if (
    context.stats.loginsCount > 1 ||
    context.protocol === 'oauth2-refresh-token'
  ) {
    return callback(null, user, context);
  }
  // check this isn't a checkSession call
  if (context.request.query.prompt !== 'none'){

  // get your slack's hook url from: https://slack.com/services/10525858050
  const SLACK_HOOK = configuration.SLACK_HOOK_URL;

  const slack = require('slack-notify')(SLACK_HOOK);
  const message =
    'New User: ' + (user.name || user.email) + ' (' + user.email + ')';
  const channel = '#some_channel';

  slack.success({
    text: message,
    channel: channel
  });

  // don’t wait for the Slack API call to finish, return right away (the request will continue on the sandbox)`
  callback(null, user, context);
  } else {
  callback(null, user, context);
  }
}

Add check for silent authentication


/* global console */

var EditorControllerMixin = Ember.Mixin.create({
    //## Computed post properties
    isPublished: Ember.computed.equal('status', 'published'),
    isDraft: Ember.computed.equal('status', 'draft'),
    /**
     * By default, a post will not change its publish state.
     * Only with a user-set value (via setSaveType action)
     * can the post's status change.
     */
    willPublish: function (key, val) {
        if (val) {
            return val;
        }
        return this.get('isPublished');
    }.property('isPublished'),

    actions: {
        save: function () {
            var status = this.get('willPublish') ? 'published' : 'draft',
                self = this;

            this.set('status', status);
            this.get('model').save().then(function () {
                console.log('saved');
                self.notifications.showSuccess('Post status saved as <strong>' +
                    self.get('status') + '</strong>.');
            }, this.notifications.showErrors);
        },

        setSaveType: function (newType) {
            if (newType === 'publish') {
                this.set('willPublish', true);
            } else if (newType === 'draft') {
                this.set('willPublish', false);
            } else {
                console.warn('Received invalid save type; ignoring.');
            }
        }
    }
});

export default EditorControllerMixin;

/* global console */

var EditorControllerMixin = Ember.Mixin.create({
    //## Computed post properties
    isPublished: Ember.computed.equal('status', 'published'),
    isDraft: Ember.computed.equal('status', 'draft'),
    /**
     * By default, a post will not change its publish state.
     * Only with a user-set value (via setSaveType action)
     * can the post's status change.
     */
    willPublish: function (key, value) {
        if (arguments.length > 1) {
            return value;
        }
        return this.get('isPublished');
    }.property('isPublished'),

    actions: {
        save: function () {
            var status = this.get('willPublish') ? 'published' : 'draft',
                self = this;

            this.set('status', status);
            this.get('model').save().then(function () {
                console.log('saved');
                self.notifications.showSuccess('Post status saved as <strong>' +
                    self.get('status') + '</strong>.');
            }, this.notifications.showErrors);
        },

        setSaveType: function (newType) {
            if (newType === 'publish') {
                this.set('willPublish', true);
            } else if (newType === 'draft') {
                this.set('willPublish', false);
            } else {
                console.warn('Received invalid save type; ignoring.');
            }
        }
    }
});

export default EditorControllerMixin;

Fix Editor Save Button not allowing unpublish


(function() {
    var clientid;
    jive.tile.onOpen(function(config, options, other, container ) {
        osapi.jive.corev3.systemExtProps.get({}).execute(function (resp) {
            if( resp && resp.content ) {
                clientid = resp.content.clientid;
            }
        });
        gadgets.window.adjustHeight();

        if ( typeof config === "string" ) {
            config = JSON.parse(config);
        }

        var json = config || { };
        json.posting = json.posting || "on";
        json.project = json.project || container.name;

        // prepopulate the sequence input dialog
        $("input[name=post_activity]").val([json["posting"]]);
        $("#project").val([json.project]);

        $("#btn_submit").click( function() {
            config.posting = $("input[name=post_activity]:checked").val();
            config.project = $("#project").val();
            config.clientid = clientid;
            jive.tile.close(config, {} );
            gadgets.window.adjustHeight(300);
        });
    });

})();

(function() {
    var clientid;
    jive.tile.onOpen(function(config, options, other, container ) {
        osapi.jive.corev3.systemExtProps.get({}).execute(function (resp) {
            if( resp && resp.content ) {
                clientid = resp.content.clientid;
            }
        });
        gadgets.window.adjustHeight();

        if ( typeof config === "string" ) {
            config = JSON.parse(config);
        }

        var json = config || { };
        json.posting = json.posting || "on";
        json.project = json.project || (container && container.name) || "";

        // prepopulate the sequence input dialog
        $("input[name=post_activity]").val([json["posting"]]);
        $("#project").val([json.project]);

        $("#btn_submit").click( function() {
            config.posting = $("input[name=post_activity]:checked").val();
            config.project = $("#project").val();
            config.clientid = clientid;
            jive.tile.close(config, {} );
            gadgets.window.adjustHeight(300);
        });
    });

})();

Handle when container is undefined.


(function(module) {
    module.directive("displayFileContents", displayFileContentsDirective);
    function displayFileContentsDirective() {
        return {
            restrict: "E",
            controller: 'DisplayFileContentsDirectiveController',
            controllerAs: 'view',
            bindToController: true,
            scope: {
                file: '=file'
            },
            templateUrl: 'application/core/projects/project/files/display-file-contents.html'
        };
    }

    module.controller("DisplayFileContentsDirectiveController", DisplayFileContentsDirectiveController);
    DisplayFileContentsDirectiveController.$inject = ["mcfile"];

    /* @ngInject */
    function DisplayFileContentsDirectiveController(mcfile) {
        var ctrl = this;
        ctrl.fileType = determineFileType(ctrl.file.mediatype);
        ctrl.fileSrc = mcfile.src(ctrl.file.id);

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

        function determineFileType(mediatype) {
            if (isImage(mediatype.mime)) {
                return "image";
            } else {
                switch(mediatype.mime) {
                case "application/pdf":
                    return "pdf";
                case "application/vnd.ms-excel":
                    return "excel";
                default:
                    return mediatype.mime;
                }
            }
        }
    }
}(angular.module('materialscommons')));

(function(module) {
    module.directive("displayFileContents", displayFileContentsDirective);
    function displayFileContentsDirective() {
        return {
            restrict: "E",
            controller: 'DisplayFileContentsDirectiveController',
            controllerAs: 'view',
            bindToController: true,
            scope: {
                file: '=file'
            },
            templateUrl: 'application/core/projects/project/files/display-file-contents.html'
        };
    }

    module.controller("DisplayFileContentsDirectiveController", DisplayFileContentsDirectiveController);
    DisplayFileContentsDirectiveController.$inject = ["mcfile"];

    /* @ngInject */
    function DisplayFileContentsDirectiveController(mcfile) {
        var ctrl = this;
        ctrl.fileType = determineFileType(ctrl.file.mediatype);
        ctrl.fileSrc = mcfile.src(ctrl.file.id);

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

        function determineFileType(mediatype) {
            if (isImage(mediatype.mime)) {
                return "image";
            } else {
                switch(mediatype.mime) {
                case "application/pdf":
                    return "pdf";
                case "application/vnd.ms-excel":
                    return "xls";
                default:
                    return mediatype.mime;
                }
            }
        }
    }
}(angular.module('materialscommons')));

Return xls as type for application/vnd.ms-excel.


(function($, BLCAdmin) {

    // Add utility functions for products to the BLCAdmin object
    BLCAdmin.product = {

        refreshSkusGrid : function($container, listGridUrl) {
            BLC.ajax({
                url : listGridUrl,
                type : "GET"
            }, function(data) {
                BLCAdmin.listGrid.replaceRelatedListGrid($($(data.trim())[0]).find('table'));
            });
        }

    };

})(jQuery, BLCAdmin);

$(document).ready(function() {

    $('body').on('click', 'button.generate-skus', function() {
        var $container = $(this).closest('div.listgrid-container');

        BLC.ajax({
            url : $(this).data('actionurl'),
            type : "GET"
        }, function(data) {
            var alertType = data.error ? 'alert' : '';

            BLCAdmin.listGrid.showAlert($container, data.message, {
                alertType: alertType,
                clearOtherAlerts: true
            });

            if (data.skusGenerated > 0) {
                BLCAdmin.product.refreshSkusGrid($container, data.listGridUrl);
            }
        });

        return false;
    });
});

(function($, BLCAdmin) {

    // Add utility functions for products to the BLCAdmin object
    BLCAdmin.product = {

        refreshSkusGrid : function($container, listGridUrl) {
            BLC.ajax({
                url : listGridUrl,
                type : "GET"
            }, function(data) {
                BLCAdmin.listGrid.replaceRelatedListGrid($(data));
            });
        }

    };

})(jQuery, BLCAdmin);

$(document).ready(function() {

    $('body').on('click', 'button.generate-skus', function() {
        var $container = $(this).closest('div.listgrid-container');

        BLC.ajax({
            url : $(this).data('actionurl'),
            type : "GET"
        }, function(data) {
            var alertType = data.error ? 'alert' : '';

            BLCAdmin.listGrid.showAlert($container, data.message, {
                alertType: alertType,
                clearOtherAlerts: true
            });

            if (data.skusGenerated > 0) {
                BLCAdmin.product.refreshSkusGrid($container, data.listGridUrl);
            }
        });

        return false;
    });
});

Fix sku generation not updating list grid properly


var gulp = require('gulp');

// 5) Update asset references in HTML
gulp.task('update-html', function () {
	var config = require('../../config'),
		path = require('path'),
		revReplace = require('gulp-rev-replace')

	var manifest = gulp.src(config.publicDirectory + "/rev-manifest.json");

	return gulp.src(path.join(config.root.dest, '/**/*.html'))
		.pipe(revReplace({manifest: manifest}))
		.pipe(gulp.dest(config.root.dest))
});

var gulp = require('gulp');

// 5) Update asset references in HTML
gulp.task('update-html', function () {
	var config = require('../../config'),
		path = require('path'),
		revReplace = require('gulp-rev-replace')

	var manifest = gulp.src(path.join(config.root.dest, "/rev-manifest.json"))

	return gulp.src(path.join(config.root.dest, config.tasks.html.dest, '/**/*.html'))
		.pipe(revReplace({manifest: manifest}))
		.pipe(gulp.dest(path.join(config.root.dest, config.tasks.html.dest)))
});

Fix html not updating references


//See gh-tabs-manager.js for use
var TabPane = Ember.Component.extend({
    classNameBindings: ['active'],

    tabsManager: Ember.computed(function () {
        return this.nearestWithProperty('isTabsManager');
    }),

    tab: Ember.computed('tabsManager.tabs.@each', function () {
        var index = this.get('tabsManager.tabPanes').indexOf(this),
            tabs = this.get('tabsManager.tabs');

        return tabs && tabs.objectAt(index);
    }),

    active: Ember.computed.alias('tab.active'),

    // Register with the tabs manager
    registerWithTabs: function () {
        this.get('tabsManager').registerTabPane(this);
    }.on('didInsertElement'),
    unregisterWithTabs: function () {
        this.get('tabsManager').unregisterTabPane(this);
    }.on('willDestroyElement')
});

export default TabPane;

//See gh-tabs-manager.js for use
var TabPane = Ember.Component.extend({
    classNameBindings: ['active'],

    tabsManager: Ember.computed(function () {
        return this.nearestWithProperty('isTabsManager');
    }),

    tab: Ember.computed('tabsManager.tabs.[]', 'tabsManager.tabPanes.[]',
    function () {
        var index = this.get('tabsManager.tabPanes').indexOf(this),
            tabs = this.get('tabsManager.tabs');

        return tabs && tabs.objectAt(index);
    }),

    active: Ember.computed.alias('tab.active'),

    // Register with the tabs manager
    registerWithTabs: function () {
        this.get('tabsManager').registerTabPane(this);
    }.on('didInsertElement'),
    unregisterWithTabs: function () {
        this.get('tabsManager').unregisterTabPane(this);
    }.on('willDestroyElement')
});

export default TabPane;

Fix GhostTab and GhostTabPane array dependencies


define([
], function (
) {

    return {
        "id": "schema/ExampleSchema",
        "description": "A simple model for testing",
        "$schema": "http://json-schema.org/draft-03/schema",
        "type": "object",
        "properties": {
            "num": {
                "type": "integer",
                "description": "an optional property.",
                "maximum": 10,
                "required": true
            }
        }
    };
});

define({
    "id": "schema/ExampleSchema",
    "description": "A simple model for testing",
    "$schema": "http://json-schema.org/draft-03/schema",
    "type": "object",
    "properties": {
        "num": {
            "type": "integer",
            "description": "an optional property.",
            "maximum": 10,
            "required": true
        }
    }
});

Use shortened AMD form for object definitions.


import { _ } from 'azk';
import { UIProxy } from 'azk/cli/ui';
import { example_system } from 'azk/generator/rules';

export class Suggestion extends UIProxy {
  constructor(...args) {
    super(...args);

    // Readable name for this suggestion
    this.name = 'rails';

    // Which rules they suggestion is valid
    this.ruleNamesList = ['rails41'];

    // Initial Azkfile.js suggestion
    this.suggestion = _.extend({}, example_system, {
      __type  : 'rails 4.1',
      image   : 'rails:4.1',
      provision: [
        'bundle install --path /azk/bundler',
        'bundle exec rake db:create',
        'bundle exec rake db:migrate',
      ],
      http    : true,
      scalable: { default: 2 },
      command : 'bundle exec rackup config.ru --port $HTTP_PORT',
      mounts  : {
        '/azk/#{manifest.dir}': {type: 'path', value: '.'},
        '/azk/bundler'        : {type: 'persistent', value: 'bundler'},
      },
      envs    : {
        RAILS_ENV : 'development',
        BUNDLE_APP_CONFIG : '/azk/bundler',
      }
    });
  }

  suggest() {
    return this.suggestion;
  }
}

import { _ } from 'azk';
import { UIProxy } from 'azk/cli/ui';
import { example_system } from 'azk/generator/rules';

export class Suggestion extends UIProxy {
  constructor(...args) {
    super(...args);

    // Readable name for this suggestion
    this.name = 'rails';

    // Which rules they suggestion is valid
    this.ruleNamesList = ['rails41'];

    // Initial Azkfile.js suggestion
    this.suggestion = _.extend({}, example_system, {
      __type  : 'rails 4.1',
      image   : 'rails:4.1',
      provision: [
        'bundle install --path /azk/bundler',
        'bundle exec rake db:create',
        'bundle exec rake db:migrate',
      ],
      http    : true,
      scalable: { default: 2 },
      command : 'bundle exec rackup config.ru --pid /tmp/rails.pid --port $HTTP_PORT',
      mounts  : {
        '/azk/#{manifest.dir}': {type: 'path', value: '.'},
        '/azk/bundler'        : {type: 'persistent', value: 'bundler'},
      },
      envs    : {
        RAILS_ENV : 'development',
        BUNDLE_APP_CONFIG : '/azk/bundler',
      }
    });
  }

  suggest() {
    return this.suggestion;
  }
}

Add argument pid at the command in rails suggestion


export default {
  title: 'Hello World',
  description: 'Simple Hello World application with common elements for a scene',
  tags: 'Cube',
  public: false,
  scripts: [
    // 'http://127.0.0.1:8080/build/three.js', // For three.js dev
    '../../libs/three/r87/three.min.js',
    '../../libs/three/r87/controls/OrbitControls.js',
    '../../libs/threex/THREEx.WindowResize.js',
    '../../libs/dat.gui/0.6.2/dat.gui.min.js',
  ],
  styles: [
    '../../libs/dat.gui/0.6.2/dat.gui.css',
  ],
};

export default {
  title: 'Hello World',
  description: 'Simple Hello World application with common elements for a scene',
  tags: 'Cube',
  public: false,
  scripts: [
    // 'http://127.0.0.1:8080/build/three.js', // For three.js dev
    '../../libs/three/r92/three.min.js',
    '../../libs/three/r92/controls/OrbitControls.js',
    '../../libs/threex/THREEx.WindowResize.js',
    '../../libs/dat.gui/0.6.2/dat.gui.min.js',
  ],
  styles: [
    '../../libs/dat.gui/0.6.2/dat.gui.css',
  ],
};

Update hello world to use r92


var transitionEnd = "webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend";

$.fn.addClassAfter = function(className) {
	var that = this;
	setTimeout(function(){
		that.addClass(className);
	},1);
};
$.fn.addClassAfterTransition = function(className) {
	var that, transitionEnd;
	that = this;
	that.bind(transitionEnd, function() {
		$(this).addClass(className);
		$(this).unbind(transitionEnd);
	});
};
$.fn.removeClassAfter = function(className) {
	var that = this;
	setTimeout(function(){
		that.removeClass(className);
	},1);
};
$.fn.removeClassAfterTransition = function(className) {
	var that, transitionEnd;
	that = this;
	that.bind(transitionEnd, function() {
		$(this).removeClass(className);
		$(this).unbind(transitionEnd);
	});
};

var transitionEnd = "webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend";

$.fn.addClassAfter = function(className) {
	var $this = this;
	setTimeout(function(){
		$this.addClass(className);
	},1);
};
$.fn.addClassAfterTransition = function(className) {
	this.bind(transitionEnd, function() {
		$(this).addClass(className);
		$(this).unbind(transitionEnd);
	});
	return this;
};
$.fn.removeClassAfter = function(className) {
	var $this = this;
	setTimeout(function(){
		$this.removeClass(className);
	},1);
};
$.fn.removeClassAfterTransition = function(className) {
	this.bind(transitionEnd, function() {
		$(this).removeClass(className);
		$(this).unbind(transitionEnd);
	});
	return this;
};

Remove an empty var was blocking correct execution of the transition events


"use strict";

var adjAdjAnimal = require("../lib/index");

var expect = require("chai").expect;

describe("the library", function () {
	describe("getting a default id", function () {
		var result;

		before(function () {
			return adjAdjAnimal()
			.then(function (id) {
				result = id;
			});
		});

		it("generates adj-adj-animal", function () {
			expect(result, "matches").to.match(/[a-z]+-[a-z]+-[a-z]+/);
		});
	});

	describe("getting an id with specified number of adjectives", function () {
		var result;

		before(function () {
			return adjAdjAnimal(6)
			.then(function (id) {
				result = id;
			});
		});

		it("generates adj-{6}animal", function () {
			expect(result, "matches").to.match(/([a-z]+-){6}[a-z]+/);
		});
	});
});

"use strict";

var adjAdjAnimal = require("../lib/index");

var expect = require("chai").expect;

describe("the library", function () {
	describe("getting a default id", function () {
		var result;

		before(function () {
			return adjAdjAnimal()
			.then(function (id) {
				result = id;
			});
		});

		it("generates adj-adj-animal", function () {
			expect(result, "matches").to.match(/[A-Z][a-z]+[A-Z][a-z]+[A-Z][a-z]+/);
		});
	});

	describe("getting an id with specified number of adjectives", function () {
		var result;

		before(function () {
			return adjAdjAnimal(6)
			.then(function (id) {
				result = id;
			});
		});

		it("generates adj-{6}animal", function () {
			expect(result, "matches").to.match(/([A-Z][a-z]+){6}[A-Z][a-z]+/);
		});
	});
});

Change test to match new word format.


define([
    'underscore',
    'backbone',
    'models/core/BaseModel',
], function(_, Backbone, BaseModel) {

    var TestSuiteModel = BaseModel.extend({

        defaults: {
            id: null,
            project_id: 1,
            name: 'No test suite name',
            description: 'No description',
            url: '#/404'
        },

        initialize: function(options) {
            _.bindAll(this, 'url', 'parse');
        },

        url: function() {
            var url = '/api/projects/' + this.get('project_id') + '/testsuites';
            if (this.get('id') != null) {
                url += '/' + this.get('id');
            }
            if (this.id)
                url += '/' + this.id;
            return url;
        },

        parse: function(obj) {
            if (typeof(obj.test_suite) != 'undefined')
                return obj.test_suite;
            return obj;
        }

    });

    return TestSuiteModel;

});

define([
    'underscore',
    'backbone',
    'models/core/BaseModel',
], function(_, Backbone, BaseModel) {

    var TestSuiteModel = BaseModel.extend({

        defaults: {
            id: null,
            project_id: 1,
            name: 'No test suite name',
            description: 'No description',
            url: '#/404'
        },

        initialize: function(options) {
            _.bindAll(this, 'url', 'parse');
        },

        url: function() {
            var url = '/api/projects/' + this.get('project_id') + '/testsuites';
            if (this.get('id') != null) {
                url += '/' + this.get('id');
            } else if (this.id) {
                url += '/' + this.id;
            }
            return url;
        },

        parse: function(obj) {
            if (typeof(obj.test_suite) != 'undefined')
                return obj.test_suite;
            return obj;
        }

    });

    return TestSuiteModel;

});

Fix logic for retrieving test suite id


function setRotation() {
    var speed = document.getElementById("test-rot-speed").value;
    drawer.api.setRotation(parseInt(speed));
}

function printState() {
    drawer.api.addChange(function(state) {
        alert(state);
        return state;
    });
}

function setRotation() {
    var speed = document.getElementById("test-rot-speed").value;
    drawer.api.setRotation(parseInt(speed) || 0);
}

function printState() {
    drawer.api.addChange(function(state) {
        alert(state);
        return state;
    });
}

Check if field value is NaN


//----------------------------------------------------------------------------//
var express = require('express');
var router = express.Router();
var pg = require('pg');
var connectionString = require('../modules/db-config.module');
//----------------------------------------------------------------------------//

// Gets a user's profile info and FAQ entries
router.get('/:id', function(req, res) {
  var userId = parseInt(req.params.id);

  pg.connect(connectionString, function(error, client, done) {
    connectionErrorCheck(error);

    client.query(
      'SELECT * FROM mentors ' +
      'JOIN faq ON mentors.id = faq.mentor_id ' +
      'WHERE mentors.id = $1', [userId],
      function(error, result) {
        if(error) {
          console.log('Error when searching mentors and FAQ tables: ', error);
          res.sendStatus(500);
        } else {
          res.send(result.rows);
        }
      }
    );
  });
});

module.exports = router;

// Checks for errors connecting to the database
function connectionErrorCheck(error) {
  if (error) {
    console.log('Database connection error: ', error);
    res.sendStatus(500);
  }
}

//----------------------------------------------------------------------------//
var express = require('express');
var router = express.Router();
var pg = require('pg');
var connectionString = require('../modules/db-config.module');
//----------------------------------------------------------------------------//

// Gets a user's profile info and FAQ entries
router.get('/:id', function(req, res) {
  var userId = parseInt(req.params.id);

  pg.connect(connectionString, function(error, client, done) {
    connectionErrorCheck(error);

    client.query(
      'SELECT * FROM mentors ' +
      'FULL OUTER JOIN faq ON mentors.id = faq.mentor_id ' +
      'WHERE mentors.id = $1', [userId],
      function(error, result) {
        done();
        if(error) {
          console.log('Error when searching mentors and FAQ tables: ', error);
          res.sendStatus(500);
        } else {
          res.send(result.rows);
        }
      }
    );
  });
});

module.exports = router;

// Checks for errors connecting to the database
function connectionErrorCheck(error) {
  if (error) {
    console.log('Database connection error: ', error);
    res.sendStatus(500);
  }
}

Add done() and modify query


var fs = require('fs'),
	page = new WebPage(),
	address, output, size;

page.settings.userAgent = 'Portrayal (https://github.com/engagedc/portrayal) 1.0.0';

if (phantom.args.length < 2 || phantom.args.length > 3) {
	console.log('Usage: rasterize.js URL filename');
	phantom.exit();
} else {
	address = phantom.args[0];
	output = phantom.args[1];
	page.viewportSize = { width: 1280, height: 600 };
	page.onConsoleMessage = function(msg) { console.log(msg); };
	page.open(address, function (status) {
		if (status !== 'success') {
			console.log('Unable to load the address!');
			phantom.exit(1);
		} else {
			window.setTimeout(function () {
				page.render(output);
				phantom.exit();
			}, 350);
		}
	});
}

var fs = require('fs'),
	page = new WebPage(),
	address, output, size;

page.settings.userAgent = 'Portrayal (https://github.com/engagedc/portrayal) 1.0.0';
page.settings.javascriptEnabled = false;

if (phantom.args.length < 2 || phantom.args.length > 3) {
	console.log('Usage: rasterize.js URL filename');
	phantom.exit();
} else {
	address = phantom.args[0];
	output = phantom.args[1];
	page.viewportSize = { width: 1400, height: 800 };
	page.onConsoleMessage = function(msg) { console.log(msg); };
	page.open(address, function (status) {
		if (status !== 'success') {
			console.log('Unable to load the address!');
			phantom.exit(1);
		} else {
			window.setTimeout(function () {
				page.render(output);
				phantom.exit();
			}, 350);
		}
	});
}

Make view-port bigger, disable JavaScript.


/**
* Copyright 2016 PT Inovação e Sistemas SA
* Copyright 2016 INESC-ID
* Copyright 2016 QUOBIS NETWORKS SL
* Copyright 2016 FRAUNHOFER-GESELLSCHAFT ZUR FOERDERUNG DER ANGEWANDTEN FORSCHUNG E.V
* Copyright 2016 ORANGE SA
* Copyright 2016 Deutsche Telekom AG
* Copyright 2016 Apizee
* Copyright 2016 TECHNISCHE UNIVERSITAT BERLIN
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/

export const CommunicationStatus = {
  OPEN: 'open',
  PENDING: 'pending',
  CLOSED: 'closed',
  PAUSED: 'paused',
  FAILED: 'failed'
};

let communication = {
  id: '',
  host: '',
  owner: '',
  startingTime: '',
  lastModified: '',
  duration: '',
  communicationStatus: '',
  communicationQuality: '',
  participants: [],
  chatMessage: {}
};

export default communication;

/**
* Copyright 2016 PT Inovação e Sistemas SA
* Copyright 2016 INESC-ID
* Copyright 2016 QUOBIS NETWORKS SL
* Copyright 2016 FRAUNHOFER-GESELLSCHAFT ZUR FOERDERUNG DER ANGEWANDTEN FORSCHUNG E.V
* Copyright 2016 ORANGE SA
* Copyright 2016 Deutsche Telekom AG
* Copyright 2016 Apizee
* Copyright 2016 TECHNISCHE UNIVERSITAT BERLIN
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/

export const CommunicationStatus = {
  OPEN: 'open',
  PENDING: 'pending',
  CLOSED: 'closed',
  PAUSED: 'paused',
  FAILED: 'failed'
};

let communication = {
  id: '',
  name: '',
  host: '',
  owner: '',
  startingTime: '',
  lastModified: '',
  duration: '',
  communicationStatus: '',
  communicationQuality: '',
  participants: [],
  chatMessage: {}
};

export default communication;

Add name to comunication model;


/**
 *  Norwegian translation for bootstrap-datepicker
 **/
;(function($){
  $.fn.datepicker.dates['no'] = {
    days: ['Søndag','Mandag','Tirsdag','Onsdag','Torsdag','Fredag','Lørdag'],
    daysShort: ['Søn','Man','Tir','Ons','Tor','Fre','Lør'],
    daysMin: ['Sø','Ma','Ti','On','To','Fr','Lø'],
    months: ['Januar','Februar','Mars','April','Mai','Juni','Juli','August','September','Oktober','November','Desember'],
    monthsShort: ['Jan','Feb','Mar','Apr','Mai','Jun','Jul','Aug','Sep','Okt','Nov','Des'],
    today: 'I dag',
    clear: 'Nullstill',
    weekStart: 0
  };
}(jQuery));

/**
 *  Norwegian translation for bootstrap-datepicker
 **/
;(function($){
  $.fn.datepicker.dates['no'] = {
    days: ['Søndag','Mandag','Tirsdag','Onsdag','Torsdag','Fredag','Lørdag'],
    daysShort: ['Søn','Man','Tir','Ons','Tor','Fre','Lør'],
    daysMin: ['Sø','Ma','Ti','On','To','Fr','Lø'],
    months: ['Januar','Februar','Mars','April','Mai','Juni','Juli','August','September','Oktober','November','Desember'],
    monthsShort: ['Jan','Feb','Mar','Apr','Mai','Jun','Jul','Aug','Sep','Okt','Nov','Des'],
    today: 'I dag',
    clear: 'Nullstill',
    weekStart: 1,
    format: 'dd.mm.yyyy'
  };
}(jQuery));

Fix weekStart and format for Norwegian


const React = require("react")

const myStyle = {
  "color": "blue"
}

export default React.createClass({
  getInitialState() {
    return {value: 0}
  },

  componentDidMount() {
    const self = this
    tick()

    function tick() {
      const {step, interval} = self.props
      self.setState({
        value: self.state.value + step,
        interval: setTimeout(tick, interval)
      })
    }
  },

  componentWillUnmount() {
    clearTimeout(this.state.interval)
  },

  render() {
    return (

{this.state.value}

    )
  }
})

const React = require("react")

const myStyle = {
  "color": "blue"
}

export default React.createClass({
  getInitialState() {
    return {value: 0}
  },

  componentDidMount() {
    const self = this
    tick()

    function tick() {
      const {step, interval} = self.props
      self.setState({
        value: this.state.value + step,
        interval: setTimeout(tick, interval)
      })
    }
  },

  componentWillUnmount() {
    clearTimeout(this.state.interval)
  },

  render() {
    return (

{this.state.value}

    )
  }
})

Fix broken reference from example


// 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
// 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 jquery.purr
//= require best_in_place
//= require twitter/bootstrap
//= require_tree .

jQuery.ajaxSetup({
  'beforeSend': function(xhr) {xhr.setRequestHeader("Accept", "text/javascript")}
})

jQuery.fn.submitWithAjax = function() {
  this.submit(function() {
    $.post(this.action, $(this).serialize(), null, "script");
    return false;
  })
  return this;
};

$(document).ready(function() {
  $('.new_note').submitWithAjax();
})

$(document).on('ajax:success', '.delete_note', function(e) {
    $(e.currentTarget).closest('div.note').fadeOut();
});

// 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
// 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 jquery.purr
//= require best_in_place
//= require twitter/bootstrap
//= require_tree .

jQuery.ajaxSetup({
  'beforeSend': function(xhr) {xhr.setRequestHeader("Accept", "text/javascript")}
});

jQuery.fn.submitWithAjax = function() {
  this.submit(function() {
    $.post(this.action, $(this).serialize(), null, "script");
    return false;
  })
  return this;
};

$(document).ready(function() {
  $('.new_note').submitWithAjax();
  $(':input:enabled:visible:first').focus();
});

$(document).on('ajax:success', '.delete_note', function(e) {
    $(e.currentTarget).closest('div.note').fadeOut();
});

Set focus to first input element.


import {createReducer} from "common/utils/reducerUtils";

import {DATA_LOADED} from "features/tools/toolConstants";

import schema from "app/schema"

const initialState = schema.getDefaultState();

export function loadData(state, payload) {
    // Create a Redux-ORM session from our entities "tables"
    const session = schema.from(state);
    // Get a reference to the correct version of model classes for this Session
    const {Pilot, MechDesign, Mech} = session;

    const {pilots, designs, mechs} = payload;

    // Queue up creation commands for each entry
    pilots.forEach(pilot => Pilot.parse(pilot));
    designs.forEach(design => MechDesign.parse(design));
    mechs.forEach(mech => Mech.parse(mech));

    // Apply the queued updates and return the updated "tables"
    return session.reduce();
}

export default createReducer(initialState, {
    [DATA_LOADED] : loadData,
});

import {createReducer} from "common/utils/reducerUtils";

import {DATA_LOADED} from "features/tools/toolConstants";

import schema from "app/schema"

const initialState = schema.getDefaultState();

export function loadData(state, payload) {
    // Create a Redux-ORM session from our entities "tables"
    const session = schema.from(state);
    // Get a reference to the correct version of model classes for this Session
    const {Pilot, MechDesign, Mech} = session;

    const {pilots, designs, mechs} = payload;

    // Clear out any existing models from state so that we can avoid
    // conflicts from the new data coming in if data is reloaded
    [Pilot, Mech, MechDesign].forEach(modelType => {
        modelType.all().withModels.forEach(model => model.delete());
        session.state = session.reduce();
    });

    // Queue up creation commands for each entry
    pilots.forEach(pilot => Pilot.parse(pilot));
    designs.forEach(design => MechDesign.parse(design));
    mechs.forEach(mech => Mech.parse(mech));

    // Apply the queued updates and return the updated "tables"
    return session.reduce();
}

export default createReducer(initialState, {
    [DATA_LOADED] : loadData,
});

Clear out existing models on load to avoid conflicts when reloading data


(function () {
  'use strict';

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

  exports = module.exports = function (guido) {
    guido.use(extends);
  };
})();

(function () {
  'use strict';

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

  exports = module.exports = function (guido) {
    guido.use(extend);
  };
})();

Fix in default processors setter module


import { app } from "electron";

export default menus => {
    if (process.platform !== "darwin") {
        menus.unshift({
            label: app.getName(),
            submenu: [
                { role: "about" },
                { type: "separator" },
                { role: "services", submenu: [] },
                { type: "separator" },
                { role: "hide" },
                { role: "hideothers" },
                { role: "unhide" },
                { type: "separator" },
                { role: "quit" }
            ]
        });

        // Edit menu
        menus[1].submenu.push(
            { type: "separator" },
            {
                label: "Speech",
                submenu: [{ role: "startspeaking" }, { role: "stopspeaking" }]
            }
        );

        // Window menu
        menus[3].submenu = [
            { role: "close" },
            { role: "minimize" },
            { role: "zoom" },
            { type: "separator" },
            { role: "front" }
        ];
    }

    return menus;
};

import { app } from "electron";

export default menus => {
    if (process.platform === "darwin") {
        menus.unshift({
            label: app.getName(),
            submenu: [
                { role: "about" },
                { type: "separator" },
                { role: "services", submenu: [] },
                { type: "separator" },
                { role: "hide" },
                { role: "hideothers" },
                { role: "unhide" },
                { type: "separator" },
                { role: "quit" }
            ]
        });

        // Edit menu
        menus[1].submenu.push(
            { type: "separator" },
            {
                label: "Speech",
                submenu: [{ role: "startspeaking" }, { role: "stopspeaking" }]
            }
        );

        // Window menu
        menus[3].submenu = [
            { role: "close" },
            { role: "minimize" },
            { role: "zoom" },
            { type: "separator" },
            { role: "front" }
        ];
    }

    return menus;
};

Fix application menu missing first menu on macOS.


module.exports = {
    exec: function (msg, args) {
        userQuery(args || msg.author.id, msg, true).then(u => {
            bot.createMessage(msg.channel.id, {
                embed: {
                    author: {
                        name: `${u.nick ? u.nick : u.username} (${u.username}#${u.discriminator})'s avatar`,
                        icon_url: bot.user.staticAvatarURL
                    },
                    image: {
                        url: u.avatarURL
                    },
                    description: `[Image not loading?](${u.avatarURL})`
                }
            });
        }).catch(console.error);
    },
    isCmd: true,
    name: "getavatar",
    display: true,
    category: 1,
    description: "Get someone's avatar!"
};

module.exports = {
    exec: function (msg, args) {
        userQuery(args || msg.author.id, msg, true).then(u => {
            bot.createMessage(msg.channel.id, {
                embed: {
                    author: {
                        name: `${u.nick ? u.nick : u.username} (${u.username}#${u.discriminator})'s avatar`,
                        icon_url: bot.user.staticAvatarURL
                    },
                    image: {
                        url: u.avatarURL
                    },
                    description: `[Image not loading?](${u.dynamicAvatarURL("png", 2048)})`
                }
            });
        }).catch(console.error);
    },
    isCmd: true,
    name: "getavatar",
    display: true,
    category: 1,
    description: "Get someone's avatar!"
};

Use PNG for the avatar link


/* global fetch */
module.exports = function(callback) {
  console.log("Fetch tutorials.");
  fetch('/getListOfTutorials')
      .then(function(data) {
        return data.json();
      }).then(function(tutorialPaths) {
        console.log("Obtaining list of tutorials successful: " + tutorialPaths);
        callback(0, tutorialPaths);
      }).catch(function(error) {
        console.log("There was an error obtaining the list of tutorial files: " +
        error);
      });
};

/* global fetch */
module.exports = function(callback) {
  console.log("Fetch tutorials.");
  fetch('/getListOfTutorials', {
    credentials: 'same-origin'
  })
      .then(function(data) {
        return data.json();
      }).then(function(tutorialPaths) {
        console.log("Obtaining list of tutorials successful: " + tutorialPaths);
        callback(0, tutorialPaths);
      }).catch(function(error) {
    console.log("There was an error obtaining the list of " +
        "tutorial files: " + error);
  });
};

Fix error about tutorials for pasword-restricted use


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 = ({ petitions, title, text, linkText }) => (

      <Container>
        <BlockContainer>

            <Heading2 text={title} />
            <Link href='/petitions'>{linkText}</Link>

        </BlockContainer>
        <TeaserGrid petitions={petitions.latest} />
      </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 = ({ petitions, title, text, linkText }) => (

      <Container>
        <BlockContainer>

            <Heading2 text={title} />
            <Link href='/petitions'>{linkText}</Link>

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

);

export default HomepagePetitions;

Change teaser grid to use trending petitions


function createColorTextureFromSteps()
const map = ColorRamp.createColorTexture(this.refs.colorbar, (i) =>
  ColorRamp.map(this.props.steps, i))

function createColorTexture (canvas, colorFunction = () => 'transparent') {
  //  const canvas = document.createElement('canvas')
  // const canvas = ReactDOM.findDOMNode(canvas)
  const context = canvas.getContext('2d')
  canvas.width = 256
  canvas.height = 16

  for (let i = 0; i < canvas.width; i++) {
    context.fillStyle = colorFunction(i)
    context.fillRect(i, 0, 1, canvas.height)
  }

  return context.getImageData(0, 0, canvas.width, canvas.height)
}

function map (steps, i) {
  const offset = i / 255 * (steps.length - 1.0)
  const base = Math.trunc(offset)
  let color = ColorRamp.mix(steps[base], steps[base + 1], offset - base)

  return 'rgba(' + color.values.rgb.toString() + ', 1.0)'
}

Fix failing build: Unexpected token, expected { (3:0)


import Ember from 'ember';
import { namespace } from './consts';

export function debug() {
  Ember.Logger.debug(namespace, ...arguments);
}

import { namespace } from './consts';

export function debug() {
  if (console.debug) {
    console.debug(namespace, ...arguments);
  } else {
    console.log(namespace, ...arguments);
  }
}

Move from Ember.Logger to console


// Copyright (c) Microsoft Open Technologies, Inc.  All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
define([
    './_Global'
    ], function baseCoreUtilsInit(_Global) {
    "use strict";

    var hasWinRT = !!_Global.Windows;

    function markSupportedForProcessing(func) {
        /// <signature helpKeyword="WinJS.Utilities.markSupportedForProcessing">
        /// <summary locid="WinJS.Utilities.markSupportedForProcessing">
        /// Marks a function as being compatible with declarative processing, such as WinJS.UI.processAll
        /// or WinJS.Binding.processAll.
        /// </summary>
        /// <param name="func" type="Function" locid="WinJS.Utilities.markSupportedForProcessing_p:func">
        /// The function to be marked as compatible with declarative processing.
        /// </param>
        /// <returns type="Function" locid="WinJS.Utilities.markSupportedForProcessing_returnValue">
        /// The input function.
        /// </returns>
        /// </signature>
        func.supportedForProcessing = true;
        return func;
    }

    return {
        _hasWinRT: hasWinRT,
        markSupportedForProcessing: markSupportedForProcessing,
        _setImmediate: _Global.setImmediate ? _Global.setImmediate.bind(_Global) : function (handler) {
            setTimeout(handler, 0);
        }
    };
});

// Copyright (c) Microsoft Open Technologies, Inc.  All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
define([
    './_Global'
    ], function baseCoreUtilsInit(_Global) {
    "use strict";

    var hasWinRT = !!_Global.Windows;

    function markSupportedForProcessing(func) {
        /// <signature helpKeyword="WinJS.Utilities.markSupportedForProcessing">
        /// <summary locid="WinJS.Utilities.markSupportedForProcessing">
        /// Marks a function as being compatible with declarative processing, such as WinJS.UI.processAll
        /// or WinJS.Binding.processAll.
        /// </summary>
        /// <param name="func" type="Function" locid="WinJS.Utilities.markSupportedForProcessing_p:func">
        /// The function to be marked as compatible with declarative processing.
        /// </param>
        /// <returns type="Function" locid="WinJS.Utilities.markSupportedForProcessing_returnValue">
        /// The input function.
        /// </returns>
        /// </signature>
        func.supportedForProcessing = true;
        return func;
    }

    return {
        hasWinRT: hasWinRT,
        markSupportedForProcessing: markSupportedForProcessing,
        _setImmediate: _Global.setImmediate ? _Global.setImmediate.bind(_Global) : function (handler) {
            setTimeout(handler, 0);
        }
    };
});

Fix small typo with hasWinRT.


(function() {

    var app = angular.module('bbUniqueModule', []);

    app.directive('bbUnique', ['$q',function($q){
       //allows us to cache template before we run link

        return {
            restrict: 'A',
            require: 'ngModel',
            link: function(scope,elem,attrs,ngModel){

            }

            }
    }]);

}());

(function() {

    var app = angular.module('bbUniqueModule', []);

    //assuming you have a data service you're passing in
    app.directive('bbUnique', ['$q', 'dataService' ,function($q, dataService){
        return {
            restrict: 'A',
            require: 'ngModel',
            link: function(scope,elem,attrs,ngModel){

                ngModel.$asyncValidators.unique = function(modelValue,viewValue){
                var deffered = $q.defer(),
                    currentValue = modelValue || viewValue,//if model value is not there, default to view
                    key = attrs.bbUniqueKey,
                    property = attrs.bbUniqueProperty;

                    if(key && property){
                        dataService.checkUniqueValue(key,property,currentValue)
                            .then(function(unique){
                                if(unique){
                                    deferred.resolve(); //got a unique val
                                }
                                else{
                                    deferred.reject();
                                }
                            });

                    return deferred.promise;

                    }
                    else{
                        return $q.when(true);
                    }

                }

            }
        }
    }]);

}());

Add ng model async validator unique function


var DB_USERS = require(process.env.APP_DB_USERS),

    passport = require("passport"),
    LocalStrategy = require('passport-local').Strategy;

module.exports = function() {
    passport.use('local-login', new LocalStrategy({
            usernameField: 'email',
            passwordField: 'password',
            passReqToCallback: true
        },
        function(req, email, password, done) {
            DB_USERS.findOne({
                    email: email.toLowerCase()
                })
                .then(function(user) {
                    if (user) {
                        if (user.compare_password(password)) {
                            done(null, user);
                        } else {
                            done("Username or password is incorrect");
                        }
                    } else {
                        done("Username or password is incorrect");
                    }
                })
                .catch(function(err) {
                    done("Unknown error");
                    console.trace(err);
                });
        }));

}

var DB_USERS = require(process.env.APP_DB_USERS),

    passport = require("passport"),
    LocalStrategy = require('passport-local').Strategy;

module.exports = function() {
    passport.use('local-login', new LocalStrategy({
            usernameField: 'email',
            passwordField: 'password',
            passReqToCallback: true
        },
        function(req, email, password, done) {
            DB_USERS.findOne({
                email: email.toLowerCase()
            }, function(err, user) {
                if (err) {
                    done(err);
                } else {
                    if (user) {
                        if (user.compare_password(password)) {
                            done(null, user);
                        } else {
                            done("Username or password is incorrect");
                        }
                    } else {
                        done("Username or password is incorrect");
                    }
                }
            });

        }));

}

Fix for login, we can now validate our password and username


import getBindingKey from './_getbindingkey';
import bindNode from '../../bindnode';
import lookForBinder from '../../lookforbinder';

// a binder for instance of Attr
const attributeBinder = {
    setValue(value) {
        this.value = value;
    }
};

// adds binding for an attribute
// its logic is much harder than for text node
// check out imported modules for more info
export default function processAttribute({
    node,
    attribute,
    object,
    eventOptions
}) {
    const { name, value } = attribute;
    const { type } = node;
    // get a key which will be actually bound to an attribute
    // getBindingKey analyzes given value, creates computable property and returns its key
    const key = getBindingKey({
        object,
        text: value
    });
    const probablyValueInput = name === 'value' && type !== 'checkbox' && type !== 'radio';
    const probablyCheckableInput = name === 'checked' && (type === 'checkbox' || type === 'radio');

    let defaultBinder;

    if (probablyValueInput || probablyCheckableInput) {
        defaultBinder = lookForBinder(node);
    }

    if (defaultBinder) {
        // if deault binder is found then this is default HTML5 form element
        // remove the attribute and use found binder
        node.setAttribute(name, '');
        bindNode(object, key, node, defaultBinder, eventOptions);
    } else {
        // simply bind an attribute
        bindNode(object, key, attribute, attributeBinder, eventOptions);
    }
}

import getBindingKey from './_getbindingkey';
import bindNode from '../../bindnode';
import lookForBinder from '../../lookforbinder';

// a binder for instance of Attr
const attributeBinder = {
    setValue(value) {
        this.value = value;
    }
};

// adds binding for an attribute
// its logic is much harder than for text node
// check out imported modules for more info
export default function processAttribute({
    node,
    attribute,
    object,
    eventOptions
}) {
    const { name, value } = attribute;
    const { type } = node;
    // get a key which will be actually bound to an attribute
    // getBindingKey analyzes given value, creates computable property and returns its key
    const key = getBindingKey({
        object,
        text: value
    });
    const probablyValueInput = name === 'value' && type !== 'checkbox' && type !== 'radio';
    const probablyCheckableInput = name === 'checked' && (type === 'checkbox' || type === 'radio');

    let defaultBinder;

    if (probablyValueInput || probablyCheckableInput) {
        defaultBinder = lookForBinder(node);
    }

    if (defaultBinder) {
        // if deault binder is found then this is default HTML5 form element
        // remove the attribute and use found binder
        node.removeAttribute(name);
        bindNode(object, key, node, defaultBinder, eventOptions);
    } else {
        // simply bind an attribute
        bindNode(object, key, attribute, attributeBinder, eventOptions);
    }
}

Remove attribute instead of raise it when bindings parser is used on HTML5 form elements


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

module.exports = {
  // or devtool: 'eval' to debug issues with compiled output:
  devtool: 'cheap-module-eval-source-map',
  entry: [
    // necessary for hot reloading with IE:
    'eventsource-polyfill',
    // listen to code updates emitted by hot middleware:
    'webpack-hot-middleware/client',
    // your code:
    './src/index'
  ],
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'bundle.js',
    publicPath: '/dist/'
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin()
  ],
  module: {
    loaders: [{
      test: /\.js$/,
      loaders: ['babel'],
      include: path.join(__dirname, 'src')
    }]
  }
};

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

module.exports = {
  // or devtool: 'eval' to debug issues with compiled output:
  devtool: 'cheap-module-eval-source-map',
  entry: [
    // necessary for hot reloading with IE:
    'eventsource-polyfill',
    // listen to code updates emitted by hot middleware:
    'webpack-hot-middleware/client',
    // your code:
    './src/index'
  ],
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'bundle.js',
    publicPath: '/dist/'
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin()
  ],
  module: {
    loaders: [{
      test: /\.js$/,
      loaders: ['babel'],
      include: path.join(__dirname, 'src')
    }]
  }
};

Revert "Remove NoErrorsPlugin until webpack-hot-middleware learns to display syntax error overlay after reload as well"


import BottomNavigation from '@material-ui/core/BottomNavigation';
import BottomNavigationAction from '@material-ui/core/BottomNavigationAction';

import React, { Component } from 'react';

import './index.css'
import kitwareLogo from './Kitware_Full_Logo.png';
import strobeLogo from './strobe.png';
import berkeleyLogo from './berkeley.jpg';

const style = {
  height: '5rem'
}

export default class Footer extends Component {
  render = () => {
    return (
      <BottomNavigation style={style}>
        <BottomNavigationAction
          icon={}
          onClick={() => this.select(0)} href="http://www.kitware.com"
          target="_blank"
        />
        <BottomNavigationAction
          icon={}
          onClick={() => this.select(0)} href="http://strobe.colorado.edu/"
          target="_blank"
        />
        <BottomNavigationAction
          icon={}
          onClick={() => this.select(0)} href="http://www.lbl.gov/"
          target="_blank"
        />
      </BottomNavigation>
    );
  }
}

import BottomNavigation from '@material-ui/core/BottomNavigation';
import BottomNavigationAction from '@material-ui/core/BottomNavigationAction';

import React, { Component } from 'react';

import './index.css'
import uclaLogo from './ucla.png';
import kitwareLogo from './Kitware_Full_Logo.png';
import strobeLogo from './strobe.png';
import berkeleyLogo from './berkeley.jpg';

const style = {
  height: '5rem'
}

export default class Footer extends Component {
  render = () => {
    return (
      <BottomNavigation style={style}>
        <BottomNavigationAction
          icon={}
          onClick={() => this.select(0)} href="http://www.ucla.edu/"
          target="_blank"
        />
        <BottomNavigationAction
          icon={}
          onClick={() => this.select(0)} href="http://www.kitware.com"
          target="_blank"
        />
        <BottomNavigationAction
          icon={}
          onClick={() => this.select(0)} href="http://strobe.colorado.edu/"
          target="_blank"
        />
        <BottomNavigationAction
          icon={}
          onClick={() => this.select(0)} href="http://www.lbl.gov/"
          target="_blank"
        />
      </BottomNavigation>
    );
  }
}

Add the UCLA icon back


/* jshint node: true */

module.exports = function(environment) {
  var ENV = {
    modulePrefix: 'dummy',
    environment: environment,
    baseURL: '/',
    locationType: 'auto',
    EmberENV: {
      FEATURES: {
        // Here you can enable experimental features on an ember canary build
        // e.g. 'with-controller': true
      }
    },

    APP: {
      // Here you can pass flags/options to your application instance
      // when it is created
    }
  };

  if (environment === 'development') {
    // ENV.APP.LOG_RESOLVER = true;
    // ENV.APP.LOG_ACTIVE_GENERATION = true;
    // ENV.APP.LOG_TRANSITIONS = true;
    // ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
    // ENV.APP.LOG_VIEW_LOOKUPS = true;
  }

  if (environment === 'test') {
    // Testem prefers this...
    ENV.baseURL = '/';
    ENV.locationType = 'none';

    // keep test console output quieter
    ENV.APP.LOG_ACTIVE_GENERATION = false;
    ENV.APP.LOG_VIEW_LOOKUPS = false;

    ENV.APP.rootElement = '#ember-testing';
  }

  if (environment === 'production') {

  }

  return ENV;
};

/* jshint node: true */

module.exports = function(environment) {
  var ENV = {
    modulePrefix: 'dummy',
    environment: environment,
    locationType: 'auto',
    EmberENV: {
      FEATURES: {
        // Here you can enable experimental features on an ember canary build
        // e.g. 'with-controller': true
      }
    },

    APP: {
      // Here you can pass flags/options to your application instance
      // when it is created
    }
  };

  if (environment === 'development') {
    // ENV.APP.LOG_RESOLVER = true;
    // ENV.APP.LOG_ACTIVE_GENERATION = true;
    // ENV.APP.LOG_TRANSITIONS = true;
    // ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
    // ENV.APP.LOG_VIEW_LOOKUPS = true;
  }

  if (environment === 'test') {
    // Testem prefers this...
    ENV.baseURL = '/';
    ENV.locationType = 'none';

    // keep test console output quieter
    ENV.APP.LOG_ACTIVE_GENERATION = false;
    ENV.APP.LOG_VIEW_LOOKUPS = false;

    ENV.APP.rootElement = '#ember-testing';
  }

  if (environment === 'production') {

  }

  return ENV;
};

Make sure base tag is not added


'use strict';

const should = require('should');

const refResolver = require('./../../lib/ref_resolver');

const schema = require('./schema.json');

describe('RefResolver', () => {

  it('should resolve refs in schema synchronous', () => {
    const resolvedSchema = refResolver(schema);

    should(resolvedSchema.string)
      .eql('string');
  });

});

'use strict';

const should = require('should');

const refResolver = require('./../../lib/ref_resolver');

const schema = require('./schema.json');
const resolvedSchema = require('./resolvedSchema.json');

describe('RefResolver', () => {

  it('should resolve refs in schema synchronous', () => {
    const resolved = refResolver(schema);

    should(resolved.string)
      .eql('string');
  });

  it('should work with already resolved schema', () => {
    const resolved = refResolver(resolvedSchema);

    should(resolved)
      .deepEqual(resolvedSchema);
  });

});

Add test for already resolved schema


// Always provide req.data.global, a virtual page
// for sitewide content such as a footer displayed on all pages

module.exports = {
  construct: function(self, options) {
    self.pageServe = function(req, callback) {
      return self.apos.docs.find(req, { slug: 'global' })
        .permission(false).toObject(function(err, doc) {
        if (err) {
          return callback(err);
        }
        req.data.global = doc ? doc : {
          slug: 'global',
          _edit: true
        };
        return callback(null);
      });
    };
  }
}

// Always provide req.data.global, a virtual page
// for sitewide content such as a footer displayed on all pages

var async = require('async');

module.exports = {
  afterConstruct: function(self) {
    self.enableMiddleware();
  },

  construct: function(self, options) {

    self.slug = options.slug || 'global';

    self.findGlobal = function(req, callback) {
      return self.apos.docs.find(req, { slug: self.slug })
        .permission(false)
        .toObject(callback);
    };

    self.modulesReady = function(callback) {
      self.initGlobal(callback);
    };

    self.initGlobal = function(callback) {
      var req = self.apos.tasks.getReq();
      var existing;
      return async.series({
        fetch: function(callback) {
          return self.findGlobal(req, function(err, result) {
            if (err) {
              return callback(err);
            }
            existing = result;
            return callback();
          });
        },
        insert: function(callback) {
          if (existing) {
            return setImmediate(callback);
          }
          return self.apos.docs.insert(req, { slug: 'global', published: true }, callback);
        }
      }, callback)
    };

    self.enableMiddleware = function(){
      self.apos.app.use(self.addGlobalToData);
    };

    self.addGlobalToData = function(req, res, next) {
      return self.findGlobal(req, function(err, result) {
        if (err) {
          return next(err);
        }
        req.data.global = result;
        return next();
      });
    };
  }
}

Implement apostrophe-global module properly, as opposed to parked page workaround.


getBestName = function(result){
    var bestName = "";
    if(typeof result["scientificName"] !== "undefined"){
        bestName = result["scientificName"];
    }
    if(typeof result["vernacularNames"] !== "undefined" && result["vernacularNames"].length > 0){
        var preferred = false;
        result["vernacularNames"].forEach(function(value){
            if(value.language === "en"){
                if(typeof value["eol_preferred"] !== "undefined" && value["eol_preferred"]){
                    preferred = true;
                    bestName = value.vernacularName;
                }
                else if(!preferred){
                    bestName = value.vernacularName;
                }
            }
        });
    }
    return bestName;
};

/**
 * Selects the best vernacularName from the object returned by the eol pages API.
 * It only considers english names (language: en) and preferes those with eol_preferred: true.
 * The scientificName is used as fallback.
 * @param eolObject {Object} object returned by the eol pages API
 * @returns {String} bestName
 */
getBestName = function(eolObject){
    var bestName = "";
    if(typeof eolObject["scientificName"] !== "undefined"){
        bestName = eolObject["scientificName"];
    }
    if(typeof eolObject["vernacularNames"] !== "undefined" && eolObject["vernacularNames"].length > 0){
        var preferred = false;
        eolObject["vernacularNames"].forEach(function(value){
            if(value.language === "en"){
                if(typeof value["eol_preferred"] !== "undefined" && value["eol_preferred"]){
                    preferred = true;
                    bestName = value.vernacularName;
                }
                else if(!preferred){
                    bestName = value.vernacularName;
                }
            }
        });
    }
    return bestName;
};

Add documentation for getBestName function


import { expect } from '../spec_helper'
import { AUTHENTICATION, PROFILE } from '../../src/constants/action_types'
import { pushSubscriptionSaga } from '../../src/sagas/push_subscription'
import { isLoggedInSelector } from '../../src/sagas/selectors'
import {
  registerForGCM,
  requestPushSubscription,
} from '../../src/actions/profile'

describe.only('push subscription saga', function () {
  const regId = 'my awesome registration id'

  describe('the saga itself', function () {
    it('registers for GCM when logged in', function () {
      const pushAction = requestPushSubscription(regId)
      const pushHandler = pushSubscriptionSaga()
      expect(pushHandler).to.take(PROFILE.REQUEST_PUSH_SUBSCRIPTION)
      expect(pushHandler.next(pushAction)).to.select(isLoggedInSelector)
      expect(pushHandler.next(true)).to.put(registerForGCM(regId))
    })

    it('defers registration for GCM until logged in', function () {
      const pushAction = requestPushSubscription(regId)
      const pushHandler = pushSubscriptionSaga()
      expect(pushHandler).to.take(PROFILE.REQUEST_PUSH_SUBSCRIPTION)
      expect(pushHandler.next(pushAction)).to.select(isLoggedInSelector)
      expect(pushHandler.next(false)).to.take(AUTHENTICATION.USER_SUCCESS)
      expect(pushHandler).to.put(registerForGCM(regId))
    })
  })
})

import { expect } from '../spec_helper'
import { AUTHENTICATION, PROFILE } from '../../src/constants/action_types'
import pushSubscriptionSaga from '../../src/sagas/push_subscription'
import { isLoggedInSelector } from '../../src/sagas/selectors'
import {
  registerForGCM,
  requestPushSubscription,
} from '../../src/actions/profile'

describe('push subscription saga', function () {
  const regId = 'my awesome registration id'

  describe('the saga itself', function () {
    it('registers for GCM when logged in', function () {
      const pushAction = requestPushSubscription(regId)
      const pushHandler = pushSubscriptionSaga()
      expect(pushHandler).to.take(PROFILE.REQUEST_PUSH_SUBSCRIPTION)
      expect(pushHandler.next(pushAction)).to.select(isLoggedInSelector)
      expect(pushHandler.next(true)).to.put(registerForGCM(regId))
    })

    it('defers registration for GCM until logged in', function () {
      const pushAction = requestPushSubscription(regId)
      const pushHandler = pushSubscriptionSaga()
      expect(pushHandler).to.take(PROFILE.REQUEST_PUSH_SUBSCRIPTION)
      expect(pushHandler.next(pushAction)).to.select(isLoggedInSelector)
      expect(pushHandler.next(false)).to.take(AUTHENTICATION.USER_SUCCESS)
      expect(pushHandler).to.put(registerForGCM(regId))
    })
  })
})

Fix the broken test and remove only.


/**
 * Copyright: (c) 2017 Max Klein
 * License: MIT
 */

define([
	'sim/Component',
	'lib/extend'
], function (Component, extend) {
	function NotComponent() {
		Component.call(this, arguments);

		this.in = [false];
		this.out = [false];
	}

	extend(NotComponent, Component);

	NotComponent.prototype.exec = function () {
		this.out[0] = !this.in[0];
	};

	return NotComponent;
});

/**
 * Copyright: (c) 2017 Max Klein
 * License: MIT
 */

define([
	'sim/Component',
	'lib/extend'
], function (Component, extend) {
	function NotComponent() {
		Component.call(this, arguments);

		this.in = [false];
		this.out = [true];
	}

	extend(NotComponent, Component);

	NotComponent.prototype.exec = function () {
		this.out[0] = !this.in[0];
	};

	return NotComponent;
});

Fix initial output of NOT component


(function(){
  "use strict";
  var POLLING_INTERVAL = 3 * 1000;
  var POLLING_URL = "/polling/alerts";

  $(function(){
    var alert = new Vue({
      el: "#alert",
      data: {
        "alerts": []
      },

      created: function(){
        var self = this;
        var fetch = function(){
          self.fetchAlertsData().then(function(alerts){
            self.alerts = alerts;
          });
        };
        fetch();
        setInterval(fetch, POLLING_INTERVAL);
      },

      computed: {
        alertsCount: {
          $get: function(){ return this.alerts.length; }
        },
        hasAlerts: {
          $get: function(){ return this.alertsCount > 0; }
        }
      },

      methods: {
        fetchAlertsData: function() {
          return new Promise(function(resolve, reject) {
            $.getJSON(POLLING_URL, resolve).fail(reject);
          });
        }
      }
    });
  });
})();

(function(){
  "use strict";
  var POLLING_INTERVAL = 3 * 1000;
  var POLLING_URL = "/polling/alerts";

  $(function(){
    var alert = new Vue({
      el: "#alert",
      data: {
        "alerts": []
      },

      created: function(){
        var self = this;
        var fetch = function(){
          self.fetchAlertsData().then(function(alerts){
            self.alerts = alerts;
          })["catch"](function(xhr){
            if(xhr.status === 401) {
              clearInterval(timer);
            }
          });
        };
        fetch();
        var timer = setInterval(fetch, POLLING_INTERVAL);
      },

      computed: {
        alertsCount: {
          $get: function(){ return this.alerts.length; }
        },
        hasAlerts: {
          $get: function(){ return this.alertsCount > 0; }
        }
      },

      methods: {
        fetchAlertsData: function() {
          return new Promise(function(resolve, reject) {
            $.getJSON(POLLING_URL, resolve).fail(reject);
          });
        }
      }
    });
  });
})();

Stop polling if receive 401 response


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

module.exports = function findGroupedPlatonThreadUrlElements() {

    var groupedElements = {};

    function addThreadUrlAndElement(threadUrl, elem) {
        if (!(threadUrl in groupedElements)) {
            groupedElements[threadUrl] = [elem];
        } else if (groupedElements[threadUrl].indexOf(threadUrl) < 0) {
            groupedElements[threadUrl].push(elem);
        }
    }

    var linksToCommentThreads = document.querySelectorAll('a[href$="#platon-comment-thread"]');

    linksToCommentThreads.forEach(function (linkElem) {
        addThreadUrlAndElement(linkElem.pathname, linkElem);
    });

    var elemsWithThreadIds = document.querySelectorAll('*[data-platon-thread-url]');

    elemsWithThreadIds.forEach(function (elem) {
        addThreadUrlAndElement(elem.getAttribute('data-platon-thread-url'), elem);
    });

    return groupedElements;
};

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

module.exports = function findGroupedPlatonThreadUrlElements() {

    var groupedElements = {};

    function addThreadUrlAndElement(threadUrl, elem) {
        if (!(threadUrl in groupedElements)) {
            groupedElements[threadUrl] = [elem];
        } else if (groupedElements[threadUrl].indexOf(threadUrl) < 0) {
            groupedElements[threadUrl].push(elem);
        }
    }

    var linksToCommentThreads = document.querySelectorAll('a[href$="#platon-comment-thread"]');

    Array.prototype.forEach.call(linksToCommentThreads, function (linkElem) {
        addThreadUrlAndElement(linkElem.pathname, linkElem);
    });

    var elemsWithThreadIds = document.querySelectorAll('*[data-platon-thread-url]');

    Array.prototype.forEach.call(elemsWithThreadIds, function (elem) {
        addThreadUrlAndElement(elem.getAttribute('data-platon-thread-url'), elem);
    });

    return groupedElements;
};

Fix iterarting over NodeList in Firefox


function handleApiError(response) {

  if(!response || response.status === 200)
    return;

  if(response.status === 403 || response.status === 401) {
    logout();
  }
  else {
    console.log('An error has occurred, please contact an administrator. ' + response.responseText);
    alert(response.responseText);
  }
}

function handleApiError(response) {

  if(!response || response.status === 200)
    return;

  if(response.status === 403 || response.status === 401) {
    logout();
  }
  else {
    console.log('An error has occurred, please contact an administrator. ' + response.responseText);
    alert('An error has occurred, please contact an administrator. ' + response.responseText);
  }
}

Add friendly error in alert box when an error occurs.


var assert = require('assert');
var seeds = require('../seeds');
var Feature = require('../feature');

describe('Feature', function () {
  before(function (done) {
    seeds(done);
  });

  describe('schema', function () {
    it('successfully creates a valid document');
    it('fails at creating an invalid document');
  });

  describe('.search()', function () {
    it('performs an empty search, returning all commands', function (done) {
      Feature.search('', function (docs) {
        assert.equal(7, docs.length);
        done();
      });
    });

    it('performs a case-insensitive search for a command', function (done) {
      Feature.search('git ADD', function (docs) {
        assert.equal(1, docs.length)
        done();
      });
    });

    it('performs a search for a command that does not exist', function (done) {
      Feature.search('git yolo', function (docs) {
        assert.equal(0, docs.length);
        done();
      });
    });
  });
});

var assert = require('assert');
var seeds = require('../seeds');
var Feature = require('../feature');

describe('Feature', function () {
  before(function (done) {
    seeds(done);
  });

  describe('schema', function () {
    it('successfully creates a valid document');
    it('fails at creating an invalid document');
  });

  describe('.search()', function () {
    it('performs an empty search, returning all commands', function (done) {
      Feature.search('', function (docs) {
        assert.equal(7, docs.length);
        done();
      });
    });

    it('performs a case-insensitive search for a command', function (done) {
      Feature.search('git ADD', function (docs) {
        assert.equal(1, docs.length);

        var doc = docs[0];
        assert.equal('add files', doc.name);
        assert.deepEqual({
          Git: 'git add',
          Mercurial: 'hg add',
          Subversion: 'svn add'
        }, doc.examples);

        done();
      });
    });

    it('performs a search for a command that does not exist', function (done) {
      Feature.search('git yolo', function (docs) {
        assert.equal(0, docs.length);
        done();
      });
    });
  });
});

Improve the specs for Feature.search()


let UserRecipes = require(`${__dirname}/../schemas.js`).UserRecipes;

module.exports = {
  signup: (req, res) => {

  },

  login: (req, res) => {

  },

  logout: (req, res) => {

  },

  //Gets all the recipes that belong to that user
  getProfile: (req, res) => {
    //find user
    //userId where username: req.params.username
  //   .then((userId) => {
  //     //find recipes by user
  //     return UserRecipes.findOne({userId: userId});
  //   }).then(result => {
  //     res.status(200).send(result);
  //   }).catch(error => {
  //     res.status(404).send(error);
  //   });
  }
};

let UserRecipes = require(`${__dirname}/../schemas.js`).UserRecipes;

module.exports = {
  signup: (req, res) => {

  },

  login: (req, res) => {

  },

  logout: (req, res) => {

  },

  //Gets all the recipes that belong to that user
  getProfile: (req, res) => {
    UserRecipes.findOne({
      username: req.params.username
    }).then(result => {
      res.status(200).send(result);
    }).catch(error => {
      res.status(404).send(error);
    });
  }
};

Update getProfile to utilize username rather than userId


/**
 * Basic engine support.
 */

require('../lib/setModuleDefaults');

var config = require('config');
var engineConfig = config.engines || {};

/**
 * Return a function that creates a plugin:
 */

module.exports = function(language){
  return {
    attach: function (/* options */){
      var languageConfig = engineConfig[language] || {};

      /**
       * If there is a specified engine for this language then use it,
       * otherwise just use the provided name:
       */

      this.engine = require(languageConfig.module || language);
      this.languageConfig = languageConfig;

      /**
       * Add key methods:
       */

      var renderFileMethodName = languageConfig.renderFileMethodName
        || 'renderFile';

      this.__express = this.engine.__express || undefined;
      this.renderFile = this.engine[renderFileMethodName] || undefined;
      this.render = this.engine.render || undefined;
    }
  , name: 'adapter-' + language
  };
};

/**
 * Basic engine support.
 */

require('../lib/setModuleDefaults');

var config = require('config');
var engineConfig = config.engines || {};

/**
 * Return a function that creates a plugin:
 */

module.exports = function(language){
  return {
    attach: function (/* options */){
      var languageConfig = engineConfig[language] || {};

      /**
       * If there is a specified engine for this language then use it,
       * otherwise just use the provided name:
       */

      var engine = require(languageConfig.module || language);

      this.engine = (languageConfig.useConstructor) ? new engine() : engine;
      this.languageConfig = languageConfig;

      /**
       * Add key methods:
       */

      var renderFileMethodName = languageConfig.renderFileMethodName
        || 'renderFile';

      this.__express = this.engine.__express || undefined;
      this.renderFile = this.engine[renderFileMethodName] || undefined;
      this.render = this.engine.render || undefined;
    }
  , name: 'adapter-' + language
  };
};

Allow an engine to be initialised using a constructor.


// @flow

import { actionCreatorName, actionCreatorRequestName, actionCreatorSuccessName, actionCreatorFailedName } from './utils'
import type { Schema } from './types'

export const generateActionCreator = (name: string, type: string) =>
`
export const ${actionCreatorName(name)} = (payload: ${type}) => ({
  meta: {
    delivery: 'grpc',
    successType: '${actionCreatorSuccessName(name)}',
    failureType: '${actionCreatorFailedName(name)}'
  },
  type: '${actionCreatorRequestName(name)}',
  payload
})
`.trim()

export const generateFileHeader = () => `// @flow`.trim()

export default (schema: Schema) => {
  const fileOutput = []
  const output = []
  const types = []
  schema.services.forEach((service) => {
    service.methods.forEach((method) => {
      types.push(method.input_type)
      output.push(generateActionCreator(method.name, method.input_type))
    }, this)
  }, this)

  fileOutput.push(generateFileHeader())
  fileOutput.push(`import { ${types.join(', ')} } from './SpiriGrpcServiceBridgeModule-flow-types'`)

  return fileOutput.concat(output).join('\n\n')
}

// @flow

import { actionCreatorName, actionCreatorRequestName, actionCreatorSuccessName, actionCreatorFailedName } from './utils'
import type { Schema } from './types'

export const generateActionCreator = (name: string, type: string) =>
`
export const ${actionCreatorName(name)} = (payload: ${type}) => ({
  meta: {
    delivery: 'grpc',
    successType: '${actionCreatorSuccessName(name)}',
    failureType: '${actionCreatorFailedName(name)}'
  },
  type: '${actionCreatorRequestName(name)}',
  payload
})
`.trim()

export const generateFileHeader = () => `// @flow`.trim()

export default (schema: Schema) => {
  const fileOutput = []
  const output = []
  const types = []
  schema.services.forEach((service) => {
    service.methods.forEach((method) => {
      if (!types.some(type => type === method.input_type)) {
        types.push(method.input_type)
      }

      output.push(generateActionCreator(method.name, method.input_type))
    }, this)
  }, this)

  fileOutput.push(generateFileHeader())
  fileOutput.push(`import type { ${types.join(', ')} } from './SpiriGrpcServiceBridgeModule-flow-types'`)

  return fileOutput.concat(output).join('\n\n')
}

Fix multiple import of the same type


import React, {PropTypes} from 'react'

const ENTER = 13
const ESCAPE = 27
const RawQueryEditor = React.createClass({
  propTypes: {
    query: PropTypes.shape({
      rawText: PropTypes.string.isRequired,
      id: PropTypes.string.isRequired,
    }).isRequired,
    onUpdate: PropTypes.func.isRequired,
  },

  getInitialState() {
    return {
      value: this.props.query.rawText,
    }
  },

  componentWillReceiveProps(nextProps) {
    if (nextProps.query.rawText !== this.props.query.rawText) {
      this.setState({value: nextProps.query.rawText})
    }
  },

  handleKeyDown(e) {
    e.preventDefault()

    if (e.keyCode === ENTER) {
      this.handleUpdate();
    } else if (e.keyCode === ESCAPE) {
      this.setState({value: this.props.query.rawText}, () => {
        this.editor.blur()
      })
    }
  },

  handleChange() {
    this.setState({
      value: this.editor.value,
    })
  },

  handleUpdate() {
    this.props.onUpdate(this.state.value)
  },

  render() {
    const {value} = this.state

    return (
      <div className="raw-text">
        <textarea
          className="raw-text--field"
          onChange={this.handleChange}
          onKeyDown={this.handleKeyDown}
          onBlur={this.handleUpdate}
          ref={(editor) => this.editor = editor}
          value={value}
          placeholder="Blank query"
        />
      </div>
    )
  },
})

export default RawQueryEditor

import React, {PropTypes} from 'react'

const ENTER = 13
const ESCAPE = 27
const RawQueryEditor = React.createClass({
  propTypes: {
    query: PropTypes.shape({
      rawText: PropTypes.string.isRequired,
      id: PropTypes.string.isRequired,
    }).isRequired,
    onUpdate: PropTypes.func.isRequired,
  },

  getInitialState() {
    return {
      value: this.props.query.rawText,
    }
  },

  componentWillReceiveProps(nextProps) {
    if (nextProps.query.rawText !== this.props.query.rawText) {
      this.setState({value: nextProps.query.rawText})
    }
  },

  handleKeyDown(e) {
    if (e.keyCode === ENTER) {
      e.preventDefault()
      this.handleUpdate();
    } else if (e.keyCode === ESCAPE) {
      this.setState({value: this.props.query.rawText}, () => {
        this.editor.blur()
      })
    }
  },

  handleChange() {
    this.setState({
      value: this.editor.value,
    })
  },

  handleUpdate() {
    this.props.onUpdate(this.state.value)
  },

  render() {
    const {value} = this.state

    return (
      <div className="raw-text">
        <textarea
          className="raw-text--field"
          onChange={this.handleChange}
          onKeyDown={this.handleKeyDown}
          onBlur={this.handleUpdate}
          ref={(editor) => this.editor = editor}
          value={value}
          placeholder="Blank query"
        />
      </div>
    )
  },
})

export default RawQueryEditor

Allow user to type :)


$(document).ready(function(){

  // var turbolinks = require('turbolinks')

  $("#walk-me-home").on("click", function(e){
    e.preventDefault();

    $.ajax({
      url: "http://172.16.50.232:8080/start",
      crossDomain : true,
    })
    .done(function(serverResponse){
      $("#walk-me-home").hide();
      $("#home-safely").show();
      console.log(serverResponse + "This is the server response");
    })
  // need to add failure response
  })

  $("#home-safely").on("click", function(e){
    console.log("I'm home!!")
    e.preventDefault();
    $.ajax({
      url: "http://172.16.50.232:8080/end",
      crossDomain : true,
    })
    .done(function(serverResponse){
      $("#home-safely").hide();
      $("#show-events").show();
      console.log(serverResponse + "This is the server response");

    })
  })

});

$(document).ready(function(){

  // var turbolinks = require('turbolinks')

  $("#walk-me-home").on("click", function(e){
    e.preventDefault();

    $.ajax({
         url: "/sms/text_friend"
       })
       .done(function(serverResponse){
         console.log(serverResponse + " This is the server response");
       })
       .fail(function(serverResponse){
         console.log("Request failed");
       })
    $.ajax({
      url: "http://172.16.50.232:8080/start",
      crossDomain : true,
    })
    .done(function(serverResponse){
      $("#walk-me-home").hide();
      $("#home-safely").show();
      console.log(serverResponse + "This is the server response");
    })
  // need to add failure response
  })

  $("#home-safely").on("click", function(e){
    console.log("I'm home!!")
    e.preventDefault();
    $.ajax({
      url: "http://172.16.50.232:8080/end",
      crossDomain : true,
    })
    .done(function(serverResponse){
      $("#home-safely").hide();
      $("#show-events").show();
      console.log(serverResponse + "This is the server response");

    })
  })

});

Add ajax call for sms script


function renderWaiting(element) {
	document.getElementById(element).innerHTML = ""
}

function unrenderWaiting() {
	$(".icon-spinner").hide()
}

function renderError(element, error, detail) {
	error_alert = "
"+error
	if (detail) { error_alert += "
"+detail }
	error_alert += "
"
	document.getElementById(element).innerHTML = error_alert
}

function metricURL(base, start, stop, step){
	return base+"&start="+start+"&stop="+stop+"&step="+step
}

function stopButtonClick() {
        stopUpdates()
	$("#autoplay_stop").hide()
	$("#autoplay_play").show()
}
function stopAll() {
        stopUpdates()
	stopButtonClick()
	unrenderWaiting()
}

Call all stopping points on error: stop updates, stop button change, and remove 'waiting' button


module.exports = {
    parser: "@typescript-eslint/parser",
    parserOptions: {
        ecmaVersion: 2020,
        sourceType: "module",
        ecmaFeatures: {
            jsx: true
        }
    },
    settings: {
        react: {
            version: "detect"
        }
    },
    extends: [
        "plugin:react/recommended",
        "plugin:react-hooks/recommended",
        "plugin:@typescript-eslint/recommended",
    ],
    rules: {
        "@typescript-eslint/array-type": [
            "warn",
            {
                "default": "array"
            }
        ],
        "@typescript-eslint/semi": [ "warn", "always" ],
        "@typescript-eslint/no-inferrable-types": 0,
        "max-len": [
            "warn",
            {
                "code": 120
            }
        ],
        "no-trailing-spaces": [
            "warn",
            {
                "skipBlankLines": true
            }
        ],
        "no-multiple-empty-lines": [
            "warn",
            {
                "max": 2
            }
        ],
        "quotes": [ "warn", "single" ],
        "jsx-quotes": [ "warn", "prefer-double" ]
    }
};

module.exports = {
    parser: "@typescript-eslint/parser",
    parserOptions: {
        ecmaVersion: 2020,
        sourceType: "module",
        ecmaFeatures: {
            jsx: true
        }
    },
    settings: {
        react: {
            version: "detect"
        }
    },
    extends: [
        "plugin:react/recommended",
        "plugin:react-hooks/recommended",
        "plugin:@typescript-eslint/recommended",
    ],
    rules: {
        "@typescript-eslint/array-type": [
            "warn",
            {
                "default": "array"
            }
        ],
        "@typescript-eslint/semi": [ "warn", "always" ],
        "@typescript-eslint/no-inferrable-types": 0,
        "max-len": [
            "warn",
            {
                "code": 120
            }
        ],
        "no-trailing-spaces": [
            "warn",
            {
                "skipBlankLines": true
            }
        ],
        "no-multiple-empty-lines": [
            "warn",
            {
                "max": 2
            }
        ],
        "quotes": [ "warn", "single" ],
        "jsx-quotes": [ "warn", "prefer-double" ],
        "object-curly-spacing": ["warn", "always"],
        "no-multi-spaces": ["warn"]
    }
};

Add eslint rules for spacing


	var cordova = require('cordova'),
		fileOpener2 = require('./FileOpener2');

	module.exports = {

	    open: function (successCallback, errorCallback, args) {
	        Windows.Storage.StorageFile.getFileFromPathAsync(args[0]).then(function (file) {
	            var options = new Windows.System.LauncherOptions();
	            options.displayApplicationPicker = true;

	            Windows.System.Launcher.launchFileAsync(file, options).then(function (success) {
	                if (success) {
	                    successCallback();
	                } else {
	                    errorCallback();
	                }
	            });

	        });
		}

	};

	require("cordova/exec/proxy").add("FileOpener2", module.exports);

	var cordova = require('cordova'),
		fileOpener2 = require('./FileOpener2');

	var schemes = [
        { protocol: 'ms-app', getFile: getFileFromApplicationUri },
        { protocol: 'file:///', getFile: getFileFromFileUri }
	]

	function getFileFromApplicationUri(uri) {
	    var applicationUri = new Windows.Foundation.Uri(uri);

	    return Windows.Storage.StorageFile.getFileFromApplicationUriAsync(applicationUri);
	}

	function getFileFromFileUri(uri) {
	    var path = Windows.Storage.ApplicationData.current.localFolder.path +
                        uri.substr(8);

	    return getFileFromNativePath(path);
	}

	function getFileFromNativePath(path) {
	    var nativePath = path.split("/").join("\\");

	    return Windows.Storage.StorageFile.getFileFromPathAsync(nativePath);
	}

	function getFileLoaderForScheme(path) {
	    var fileLoader = getFileFromNativePath;

	    schemes.some(function (scheme) {
	        return path.indexOf(scheme.protocol) === 0 ? ((fileLoader = scheme.getFile), true) : false;
	    });

	    return fileLoader;
	}

	module.exports = {

	    open: function (successCallback, errorCallback, args) {
	        var path = args[0];

	        var getFile = getFileLoaderForScheme(path);

	        getFile(path).then(function (file) {
	            var options = new Windows.System.LauncherOptions();
	            options.displayApplicationPicker = true;

	            Windows.System.Launcher.launchFileAsync(file, options).then(function (success) {
	                if (success) {
	                    successCallback();
	                } else {
	                    errorCallback();
	                }
	            });

	        }, function (errror) {
	            console.log("Error abriendo el archivo");
	        });
		}

	};

	require("cordova/exec/proxy").add("FileOpener2", module.exports);

Add suport for Windows Uri Schemes