// activity-test.js
//
// Test the activity module
//
// Copyright 2012, StatusNet Inc.
//
// 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.

var assert = require('assert'),
    vows = require('vows');

vows.describe('activity module interface').addBatch({
    'When we check for a test suite': {
        topic: function() {
            return false;
        },
        'there is one': function(tsExists) {
            assert.isTrue(tsExists);
        }
    }
}).export(module);

// activity-test.js
//
// Test the activity module
//
// Copyright 2012, StatusNet Inc.
//
// 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.

var assert = require('assert'),
    vows = require('vows'),
    databank = require('databank'),
    URLMaker = require('../lib/urlmaker').URLMaker,
    modelBatch = require('./lib/model').modelBatch,
    Databank = databank.Databank,
    DatabankObject = databank.DatabankObject;

var suite = vows.describe('activity module interface');

var testSchema = {
    pkey: 'id',
    fields: ['actor',
             'content',
             'generator',
             'icon',
             'id',
             'object',
             'published',
             'provider',
             'target',
             'title',
             'url',
             'uuid',
             'updated',
             'verb'],
    indices: ['actor.id', 'object.id', 'uuid']
};

var testData = {
    'create': {
        actor: {
            id: "urn:uuid:8f64087d-fffc-4fe0-9848-c18ae611cafd",
            displayName: "Delbert Fnorgledap",
            objectType: "person"
        },
        verb: "post",
        object: {
            objectType: "note",
            content: "Feeling groovy."
        }
    },
    'update': {
        mood: {
            displayName: "groovy"
        }
    }
};

suite.addBatch(modelBatch('activity', 'Activity', testSchema, testData));

suite.export(module);

Test out activity stuff for basic DataObject stuff


({
    appDir: "./app",
    baseUrl: "scripts",
    mainConfigFile: "./app/scripts/main.js",
    dir: "./build",
    pragmasOnSave: {
      excludeTpl: true
    },
    excludeShallow: [
      'css-builder',
      'less-builder',
      'lessc-server'
    ],
    findNestedDependencies: true,
    optimize: "uglify",
    closure: {
      CompilerOptions: {
      },
      charset: 'UTF-8',
      CompilationLevel: 'SIMPLE_OPTIMIZATIONS',
      loggingLevel: 'SEVERE'
    },
    fileExclusionRegExp: /^\.|spec|tests/,
    optimizeCss: "standard.keepLines",
    generateSourceMaps: false,
    preserveLicenseComments: false,
    modules: [
      {
        name: "main",
        include: [
          "jquery",
          "styles"
        ],
        excludeShallow: [
          'spec_runner'
        ]
      },
      {
        name: "styles"
      }
    ]
})

({
    appDir: "./app",
    baseUrl: "scripts",
    mainConfigFile: "./app/scripts/main.js",
    dir: "./build",
    pragmasOnSave: {
      excludeTpl: true
    },
    excludeShallow: [
      'css-builder',
      'less-builder',
      'lessc-server'
    ],
    findNestedDependencies: true,
    optimize: "uglify",
    fileExclusionRegExp: /^\.|spec|tests/,
    optimizeCss: "standard.keepLines",
    generateSourceMaps: false,
    preserveLicenseComments: false,
    modules: [
      {
        name: "main",
        include: [
          "jquery",
          "styles"
        ],
        excludeShallow: [
          'spec_runner'
        ]
      },
      {
        name: "styles"
      }
    ]
})

Remove unused Closure compiler configuration


angular.module('SceneSkeleton')
	.controller('SceneListController',function($scope,Scenes){
		$scope.scenes = [];
		Scenes.query(function(data) {
			$scope.rows = [];
			$scope.totalScenes = data.length;

			var scenes =  data.sort(function(a,b){
				return a.createdAt < b.createdAt;
			});

			var currentRow = [];

			scenes.forEach(function(scene){
				if(currentRow.length >= 3){
					$scope.rows.push(currentRow);
					currentRow = [];
				}
				currentRow.push(scene);
			});
			if(currentRow.length > 0){
				$scope.rows.push(currentRow);
			}
		});
	});

angular.module('SceneSkeleton')
	.controller('SceneListController',function($scope,Scenes){
		$scope.scenes = [];
		Scenes.query(function(data) {
			$scope.rows = [];
			$scope.totalScenes = data.length;

			var scenes =  data;

			var currentRow = [];

			scenes.forEach(function(scene){
				if(currentRow.length >= 3){
					$scope.rows.push(currentRow);
					currentRow = [];
				}
				currentRow.push(scene);
			});
			if(currentRow.length > 0){
				$scope.rows.push(currentRow);
			}
		});
	});

Remove sorting so it doesn't appear we're ordering things correctly


/**
 * cbpAnimatedHeader.js v1.0.0
 * http://www.codrops.com
 *
 * Licensed under the MIT license.
 * http://www.opensource.org/licenses/mit-license.php
 *
 * Copyright 2013, Codrops
 * http://www.codrops.com
 */
(function() {

	var docElem = document.documentElement,
		header = document.querySelector( '.navbar-fixed-top' ),
		didScroll = false,
		changeHeaderOn = 300;

	function init() {
		window.addEventListener( 'scroll', function() {
			if( !didScroll ) {
				didScroll = true;
				setTimeout( scrollPage, 250 );
			}
		}, false );
	}

	function scrollPage() {
		var sy = scrollY();
		if ( sy >= changeHeaderOn ) {
			$(header).addClass('navbar-shrink');
		}
		else {
			$(header).removeClass('navbar-shrink');
		}
		didScroll = false;
	}

	function scrollY() {
		return window.pageYOffset || docElem.scrollTop;
	}

	init();

})();

/**
 * cbpAnimatedHeader.js v1.0.0
 * http://www.codrops.com
 *
 * Licensed under the MIT license.
 * http://www.opensource.org/licenses/mit-license.php
 *
 * Copyright 2013, Codrops
 * http://www.codrops.com
 */
(function() {

	var docElem = document.documentElement,
		header = document.querySelector( '.navbar-fixed-top' ),
		didScroll = false,
		changeHeaderOn = 300;

	function init() {
		window.addEventListener( 'scroll', function() {
			if( !didScroll ) {
				didScroll = true;
				setTimeout( scrollPage, 250 );
			}
		}, false );
	}

	function scrollPage() {
		var sy = scrollY();
		if ( sy >= changeHeaderOn ) {
			$(header).addClass('navbar-shrink');
		}
		else {
			$(header).removeClass('navbar-shrink');
		}
		didScroll = false;
	}

	function scrollY() {
		return window.pageYOffset || docElem.scrollTop;
	}

	init();

}());

Move invocation into parens that contain function


import Component from '@ember/component';
import { later } from '@ember/runloop';

export default Component.extend({
  tagName: '',
  copyText: '',
  showSuccess: false,
  showNotification: false,
  toggleClipboardProps(isSuccess) {
    this.setProperties({
      showSuccess: isSuccess,
      showNotification: true,
    });
    later(
      this,
      () => {
        this.set('showNotification', false);
      },
      2000,
    );
  },
  actions: {
    copySuccess(event) {
      event.clearSelection();
      this.toggleClipboardProps(true);
    },

    copyError() {
      this.toggleClipboardProps(false);
    },
  },
});

import Component from '@ember/component';
import { later } from '@ember/runloop';

export default Component.extend({
  tagName: '',
  copyText: '',
  showSuccess: false,
  showNotification: false,
  toggleClipboardProps(isSuccess) {
    this.setProperties({
      showSuccess: isSuccess,
      showNotification: true,
    });
    later(
      this,
      () => {
        this.set('showNotification', false);
      },
      2000,
    );
  },
  actions: {
    copySuccess() {
      this.toggleClipboardProps(true);
    },

    copyError() {
      this.toggleClipboardProps(false);
    },
  },
});

Fix `event.clearSelection is not a function` error


import Ember from 'ember';

export default Ember.Component.extend({
  /**
  Selector for the container to watch for scroll events. Defaults to `window`.

  @property
  @type String
  */
  containerSelector: null,

  didInsertElement: function() {
    var container = $(this.get('containerSelector') || window);

    container.on('scroll', () => {
      Ember.run.debounce(this, this.checkScroll, container, 20);
    });
  },

  checkScroll: function(container) {
    var content = this.$();

    if (content.height() < (container.height() * 2) + container.scrollTop()) {
      this.get('contents').nextPage();
    }
  }
});

import Ember from 'ember';

export default Ember.Component.extend({
  /**
  Selector for the container to watch for scroll events. Defaults to the component's element.

  @property
  @type String
  */
  containerSelector: null,

  /**
  The paginated array containing the elements to scroll

  @property
  @type PaginatedArray
  */
  contents:null,

  didInsertElement: function() {
    var container = Ember.$(this.get('containerSelector') || this.$());

    container.on('scroll', () => {
      Ember.run.debounce(this, this.checkScroll, container, 20);
    });

    this.checkScroll(container);
  },

  checkScroll: function(container) {
    var content = this.$();
    var contentHeight = content.height();
    if (content[0].scrollHeight) {
      //  if the content element is scrollable, use the scrollHeight to calculate the content height
      // this allows us to use the same element for content and container.
      contentHeight = content[0].scrollHeight;
    }
    if ("scroll", contentHeight < (container.height() * 2) + container.scrollTop()) {
      if (!this.get('contents.isUpdating')) {
        this.get('contents').nextPage();
      }
    }
  }
});

Use the component's element as the scroll container, by default


var express = require('express'),
    path = require('path'),
    config = require('config'),
    setupPassport = require('./setupPassport'),
    flash = require('connect-flash'),
    appRouter = require('./routers/appRouter.js')(express),
    session = require('express-session'),
    bodyParser = require('body-parser'),
    cookieParser = require('cookie-parser'),
    jsonParser = bodyParser.json(),
    expressJwt = require('express-jwt'),
    db = require('./model/models')

var app = express()
app.set('views', __dirname + '/views')
app.set('port', process.env.PORT || 8080)
app.use(jsonParser)
app.use(bodyParser.urlencoded({
  extended: true
}))

app.use('/', express.static(path.join(__dirname, '/public')))
app.use('/styles', express.static(path.join(__dirname, '/styles')))
app.use('/', appRouter)

setupPassport(app)  // is there a better way to do this?

db.sync().then(() => app.listen(app.get('port')))

console.log(`Server started on port ${app.get('port')}`)

module.exports.getApp = app

var express = require('express'),
    path = require('path'),
    config = require('config'),
    setupPassport = require('./setupPassport'),
    flash = require('connect-flash'),
    appRouter = require('./routers/appRouter.js')(express),
    session = require('express-session'),
    bodyParser = require('body-parser'),
    cookieParser = require('cookie-parser'),
    jsonParser = bodyParser.json(),
    expressJwt = require('express-jwt'),
    db = require('./model/models')

var app = express()

app.use(jsonParser)
app.use(bodyParser.urlencoded({
  extended: true
}))

setupPassport(app)  // is there a better way to do this?

app.use('/', express.static(path.join(__dirname, '/public')))
app.use('/styles', express.static(path.join(__dirname, '/styles')))
app.use('/', appRouter)
app.set('views', __dirname + '/views')
app.set('port', process.env.PORT || 8080)

db.sync().then(() => app.listen(app.get('port')))

console.log(`Server started on port ${app.get('port')}`)

module.exports.getApp = app

Rearrange express config according to docs


// Initial timer length in seconds
var timerCount = 15,
    remote = require('electron').remote,
    arguments = remote.getGlobal('sharedObject').prop1;

// debug mode ensures that computer doesnt shut down while testing
// npm start --debug
if (arguments[2] == '--debug') {
  console.log('Debug mode enabled');
  var debug = true;
}

// Timer constructor
var TimerFunc = function() {
  window.onload = function() {
    this.timerID = document.getElementById('timer');
    // Disply initial timer length, currently in seconds
    this.timerID.innerHTML = timerCount;

    if (debug) {
      document.getElementById('titleHeader').innerHTML += '**DEBUG**';
    };
  };
};

// Function to start countdown, decrements timerCount in seconds
TimerFunc.prototype.startTimer = function() {
  var self = this;
  if (timerCount > 0) {
    self.countdown = window.setInterval(function() {
      timerCount--;
      this.timerID.innerHTML = timerCount;
      if (timerCount <= 0) {
        self.endTimer();
        shutdown();
      }
    }, 1000);
  };
};

// Function to manually stop timer from counting
TimerFunc.prototype.endTimer = function() {
  clearInterval(this.countdown);
};

var timer = new TimerFunc();

function shutdown() {
  if (debug) {
    alert('Debug: Shutting it down!');
  } else {

  };
};

// Initial timer length in seconds
var timerCount = 15,
    remote = require('electron').remote,
    arguments = remote.getGlobal('sharedObject').prop1;

// debug mode ensures that computer doesnt shut down while testing
// npm start --debug
if (arguments[2] == '--debug') {
  console.log('Debug mode enabled');
  var debug = true;
}

// Timer constructor
var TimerFunc = function() {
  window.onload = function() {
    this.timerID = document.getElementById('timer');
    // Disply initial timer length, currently in seconds
    this.timerID.innerHTML = timerCount;

    if (debug) {
      document.getElementById('titleHeader').innerHTML += '**DEBUG**';
    };
  };
};

// Function to start countdown, decrements timerCount in seconds
TimerFunc.prototype.startTimer = function() {
  var self = this;
  if (timerCount > 0) {
    self.countdown = window.setInterval(function() {
      timerCount--;
      this.timerID.innerHTML = timerCount;
      if (timerCount <= 0) {
        self.endTimer();
        shutdown();
      }
    }, 1000);
  };
};

// Function to manually stop timer from counting
TimerFunc.prototype.endTimer = function() {
  clearInterval(this.countdown);
};

var timer = new TimerFunc();

function shutdown() {
  if (debug) {
    alert('Debug: Shutting it down!');
  } else {
    var exec = require('child-process').exec;
    var cmd = 'shutdown -t 15 -s';

    exec(cmd, function(error, stdout, stderr) {});
  };
};

Implement shutdown command for Windows


angular.module('snap')
  .directive('snapContent', ['SnapConstructor', 'snapRemote', function (SnapConstructor, snapRemote) {
    'use strict';
    return {
      restrict: 'AE',
      link: function postLink(scope, element, attrs) {
        element.addClass('snap-content');

        var snapOptions = {
          element: element[0]
        };

        angular.extend(snapOptions, snapRemote.globalOptions);

        var snapId = attrs.snapId;
        if(!!snapId) {
          snapId = scope.$eval(attrs.snapId);
        }

        // override snap options if some provided in snap-options attribute
        if(angular.isDefined(attrs.snapOptions) && attrs.snapOptions) {
          angular.extend(snapOptions, scope.$eval(attrs.snapOptions));
        }

        snapRemote.register(new SnapConstructor(snapOptions), snapId);

        // watch snapOptions for updates
        if(angular.isDefined(attrs.snapOptions) && attrs.snapOptions) {
          scope.$watch(attrs.snapOptions, function(newSnapOptions) {
            snapRemote.getSnapper(snapId).then(function(snapper) {
              snapper.settings(newSnapOptions);
            });
          }, true);
        }

        scope.$on('$destroy', function() {
          snapRemote.unregister(snapId);
        });
      }
    };
  }]);

angular.module('snap')
  .directive('snapContent', ['SnapConstructor', 'snapRemote', function (SnapConstructor, snapRemote) {
    'use strict';
    return {
      restrict: 'AE',
      link: function postLink(scope, element, attrs) {
        element.addClass('snap-content');

        var snapOptions = angular.extend({}, snapRemote.globalOptions);

        // Get `snapOpt*` attrs, for now there is no *binding* going on here.
        // We're just providing a more declarative way to set initial values.
        angular.forEach(attrs, function(val, attr) {
          if(attr.indexOf('snapOpt') === 0) {
            attr = attr.substring(7);
            if(attr.length) {
              attr = attr[0].toLowerCase() + attr.substring(1);
              snapOptions[attr] = scope.$eval(val);
            }
          }
        });

        // Always force the snap element to be the one this directive is
        // attached to.
        snapOptions.element = element[0];

        var snapId = attrs.snapId;
        if(!!snapId) {
          snapId = scope.$eval(attrs.snapId);
        }

        // override snap options if some provided in snap-options attribute
        if(angular.isDefined(attrs.snapOptions) && attrs.snapOptions) {
          angular.extend(snapOptions, scope.$eval(attrs.snapOptions));
        }

        snapRemote.register(new SnapConstructor(snapOptions), snapId);

        // watch snapOptions for updates
        if(angular.isDefined(attrs.snapOptions) && attrs.snapOptions) {
          scope.$watch(attrs.snapOptions, function(newSnapOptions) {
            snapRemote.getSnapper(snapId).then(function(snapper) {
              snapper.settings(newSnapOptions);
            });
          }, true);
        }

        scope.$on('$destroy', function() {
          snapRemote.unregister(snapId);
        });
      }
    };
  }]);

Allow use of snap-opt-* attrs for opts


describe('Satellite game', function () {
  it('should exists', function () {
      expect(s).to.be.an('object');
      expect(s).to.have.property('config');
      expect(s).to.have.property('init');
  });

  it('should contains ship properties', function () {
    expect(s.config.ship).to.be.an('object');
    expect(s).to.have.deep.property('config.ship.hull');
    expect(s).to.have.deep.property('config.ship.shields');
    expect(s).to.have.deep.property('config.ship.maxSpeed');
  });

  it('should init the game', function () {
    var spy = sinon.spy(s, 'init');
    s.init();
    expect(spy.called).to.equal(true);

    expect(s).to.have.property('projector').and.to.be.an('object');
    expect(s).to.have.property('loader').and.to.be.an('object');
    expect(s).to.have.property('game').and.to.be.an('object');
  });
});

describe('Satellite game', function () {
  it('should exists', function () {
      expect(s).to.be.an('object');
      expect(s).to.have.property('config');
      expect(s).to.have.property('init');
  });

  it('should contains ship properties', function () {
    expect(s.config.ship).to.be.an('object');
    expect(s).to.have.deep.property('config.ship.hull');
    expect(s).to.have.deep.property('config.ship.shields');
    expect(s).to.have.deep.property('config.ship.maxSpeed');
  });

  it('should init game variables', function () {
    expect(s).to.have.property('projector').and.to.be.an('object');
    expect(s).to.have.property('loader').and.to.be.an('object');
    expect(s).to.have.property('game').and.to.be.an('object');
  });
});

Remove unnecessary code to init the game


import React, { PropTypes } from 'react';

const Comhead = ({ url }) => {
  if (url) {
    const comhead = url.match(/^https?:\/\/(?:www.)*([^\/?#]+)/)[1];
    const urlLink = `from?site=${comhead}`;

    return (

        ({comhead})

    );
  }
  return ;
};

Comhead.propTypes = {
  url: PropTypes.string,
};

export default Comhead;

import React, { PropTypes } from 'react';

const Comhead = ({ url }) => {
  if (url) {
    const comhead = url
      .toLowerCase()
      .match(/^https?:\/\/(?:www.)*([^\/?#]+)/)[1];
    const urlLink = `from?site=${comhead}`;

    return (

        ({comhead})

    );
  }
  return ;
};

Comhead.propTypes = {
  url: PropTypes.string,
};

export default Comhead;

Convert URL to lower case before applying regex


define(function() {

  // Export
  return function(Prism) {

    Prism.languages.cpp = Prism.languages.extend('c', {
    	'keyword': /\b(alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|class|compl|const|constexpr|const_cast|continue|decltype|default|delete|delete\[\]|do|double|dynamic_cast|else|enum|explicit|export|extern|float|for|friend|goto|if|inline|int|long|mutable|namespace|new|new\[\]|noexcept|nullptr|operator|private|protected|public|register|reinterpret_cast|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/g,
    	'operator': /[-+]{1,2}|!=?|<{1,2}=?|>{1,2}=?|\->|:{1,2}|={1,2}|\^|~|%|(&){1,2}|\|?\||\?|\*|\/|\b(and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/g
    });

  };

});

define(function() {
  return function(Prism) {
    Prism.languages.cpp = Prism.languages.extend('c', {
      'keyword': /\b(alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|class|compl|const|constexpr|const_cast|continue|decltype|default|delete|delete\[\]|do|double|dynamic_cast|else|enum|explicit|export|extern|float|for|friend|goto|if|inline|int|long|mutable|namespace|new|new\[\]|noexcept|nullptr|operator|private|protected|public|register|reinterpret_cast|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/g,
      'operator': /[-+]{1,2}|!=?|<{1,2}=?|>{1,2}=?|\->|:{1,2}|={1,2}|\^|~|%|(&){1,2}|\|?\||\?|\*|\/|\b(and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/g
    });
  };
});

Fix indentation of the C++ grammar


const OS = require("os");
const serveCommand = require("./commands/serve");

const usage =
  "truffle db <sub-command> [options]" +
  OS.EOL +
  OS.EOL +
  "  Available sub-commands: " +
  OS.EOL +
  OS.EOL +
  "  serve \tStart the GraphQL server" +
  OS.EOL;

const command = {
  command: "db",
  description: "Database interface commands",
  builder: function (yargs) {
    return yargs.commandDir("commands").demandCommand();
  },

  subCommands: {
    serve: {
      help: serveCommand.help,
      description: serveCommand.description
    }
  },

  help: {
    usage,
    options: []
  },

  run: async function (args) {
    const [subCommand] = args._;
    switch (subCommand) {
      case "serve":
        await serveCommand.run(args);
        break;

      default:
        console.log(`Unknown command: ${subCommand}`);
    }
  }
};

module.exports = command;

const OS = require("os");
const serveCommand = require("./commands/serve");

const usage =
  "truffle db <sub-command> [options]" +
  OS.EOL +
  OS.EOL +
  "  Available sub-commands: " +
  OS.EOL +
  OS.EOL +
  "  serve \tStart the GraphQL server" +
  OS.EOL;

const command = {
  command: "db",
  description: "Database interface commands",
  builder: function (yargs) {
    return yargs.command(serveCommand).demandCommand();
  },

  subCommands: {
    serve: {
      help: serveCommand.help,
      description: serveCommand.description
    }
  },

  help: {
    usage,
    options: []
  },

  run: async function (args) {
    const [subCommand] = args._;
    switch (subCommand) {
      case "serve":
        await serveCommand.run(args);
        break;

      default:
        console.log(`Unknown command: ${subCommand}`);
    }
  }
};

module.exports = command;

Fix db yargs hookup for bundle


var childProcess = require('child_process');

// Exit the process if the command failed and only call the callback if the
// command succeed, output of the command would also be piped.
exports.safeExec = function(command, options, callback) {
  if (!callback) {
    callback = options;
    options = {};
  }
  if (!options)
    options = {};

  // This needed to be increased for `apm test` runs that generate many failures
  // The default is 200KB.
  options.maxBuffer = 1024 * 1024;

  var child = childProcess.exec(command, options, function(error, stdout, stderr) {
    if (error)
      process.exit(error.code || 1);
    else
      callback(null);
  });
  child.stderr.pipe(process.stderr);
  if (!options.ignoreStdout)
    child.stdout.pipe(process.stdout);
}

// Same with safeExec but call child_process.spawn instead.
exports.safeSpawn = function(command, args, options, callback) {
  if (!callback) {
    callback = options;
    options = {};
  }
  var child = childProcess.spawn(command, args, options);
  child.stderr.pipe(process.stderr);
  child.stdout.pipe(process.stdout);
  child.on('error', function(error) {
    console.error('Command \'' + command + '\' failed: ' + error.message);
  });
  child.on('exit', function(code) {
    if (code != 0)
      process.exit(code);
    else
      callback(null);
  });
}

var childProcess = require('child_process');

// Exit the process if the command failed and only call the callback if the
// command succeed, output of the command would also be piped.
exports.safeExec = function(command, options, callback) {
  if (!callback) {
    callback = options;
    options = {};
  }
  if (!options)
    options = {};

  // This needed to be increased for `apm test` runs that generate many failures
  // The default is 200KB.
  options.maxBuffer = 1024 * 1024;

  var child = childProcess.exec(command, options, function(error, stdout, stderr) {
    if (error)
      process.exit(error.code || 1);
    else if (callback)
      callback(null);
  });
  child.stderr.pipe(process.stderr);
  if (!options.ignoreStdout)
    child.stdout.pipe(process.stdout);
}

// Same with safeExec but call child_process.spawn instead.
exports.safeSpawn = function(command, args, options, callback) {
  if (!callback) {
    callback = options;
    options = {};
  }
  var child = childProcess.spawn(command, args, options);
  child.stderr.pipe(process.stderr);
  child.stdout.pipe(process.stdout);
  child.on('error', function(error) {
    console.error('Command \'' + command + '\' failed: ' + error.message);
  });
  child.on('exit', function(code) {
    if (code != 0)
      process.exit(code);
    else
      callback(null);
  });
}

Fix potential null reference callback invokation


angular.module('openspecimen')
  .directive('osRightDrawer', function(osRightDrawerSvc) {
    return {
      restrict: 'A',

      link: function(scope, element, attrs) {
        element.addClass('os-right-drawer');
        element.removeAttr('os-right-drawer');

        var header = element.find('div.os-head');
        if (header) {
          var divider = angular.element('<div/>')
            .addClass('os-divider');
          header.after(divider);
        }

        osRightDrawerSvc.setDrawer(element);
      }
    };
  })
  .directive('osRightDrawerToggle', function(osRightDrawerSvc) {
    return {
      restrict: 'A',
      link: function(scope, element, attrs) {
        element.on('click', function() {
          osRightDrawerSvc.toggle();
        });
      }
    };
  })
  .factory('osRightDrawerSvc', function() {
    var drawerEl = undefined;
    return {
      setDrawer: function(drawer) {
        drawerEl = drawer;
      },

      toggle: function() {
        drawerEl.toggleClass('active');

        var cardsDiv = drawerEl.parent().find("div.os-cards");
        if (drawerEl.hasClass('active')) {
          cardsDiv.css("width", "75%");
        } else {
          cardsDiv.css("width", "100%");
        }
      }
    }
  });

angular.module('openspecimen')
  .directive('osRightDrawer', function(osRightDrawerSvc) {
    return {
      restrict: 'A',

      link: function(scope, element, attrs) {
        element.addClass('os-right-drawer');
        element.removeAttr('os-right-drawer');

        var header = element.find('div.os-head');
        if (header) {
          var divider = angular.element('<div/>')
            .addClass('os-divider');
          header.after(divider);
        }

        osRightDrawerSvc.setDrawer(element);
      }
    };
  })
  .directive('osRightDrawerToggle', function(osRightDrawerSvc) {
    return {
      restrict: 'A',
      link: function(scope, element, attrs) {
        element.on('click', function() {
          osRightDrawerSvc.toggle();
        });
      }
    };
  })
  .factory('osRightDrawerSvc', function() {
    var drawerEl = undefined;
    return {
      setDrawer: function(drawer) {
        drawerEl = drawer;
      },

      toggle: function() {
        drawerEl.toggleClass('active');

        var cardsDiv = drawerEl.parent().find("div.os-cards");
        if (drawerEl.hasClass('active')) {
          var firstInput = drawerEl.find("input")[0];
          if (firstInput) {
            firstInput.focus();
          }
          cardsDiv.css("width", "75%");
        } else {
          cardsDiv.css("width", "100%");
        }
      }
    }
  });

Set defalut focus on first input element in search panel


/**
 * This replaces every occurence of variable "foo".
 */
module.exports = function(fileInfo, api) {
  const j = api.jscodeshift;
  const root = j(fileInfo.source);

  const callExpressions = root.find(j.CallExpression, {
    callee: {
      type: 'Identifier',
      name: '__issStyleSheetCreate__',
    }
  }).filter(p => p.value.arguments.length == 1 && p.value.arguments[0].type === 'ObjectExpression');

  callExpressions.replaceWith(p => p.value.arguments[0]);

  return root.toSource();
};

module.exports = function(fileInfo, api) {
  const j = api.jscodeshift;
  const root = j(fileInfo.source);

  root.find(j.CallExpression, {
    callee: {
      type: 'Identifier',
      name: '__issStyleSheetCreate__',
    }
  }).filter(function(expr) {
    const args = expr.value.arguments;
    return args.length === 1 && args[0].type === 'ObjectExpression';
  }).replaceWith(function(expr) {
    const styleExpr = expr.value.arguments[0];

    const props = styleExpr.properties.map(function(property) {
      // This is what we write out to a stylesheet, with a selector governed
      // by the same name we pass as the literal value below.
      console.log(j(property.value).toSource());
      return j.property('init', property.key, j.literal('swag'));
    });

    return j.objectExpression(props);
  });

  return root.toSource();
};

Update jscodeshift transform to replace and rewrite style expr


'use strict';

const
  Promise = require('bluebird'),
  models = require('../lib/model/db');

const
  YEAR_FROM = '2017',
  YEAR_TO   = '2018';

/*
 *  1. Get all users
 *
 *  2. Iterate through users and:
 *
 *  3. Calculate remaining days for current year
 *
 *  4. Put value from step 3 into user_allowance_adjustment.carried_over_allowance
 *     of next year
 *
 * */

models.User
  .findAll()
  .then(users => Promise.map(
    users,
    user => {
      return user
        .reload_with_leave_details({YEAR_FROM})
        .then( user => user.promise_number_of_days_available_in_allowance(YEAR_FROM) )
        .then(remainer => {
          return user.promise_to_update_carried_over_allowance({
            year                   : YEAR_TO,
            carried_over_allowance : remainer,
          });
        })
        .then(() => Promise.resolve(console.log('Done with user ' + user.id)));
    },
    {concurrency : 1}
  ));

'use strict';

const
  Promise = require('bluebird'),
  moment = require('moment'),
  models = require('../lib/model/db');

const
  YEAR_FROM = '2018',
  YEAR_TO   = '2019';

/*
 *  1. Get all users
 *
 *  2. Iterate through users and:
 *
 *  3. Calculate remaining days for current year
 *
 *  4. Put value from step 3 into user_allowance_adjustment.carried_over_allowance
 *     of next year
 *
 * */

models.User
  .findAll()
  .then(users => Promise.map(
    users,
    user => {
      let carryOver;
      return Promise.resolve(user.getCompany().then(c => carryOver = c.carry_over))
        .then(() => user.reload_with_leave_details({YEAR_FROM}))
        .then(user => user.promise_allowance({year:moment.utc(YEAR_FROM, 'YYYY')}))
        .then(allowance => {
          return user.promise_to_update_carried_over_allowance({
            year                   : YEAR_TO,
            carried_over_allowance : Math.min(allowance.number_of_days_available_in_allowance, carryOver),
          });
        })
        .then(() => Promise.resolve(console.log('Done with user ' + user.id)));
    },
    {concurrency : 1}
  ));

Fix the carry over script


'use strict';
/**
 * @file Retrieve mails from the account
 */

var googleapis = require('googleapis');

// Retrieve all mails associated with this user account,
// using refresh_token
// starting from message #`cursor`.
module.exports = function retrieveMails(options, cursor, mails, cb) {
  options.q = "after:" + cursor.date.getFullYear() + "/" + (cursor.date.getMonth() + 1) + "/" + cursor.date.getDate();
  googleapis.gmail('v1').users.messages
    .list(options, function(err, res) {
      if(err) {
        return cb(err);
      }

      // We can't use a cursor in the query, so we need to do the sorting ourselves
      res.messages.reverse();
      mails = mails.concat(res.messages.filter(function(mail) {
        return parseInt(mail.id, 16) > cursor.id;
      }));

      if(res.nextPageToken) {
        options.pageToken = res.nextPageToken;
        retrieveMails(options, cursor, mails, cb);
      }
      else {
        cb(null, {
          date: new Date((new Date()).getTime() - 1000 * 3600 * 24),
          id: mails.length > 0 ? parseInt(mails[mails.length - 1].id, 16) : cursor.id
        }, mails);
      }
    });
};

'use strict';
/**
 * @file Retrieve mails from the account
 */

var googleapis = require('googleapis');

// Retrieve all mails associated with this user account,
// using refresh_token
// starting from message #`cursor`.
module.exports = function retrieveMails(options, cursor, mails, cb) {
  options.q = "-is:chat after:" + cursor.date.getFullYear() + "/" + (cursor.date.getMonth() + 1) + "/" + cursor.date.getDate();
  googleapis.gmail('v1').users.messages
    .list(options, function(err, res) {
      if(err) {
        return cb(err);
      }

      // We can't use a cursor in the query, so we need to do the sorting ourselves
      res.messages.reverse();
      mails = mails.concat(res.messages.filter(function(mail) {
        return parseInt(mail.id, 16) > cursor.id;
      }));

      if(res.nextPageToken) {
        options.pageToken = res.nextPageToken;
        retrieveMails(options, cursor, mails, cb);
      }
      else {
        cb(null, {
          date: new Date((new Date()).getTime() - 1000 * 3600 * 24),
          id: mails.length > 0 ? parseInt(mails[mails.length - 1].id, 16) : cursor.id
        }, mails);
      }
    });
};

Remove chat messages from mails


import RSS from 'rss';
import { errorHtml } from '../util';

/**
 * Generates RSS.
 *
 * @param {object} props
 * @return {string}
 */
export default function renderRss(props = {}) {
	const missingProp = prop => errorHtml(`Error while rendering an RSS feed ${props.sourcePath}: ` +
		`missing required property "${prop}".`);

	const requiredProps = ['title', 'description', 'items'];
	for (let prop of requiredProps) {
		if (!props[prop]) {
			return missingProp(prop);
		}
	}

	props = {
		...props,
		language: props.lang,
		feed_url: props.absolutizeUrl(props.feedUrl || `${props.url}.xml`),
		site_url: props.absolutizeUrl(props.siteUrl || ''),
		image_url: props.imageUrl && props.absolutizeUrl(props.imageUrl),
		custom_namespaces: props.customNamespaces,
		custom_elements: props.customElements,
	};

	let feed = new RSS(props);

	props.items.forEach(item => {
		feed.item({
			...item,
			url: props.absolutizeUrl(item.url),
			description: props.absolutizeLinks(item.description),
			custom_elements: item.customElements,
		});
	});

	return feed.xml({ indent: true });
}

import RSS from 'rss';
import { errorHtml } from '../util';

/**
 * Generates RSS.
 *
 * @param {object} props
 * @return {string}
 */
export default function renderRss(props = {}) {
	const missingProp = prop => errorHtml(`Error while rendering an RSS feed ${props.sourcePath}: ` +
		`missing required property "${prop}".`);

	const requiredProps = ['title', 'description', 'items'];
	for (let prop of requiredProps) {
		if (!props[prop]) {
			return missingProp(prop);
		}
	}

	props = {
		...props,
		language: props.lang,
		feed_url: props.absolutizeUrl(props.feedUrl || `${props.url}.xml`),
		site_url: props.absolutizeUrl(props.siteUrl || ''),
		image_url: props.imageUrl && props.absolutizeUrl(props.imageUrl),
		custom_namespaces: props.customNamespaces,
		custom_elements: props.customElements,
	};

	let feed = new RSS(props);

	props.items.forEach(item => {
		feed.item({
			...item,
			url: props.absolutizeUrl(item.url),
			description: props.absolutizeLinks(item.content),
			custom_elements: item.customElements,
		});
	});

	return feed.xml({ indent: true });
}

Use content field for description: no need to prepare posts for RSS.


import queryString from 'query-string'

export default class CrowiAuth {
  constructor(crowi) {
    this.crowi = crowi
    this.location = crowi.location
    this.localStorage = crowi.localStorage

    this.onStorage = this.onStorage.bind(this)

    this.subscribeStorage()
    this.updateState()
  }

  isAuthenticated() {
    const { id, name } = this.crowi.getUser()
    return !!(id && name)
  }

  subscribeStorage() {
    window.addEventListener('storage', this.onStorage)
  }

  shouldUpdate() {
    return this.isAuthenticated() !== this.localStorage.getItem('authenticated')
  }

  updateState() {
    if (this.shouldUpdate()) {
      this.localStorage.setItem('authenticated', this.isAuthenticated())
    }
  }

  onStorage(e) {
    const { key, newValue } = e
    const isAuthenticated = newValue === 'true'
    if (key === 'authenticated') {
      if (isAuthenticated) {
        this.onLogin()
      } else {
        this.onLogout()
      }
    }
  }

  onLogin() {
    const { continue: continueUrl } = queryString.parse(this.location.search)
    if (continueUrl) {
      top.location.href = continueUrl
    } else {
      this.location.reload()
    }
  }

  onLogout() {
    this.location.reload()
  }
}

import queryString from 'query-string'

export default class CrowiAuth {
  constructor(crowi) {
    this.crowi = crowi
    this.window = crowi.window
    this.location = crowi.location
    this.localStorage = crowi.localStorage

    this.onStorage = this.onStorage.bind(this)

    this.subscribeStorage()
    this.updateState()
  }

  isAuthenticated() {
    const { id, name } = this.crowi.getUser()
    return !!(id && name)
  }

  subscribeStorage() {
    this.window.addEventListener('storage', this.onStorage)
  }

  shouldUpdate() {
    return this.isAuthenticated() !== this.localStorage.getItem('authenticated')
  }

  updateState() {
    if (this.shouldUpdate()) {
      this.localStorage.setItem('authenticated', this.isAuthenticated())
    }
  }

  onStorage(e) {
    const { key, newValue } = e
    const isAuthenticated = newValue === 'true'
    if (key === 'authenticated') {
      if (isAuthenticated) {
        this.onLogin()
      } else {
        this.onLogout()
      }
    }
  }

  onLogin() {
    const { continue: continueUrl } = queryString.parse(this.location.search)
    if (continueUrl) {
      top.location.href = continueUrl
    } else {
      this.location.reload()
    }
  }

  onLogout() {
    this.location.reload()
  }
}

Handle `window` object through crowi instance


/* global define */

define([
        'backbone'
], function(Backbone) {
    'use strict';

    var AppRouter = Backbone.Router.extend({

      routes: {
        '': 'loadDefault'
      },

      initialize: function() {
        window.console.log('Router initialized!');
      },

      loadDefault: function() {
        window.console.log('loadDefault fired');
      }

    });

    return AppRouter;
});

Remove more AMD-style module code


import { drawCard } from './deck';
import { addAndFillMana } from './character';

export const NEW_GAME = 'NEW_GAME';

export function newGame({ yourName, opponentName, isPlayerStarting }) {
  return dispatch => {
    dispatch(addAndFillMana({ target: isPlayerStarting ? 'PLAYER' : 'OPPONENT' }));
    dispatch(drawCard({ name: 'The Coin', target: isPlayerStarting ? 'OPPONENT' : 'PLAYER' }));

    return dispatch({
      yourName,
      opponentName,
      isPlayerStarting,
      type: NEW_GAME,
    });
  };
}

import { drawCard } from './deck';
import { addAndFillMana } from './character';

export const NEW_GAME = 'NEW_GAME';

function newGameAction({ yourName, opponentName, isPlayerStarting }) {
  return {
    yourName,
    opponentName,
    isPlayerStarting,
    type: NEW_GAME,
  };
}

export function newGame({ yourName, opponentName, isPlayerStarting }) {
  return dispatch => {
    dispatch(newGameAction({ yourName, opponentName, isPlayerStarting }));
    dispatch(addAndFillMana({ target: isPlayerStarting ? 'PLAYER' : 'OPPONENT' }));
    dispatch(drawCard({ name: 'The Coin', target: isPlayerStarting ? 'OPPONENT' : 'PLAYER' }));
  };
}

Put newGameAction into its own function


const merge = require('webpack-merge');
const config = require('./webpack.config');

const host = process.env.SDF_HOST || 'localhost';
const port = process.env.SDF_PORT || '3000';

module.exports = merge.smart(config, {
  devtool: 'eval-source-map',
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          {
            loader: 'style-loader',
          },
          {
            loader: 'css-loader',
            options: {
              sourceMap: true,
              modules: true,
              localIdentName: '[name]__[local]___[hash:base64:5]',
            },
          },
        ],
      },
    ],
  },
  devServer: {
    proxy: [
      {
        context: ['/socket.io/', '/login', '/auth', '/logout'],
        target: `http://${host}:${port}`,
      },
    ],
    historyApiFallback: {
      index: '/',
    },
  },
});

const merge = require('webpack-merge');
const config = require('./webpack.config');

const host = process.env.SDF_HOST || 'localhost';
const port = process.env.SDF_PORT || '3000';

const backendHost = process.env.SDF_BACKEND_HOST || 'backend';
const backendPort = process.env.SDF_BACKEND_PORT || '3000';

module.exports = merge.smart(config, {
  devtool: 'eval-source-map',
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          {
            loader: 'style-loader',
          },
          {
            loader: 'css-loader',
            options: {
              sourceMap: true,
              modules: true,
              localIdentName: '[name]__[local]___[hash:base64:5]',
            },
          },
        ],
      },
    ],
  },
  devServer: {
    proxy: [
      {
        context: ['/socket.io/', '/login', '/auth', '/logout'],
        target: `http://${backendHost}:${backendPort}`,
      },
    ],
    historyApiFallback: {
      index: '/',
    },
  },
});

Make proxy proxy to backend


View = (function() {

  // Fetch a template based on its ID from the HTML page.  Return the
  // text content of the template without rendering it.
  var get = function(templateID) {
  };

  // Render a single object using the given a template.
  // Mustache API Docs: https://github.com/janl/mustache.js
  var render = function(template, object) {
  };

  // Given a template ID and an object, fetch the template, render it,
  // and insert its text into the HTML page.  Place the rendered
  // template text into the `

' section of
  // index.html.
  var set = function(templateID, object) {
  };

  return {
    get:    get,
    render: render,
    set:    set,
  };
})();

View = (function() {

  // Fetch a template based on its ID from the HTML page.  Return the
  // text content of the template without rendering it.
  var get = function(templateID) {
  };

  // Render a single object using the given template.
  // Mustache API Docs: https://github.com/janl/mustache.js
  var render = function(template, object) {
  };

  // Given a template ID and an object, fetch the template, render it,
  // and insert its text into the HTML page.  Place the rendered
  // template text into the `

' section of
  // index.html.
  var set = function(templateID, object) {
  };

  return {
    get:    get,
    render: render,
    set:    set,
  };
})();

Fix a typo in a description


/**
 * Created by oleksandr at 6/1/14 1:47 PM
 */
angular.module('myApp')
  .directive('angAuth', [function() {
    function link($scope) {
      $scope.$on('event:auth-loginRequired', function() {
        console.log('loginRequired');
        // TODO
      });
      $scope.$on('event:auth-loginConfirmed', function() {
        console.log('loginConfirmed');
        // TODO
      });
    }
    return {
      restrict: 'A',
      link: link
    };
}]);

/**
 * Created by oleksandr at 6/1/14 1:47 PM
 */
angular.module('myApp')
  .directive('angAuth', ['AuthModal', 'AuthInterceptorService', function(AuthModal, AuthInterceptorService) {
    function link($scope) {
      $scope.$on('event:auth-loginRequired', function(event, rejection) {
        // directive is great place to call modal, since directive is bound to element in the DOM
        // and can even e.g. pass to the modal an element to stick with.
        AuthModal.open().then(function(user) {
          $scope.currentUser = user; // save as application-wide object
          AuthInterceptorService.loginConfirmed();
        }, function() {
            AuthInterceptorService.loginCancelled(null/*data for event broadcast*/, rejection);
          }
        );
      });
    }
    return {
      restrict: 'A',
      link: link
    };
}]);

Call AuthModal inside ang-auth directive and save authenticated user in the scope.


module.exports = ctx => {
  if (!ctx) return {}
  const request = ctx.req
  const connection = request.connection
  const address = connection && connection.address && connection.address()
  const portNumber = address && address.port
  const url = `${ctx.request.href}`
  return {
    url,
    path: request.url,
    httpMethod: request.method,
    headers: request.headers,
    httpVersion: request.httpVersion,
    query: ctx.request.query,
    referer: request.headers.referer,
    clientIp: ctx.ip,
    connection: request.connection ? {
      remoteAddress: request.connection.remoteAddress || request.ip,
      remotePort: request.connection.remotePort,
      bytesRead: request.connection.bytesRead,
      bytesWritten: request.connection.bytesWritten,
      localPort: portNumber,
      localAddress: address ? address.address : undefined,
      IPVersion: address ? address.family : undefined
    } : undefined
  }
}

module.exports = ctx => {
  if (!ctx) return {}
  const request = ctx.req
  const connection = request.connection
  const address = connection && connection.address && connection.address()
  const portNumber = address && address.port
  const url = `${ctx.request.href}`
  return {
    url,
    path: request.url,
    httpMethod: request.method,
    headers: request.headers,
    httpVersion: request.httpVersion,
    query: ctx.request.query,
    referer: request.headers.referer || request.headers.referrer,
    clientIp: ctx.ip || (request.connection ? request.connection.remoteAddress : undefined),
    connection: request.connection ? {
      remoteAddress: request.connection.remoteAddress,
      remotePort: request.connection.remotePort,
      bytesRead: request.connection.bytesRead,
      bytesWritten: request.connection.bytesWritten,
      localPort: portNumber,
      localAddress: address ? address.address : undefined,
      IPVersion: address ? address.family : undefined
    } : undefined
  }
}

Make Koa/Express ip logic consistent


define([
    "jquery",
    "../registry"
], function($, registry) {
    var _ = {
        name: "autoscale",
        trigger: ".pat-auto-scale",
	transform: $('html').hasClass('csstransforms'),

        init: function($el, options) {
            return $el.each(_.resizeElement);
        },

        resizeElement: function() {
            var $this = $(this),
                scale;

            if (this.tagName.toLowerCase()==='body')
                scale = $(window).width()/$this.outerWidth();
            else
                scale = $this.parent().outerWidth()/$this.outerWidth();

            if (_.transform)
                $this.css('transform', 'scale(' + scale + ')');
            else
                $this.css('zoom', scale);
        },

        resizeEvent: function() {
            $(_.trigger).each(_.resizeElement);
        }
    };

    $(window).resize(_.resizeEvent);
    registry.register(_);
    return _;
});

define([
    "jquery",
    "../registry"
], function($, registry) {
    var _ = {
        name: "autoscale",
        trigger: ".pat-auto-scale",
	transform: $('html').hasClass('csstransforms'),

        init: function($el, options) {
            return $el.each(_.resizeElement);
        },

        resizeElement: function() {
            var $this = $(this),
                scale;

            if (this.tagName.toLowerCase()==='body')
                scale = $(window).width()/$this.outerWidth();
            else
                scale = $this.parent().outerWidth()/$this.outerWidth();

            if (_.transform)
                $this.css('transform', 'scale(' + scale + ')');
            else
                $this.css('zoom', scale);
            $this.addClass("scaled");
        },

        resizeEvent: function() {
            $(_.trigger).each(_.resizeElement);
        }
    };

    $(window).resize(_.resizeEvent);
    registry.register(_);
    return _;
});

Add "scaled" class after scaling elements.


(function () {
  $('a[data-disable-with][data-onloadingtext]')
    .live("ajax:before", setLoadingText);

  $("a[data-disable-with][data-oncompletetext]")
    .live("ajax:complete", setCompletedText);

  $('a[data-disable-with][data-onloadingtext]:not([data-remote])')
    .live('click', setLoadingText);

  function setLoadingText() {
    $(this)
      .text( $(this).attr('data-onloadingtext') )
      .attr('onclick','return false;')
      .addClass('disabled');
  }

  function setCompletedText() {
    $(this).text( $(this).attr('data-oncompletetext') );
  }
}());

(function () {
  $('a[data-disable-with][data-onloadingtext]')
    .live("ajax:before", setLoadingText);

  $("a[data-disable-with][data-oncompletetext]")
    .live("ajax:complete", setCompletedText);

  $('a[data-disable-with][data-onloadingtext]:not([data-remote])')
    .live('click', setLoadingText);

  function setLoadingText() {
    var el = $(this);
    var isDataRemote = el.data('remote');

    el.text( $(this).attr('data-onloadingtext') )
      .addClass('disabled');

    if (isDataRemote) {
      el.attr('onclick','return false;');
    }
  }

  function setCompletedText() {
    $(this).text( $(this).attr('data-oncompletetext') );
  }
}());

Disable text return false on ajax links only


import Route from '@ember/routing/route';
import ApplicationRouteMixin from 'ember-simple-auth/mixins/application-route-mixin';

export default Route.extend(ApplicationRouteMixin, {
});

import Route from '@ember/routing/route';
import ApplicationRouteMixin from 'ember-simple-auth/mixins/application-route-mixin';
import { inject as service } from '@ember/service';

export default Route.extend(ApplicationRouteMixin, {
  intl: service(),
  moment: service(),
  beforeModel() {
    this.intl.setLocale('en-us');
    this.moment.setLocale('en');
  }
});

Set the locale in dummy app


import React, { PropTypes } from 'react'
import { connectFromRoute } from '../routes'
import { sparqlConnect } from '../sparql/configure-sparql'
import { LOADED } from 'sparql-connect'
import ServiceList from './service-list.js'

function ServicesByGsimInputOrOutput({ loaded, services }){
  if (loaded !== LOADED) return loading services
  return <ServiceList services={services} />
}

const ServicesByGsimInput =
  sparqlConnect.gsimInputServices(ServicesByGsimInputOrOutput)

const ServicesByGsimOuput =
  sparqlConnect.gsimOutputServices(ServicesByGsimInputOrOutput)

function ServicesByGsimInputAndOutput({ gsimClass }) {
    return (

        As input:
        <ServicesByGsimInput gsimClass={gsimClass} />
        As output:
        <ServicesByGsimOuput gsimClass={gsimClass} />

    )
}

ServicesByGsimInputAndOutput.propTypes = {
  gsimClass: PropTypes.string.isRequired
}

export default connectFromRoute(ServicesByGsimInputAndOutput)

import React, { PropTypes } from 'react'
import { connectFromRoute } from '../routes'
import { sparqlConnect } from '../sparql/configure-sparql'
import { LOADED } from 'sparql-connect'
import ServiceList from './service-list.js'

function ServicesByGsimInputOrOutput({ loaded, services }){
  if (loaded !== LOADED) return loading services
  return <ServiceList services={services} />
}

const ServicesByGsimInput =
  sparqlConnect.gsimInputServices(ServicesByGsimInputOrOutput)

const ServicesByGsimOuput =
  sparqlConnect.gsimOutputServices(ServicesByGsimInputOrOutput)

function ServicesByGsimInputAndOutput({ gsimClass }) {
    return (

Inputs

        <ServicesByGsimInput gsimClass={gsimClass} />

Output

        <ServicesByGsimOuput gsimClass={gsimClass} />

    )
}

ServicesByGsimInputAndOutput.propTypes = {
  gsimClass: PropTypes.string.isRequired
}

export default connectFromRoute(ServicesByGsimInputAndOutput)

Improve services by gsim appearance


import Util from "../helpers/Util";

// Default values for the config form editor
export const AppConfigFormDefaultValues = Util.deepFreeze({
  id: "",
  cmd: "",
  cpus: 1,
  mem: 128,
  disk: 0,
  instances: 1,
  ports: "0"
});

// Default values for an 'empty' app config, or the JSON editor
export const AppConfigDefaultValues = Util.deepFreeze({
  id: null,
  cmd: null,
  cpus: 1,
  mem: 128,
  disk: 0,
  instances: 1,
  ports: [ 0 ]
});

// Complete app configuration with default values
export const AllAppConfigDefaultValues = Util.deepFreeze(
  Object.assign({}, AppConfigDefaultValues, {
    args: null,
    env: {},
    executor: "",
    constraints: [],
    uris: [],
    fetch: [],
    storeUrls: [],
    requirePorts: false,
    backoffSeconds: 1,
    backoffFactor: 1.15,
    maxLaunchDelaySeconds: 3600,
    container: null,
    healthChecks: [],
    dependencies: [],
    upgradeStrategy: {
      minimumHealthCapacity: 1,
      maximumOverCapacity: 1
    },
    labels: {},
    acceptedResourceRoles: null
  })
);

import Util from "../helpers/Util";

// Default values for the config form editor
export const AppConfigFormDefaultValues = Util.deepFreeze({
  id: "",
  cmd: "",
  cpus: 1,
  mem: 128,
  disk: 0,
  instances: 1,
  ports: "0"
});

// Default values for an 'empty' app config, or the JSON editor
export const AppConfigDefaultValues = Util.deepFreeze({
  id: null,
  cmd: null,
  cpus: 1,
  mem: 128,
  disk: 0,
  instances: 1,
  ports: [ 0 ]
});

// Complete app configuration with default values
export const AllAppConfigDefaultValues = Util.deepFreeze(
  Object.assign({}, AppConfigDefaultValues, {
    args: null,
    env: {},
    executor: "",
    constraints: [],
    uris: [],
    fetch: [],
    storeUrls: [],
    requirePorts: false,
    backoffSeconds: 1,
    backoffFactor: 1.15,
    maxLaunchDelaySeconds: 3600,
    container: null,
    healthChecks: [],
    dependencies: [],
    upgradeStrategy: {
      minimumHealthCapacity: 1,
      maximumOverCapacity: 1
    },
    labels: {},
    acceptedResourceRoles: null,
    portDefinitions: []
  })
);

Add portDefinitionos to app config defaults


var WebGameMaker = {};

WebGameMaker.init = function() {

    var plugins = WebGameMaker.PluginManager.getPlugins();
    for (p in plugins) {
        WebGameMaker.UI.addPluginButton(plugins[p]);
    }

}

window.addEventListener('load', function() {
    WebGameMaker.init();
}, false);

var WebGameMaker = {};

WebGameMaker.init = function() {

    var plugins = WebGameMaker.PluginManager.getPlugins();
    for (p in plugins) {
        WebGameMaker.UI.addPluginButton(plugins[p]);
    }

    WebGameMaker.Game = new Game();
    WebGameMaker.update();
}

WebGameMaker.update = function() {
    draw_info = {
        'canvas_context': document.getElementById('canvas').getContext('2d'),
    }

    WebGameMaker.Game.redraw(draw_info);
    window.requestAnimationFrame(WebGameMaker.update);
}

window.addEventListener('load', function() {
    WebGameMaker.init();
}, false);

Add update method to WebGameMaker


const fs = require('fs');
const path = require('path');
const formatter = require('eslint-friendly-formatter');

const port = process.env.PORT || 9000;

module.exports = {
  devtool: 'inline-source-map',
  entry: './src/index.js',
  module: {
    rules: [
      {
        test: /\.(js|vue)$/,
        loader: 'eslint-loader',
        enforce: 'pre',
        exclude: /node_modules/,
        options: { formatter }
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader'
        }
      }
    ]
  },

  output: {
    library: 'Muxy',
    libraryTarget: 'umd',
    umdNamedDefine: true,
    path: path.resolve(__dirname, 'dist'),
    filename: 'muxy-extensions-sdk.js'
  },

  devServer: {
    port,
    https: {
      key: fs.readFileSync('./certs/testing.key'),
      cert: fs.readFileSync('./certs/testing.crt')
    }
  }
};

const fs = require('fs');
const path = require('path');
const webpack = require('webpack');
const formatter = require('eslint-friendly-formatter');

const port = process.env.PORT || 9000;

module.exports = {
  devtool: 'inline-source-map',

  entry: {
    'muxy-extensions-sdk': './src/index.js',
    'muxy-extensions-sdk.min': './src/index.js'
  },

  module: {
    rules: [
      {
        test: /\.(js|vue)$/,
        loader: 'eslint-loader',
        enforce: 'pre',
        exclude: /node_modules/,
        options: { formatter }
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader'
        }
      }
    ]
  },

  output: {
    library: 'Muxy',
    libraryTarget: 'umd',
    umdNamedDefine: true,
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
  },

  plugins: [
    new webpack.optimize.UglifyJsPlugin({
      include: /\.min\.js$/,
      minimize: true
    })
  ],

  devServer: {
    port,
    https: {
      key: fs.readFileSync('./certs/testing.key'),
      cert: fs.readFileSync('./certs/testing.crt')
    }
  }
};

Build minimized versions of sdk


const webpack = require('webpack');
const { env } = require('process');
const isProd = env.NODE_ENV === 'production';
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const isNonNil = x => x != null;
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const isProfile = env.PROFILE == 'true';

let conf = {
  entry: './src/client/index.js',

  output: {
    filename: './build/bundle.js'
  },

  devtool: 'inline-source-map',
  module: {
    rules: [
      { test: /node_modules\/cytoscape\/.*/, loader: 'babel-loader'},
      { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' }
    ]
  },
  resolve: {
    alias: {
      cytoscape: 'cytoscape/src/umd'
    }
  },
  plugins: [
    isProfile ? new BundleAnalyzerPlugin() : null,

    new webpack.EnvironmentPlugin(['NODE_ENV']),

    new webpack.optimize.CommonsChunkPlugin({
      name: 'deps',
      filename: './build/deps.js',
      minChunks( module ){
        let context = module.context || '';

        return context.indexOf('node_modules') >= 0;
      }
    }),

    isProd ? new UglifyJSPlugin() : null
  ].filter( isNonNil )
};

module.exports = conf;

const webpack = require('webpack');
const { env } = require('process');
const isProd = env.NODE_ENV === 'production';
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const isNonNil = x => x != null;
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const isProfile = env.PROFILE == 'true';

let conf = {
  entry: './src/client/index.js',

  output: {
    filename: './build/bundle.js'
  },

  devtool: 'inline-source-map',
  module: {
    rules: [
      { test: /node_modules\/cytoscape\/.*/, loader: 'babel-loader'},
      { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' }
    ]
  },
  resolve: {
    alias: {
      cytoscape: 'cytoscape/src/cjs'
    }
  },
  plugins: [
    isProfile ? new BundleAnalyzerPlugin() : null,

    new webpack.EnvironmentPlugin(['NODE_ENV']),

    new webpack.optimize.CommonsChunkPlugin({
      name: 'deps',
      filename: './build/deps.js',
      minChunks( module ){
        let context = module.context || '';

        return context.indexOf('node_modules') >= 0;
      }
    }),

    isProd ? new UglifyJSPlugin() : null
  ].filter( isNonNil )
};

module.exports = conf;

Update the cytoscape alias to `cytoscape/src/cjs`


var path = require('path')

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['react', 'es2015']
          }
        }
      },
      {
        test: /\.scss$/,
        use: [{
          loader: "style-loader" // creates style nodes from JS strings
        }, {
          loader: "css-loader" // translates CSS into CommonJS
        }, {
          loader: "sass-loader" // compiles Sass to CSS
        }]
      }
    ]
  }
};

var path = require('path')

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'public'),
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['react', 'es2015']
          }
        }
      },
      {
        test: /\.scss$/,
        use: [{
          loader: "style-loader" // creates style nodes from JS strings
        }, {
          loader: "css-loader" // translates CSS into CommonJS
        }, {
          loader: "sass-loader" // compiles Sass to CSS
        }]
      }
    ]
  }
};

Change bundle output dir to public


'use strict';

var gulp = require('gulp');

var config = require('./_config.js');
var paths = config.paths;
var $ = config.plugins;

var istanbul = require('browserify-istanbul');

gulp.task('clean', function () {
  return gulp.src(paths.tmp, {read: true})
    .pipe($.rimraf());
});

gulp.task('build', ['index.html', 'js', 'css']);

gulp.task('index.html', function () {
  return gulp.src(paths.app + '/index.jade')
    .pipe($.jade({
      pretty: true
    }))
    .pipe(gulp.dest(paths.tmp));
});

gulp.task('jade', function () {
  return gulp.src(paths.app + '/*.html')
    .pipe(gulp.dest(paths.tmp));
});

gulp.task('js', function () {
  return gulp.src(paths.app + '/js/main.js', { read: false })
    .pipe($.browserify({
      transform: [istanbul],
      debug: true
    }))
    .pipe(gulp.dest(paths.tmp + '/js'));
});

gulp.task('css', function () {
  // FIXME
  return gulp.src('mama');
});

'use strict';

var gulp = require('gulp');

var config = require('./_config.js');
var paths = config.paths;
var $ = config.plugins;

var istanbul = require('browserify-istanbul');

gulp.task('clean', function () {
  return gulp.src(paths.tmp, { read: false })
    .pipe($.rimraf());
});

gulp.task('build', ['index.html', 'js', 'css']);

gulp.task('index.html', function () {
  return gulp.src(paths.app + '/index.jade')
    .pipe($.jade({
      pretty: true
    }))
    .pipe(gulp.dest(paths.tmp));
});

gulp.task('jade', function () {
  return gulp.src(paths.app + '/*.html')
    .pipe(gulp.dest(paths.tmp));
});

gulp.task('js', function () {
  return gulp.src(paths.app + '/js/main.js', { read: false })
    .pipe($.browserify({
      transform: [istanbul],
      debug: true
    }))
    .pipe(gulp.dest(paths.tmp + '/js'));
});

gulp.task('css', function () {
  // FIXME
  return gulp.src('mama');
});

Fix gulp clean task to not read files before sending them before deletion.


let str = "";
let size = 8;
for (let i = 0; i < size; ++i) {
    for (let j = 0; j < size; ++j) {
        str += (j + i) % 2 ? "#" : " ";
    }
    str += "\n";
}
console.log(str)

let size = 8;

for (let i = 0; i < size; i++) {
	let line = "";
	for (let j = 0; j < size; j++) {
		line += i%2 == j%2 ? " " : "#";
	}
	console.log(line);
}

Add chapter 2, exercise 3


import React from 'react';

export default class Button extends React.Component {

  render() {
    return (
      <div>
        <form action="http://hack.cwru.edu">
          <input type="submit" value="Register!" />
        </form>
      </div>
    );
  }

}

import PropTypes from 'prop-types';
import React from 'react';

export default class Button extends React.Component {

  renderButton() {
    return (

        <button>
          {this.props.title}
        </button>

    )
  }

  render() {
    const { externalLink } = this.props;

    return (

        {externalLink ?
          {this.renderButton()}
         : this.renderButton()}

    );
  }

}

Button.propTypes = {
  title: PropTypes.string,
  externalLink: PropTypes.string
};

Add Button title and externalLink props


// Sample spec for a game of cards

const _ = require('lodash');

const s = require('../lib/spec');

const isSuit = [':club', ':diamond', ':heart', ':spade'];
const isRank = _.range(2, 11).concat([':jack', ':queen', ':king', ':ace']);

// Example: game of cards

const _ = require('lodash');

const s = require('../lib/spec');

const {isInt, isString} = require('../test/utils');

//
// Spec to model a game of cards
//

const isSuit = [':club', ':diamond', ':heart', ':spade'];
const isRank = _.range(2, 11).concat([':jack', ':queen', ':king', ':ace']);

// Original Clojure code to generate deck is way more elegant:
// (def deck (for [suit suit? rank rank?] [rank suit]))

const deck = [...(function* () {for (let suit of isSuit) for (let rank of isRank) yield [suit, rank];})()];

s.def('::card', s.tuple(isRank, isSuit));
s.def('::hand', s.star('::card'));

s.def('::name', isString);
s.def('::score', isInt);
s.def('::player', s.keys({req: ['::name', '::score', '::hand']}));

s.def('::players', s.star('::player'));
s.def('::deck', s.star('::card'));
s.def('::game', s.keys({req: ['::players', '::deck']}));

Make game of cards example more complete


define(function() {
	var Array2d = function () {
		this._columns = {};
	};

	Array2d.prototype.get = function(location) {
		var row = this._columns[location.x];

		if (row)
			return row[location.y];
	};

	Array2d.prototype.set = function (location, value) {
		var row = this._columns[location.x] || (this._columns[location.x] = {});

		if (row[location.y])
			return false;

		row[location.y] = value;

		return true;
	};

	Array2d.prototype.clear = function (location) {
		var row = this._columns[location.x];

		if (!row)
			return false;

		var value = row[location.y];

		if (value) {
			delete row[location.y];

			return value;
		}

		return false;
	};

	return Array2d;
});

define(function() {
	var Array2d = function () {
		this._columns = {};
	};

	Array2d.prototype.get = function(location) {
		var row = this._columns[location.x];

		if (row)
			return row[location.y];
	};

	Array2d.prototype.set = function (location, value) {
		var row = this._columns[location.x] || (this._columns[location.x] = {});

		if (row[location.y])
			return false;

		row[location.y] = value;

		return true;
	};

	Array2d.prototype.each = function (callback, context) {
		var context = context || this;
		var columns;

		Object.keys(this._columns).forEach(function (outerKey) {
			columns = this._columns[key];

			Object.keys(columns).forEach(function (innerKey) {
				callback.call(context, outerKey, innerKey, columns[innerKey]);
			}, this);
		}, this);
	};

	Array2d.prototype.clear = function (location) {
		var row = this._columns[location.x];

		if (!row)
			return false;

		var value = row[location.y];

		if (value) {
			delete row[location.y];

			return value;
		}

		return false;
	};

	return Array2d;
});

Add 'each' method to Array2d.


import waitUntil from '../wait-until';
import { getContext } from '../setup-context';
import getElement from './-get-element';

/**
  @method waitFor
  @param {string|Element} target
  @param {Object} [options]
  @param {number} [options.timeout=1000]
  @param {number} [options.count=1]
*/
export default function waitFor(selector, { timeout = 1000, count = null } = {}) {
  let callback;
  if (count !== null) {
    callback = () => {
      let context = getContext();
      let rootElement = context && context.element;
      let elements = rootElement.querySelectorAll(selector);
      if (elements.length === count) {
        return elements;
      }
    };
  } else {
    callback = () => getElement(selector);
  }
  return waitUntil(callback, { timeout });
}

import waitUntil from '../wait-until';
import { getContext } from '../setup-context';
import getElement from './-get-element';

function toArray(nodelist) {
  let array = new Array(nodelist.length);
  for (let i = 0; i < nodelist.length; i++) {
    array[i] = nodelist[i];
  }

  return array;
}

/**
  @method waitFor
  @param {string|Element} target
  @param {Object} [options]
  @param {number} [options.timeout=1000]
  @param {number} [options.count=1]
  @returns {Element|Array<Element>}
*/
export default function waitFor(selector, { timeout = 1000, count = null } = {}) {
  let callback;
  if (count !== null) {
    callback = () => {
      let context = getContext();
      let rootElement = context && context.element;
      let elements = rootElement.querySelectorAll(selector);
      if (elements.length === count) {
        return toArray(elements);
      }
    };
  } else {
    callback = () => getElement(selector);
  }
  return waitUntil(callback, { timeout });
}

Return an array of elements for `waitFor` with `count`.


// use higher values to slow down tests for debugging
var promisesDelay;

// const syncingTestsActive = (process.argv.length > 5 && process.argv[5] == '--suite=syncing');
promisesDelay = 0;

function delayPromises(milliseconds) {
    var executeFunction = browser.driver.controlFlow().execute;

    browser.driver.controlFlow().execute = function() {
        var args = arguments;

        executeFunction.call(browser.driver.controlFlow(), function() {
            return protractor.promise.delayed(milliseconds);
        });

        return executeFunction.apply(browser.driver.controlFlow(), args);
    };
}

console.log("Set promises delay to " + promisesDelay + " ms.");
delayPromises(promisesDelay);

var ECWaitTime = 20000;
var shortRest = 200;

module.exports = {
    ECWaitTime: ECWaitTime,
    shortRest: shortRest
};

// use higher values to slow down tests for debugging
var promisesDelay;

const syncingTestsActive = (process.argv.length > 5 && process.argv[5] == '--suite=syncing');
if (syncingTestsActive) {
    promisesDelay = 150;
} else {
    promisesDelay = 0;
}

function delayPromises(milliseconds) {
    var executeFunction = browser.driver.controlFlow().execute;

    browser.driver.controlFlow().execute = function() {
        var args = arguments;

        executeFunction.call(browser.driver.controlFlow(), function() {
            return protractor.promise.delayed(milliseconds);
        });

        return executeFunction.apply(browser.driver.controlFlow(), args);
    };
}

console.log("Set promises delay to " + promisesDelay + " ms.");
delayPromises(promisesDelay);

var ECWaitTime = 20000;
var shortRest = 200;

module.exports = {
    ECWaitTime: ECWaitTime,
    shortRest: shortRest
};

Revert "set promises delay to 0ms for syncing tests"


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

var should = chai.should();
chai.Assertion.includeStack = true;
chai.use(require('sinon-chai'));

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

require('sinon-doublist')('mocha');

describe('cli-mod', function() {
  describe('CliMod', function() {
    beforeEach(function() {
      this.input = {};
      this.ci = cliMod.create();
    });

    it('should do something', function() {
      console.log('\x1B[33m<---------- INCOMPLETE');
    });
  });
});

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

var should = chai.should();
chai.Assertion.includeStack = true;
chai.use(require('sinon-chai'));

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

require('sinon-doublist')(sinon, 'mocha');

describe('cli-mod', function() {
  describe('CliMod', function() {
    beforeEach(function() {
      this.input = {};
      this.ci = cliMod.create();
    });

    it('should do something', function() {
      console.log('\x1B[33m<---------- INCOMPLETE');
    });
  });
});

Update use to latest API


// Minimap

var minimap = {
    minilist: function () {
        console.log("Mini list started");
        $pages = jQuery("#minimap__plugin  .list-group-item")
        $pages.map(a => console.log($pages[a].innerText));

    }
}

window.addEventListener("load", function() {
    minimap.minilist();
});

// Minimap

var minimap = {
    minilist: function () {
        console.log("Mini list started");
        $pages = jQuery("#minimap__plugin  .list-group-item")
        $pages.map(a => console.log($pages[a].innerText));
    }
}

// window.addEventListener("load", function() {
//     minimap.minilist();
// });

Disable the load of the experimental code


(function() {
  'use strict';

  var app = angular.module('radar.store');

  function unauthorizedResponseFactory($q, session, notificationService, $state) {
    return function(promise) {
      return promise['catch'](function(response) {
        // API endpoint requires login (token may have expired)
        if (response.status === 401) {
          session.logout();
          notificationService.info({message: 'You have been logged out.', timeout: 0});
          $state.go('login');
        }

        return $q.reject(response);
      });
    };
  }

  unauthorizedResponseFactory.$inject = [
    '$q', 'authService', 'notificationService', '$state'
  ];

  app.factory('unauthorizedResponse', unauthorizedResponseFactory);

  app.config(['adapterProvider', function(adapterProvider) {
    adapterProvider.afterResponse('unauthorizedResponse');
  }]);
})();

(function() {
  'use strict';

  var app = angular.module('radar.store');

  function unauthorizedResponseFactory($q, session, notificationService, $state, $rootScope) {
    var notification = null;

    // Hide logout notification on login
    $rootScope.$on('sessions.login', function() {
      if (notification !== null) {
        notification.remove();
        notification = null;
      }
    });

    return function(promise) {
      return promise['catch'](function(response) {
        // API endpoint requires login (token may have expired)
        if (response.status === 401) {
          session.logout();

          if (notification === null) {
            notification = notificationService.info({message: 'You have been logged out.', timeout: 0});
          }

          $state.go('login');
        }

        return $q.reject(response);
      });
    };
  }

  unauthorizedResponseFactory.$inject = [
    '$q', 'authService', 'notificationService', '$state', '$rootScope'
  ];

  app.factory('unauthorizedResponse', unauthorizedResponseFactory);

  app.config(['adapterProvider', function(adapterProvider) {
    adapterProvider.afterResponse('unauthorizedResponse');
  }]);
})();

Hide logout message on login


import Ember from 'ember';

export default Ember.Route.extend({
  model: function() {
    return this.store.createRecord('runscript');
  },
  actions: {
    cancel: function() {
      this.transitionTo('runscripts');
    },

    save: function(runscript) {
      console.log(runscript.get("name"));
      runscript.save();
    }
  }
});

import Ember from 'ember';

export default Ember.Route.extend({
  model: function() {
    return Ember.Object.create();
  },

  actions: {
    cancel: function() {
      this.transitionTo('runscripts');
    },

    save: function(form) {
      var runscript = this.store.createRecord('runscript', {
        name: form.get('name'),
        script: form.get('script')
      });

      runscript.save();
    }
  }
});

Use an Ember.Object as model


$(document).ready(function() {
  var $body = $('body');
  var className = 'dial-congress';
  var senateData;

  var scan = function() {
    $.each(senateData, function(i, senator) {
      var firstLast = senator.firstName + '\\s+' + senator.lastName;
      var lastFirst = senator.lastName + ',\\s*' + senator.firstName;
      var withTitle = '(?:Senator\\s+|Sen.\\s+|Congressman\\s+|Congresswoman\\s+)(' + firstLast + '|' + senator.lastName + ')';
      var regExp = new RegExp(firstLast + '|' + lastFirst + '|' + withTitle, 'ig');

      $body.highlightRegex(regExp, {
        className: className,
        attrs: {
          'data-phone': senator.phone,
          'title': senator.phone
        }
      });
    });
  }

  var createTooltips = function() {
    var $critters = $('.' + className);

    $critters.each(function(i, critter) {
      var $critter = $(critter);
      $critter.tooltipster();
    });
  }

  $.when(
    $.get(chrome.extension.getURL('js/senate.json'), function(data) {
      senateData = JSON.parse(data);
    })
  ).then(function() {
    scan();
    createTooltips();
  })
});

$(document).ready(function() {
  var $body = $('body');
  var className = 'dial-congress';
  var senateData;

  var scan = function() {
    $.each(senateData, function(i, senator) {
      var firstLast = senator.firstName + '\\s+' + senator.lastName;
      var lastFirst = senator.lastName + ',\\s*' + senator.firstName;
      var withTitle = '(?:Senator\\s+|Sen.\\s+|Congressman\\s+|Congresswoman\\s+)(' + firstLast + '|' + senator.lastName + ')';
      var regExp = new RegExp(firstLast + '|' + lastFirst + '|' + withTitle, 'ig');

      $body.highlightRegex(regExp, {
        className: className,
        attrs: {
          'title': senator.party + '/' + senator.state + ': ' + senator.phone
        }
      });
    });
  }

  var createTooltips = function() {
    var $critters = $('.' + className);

    $critters.each(function(i, critter) {
      var $critter = $(critter);
      $critter.tooltipster({
        interactive: true
      });
    });
  }

  $.when(
    $.get(chrome.extension.getURL('js/senate.json'), function(data) {
      senateData = JSON.parse(data);
    })
  ).then(function() {
    scan();
    createTooltips();
  })
});

Add party and state to tooltip


var EC = protractor.ExpectedConditions;
var map = require('./map/map.page');

describe('map', function() {
    it('should include a heatmap on the grako_map page', function() {
        browser.get('/project/grako_map');
        var heatmap = map.getHeatmap();
        expect(heatmap.isPresent()).toBe(true);
    });

    it('it should show some markers for small result size', function () {
        browser.get('/map?zoom=12&lat=50.42116487566384&lng=4.902398681640625');
       	var marker = map.getMarkers();
       	expect(marker.count()).not.toBeLessThan(1);
    });

    xit('should show as many markers as many previous storage places exist', function() {

    	browser.get('/entity/1076902');

		browser.sleep(500).then(function() {
			var marker = map.getMarkers();
			expect(marker.count()).toBe(3);
		})

	})
});

var EC = protractor.ExpectedConditions;
var map = require('./map/map.page');

describe('map', function() {
    it('should include a heatmap on the grako_map page', function() {
        browser.get('/project/grako_map?lang=de');
        var heatmap = map.getHeatmap();
        expect(heatmap.isPresent()).toBe(true);
    });

    it('it should show some markers for small result size', function () {
        browser.get('/map?zoom=12&lat=50.42116487566384&lng=4.902398681640625');
       	var marker = map.getMarkers();
       	expect(marker.count()).not.toBeLessThan(1);
    });

    xit('should show as many markers as many previous storage places exist', function() {

    	browser.get('/entity/1076902');

		browser.sleep(500).then(function() {
			var marker = map.getMarkers();
			expect(marker.count()).toBe(3);
		})

	})
});

Use the german version of grako_map.


Target = function(x, y) {
  this.x = x;
  this.y = y;
  this.life = 11;
  return this;
}
Target.prototype.render = (function(context) {
  function render() {
    var x = this.x - gCamera.x;
    var y = this.y - gCamera.y;

    // Only render if within the camera's bounds
    if (x + 12 > 0 && x - 12 < SCREEN_WIDTH && y + 12 > 0 && y - 12 < SCREEN_HEIGHT) {
      context.beginPath();
      context.arc(x + 2, y + 2, 13, 0, Math.PI * 2);
      context.fillStyle = '#222';
      context.fill();
      context.closePath();

      context.beginPath();
      context.arc(x, y, 12, 0, Math.PI * 2);
      context.fillStyle = 'rgb(' + Math.round(255 * (this.life / 12)) + ',' + Math.round(255 * ((12 - this.life) / 12)) + ',' + 0x22 + ')';
      context.fill();
      context.closePath();
    }
  }
  return render;
})(gRenderer.context);

Target = function(x, y) {
  this.x = x;
  this.y = y;
  this.life = 11;
  this.size = 12;

  return this;
}
Target.prototype.render = (function(context) {
  function render() {
    var x = this.x - gCamera.x;
    var y = this.y - gCamera.y;

    // Only render if within the camera's bounds
    if (gCamera.isInView(this)) {
      context.beginPath();
      context.arc(x + 2, y + 2, 13, 0, Math.PI * 2);
      context.fillStyle = '#222';
      context.fill();
      context.closePath();

      context.beginPath();
      context.arc(x, y, this.size, 0, Math.PI * 2);
      context.fillStyle = 'rgb(' + Math.round(255 * (this.life / this.size)) + ',' + Math.round(255 * ((this.size - this.life) / this.size)) + ',' + 0x22 + ')';
      context.fill();
      context.closePath();
    }
  }
  return render;
})(gRenderer.context);

Update Target rendering to use gCamera.isInView


export default {
  title: 'Load Texture',
  description: 'Simple example to load a texture',
  tags: 'ImageLoader, Image',
  public: true,
  scripts: [
    // '../../libs/three/r84/three.js',
    'http://127.0.0.1:8080/build/three.js',
    '../../libs/three/r83/controls/OrbitControls.js',
    '../../libs/stats/r17/stats.min.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: 'Load Texture on other domain',
  description: 'Simple example to load a texture with CORS',
  tags: 'ImageLoader, Image, CORS',
  public: true,
  scripts: [
    // '../../libs/three/r84/three.js',
    'http://127.0.0.1:8080/build/three.js',
    '../../libs/three/r83/controls/OrbitControls.js',
    '../../libs/stats/r17/stats.min.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 description of load texture


import Immutable from 'immutable';

const project = {
  projectKey: '12345',
  sources: {html: '', css: '', javascript: ''},
  enabledLibraries: [],
};

const state = {
  currentProject: new Immutable.Map({projectKey: exports.project.projectKey}),
  projects: Immutable.fromJS({12345: exports.project}),
};

export {project, state};

import Immutable from 'immutable';

const project = {
  projectKey: '12345',
  sources: {html: '', css: '', javascript: ''},
  enabledLibraries: [],
};

const state = {
  currentProject: new Immutable.Map({projectKey: project.projectKey}),
  projects: Immutable.fromJS({12345: project}),
};

export {project, state};

Remove incorrect references to exports


'use strict';

/**
* Test if the global scope is bound to the "global" variable present in Node.js environments. When creating a new function using the `Function(){}` constructor, the execution scope aliased by the `this` variable is the global scope.
*
* @private
* @returns {boolean} boolean indicating if global scope is bound to "global" variable
*/
function globalScope() {
	/* jshint evil:true */ // TODO: eslint
	var fcn = '';
	fcn += 'try {';
	fcn += 'return this === global;';
	fcn += '} catch ( err ) {';
	fcn += 'return false;';
	fcn += '}';
	return (new Function( fcn ))();
} // end FUNCTION globalScope()

// EXPORTS //

module.exports = globalScope();

/* eslint-disable no-new-func */
'use strict';

/**
* Test if the global scope is bound to the "global" variable present in Node.js environments. When creating a new function using the `Function(){}` constructor, the execution scope aliased by the `this` variable is the global scope.
*
* @private
* @returns {boolean} boolean indicating if global scope is bound to "global" variable
*/
function globalScope() {
	var fcn = '';
	fcn += 'try {';
	fcn += 'return this === global;';
	fcn += '} catch ( err ) {';
	fcn += 'return false;';
	fcn += '}';
	return (new Function( fcn ))();
} // end FUNCTION globalScope()

// EXPORTS //

module.exports = globalScope();

Disable no-new-func lint rule and remove TODO


'use strict';

var hairfieApi = require('../../services/hairfie-api-client');
var Facebook = require('../../services/facebook');
var login = require('./utils').login;

module.exports =  function (context, payload, done) {
    Facebook
        .load()
        .then(function (fb) {
            return login(fb, payload.scope);
        })
        .then(function (token) {
            return hairfieApi.linkFacebookToken(token, context.getAuthToken());
        })
        .then(function () {
            done();
        })
        .fail(function (error) {
            done(error);
        });
};

'use strict';

var hairfieApi = require('../../services/hairfie-api-client');
var Facebook = require('../../services/facebook');
var FacebookEvents = require('../../constants/FacebookConstants').Events;
var login = require('./utils').login;
var RefreshPermissions = require('./RefreshPermissions');

module.exports =  function (context, payload, done) {
    Facebook
        .load()
        .then(function (fb) {
            return login(fb, payload.scope)
                .then(function(token) {
                    // dispatch status to refresh permissions
                    // TODO: dispatch a dedicated event?
                    fb.getLoginStatus(function (loginStatus) {
                        context.dispatch(FacebookEvents.RECEIVE_LOGIN_STATUS, {
                            loginStatus: loginStatus
                        });
                    });

                    return token;
                });
        })
        .then(function (token) {
            return hairfieApi.linkFacebookToken(token, context.getAuthToken());
        })
        .then(done.bind(null, null), done);
};

Refresh login status on link


const path = require("path");

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"]
      },
      {
        test: /\.js$/,
        exclude: /(node_modules)/,
        use: {
          loader: "babel-loader",
          options: {
            forceEnv: "library"
          }
        }
      },
      {
        test: /\.(wsz|mp3)$/,
        use: [
          {
            loader: "file-loader",
            options: {
              emitFile: true,
              name: "[path][name]-[hash].[ext]"
            }
          }
        ]
      }
    ],
    noParse: [/jszip\.js$/]
  },
  entry: ["./js/winamp.js"],
  output: {
    path: path.resolve(__dirname, "built"),
    filename: "winamp.bundle.js",
    library: "winamp2js",
    libraryTarget: "umd"
  }
};

const path = require("path");
const webpack = require("webpack");

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"]
      },
      {
        test: /\.js$/,
        exclude: /(node_modules)/,
        use: {
          loader: "babel-loader",
          options: {
            forceEnv: "library"
          }
        }
      },
      {
        test: /\.(wsz|mp3)$/,
        use: [
          {
            loader: "file-loader",
            options: {
              emitFile: true,
              name: "[path][name]-[hash].[ext]"
            }
          }
        ]
      }
    ],
    noParse: [/jszip\.js$/]
  },
  plugins: [
    new webpack.DefinePlugin({
      "process.env": {
        NODE_ENV: JSON.stringify("production")
      }
    }),
    new webpack.optimize.UglifyJsPlugin({
      minimize: true,
      include: /\.min\.js$/
    })
  ],
  entry: {
    bundle: "./js/winamp.js",
    "bundle.min": "./js/winamp.js"
  },
  output: {
    path: path.resolve(__dirname, "built"),
    filename: "[name].js",
    library: "winamp2js",
    libraryTarget: "umd"
  }
};

Move NPM repo to production mode. Add minified version.


angular.module('Mccy.routes', [
        'ngRoute'
    ])

    .config(function ($routeProvider) {
        $routeProvider
            .when('/view', {
                templateUrl: 'views/view-containers.html',
                controller: 'ViewContainersCtrl'
            })
            .when('/new-server', {
                templateUrl: 'views/create-container.html',
                controller: 'NewContainerCtrl'
            })
            .when('/upload-mod', {
                templateUrl: 'views/upload-mod.html',
                controller: 'UploadModCtrl'
            })
            .when('/manage-mods', {
                templateUrl: 'views/manage-mods.html',
                controller: 'ManageModsCtrl'
            })
            .otherwise('/view')
    })

    .constant('MccyViews', [
        {
            view: '/view',
            label: 'Current Containers'
        },
        {
            view: '/new-server',
            label: 'New Container'
        },
        {
            view: '/upload-mod',
            label: 'Upload Mods'
        },
        {
            view: '/manage-mods',
            label: 'Manage Mods'
        }
    ])
;

angular.module('Mccy.routes', [
        'ngRoute'
    ])

    .config(function ($routeProvider) {
        $routeProvider
            .when('/view', {
                templateUrl: 'views/view-containers.html',
                controller: 'ViewContainersCtrl'
            })
            .when('/new-server', {
                templateUrl: 'views/create-container.html',
                controller: 'NewContainerCtrl'
            })
            .when('/upload-mod', {
                templateUrl: 'views/upload-mod.html',
                controller: 'UploadModCtrl'
            })
            .when('/manage-mods', {
                templateUrl: 'views/manage-mods.html',
                controller: 'ManageModsCtrl'
            })
            .otherwise('/view')
    })

    .constant('MccyViews', [
        {
            view: '/view',
            label: 'Current Containers'
	    icon: 'icon fa fa-heartbeat'
	    show: true
        },
        {
            view: '/new-server',
            label: 'New Container'
	    icon: 'icon fa fa-magic'
	    show: true
        },
        {
            view: '/upload-mod',
            label: 'Upload Mods'
	    icon: 'icon fa fa-upload'
	    show: true
        },
        {
            view: '/manage-mods',
            label: 'Manage Mods'
	    icon: 'icon fa fa-flask'
	    show: true
        }
    ])
;

Add icon information for views


/**
 * UI and function to add project
 */
'use strict';

var $ = require('jquery');
var m = require('mithril');
var $osf = require('js/osfHelpers');

var AddProject = {
    controller : function (options) {
        var self = this;
        self.defaults = {
            parent : null
        };
        self.viewState = m.prop('form'); // 'processing', 'done', 'error';
        self.options = $.extend(self.defaults, options);
        self.init = function _init () {

        };
        self.add = function _add () {

        };
    },
    view : function (ctrl, options) {
        var templates = {
            form : m('.modal-content', [
                m('.modal-header', [
                    m('button.close[data-dismiss="modal"][aria-label="Close"]', [
                        m('span[aria-hidden="true"]','×'),
                    ]),
                    m('h3.modal-title#addProject', 'Add New Project')
                ]),
                m('.modal-body', [
                    m('p', 'You are adding project '),
                    m('.form-inline', [
                        m('.form-group', [
                            m('label[for="addCollInput]', 'Collection Name'),
                            m('input[type="text"].form-control.m-l-sm#addCollInput', {onchange: m.withAttr('value', ctrl.newCollectionName), value: ctrl.newCollectionName()})

                        ])
                    ]),
                    m('p.m-t-sm', 'After you create your collection drag and drop projects to the collection. ')
                ]),
                m('.modal-footer', [
                    m('button[type="button"].btn.btn-default[data-dismiss="modal"]', 'Cancel'),
                    m('button[type="button"].btn.btn-success', { onclick : ctrl.add },'Add')
                ])
            ]),
            processing : m('.modal-content', []),
            done : m('.modal-content', []),
            error : m('.modal-content', [])
        };

        return  m('.addProjectModal', [
            m('.modal.fade[tabindex=-1][role="dialog"][aria-labelledby="addProject"][aria-hidden="true"]',
                m('.modal-dialog',
                    templates[ctrl.viewState]
                )
            )
        ]);
    }
};

Add new project adding module


import React, { Component } from 'react'

import globalConfig from '../../config'

import { fetchLocation, fetchOrganization } from '../../core/firebaseRestAPI'

import Layout from '../../components/Layout'
import Loading from '../../components/Loading'
import Location from '../../components/Location'

export default class LocationPage extends Component {
  constructor(props) {
    super(props)
    this.state = {
      location: null,
      organization: null,
    }
  }

  componentDidMount() {
    const match = this.props.route.pattern.exec(window.location.pathname)
    const locationId = match[1]

    fetchLocation(locationId)
      .then(location => {
        this.setState({ location })
        fetchOrganization(location.organizationId)
          .then(organization => {
            this.setState({ organization })
          })
      })
  }

  componentDidUpdate() {
    const { location } = this.state
    document.title = location ? location.name : globalConfig.title
  }

  render() {
    const { location, organization } = this.state
    console.log(location)
    console.log(organization)

    return (
      <Layout>
        {(location && organization) ?
          <Location location={location} organization={organization} /> :
          <Loading />
        }
      </Layout>
    )
  }
}

import React, { Component } from 'react'

import globalConfig from '../../config'

import { fetchLocation, fetchOrganization } from '../../core/firebaseRestAPI'

import Layout from '../../components/Layout'
import Loading from '../../components/Loading'
import Location from '../../components/Location'

export default class LocationPage extends Component {
  constructor(props) {
    super(props)
    this.state = {
      location: null,
      organization: null,
    }
  }

  componentDidMount() {
    const match = this.props.route.pattern.exec(window.location.pathname)
    const locationId = match[1]

    fetchLocation(locationId)
      .then(location => {
        this.setState({ location })
      })
    fetchOrganization(location.organizationId)
      .then(organization => {
        this.setState({ organization })
      })
  }

  componentDidUpdate() {
    const { location } = this.state
    document.title = location ? location.name : globalConfig.title
  }

  render() {
    const { location, organization } = this.state

    return (
      <Layout>
        {(location && organization) ?
          <Location location={location} organization={organization} /> :
          <Loading />
        }
      </Layout>
    )
  }
}

Revert "Load org inside location"


// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

'use strict';

base.require('base.guid');
base.require('tracing.trace_model.event');

/**
 * @fileoverview Provides the TimedEvent class.
 */
base.exportTo('tracing.trace_model', function() {
  /**
   * A TimedEvent is the base type for any piece of data in the trace model with
   * a specific start and duration.
   *
   * @constructor
   */
  function TimedEvent(start) {
    tracing.trace_model.Event.call(this);
    this.guid_ = base.GUID.allocate();
    this.start = start;
    this.duration = 0;
  }

  TimedEvent.prototype = {
    __proto__: tracing.trace_model.Event.prototype,

    get end() {
      return this.start + this.duration;
    },

    addBoundsToRange: function(range) {
      range.addValue(this.start);
      range.addValue(this.end);
    }
  };

  return {
    TimedEvent: TimedEvent
  };
});

// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

'use strict';

base.require('base.guid');
base.require('tracing.trace_model.event');

/**
 * @fileoverview Provides the TimedEvent class.
 */
base.exportTo('tracing.trace_model', function() {
  /**
   * A TimedEvent is the base type for any piece of data in the trace model with
   * a specific start and duration.
   *
   * @constructor
   */
  function TimedEvent(start) {
    tracing.trace_model.Event.call(this);
    this.start = start;
    this.duration = 0;
  }

  TimedEvent.prototype = {
    __proto__: tracing.trace_model.Event.prototype,

    get end() {
      return this.start + this.duration;
    },

    addBoundsToRange: function(range) {
      range.addValue(this.start);
      range.addValue(this.end);
    }
  };

  return {
    TimedEvent: TimedEvent
  };
});

Fix TimedEvent sets GUID twice