'use strict';
var alter = require('./alter');
var batoto = require('./batoto');
var kissmanga = require('./kissmanga');
var mangafox = require('./mangafox');

/**
 * Retrieves a series.
 * @param {string} address
 * @return {Series}
 */
module.exports = function (address) {
    var series = batoto(address) || kissmanga(address) || mangafox(address);
    if (series) {
        var populate = series.children;
        series.children = function ($) {
            var children = populate($);
            alter(children);
            return children;
        };
    }
    return series;
};

'use strict';
var alter = require('./alter');
var batoto = require('./batoto');
var kissmanga = require('./kissmanga');
var mangafox = require('./mangafox');
var providers = [batoto, kissmanga, mangafox];

/**
 * Retrieves a series.
 * @param {string} address
 * @return {Series}
 */
module.exports = function (address) {
    var series;
    providers.every(function (provider) {
        series = provider(address);
        return !Boolean(series);
    });
    if (series) {
        var populate = series.children;
        series.children = function ($) {
            var children = populate($);
            alter(children);
            return children;
        };
    }
    return series;
};

/**
 * Adds a provider.
 * @param {!Provider} provider
 */
module.exports.add = function (provider) {
    providers.push(provider);
};

Enable runtime addition of third party providers.


var round = function(num)
{
  return (num + 0.5) | 0
}
var floor = function(num)
{
  return num | 0
}
var ceil = function(num)
{
  return (num | 0) == num ? num | 0 : (num + 1) | 0
}
var abs = Math.abs
var sqrt = Math.sqrt

var log = function(num)
{
  var result = Math.log(num)

  return result
}

var signed_log = function(num)
{
  var sign = num < 0 ? -1 : 1;

  var result = Math.log(abs(num))

  return sign * result
}

var max = function(a, b)
{
  return (a > b) ? a : b;
}

var min = function(a, b)
{
  return (a < b) ? a : b;
}

var compact_array = function(old_all)
{
  var new_all = []
  for (var obj in old_all)
  {
    if (old_all[obj] != undefined)
    {
      new_all.push(old_all[obj])
    }
  }
  return new_all;
}

var count_object_keys = function(obj)
{
  var result = 0;
  for (var prop in obj)
  {
    if (obj.hasOwnProperty(prop))
      result++
  }

  return result
}

var warn = function()
{
  console.log.apply(console, arguments)
}

var die = function(e)
{
  throw new Error(e)
}

Add a die function to quickly throw errors


import { apiProxyAxios } from '../../../components/Task/utils'
import { transformResponseToEventDetails } from '../transformers'

export const getEventDetails = (eventId) =>
  apiProxyAxios
    .get(`/api-proxy/v3/event/${eventId}`)
    .then(({ data }) => transformResponseToEventDetails(data))

import { apiProxyAxios } from '../../../components/Task/utils'
import { transformResponseToEventDetails } from '../transformers'

export const getEventDetails = (eventId) =>
  apiProxyAxios
    .get(`/api-proxy/v4/event/${eventId}`)
    .then(({ data }) => transformResponseToEventDetails(data))

Update the event endpoint from v3 to v4


const lag = require('./lag');

module.exports = (user, pings) => {
  const original = document.title;
  user.events.on('read', () => {
    document.title = `${original} (${lag.humanize(pings.currentLag())})`;
  });
};

const lag = require('./lag');

module.exports = (user, pings) => {
  const original = document.title;
  user.events.on('read', () => {
    document.title = `${lag.humanize(pings.currentLag())} - ${original}`;
  });
};

Increase the chances of seeing the actual lag when on narrow page tabs


$(document).ready(function() {
  var supervisor_provider = new Bloodhound({
    datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
    queryTokenizer: Bloodhound.tokenizers.whitespace,
    remote: '/employees?q=%QUERY'
  });

  supervisor_provider.initialize();

  $('.typeahead-supervisor').typeahead(null, {
    name: 'supervisor',
    displayKey: 'value',
    source: supervisor_provider.ttAdapter(),
    templates: {
      empty: [
      '
',
      $.i18n._('notice_supervisor_not_found'),
      '
'
      ].join('\n'),
      suggestion: Handlebars.compile([
        '
{{value}}
',
        '
{{academy_unit}}
'
        ].join(''))
    }
  }).on('typeahead:selected', function(event, datum) {
    $(event.target).closest("div").children().last().val(datum.id);
  });
  $('.typeahead.input-sm').siblings('input.tt-hint').addClass('hint-small');
  $('.typeahead.input-lg').siblings('input.tt-hint').addClass('hint-large');

});

var supervisor_autocomplete = function() {
  var supervisor_provider = new Bloodhound({
    datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
    queryTokenizer: Bloodhound.tokenizers.whitespace,
    remote: '/employees?q=%QUERY'
  });

  supervisor_provider.initialize();

  $('.typeahead-supervisor').typeahead(null, {
    name: 'supervisor',
    displayKey: 'value',
    source: supervisor_provider.ttAdapter(),
    templates: {
      empty: [
      '
',
      $.i18n._('notice_supervisor_not_found'),
      '
'
      ].join('\n'),
      suggestion: Handlebars.compile([
        '
{{value}}
',
        '
{{academy_unit}}
'
        ].join(''))
    }
  }).on('typeahead:selected', function(event, datum) {
    $(event.target).closest("div").children().last().val(datum.id);
  });
  $('.typeahead.input-sm').siblings('input.tt-hint').addClass('hint-small');
  $('.typeahead.input-lg').siblings('input.tt-hint').addClass('hint-large');
};

$(document).ready(function() {
  supervisor_autocomplete();
});

Move supervisor autocomplete into function


var imageStore = new FS.Store.GridFS('images')

Images = new FS.Collection('images', {
  stores: [imageStore]
});

Images.allow({
  'insert' : function(){
    return true;
  },
  'update' : function(){
    return true;
  }
});

var imageStore = new FS.Store.GridFS('images')

Images = new FS.Collection('images', {
  stores: [imageStore]
});

Images.allow({
  'insert' : function(){
    return true;
  },
  'update' : function(){
    return true;
  },
  'download' : function(){
    return true;
  }
});

Set permission to download photo from CFS


/*
 * composable-middleware
 * https://github.com/randymized/composable-middleware
 *
 * Copyright (c) 2013 Randy McLaughlin
 * Licensed under the MIT license.
 */

'use strict';

module.exports= function composable_middleware(components) {
  var stack= [];

  function middleware(req,res,out) {
    var layer= 0;
    var stacklength= stack.length;
    (function next(err) {
      var fn= stack[layer++];
      if (!fn) {
        return out(err);
      }
      else {
        if (err) {
          if (fn.length == 4) {
            fn(err,req,res,next);
          }
          else {
            next(err);
          }
        }
        else {
          if (fn.length == 3) {
            fn(req,res,next);
          }
          else {
            next();
          }
        }
      }
    })()
  }

  var use= middleware.use= function(mw) {
    for (var i=0, len=arguments.length; i<len; i++) {
      var a= arguments[i];
      if (Array.isArray(a)) {
        use.apply(this,a)
      }
      else if (typeof a === 'function' && (a.length === 3 || a.length === 4)){
        stack.push(a);
      }
      else {
        throw new TypeError('Middleware function was expected');
      }
    }
    return middleware;
  }

  use.apply(middleware,arguments);
  return middleware;
}

/*
 * composable-middleware
 * https://github.com/randymized/composable-middleware
 *
 * Copyright (c) 2013 Randy McLaughlin
 * Licensed under the MIT license.
 */

'use strict';

module.exports= function composable_middleware(components) {
  var stack= [];

  function middleware(req,res,out) {
    var layer= 0;
    var stacklength= stack.length;
    (function next(err) {
      var fn= stack[layer++];
      if (fn == null) {
        return out(err);
      }
      else {
        if (err) {
          if (fn.length == 4) {
            fn(err,req,res,next);
          }
          else {
            next(err);
          }
        }
        else {
          if (fn.length == 3) {
            fn(req,res,next);
          }
          else {
            next();
          }
        }
      }
    })()
  }

  var use= middleware.use= function(mw) {
    for (var i=0, len=arguments.length; i<len; i++) {
      var a= arguments[i];
      if (Array.isArray(a)) {
        use.apply(this,a)
      }
      else if (typeof a === 'function' && (a.length === 3 || a.length === 4)){
        stack.push(a);
      }
      else {
        throw new TypeError('Middleware function was expected');
      }
    }
    return middleware;
  }

  use.apply(middleware,arguments);
  return middleware;
}

Test reaching end of stack


import DS from 'ember-data';

export default DS.Transform.extend({
  deserialize(serialized) {
    return serialized;
  },

  serialize(deserialized) {
    return deserialized;
  }
});

import DS from 'ember-data';

const format = new ol.format.GeoJSON()

export default DS.Transform.extend({
  deserialize(serialized) {
    return format.readGeometry(serialized);
  },
  serialize(deserialized) {
    return format.writeGeometry(deserialized);
  }
});

Add GeoJSON transform for geometry


import React, { Component, PropTypes, StyleSheet, Text, View  } from 'react-native';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import getLocationToSave from '../actions/action_dropNewPin.js';
import updatePins from '../actions/action_updatePins.js';
import deletePin from '../actions/action_deletePin.js';
import updateRecent from '../actions/action_updateRecent.js';
import setTarget from '../actions/action_setTarget.js';
import ViewContainer from '../components/ViewContainer.js';

function mapStateToProps(state) {
  return {
    pins: state.pins,
    recent: state.recent,
    friends: state.friends,
    user: state.user,
  };
}

export default connect(mapStateToProps, { getLocationToSave, updatePins, deletePin, updateRecent, setTarget })(ViewContainer);

import React, { Component, PropTypes, StyleSheet, Text, View  } from 'react-native';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import getLocationToSave from '../actions/action_dropNewPin';
import updatePins from '../actions/action_updatePins';
import deletePin from '../actions/action_deletePin';
import updateRecent from '../actions/action_updateRecent';
import setTarget from '../actions/action_setTarget';
import ViewContainer from '../components/ViewContainer';

function mapStateToProps(state) {
  return {
    pins: state.pins,
    recent: state.recent,
    friends: state.friends,
    user: state.user,
  };
}

export default connect(mapStateToProps, { getLocationToSave, updatePins, deletePin, updateRecent, setTarget })(ViewContainer);

Remove .js suffix in imports for consistency


/**
 * This file specifies the database collection and field names.
 */
export const COLLECTION_TRIPS = 'trips';
export const TRIPS_NAME = 'name';
export const TRIPS_DESCRIPTION = 'description';
export const TRIPS_DESTINATION = 'destination';
export const TRIPS_COLLABORATORS = 'collaborators';
export const TRIPS_START_DATE = 'start_date';
export const TRIPS_END_DATE = 'end_date';
export const TRIPS_CREATION_TIME = 'trip_creation_time';

export const COLLECTION_ACTIVITIES = 'activities';
export const ACTIVITIES_START_TIME = 'start_time';
export const ACTIVITIES_END_TIME = 'end_time';
export const ACTIVITIES_TITLE = 'title';
export const ACTIVITIES_DESCRIPTION = 'description';

/**
 * This file specifies the database collection and field names.
 */
export const COLLECTION_TRIPS = 'trips';
export const TRIPS_TITLE = 'title';
export const TRIPS_DESCRIPTION = 'description';
export const TRIPS_DESTINATION = 'destination';
export const TRIPS_COLLABORATORS = 'collaborators';
export const TRIPS_START_DATE = 'start_date';
export const TRIPS_END_DATE = 'end_date';
export const TRIPS_UPDATE_TIMESTAMP = 'trip_update_timestamp';

export const COLLECTION_ACTIVITIES = 'activities';
export const ACTIVITIES_START_TIME = 'start_time';
export const ACTIVITIES_END_TIME = 'end_time';
export const ACTIVITIES_TITLE = 'title';
export const ACTIVITIES_DESCRIPTION = 'description';

Update timestamp and trip title fields to better naming convention.


'use strict';

/**
* Time in seconds since the epoch.
*
* @module @stdlib/time/now
*
* @example
* var now = require( '@stdlib/time/now' );
*
* var ts = now();
* // returns <number>
*/

// MODULES //

var bool = require( './detect.js' );

// MAIN //

var now;
if ( bool ) {
	now = require( './now.js' );
} else {
	now = require( './polyfill.js' );
}

// EXPORTS //

module.exports = now;

'use strict';

/**
* Time in seconds since the epoch.
*
* @module @stdlib/time/now
*
* @example
* var now = require( '@stdlib/time/now' );
*
* var ts = now();
* // returns <number>
*/

// MODULES //

var bool = require( './detect.js' );
var main = require( './now.js' );
var polyfill = require( './polyfill.js' );

// MAIN //

var now;
if ( bool ) {
	now = main;
} else {
	now = polyfill;
}

// EXPORTS //

module.exports = now;

Refactor to avoid dynamic module resolution


const State  = require('../../binary/base/storage').State;
const Client = require('../../binary/base/client').Client;
const PortfolioWS = require('../../binary/websocket_pages/user/account/portfolio/portfolio.init').PortfolioWS;

const JapanPortfolio = (function() {
    let $portfolio,
        isPortfolioActive = false;

    function init() {
        if (isActive()) {
            $('#tab_portfolio').removeClass('invisible');
        }

        const $container = $('#tab_portfolio-content');
        $portfolio = $portfolio || $('#portfolio');

        if ($portfolio &&
        (!$portfolio.parent().length ||
        $portfolio.parent().get(0).id !== 'tab_portfolio-content')) {
            $portfolio.detach();
            $container.append($portfolio);
        }
    }

    function show() {
        if (isTradePage() && !isPortfolioActive) {
            PortfolioWS.onLoad();
            isPortfolioActive = true;
        }
    }

    function isActive() {
        return !!(Client.is_logged_in() && isTradePage());
    }

    function hide() {
        if (isTradePage() && isPortfolioActive) {
            PortfolioWS.onUnload();
            isPortfolioActive = false;
        }
    }

    function isTradePage() {
        return State.get('is_mb_trading');
    }

    return {
        init    : init,
        show    : show,
        hide    : hide,
        isActive: isActive,
    };
})();

module.exports = {
    JapanPortfolio: JapanPortfolio,
};

const State  = require('../../binary/base/storage').State;
const Client = require('../../binary/base/client').Client;
const PortfolioWS = require('../../binary/websocket_pages/user/account/portfolio/portfolio.init').PortfolioWS;

const JapanPortfolio = (function() {
    let $portfolio,
        isPortfolioActive = false;

    function init() {
        if (isActive()) {
            $('#tab_portfolio').removeClass('invisible');
        }

        const $container = $('#tab_portfolio-content');
        $portfolio = $portfolio || $('#portfolio');

        if ($portfolio &&
        (!$portfolio.parent().length ||
        $portfolio.parent().get(0).id !== 'tab_portfolio-content')) {
            $portfolio.detach();
            $container.append($portfolio);
        }
    }

    function show() {
        if (isTradePage() && !isPortfolioActive) {
            PortfolioWS.onLoad();
            isPortfolioActive = true;
        }
    }

    function isActive() {
        return !!(Client.is_logged_in() && isTradePage());
    }

    function hide() {
        if (isTradePage() && isPortfolioActive) {
            PortfolioWS.onUnload();
            isPortfolioActive = false;
            $portfolio = undefined;
        }
    }

    function isTradePage() {
        return State.get('is_mb_trading');
    }

    return {
        init    : init,
        show    : show,
        hide    : hide,
        isActive: isActive,
    };
})();

module.exports = {
    JapanPortfolio: JapanPortfolio,
};

Fix portolio tab loading when loaded by pjax


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

/**
 * WordPress dependencies
 */
import apiFetch from '@wordpress/api-fetch';

const get = (path, options = {}) => apiFetch({ path, ...options });

const post = (path, options = {}) =>
  apiFetch({
    path,
    ...options,
    method: 'POST',
  });

const deleteRequest = (path, options = {}) =>
  apiFetch({
    path,
    ...options,
    method: 'DELETE',
  });

export default {
  get,
  post,
  deleteRequest,
};

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

/**
 * External dependencies
 */
import queryString from 'query-string';

/**
 * WordPress dependencies
 */
import apiFetch from '@wordpress/api-fetch';

const get = (path, options = {}) => apiFetch({ path, ...options });

const post = (path, options = {}) =>
  apiFetch({
    path,
    ...options,
    method: 'POST',
  });

// `apiFetch` by default turns `DELETE` requests into `POST` requests
// with `X-HTTP-Method-Override: DELETE` headers.
// However, some Web Application Firewall (WAF) solutions prevent this.
// `?_method=DELETE` is an alternative solution to override the request method.
// See https://developer.wordpress.org/rest-api/using-the-rest-api/global-parameters/#_method-or-x-http-method-override-header
const deleteRequest = (path, options = {}) =>
  apiFetch({
    path: queryString.stringifyUrl({
      url: path,
      query: {
        _method: 'DELETE',
      },
    }),
    ...options,
    method: 'POST',
  });

export default {
  get,
  post,
  deleteRequest,
};

Use `_method=DELETE` instead for deletion


  /**
   * This is the default configuration for the layout algorithm. It results in
   * a grid-like layout. A perfect grid when the number of elements to layout
   * is a perfect square. Otherwise the last couple of elements will be
   * stretched.
   */
  var defaultConfig = {
    itemSize: function(item) { return 1; },
    phrase: function() { return d3.layout.phrase.TOP_LEFT_TO_RIGHT; },
    recurse: function() { return false; },
    score: function(itemCount) { // Grid scoring function
      var chunkItemCount = 0;
      var itemCountWithIntSquare = Math.pow(Math.ceil(Math.sqrt(itemCount)), 2);
      return function(chunk, item) {
        var score = Math.pow(chunkItemCount, 2) < itemCountWithIntSquare ? 1 : 0;
        if (score === 1) {
          chunkItemCount++;
        } else {
          chunkItemCount = 0;
        }
        return score;
      }
    }
  }

  d3.treemap = function() {
    var size = [1, 1]; // width, height,
        phrase = undefined,
        itemSize = undefined,
        order = undefined,
        recurse = undefined,
        score = undefined;

    function _layout(nodes) {
      var config = {};
      config.size = size;
      config.phrase = phrase ? phrase : defaultConfig.phrase;
      config.itemSize = itemSize ? itemSize : defaultConfig.itemSize;
      config.recurse = recurse ? recurse : defaultConfig.recurse;
      config.score = score ? score : defaultConfig.score(nodes.length);

      var chunks = layout.apply(config, [order ? order(nodes) : nodes]);
      return chunks.reduce(function(rects, chunk) {
        var itemRects = chunk.itemRects().map(function(rect) { return rect.toPojo(); });
        return rects.concat(itemRects);
      }, []);
    }

    _layout.size = function(_) {
      if (!arguments.length) return size;
      size = _;
      return _layout;
    }

    _layout.itemSize = function(_) {
      if (!arguments.length) return itemSize;
      itemSize = _;
      return _layout;
    }

    _layout.phrase = function(_) {
      if (!arguments.length) return phrase;
      phrase = _;
      return _layout;
    }

    _layout.order = function(_) {
      if (!arguments.length) return order;
      order = _;
      return _layout;
    }

    _layout.recurse = function(_) {
      if (!arguments.length) return recurse;
      recurse = _;
      return _layout;
    }

    _layout.score = function(_) {
      if (!arguments.length) return score;
      score = _;
      return _layout;
    }

    return _layout;
  }

})();

Add some wrapper code which defines the public API.


module.exports = Zeppelin.CollectionView.extend({
  tagName: 'ol',

  className: 'cards-list list-unstyled clearfix',

  subscriptions: {
    'cardsList:layout': 'triggerLayout'
  },

  itemView: function(model) {
    return require('account/views/' + model.get('type'));
  },

  collection: function() {
    return App.Cards;
  },

  initialize: function() {
    this.$parent = $('div.content');
    _.bindAll(this, ['layout']);
  },

  onRenderItems: function() {
    this.wall = new freewall(this.$list);
    this.triggerLayout();
  },

  onAppendItem: function() {
    if (!this.isFirstCollectionRender()) this.layout();
  },

  triggerLayout: function() {
    _.delay(this.layout, 10);
  },

  layout: function() {
    this.wall.reset({
      delay: 0,
      cellW: 222,
      cellH: 222,
      animate: false,
      selector: 'li.card',
      onResize: _.bind(function() {
        this.wall.fitWidth();
      }, this)
    });

    this.wall.fitWidth(this.$parent.width());
  }
});

module.exports = Zeppelin.CollectionView.extend({
  tagName: 'ol',

  className: 'cards-list list-unstyled clearfix',

  subscriptions: {
    'cardsList:layout': 'triggerLayout'
  },

  itemView: function(model) {
    return require('account/views/' + model.get('type'));
  },

  collection: function() {
    return App.Cards;
  },

  initialize: function() {
    this.$parent = $('div.content');
    _.bindAll(this, ['layout']);
  },

  onRenderItems: function() {
    this.wall = new freewall(this.$list);
    this.triggerLayout();
  },

  onAppendItem: function() {
    if (!this.isFirstCollectionRender()) this.layout();
  },

  triggerLayout: function() {
    _.delay(this.layout, 1);
  },

  layout: function() {
    this.wall.reset({
      delay: 0,
      cellW: 222,
      cellH: 222,
      animate: false,
      selector: 'li.card',
      onResize: _.bind(function() {
        this.wall.fitWidth();
      }, this)
    });

    this.wall.fitWidth(this.$parent.width());
  }
});

Reduce grid layout logic delay.


RippleTransaction = require("../models/ripple_transaction");
var async = require("async");

module.exports = (function(){
  function userIndex(req, res) {
    req.user.balances(function(err, balances) {
      res.send({ error: err, balances: balances });
    });
  }

  return {
    userIndex: userIndex
  }
})();

RippleTransaction = require("../models/ripple_transaction");
var async = require("async");

module.exports = (function(){
  function index(req, res) {
    req.user.balances(function(err, balances) {
      res.send({ error: err, balances: balances });
    });
  }

  return {
    index: index
  }
})();

Rename userIndex controller action to index.


import Route from '@ember/routing/route';
import ajax from 'ember-fetch/ajax';

export default Route.extend({
  async model(params) {
    try {
      await ajax(`/api/v1//me/crate_owner_invitations/accept/${params.token}`, { method: 'PUT', body: '{}' });
      this.set('response', { accepted: true });
      return { response: this.get('response') };
    } catch (error) {
      this.set('response', { accepted: false });
      return { response: this.get('response') };
    }
  },
});

import Route from '@ember/routing/route';
import ajax from 'ember-fetch/ajax';

export default Route.extend({
  async model(params) {
    try {
      await ajax(`/api/v1/me/crate_owner_invitations/accept/${params.token}`, { method: 'PUT', body: '{}' });
      this.set('response', { accepted: true });
      return { response: this.get('response') };
    } catch (error) {
      this.set('response', { accepted: false });
      return { response: this.get('response') };
    }
  },
});

Remove extra slash in URL


histomicstk.views.AnnotationSelectorWidget = histomicstk.views.Panel.extend({
    events: _.extend(histomicstk.views.Panel.prototype.events, {
        'click .h-annotation > span': 'toggleAnnotation'
    }),
    setItem: function (item) {
        if (this.collection) {
            this.stopListening(this.collection);
        }

        this.parentItem = item;
        this.collection = this.parentItem.annotations;

        if (this.collection) {
            this.collection.append = true;

            this.listenTo(this.collection, 'g:changed', this.render);
            this.listenTo(this.collection, 'add', this.render);
            this.collection.fetch();
        } else {
            this.collection = new Backbone.Collection();
        }
        return this;
    },
    render: function () {
        this.$el.html(histomicstk.templates.annotationSelectorWidget({
            annotations: this.collection.toArray(),
            id: 'annotation-panel-container',
            title: 'Annotations'
        }));
        return this;
    },
    toggleAnnotation: function (evt) {
        var id = $(evt.currentTarget).data('id');
        var model = this.collection.get(id);
        model.set('displayed', !model.get('displayed'));
        this.render();
    }
});

histomicstk.views.AnnotationSelectorWidget = histomicstk.views.Panel.extend({
    events: _.extend(histomicstk.views.Panel.prototype.events, {
        'click .h-annotation > span': 'toggleAnnotation'
    }),
    initialize: function () {
        this.listenTo(girder.eventStream, 'g:event.job_status', function (evt) {
            if (evt.data.status > 2) {
                this.collection.fetch();
            }
        });
    },
    setItem: function (item) {
        if (this.collection) {
            this.stopListening(this.collection);
        }

        this.parentItem = item;
        this.collection = this.parentItem.annotations;

        if (this.collection) {
            this.collection.append = true;

            this.listenTo(this.collection, 'g:changed', this.render);
            this.listenTo(this.collection, 'add', this.render);
            this.collection.fetch();
        } else {
            this.collection = new Backbone.Collection();
        }
        return this;
    },
    render: function () {
        this.$el.html(histomicstk.templates.annotationSelectorWidget({
            annotations: this.collection.toArray(),
            id: 'annotation-panel-container',
            title: 'Annotations'
        }));
        return this;
    },
    toggleAnnotation: function (evt) {
        var id = $(evt.currentTarget).data('id');
        var model = this.collection.get(id);
        model.set('displayed', !model.get('displayed'));
        this.render();
    }
});

Update annotation list on job completion


'use strict';

/*
 * Simple wrapper around $ionicPopup for modals and alerts and such
 */

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

modalhandler.factory('modalFactory', function(i$ionicPopup) {
    return {}
});

'use strict';

/*
 * Simple wrapper around $ionicPopup for modals and alerts and such
 */

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

modalhandler.factory('modalFactory', function(i$ionicPopup) {
    return {
        alert: function (title, template) {
            return $ionicPopup.alert({
                title    : title,
                template : template
            });
        }
    }
});

Add alert functionality to modal handler


import {
  Entity,
  EditorState,
  AtomicBlockUtils
} from 'draft-js'

export default (editorState, url) => {
  const urlType = 'embed'
  const entityKey = Entity.create(urlType, 'IMMUTABLE', { src: url })
  const newEditorState = AtomicBlockUtils.insertAtomicBlock(
    editorState,
    entityKey,
    ' '
  )
  return EditorState.forceSelection(
    newEditorState,
    editorState.getCurrentContent().getSelectionAfter()
  )
}

import {
  Entity,
  EditorState,
  AtomicBlockUtils
} from 'draft-js'

export default (editorState, url) => {
  const urlType = 'embed'
  const entityKey = Entity.create(urlType, 'IMMUTABLE', { src: url })
  const newEditorState = AtomicBlockUtils.insertAtomicBlock(
    editorState,
    entityKey,
    ' '
  )
  return EditorState.forceSelection(
    newEditorState,
    newEditorState.getCurrentContent().getSelectionAfter()
  )
}

Move cursor after embed when inserting


export default function createNodeIterator(root, whatToShow, filter = null) {
  var iter = _create.call(window.document, root, whatToShow, filter, false);
  return typeof(iter.referenceNode) === 'undefined' ? shim(iter, root) : iter;
}

function shim(iter, root) {
  var _referenceNode = root;
  var _pointerBeforeReferenceNode = true;

  return Object.create(NodeIterator.prototype, {
    root: {
      get: () => iter.root
    },

    whatToShow: {
      get: () => iter.whatToShow
    },

    filter: {
      get: () => iter.filter
    },

    referenceNode: {
      get: () => _referenceNode
    },

    pointerBeforeReferenceNode: {
      get: () => _pointerBeforeReferenceNode
    },

    detach: {
      get: () => iter.detach
    },

    nextNode: {
      value: () => {
        let result = iter.nextNode();
        _pointerBeforeReferenceNode = false;
        if (result === null) {
          return null;
        } else {
          _referenceNode = result;
           return _referenceNode;
        }
      }
    },

    previousNode: {
      value: () => {
        let result = iter.previousNode();
        _pointerBeforeReferenceNode = true;
        if (result === null) {
           return null;
        } else {
          _referenceNode = result;
           return _referenceNode;
        }
      }
    }
  });
}

export default function createNodeIterator(root, whatToShow, filter = null) {
  let document = root.ownerDocument;
  var iter = document.createNodeIterator(root, whatToShow, filter, false);
  return typeof(iter.referenceNode) === 'undefined' ? shim(iter, root) : iter;
}

function shim(iter, root) {
  var _referenceNode = root;
  var _pointerBeforeReferenceNode = true;

  return Object.create(NodeIterator.prototype, {
    root: {
      get: () => iter.root
    },

    whatToShow: {
      get: () => iter.whatToShow
    },

    filter: {
      get: () => iter.filter
    },

    referenceNode: {
      get: () => _referenceNode
    },

    pointerBeforeReferenceNode: {
      get: () => _pointerBeforeReferenceNode
    },

    detach: {
      get: () => iter.detach
    },

    nextNode: {
      value: () => {
        let result = iter.nextNode();
        _pointerBeforeReferenceNode = false;
        if (result === null) {
          return null;
        } else {
          _referenceNode = result;
           return _referenceNode;
        }
      }
    },

    previousNode: {
      value: () => {
        let result = iter.previousNode();
        _pointerBeforeReferenceNode = true;
        if (result === null) {
           return null;
        } else {
          _referenceNode = result;
           return _referenceNode;
        }
      }
    }
  });
}

Fix bad invocation of old, dead code


// Marionette.Component
// --------------------
// An object to represent an application component, typically
// something visual, encapsulated in to an object that can be
// instantiated and dispalyed on screen as needed.
//
// Marionette.Component can optionally have a `region`, `model`,
// and/or `collection` passed to it through the constructor options.

var Component = Marionette.Controller.extend({
  constructor: function(options) {
    options = options || {};

    this.region     = options.region;
    this.model      = options.model;
    this.collection = options.collection;

    Marionette.Controller.prototype.constructor.apply(this, arguments);
  },

  show: function() {
    this.showLayout();
  },

  showLayout: function() {
    var layout = this.layout = this.getLayout();

    this.listenTo(layout, 'show', function() {
      this.triggerMethod('show:layout');
    });

    this.region.show(layout);
  }
});

// Marionette.Component
// --------------------
// An object to represent an application component, typically
// something visual, encapsulated in to an object that can be
// instantiated and dispalyed on screen as needed.
//
// Marionette.Component can optionally have a `region`, `model`,
// and/or `collection` passed to it through the constructor options.

var Component = Marionette.Controller.extend({
  constructor: function(options) {
    options = options || {};

    this.region     = options.region;
    this.model      = options.model;
    this.collection = options.collection;

    Marionette.Controller.prototype.constructor.apply(this, arguments);
  },

  show: function() {
    this._showView();
  },

  _showView: function() {
    var view = this.view = this._getView();

    this.listenTo(view, 'show', function() {
      this.triggerMethod('show:view');
    });

    this.region.show(view);
  }
});

Generalize and internalize view methods


var mongoose = require('mongoose');

var COLLECTION_NAME = 'tweet';

var tweetSchema = new mongoose.Schema({
  id: {type: String, required: true},
  text: {type: String, required: true},
  user: {
    id: {type: String, required: true}
  },
  media: [{
    url: {type: String, required: true}
  }],
  coordinates: {
    latitude: {type: String, required: false},
    longitude: {type: String, required: false}
  }
}, { collection: COLLECTION_NAME });

module.exports = mongoose.model('Tweet', tweetSchema);

var mongoose = require('mongoose');

var COLLECTION_NAME = 'tweet';

var tweetSchema = new mongoose.Schema({
  id: {type: String, required: true},
  text: {type: String},
  user: {
    id: {type: String, required: true}
  },
  media: [{
    url: {type: String, required: true}
  }],
  coordinates: {
    latitude: {type: String, required: false},
    longitude: {type: String, required: false}
  },
  date_updated: {type: Date, default: (new Date())}
}, { collection: COLLECTION_NAME });

module.exports = mongoose.model('Tweet', tweetSchema);

Add date updated, remove required from text


module.exports = function (grunt) {

   'use strict';

   grunt.initConfig({
      pkg: grunt.file.readJSON('package.json'),

      nodeunit: {
         unit: ['test/unit/test*.js'],
         integration: ['test/integration/test*.js']
      },

      release: {
         options: {
            file: 'package.json',
            tagName: '<%= version %>', //default: '<%= version %>'
            commitMessage: 'Release <%= version %>', //default: 'release <%= version %>'
            tagMessage: 'Tag version <%= version %>' //default: 'Version <%= version %>'
         }
      }
   });

   grunt.loadNpmTasks('grunt-contrib-nodeunit');
   grunt.loadNpmTasks('grunt-release-steps');

   grunt.registerTask('-to-npm', ['release:npm']);
   grunt.registerTask('-to-git', ['release:add:commit:push']);
   grunt.registerTask('-tag',    ['release:tag:pushTags']);

   'patch minor major'.split(' ').forEach(function (type) {
      grunt.registerTask(type, ['test', 'release:bump:' + type, '-tag', '-to-git', '-to-npm']);
   });

   grunt.registerTask('default', ['patch']);
   grunt.registerTask('test', ['nodeunit:unit']);

};

module.exports = function (grunt) {

   'use strict';

   grunt.initConfig({
      pkg: grunt.file.readJSON('package.json'),

      nodeunit: {
         unit: ['test/unit/test*.js'],
         integration: ['test/integration/test*.js']
      },

      release: {
         options: {
            file: 'package.json',
            tagName: '<%= version %>', //default: '<%= version %>'
            commitMessage: 'Release <%= version %>', //default: 'release <%= version %>'
            tagMessage: 'Tag version <%= version %>' //default: 'Version <%= version %>'
         }
      }
   });

   grunt.loadNpmTasks('grunt-contrib-nodeunit');
   grunt.loadNpmTasks('grunt-release-steps');

   grunt.registerTask('-to-npm', ['release:npm']);
   grunt.registerTask('-to-git', ['release:add:commit:push']);
   grunt.registerTask('-tag',    ['release:tag:pushTags']);

   'patch minor major'.split(' ').forEach(function (type) {
      grunt.registerTask(type, ['test', 'release:bump:' + type, '-tag', '-to-git', '-to-npm']);
   });

   grunt.registerTask('default', ['patch']);
   grunt.registerTask('test', ['nodeunit:unit', 'nodeunit:integration']);

};

Add integration tests to standard `grunt test`


angular.module('OpenMining', [])
.value('API_URL', '/process.json?')

.controller('Process',
  function($scope, $http, $location, API_URL) {
    for (var key in $location.search()){
      API_URL += key + "=" + $location.search()[key] + "&";
    }
    $scope.loading = true;
    $http({method: 'POST', url: API_URL}).
      success(function(data, status, headers, config) {
        $scope.process = data.json;
        $scope.columns = data.columns;
        $scope.loading = false;
      });
});

angular.module('OpenMining', [])
.controller('Process',
  function($scope, $http, $location) {
    $scope.loading = true;
    $scope.init = function(slug) {
      API_URL = "/process/" + slug + ".json?";
      for (var key in $location.search()){
        API_URL += key + "=" + $location.search()[key] + "&";
      };

      $http({method: 'POST', url: API_URL}).
        success(function(data, status, headers, config) {
          $scope.process = data.json;
          $scope.columns = data.columns;
          $scope.loading = false;
        });
    };
});

Use dynamic process (json) ajax load


'use strict';
const gulp = require('gulp');
const shell = require('shelljs');
const size = require('gulp-size');
const argv = require('yargs').argv;

// 'gulp jekyll:tmp' -- copies your Jekyll site to a temporary directory
// to be processed
gulp.task('site:tmp', () =>
  gulp.src(['src/**/*', '!src/assets/**/*', '!src/assets'])
    .pipe(gulp.dest('.tmp/src'))
    .pipe(size({title: 'Jekyll'}))
);

// 'gulp jekyll' -- builds your site with development settings
// 'gulp jekyll --prod' -- builds your site with production settings
gulp.task('site', done => {
  if (!argv.prod) {
    shell.exec('jekyll build');
    done();
  } else if (argv.prod) {
    shell.exec('jekyll build --config _config.yml,_config.build.yml');
    done();
  }
});

// 'gulp doctor' -- literally just runs jekyll doctor
gulp.task('site:check', done => {
  shell.exec('jekyll doctor');
  done();
});

'use strict';
const gulp = require('gulp');
const shell = require('shelljs');
const size = require('gulp-size');
const argv = require('yargs').argv;

// 'gulp jekyll:tmp' -- copies your Jekyll site to a temporary directory
// to be processed
gulp.task('site:tmp', () =>
  gulp.src(['src/**/*', '!src/assets/**/*', '!src/assets'], {dot: true})
    .pipe(gulp.dest('.tmp/src'))
    .pipe(size({title: 'Jekyll'}))
);

// 'gulp jekyll' -- builds your site with development settings
// 'gulp jekyll --prod' -- builds your site with production settings
gulp.task('site', done => {
  if (!argv.prod) {
    shell.exec('jekyll build');
    done();
  } else if (argv.prod) {
    shell.exec('jekyll build --config _config.yml,_config.build.yml');
    done();
  }
});

// 'gulp doctor' -- literally just runs jekyll doctor
gulp.task('site:check', done => {
  shell.exec('jekyll doctor');
  done();
});

Include dotfiles when copying the site


import Ember from 'ember';
import moment from 'moment';
import stringToHue from 'bus-detective/utils/string-to-hue';
var inject = Ember.inject;

export default Ember.Component.extend({
  tagName: 'li',
  clock: inject.service(),
  attributeBindings: ['style'],
  classNames: ['arrival'],
  classNameBindings: ['isPast:arrival--past'],

  timeFromNow: Ember.computed('clock.time', function() {
    return moment(this.get('arrival.time')).fromNow();
  }),

  isPast: Ember.computed('clock.time', function() {
    return new Date(this.get('arrival.time')) < new Date();
  }),

  style: Ember.computed('arrival.route_id', function() {
    var hue = stringToHue(this.get('arrival.route_id').toString());
    return `color:            hsl(${hue}, 60%, 100%);
            background-color: hsl(${hue}, 50%, 55%);
            border-color:     hsl(${hue}, 50%, 55%);`;
  })
});

import Ember from 'ember';
import moment from 'moment';
import stringToHue from 'bus-detective/utils/string-to-hue';
var inject = Ember.inject;

export default Ember.Component.extend({
  tagName: 'li',
  clock: inject.service(),
  attributeBindings: ['style'],
  classNames: ['arrival'],
  classNameBindings: ['isPast:arrival--past'],

  timeFromNow: Ember.computed('clock.time', function() {
    return moment(this.get('arrival.time')).fromNow('mm');
  }),

  isPast: Ember.computed('clock.time', function() {
    return new Date(this.get('arrival.time')) < new Date();
  }),

  style: Ember.computed('arrival.route_id', function() {
    var hue = stringToHue(this.get('arrival.route_id').toString());
    return `color:            hsl(${hue}, 60%, 100%);
            background-color: hsl(${hue}, 50%, 55%);
            border-color:     hsl(${hue}, 50%, 55%);`;
  })
});

Format how time from now is displayed


'use strict';

module.exports = function (grunt) {

    // Load the project's grunt tasks from a directory
    require('grunt-config-dir')(grunt, {
        configDir: require('path').resolve('tasks')
    });

    /*
     * Register group tasks
     */

    //npm install
    grunt.registerTask('npm_install', 'install dependencies', function() {
        var exec = require('child_process').exec;
        var cb = this.async();
        exec('npm install', {cwd: './'}, function(err, stdout) {
            console.log(stdout);
            cb();
        });
    });

    grunt.registerTask('open_coverage', 'open coverage report in default browser', function() {
        var exec = require('child_process').exec;
        var cb = this.async();
        exec('open coverage/lcov-report/index.html', {cwd: './'}, function(err, stdout) {
            console.log(stdout);
            cb();
        });
    });

    grunt.registerTask('clean_all', [ 'clean:node_modules', 'clean:coverage', 'npm_install' ]);
    grunt.registerTask('test', ['env:test', 'clean:coverage', 'jshint', 'mocha_istanbul']);
    grunt.registerTask('coverage', ['test', 'open_coverage']);
    grunt.registerTask('zip', ['compress:icd10-api']);
    grunt.registerTask('serve', ['env:sandbox', 'jshint','develop','watch']);
};

'use strict';

module.exports = function (grunt) {

    // Load the project's grunt tasks from a directory
    require('grunt-config-dir')(grunt, {
        configDir: require('path').resolve('tasks')
    });

    /*
     * Register group tasks
     */

    //npm install
    grunt.registerTask('npm_install', 'install dependencies', function() {
        var exec = require('child_process').exec;
        var cb = this.async();
        exec('npm install', {cwd: './'}, function(err, stdout) {
            console.log(stdout);
            cb();
        });
    });

    grunt.registerTask('open_coverage', 'open coverage report in default browser', function() {
        var exec = require('child_process').exec;
        var cb = this.async();
        exec('open coverage/lcov-report/index.html', {cwd: './'}, function(err, stdout) {
            console.log(stdout);
            cb();
        });
    });

    grunt.registerTask('clean_all', [ 'clean:node_modules', 'clean:coverage', 'npm_install' ]);
    grunt.registerTask('test', ['env:test', 'clean:coverage', 'jshint', 'mocha_istanbul']);
    grunt.registerTask('coverage', ['test', 'open_coverage']);
    grunt.registerTask('dist', ['compress:hmda-edit-check-api']);
    grunt.registerTask('serve', ['env:sandbox', 'jshint','develop','watch']);
};

Update the zip task to dist and correct api task


"use strict";

module.exports = function (grunt) {
    grunt.initConfig({
        bump: {
            options: {
                files: ["package.json"],
                commit: true,
                commitMessage: "Release %VERSION%",
                commitFiles: ["-a"],
                createTag: true,
                tagName: "%VERSION%",
                tagMessage: "Version %VERSION%",
                push: false,
            }
        },
        shell: {
            options: {
                stdout: true,
                stderr: true,
                failOnError: true
            },
            push: {
                command: "git push -u -f --tags origin master"
            },
            publish: {
                command: "npm publish"
            },
            update: {
                command: "npm-check-updates -u"
            }
        }
    });

    grunt.registerTask("update", "shell:update");
    grunt.registerTask("release", ["bump", "shell:push", "shell:publish"]);
    grunt.registerTask("minor", ["bump:minor", "shell:push", "shell:publish"]);
    grunt.registerTask("major", ["bump:major", "shell:push", "shell:publish"]);

    grunt.loadNpmTasks("grunt-bump");
    grunt.loadNpmTasks("grunt-shell");
};

"use strict";

module.exports = function (grunt) {
    grunt.initConfig({
        bump: {
            options: {
                files: ["package.json"],
                commit: true,
                commitMessage: "Release %VERSION%",
                commitFiles: ["-a"],
                createTag: true,
                tagName: "%VERSION%",
                tagMessage: "Version %VERSION%",
                push: false,
            }
        },
        shell: {
            options: {
                stdout: true,
                stderr: true,
                failOnError: true
            },
            push: {
                command: "git push -u -f --tags origin master"
            },
            publish: {
                command: "npm publish"
            },
            update: {
                command: "npm-check-updates -u"
            },
            modules: {
                command: "rm -rf node_modules && npm install"
            }
        }
    });

    grunt.registerTask("update",  ["shell:update", "shell:modules"]);
    grunt.registerTask("release", ["bump", "shell:push", "shell:publish"]);
    grunt.registerTask("minor",   ["bump:minor", "shell:push", "shell:publish"]);
    grunt.registerTask("major",   ["bump:major", "shell:push", "shell:publish"]);

    grunt.loadNpmTasks("grunt-bump");
    grunt.loadNpmTasks("grunt-shell");
};

Clean up node_modules after updating them


FamousFramework.scene('famous-tests:webgl:custom-shader:fragment', {
    behaviors: {
        '.sphere': {
            'size': [200, 200],
            'align': [0.5, 0.5],
            'mount-point': [0.5, 0.5],
            'base-color': {
                'name': 'sphereFragment',
                'glsl': 'vec4(0.0, 1.0, 1.0, 1.0);',
                'output': 4,
            },
            'geometry': {
                'shape': 'GeodesicSphere',
                'options': {
                    'detail': 3
                }
            }
        },
        '.light': {
            'size': [200, 200],
            'align': [0.5, 0.5],
            'mount-point': [0.5, 0.5],
            'position-z': 50,
            'light': {
                'type': 'point',
                'color': 'white'
            }
        },
        '.background': {
            'size': [undefined, undefined],
            'align': [0.5, 0.5],
            'mount-point': [0.5, 0.5],
            'geometry': {
                'shape': 'Plane'
            }
        }
    },
    events: {},
    states: {},
    tree: `
        <node class="background"></node>
        <node class="sphere"></node>
        <node class="light"></node>
    `
});

FamousFramework.scene('famous-tests:webgl:custom-shader:fragment', {
    behaviors: {
        '.sphere': {
            'size': [200, 200],
            'align': [0.5, 0.5],
            'mount-point': [0.5, 0.5],
            'base-color': {
                'name': 'sphereFragment',
                'glsl': 'vec4((v_normal + 1.0) * 0.5, 1.0);',
                'output': 4,
            },
            'geometry': {
                'shape': 'GeodesicSphere',
                'options': {
                    'detail': 3
                }
            }
        },
        '.light': {
            'size': [200, 200],
            'align': [0.5, 0.5],
            'mount-point': [0.5, 0.5],
            'position-z': 500,
            'light': {
                'type': 'point',
                'color': 'white'
            }
        },
        '.background': {
            'size': [undefined, undefined],
            'align': [0.5, 0.5],
            'mount-point': [0.5, 0.5],
            'geometry': {
                'shape': 'Plane'
            }
        }
    },
    events: {},
    states: {},
    tree: `
        <node class="background"></node>
        <node class="sphere"></node>
        <node class="light"></node>
    `
});

Update to use v_normals for color


import {Experiment, ExperimentStep} from './experiment.model';

angular.module('materialscommons').component('mcExperiment', {
    templateUrl: 'app/project/experiments/experiment/mc-experiment.html',
    controller: MCExperimentComponentController
});

/*@ngInject*/
function MCExperimentComponentController($scope, moveStep) {
    let ctrl = this;
    ctrl.currentStep = null;
    ctrl.currentNode = null;
    ctrl.showSidebar = true;

    // Create the initial hard coded experiment
    ctrl.experiment = new Experiment('test experiment');
    let s = new ExperimentStep('', '');
    s.id = "simple0";
    ctrl.experiment.steps.push(s);
    ctrl.currentStep = s;

    ctrl.moveLeft = () => moveStep.left(ctrl.currentNode, ctrl.currentStep, ctrl.experiment);
    ctrl.moveRight = () => moveStep.right(ctrl.currentNode, ctrl.currentStep);
    ctrl.moveUp = () => moveStep.up(ctrl.currentNode, ctrl.currentStep);
    ctrl.moveDown = () => moveStep.down(ctrl.currentNode, ctrl.currentStep, ctrl.experiment);
    ctrl.expandAll = () => $scope.$broadcast('angular-ui-tree:expand-all');
    ctrl.collapseAll = () => $scope.$broadcast('angular-ui-tree:collapse-all');
    ctrl.showStepMaximized = () => ctrl.currentStep && ctrl.currentStep.displayState.maximize;
}

import {Experiment, ExperimentStep} from './experiment.model';

angular.module('materialscommons').component('mcExperiment', {
    templateUrl: 'app/project/experiments/experiment/mc-experiment.html',
    controller: MCExperimentComponentController
});

/*@ngInject*/
function MCExperimentComponentController($scope, moveStep) {
    let ctrl = this;
    ctrl.currentStep = null;
    ctrl.currentNode = null;
    ctrl.showSidebar = false;

    // Create the initial hard coded experiment
    ctrl.experiment = new Experiment('test experiment');
    let s = new ExperimentStep('', '');
    s.id = "simple0";
    ctrl.experiment.steps.push(s);
    ctrl.currentStep = s;

    ctrl.moveLeft = () => moveStep.left(ctrl.currentNode, ctrl.currentStep, ctrl.experiment);
    ctrl.moveRight = () => moveStep.right(ctrl.currentNode, ctrl.currentStep);
    ctrl.moveUp = () => moveStep.up(ctrl.currentNode, ctrl.currentStep);
    ctrl.moveDown = () => moveStep.down(ctrl.currentNode, ctrl.currentStep, ctrl.experiment);
    ctrl.expandAll = () => $scope.$broadcast('angular-ui-tree:expand-all');
    ctrl.collapseAll = () => $scope.$broadcast('angular-ui-tree:collapse-all');
    ctrl.showStepMaximized = () => ctrl.currentStep && ctrl.currentStep.displayState.maximize;
}

Hide the sidebar by default.


'use strict'

/**
 * Attempts to prevent stalled rooms by launching filler creeps at 300 energy.
 */

class CityReboot extends kernel.process {
  constructor (...args) {
    super(...args)
    this.priority = PRIORITIES_CITY_REBOOT
  }

  main () {
    if (!Game.rooms[this.data.room]) {
      return this.suicide()
    }
    this.room = Game.rooms[this.data.room]
    if (this.room.find(FIND_MY_CREEPS).length <= 0) {
      this.launchCreepProcess('rebooter', 'filler', this.data.room, 2, {
        priority: 1,
        energy: 300
      })
    }
  }
}

module.exports = CityReboot

'use strict'

/**
 * Attempts to prevent stalled rooms by launching filler creeps at 300 energy.
 */

class CityReboot extends kernel.process {
  constructor (...args) {
    super(...args)
    this.priority = PRIORITIES_CITY_REBOOT
  }

  main () {
    if (!Game.rooms[this.data.room]) {
      return this.suicide()
    }
    this.room = Game.rooms[this.data.room]
    const fillers = this.room.find(FIND_MY_CREEPS, {filter: (creep) => creep.name.startsWith('filler')})
    if (fillers.length <= 0) {
      this.launchCreepProcess('rebooter', 'filler', this.data.room, 2, {
        priority: 1,
        energy: 300
      })
    }
  }
}

module.exports = CityReboot

Reboot when there are no fillers (as opposed to no creeps at all)


function simulateNumericDecision(fleet1, fleet2){
  if (fleet1.ships > fleet2.ships) return fleet1;
  if (fleet1.ships < fleet2.ships) return fleet2;
  return null;
}

const orion = {
  name: 'orion',
  ships: 295
}

const commonwealth = {
  name: 'commonwealth',
  ships: 300
}

console.log('simulateNumericDecision \nwinner:', simulateNumericDecision(orion, commonwealth));

function simulateNumericDecision(fleet1, fleet2){
  if (fleet1.ships > fleet2.ships) return fleet1;
  if (fleet1.ships < fleet2.ships) return fleet2;
  return null;
}

const orion = {
  name: 'orion',
  ships: 295
}

const commonwealth = {
  name: 'commonwealth',
  ships: 300
}

const hasle = {
  name: 'commonwealth',
  ships: 300
}

console.log('simulateNumericDecision fight: 1\nwinner:', simulateNumericDecision(orion, commonwealth));
console.log('simulateNumericDecision fight: 2\nwinner:', simulateNumericDecision(commonwealth, orion));
console.log('simulateNumericDecision fight: 3\nwinner:', simulateNumericDecision(hasle, commonwealth));

Add additional simulations to show different results


/* eslint-disable no-undef */
import { Bert } from 'meteor/themeteorchef:bert';
import { upsertDocument } from '/imports/api/documents/methods';
import '/imports/lib/validation';

let component;

const handleUpsert = () => {
  const { doc, history } = component.props;
  const confirmation = doc && doc._id ? 'Document mise à jour !' : 'Document ajouté !';
  const upsert = {
    title: document.querySelector('[name="title"]').value.trim(),
    body: document.querySelector('[name="body"]').value.trim(),
  };

  if (doc && doc._id) upsert._id = doc._id;

  upsertDocument.call(upsert, (error, response) => {
    if (error) {
      Bert.alert(error.reason, 'danger');
    } else {
      component.documentEditorForm.reset();
      Bert.alert(confirmation, 'success');
      history.push(`/documents/${response.insertedId || doc._id}`);
    }
  });
};

const validate = () => {
  $(component.documentEditorForm).validate({
    rules: {
      title: {
        required: true,
      },
      body: {
        required: true,
      },
    },
    messages: {
      title: {
        required: 'Le titre est requis',
      },
      body: {
        required: 'Le corp est requis',
      },
    },
    submitHandler() { handleUpsert(); },
  });
};

export default function documentEditor(options) {
  component = options.component;
  validate();
}

/* eslint-disable no-undef */
import { Bert } from 'meteor/themeteorchef:bert';
import { upsertDocument } from '/imports/api/documents/methods';
import '/imports/lib/validation';

let component;

const handleUpsert = () => {
  const { doc, history } = component.props;
  const confirmation = doc && doc._id ? 'Document mis à jour !' : 'Document ajouté !';
  const upsert = {
    title: document.querySelector('[name="title"]').value.trim(),
    body: document.querySelector('[name="body"]').value.trim(),
  };

  if (doc && doc._id) upsert._id = doc._id;

  upsertDocument.call(upsert, (error, response) => {
    if (error) {
      Bert.alert(error.reason, 'danger');
    } else {
      component.documentEditorForm.reset();
      Bert.alert(confirmation, 'success');
      history.push(`/documents/${response.insertedId || doc._id}`);
    }
  });
};

const validate = () => {
  $(component.documentEditorForm).validate({
    rules: {
      title: {
        required: true,
      },
      body: {
        required: true,
      },
    },
    messages: {
      title: {
        required: 'Le titre est requis',
      },
      body: {
        required: 'Le corp est requis',
      },
    },
    submitHandler() { handleUpsert(); },
  });
};

export default function documentEditor(options) {
  component = options.component;
  validate();
}

Correct french translation module documents


module.exports = function(req, res) {
  console.log('req.body', req.body)
  var data = JSON.parse(req.body.params);
  if (!data) {
    res.status(403 /* Unauthorized */ ).send('Invalid params');
    return;
  }

  var width = data.width > 600 ? 600 : data.width;
  var html = '

';
  res.json({
    body: html
  });
};

module.exports = function(req, res) {
  console.log('req.body', req.body)
  var data = JSON.parse(req.body.params);
  if (!data) {
    res.status(403 /* Unauthorized */ ).send('Invalid params');
    return;
  }

  var width = data.width > 600 ? 600 : data.width;
  var html = '<img style="max-width:100%;" src="' + data.src + '" width="' + width + '"/>';
  res.json({
    body: html
    // Add raw:true if you're returning content that you want the user to be able to edit
  });
};

Remove paragraph that adds too much padding.


import { ApolloClient, InMemoryCache } from 'apollo-boost';
import { createUploadLink } from 'apollo-upload-client';
import fetch from 'isomorphic-unfetch';

let apolloClient = null;

let isBrowser = typeof window !== 'undefined';

function create(initialState) {
  return new ApolloClient({
    connectToDevTools: isBrowser,
    ssrMode: !isBrowser,
    link: createUploadLink({ uri: '/admin/api', fetch }),
    cache: new InMemoryCache().restore(initialState || {}),
  });
}

export default function initApollo(initialState) {
  // Make sure to create a new client for every server-side request so that data
  // isn't shared between connections (which would be bad)
  if (!isBrowser) {
    return create(initialState);
  }

  // Reuse client on the client-side
  if (!apolloClient) {
    apolloClient = create(initialState);
  }

  return apolloClient;
}

import { ApolloClient, InMemoryCache } from 'apollo-boost';
import { createUploadLink } from 'apollo-upload-client';
import fetch from 'isomorphic-unfetch';

let apolloClient = null;

let isBrowser = typeof window !== 'undefined';

function create(initialState) {
  // TODO: server-side requests must have an absolute URI. We should find a way
  // to make this part of the project config, seems highly opinionated here
  const uriHost = !isBrowser ? 'http://localhost:3000' : '';
  const uri = `${uriHost}/admin/api`;

  return new ApolloClient({
    connectToDevTools: isBrowser,
    ssrMode: !isBrowser,
    link: createUploadLink({ uri, fetch }),
    cache: new InMemoryCache().restore(initialState || {}),
  });
}

export default function initApollo(initialState) {
  // Make sure to create a new client for every server-side request so that data
  // isn't shared between connections (which would be bad)
  if (!isBrowser) {
    return create(initialState);
  }

  // Reuse client on the client-side
  if (!apolloClient) {
    apolloClient = create(initialState);
  }

  return apolloClient;
}

Fix apollo client in SSR mode


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

/**
 * A tag function that escapes URI components in a template string.
 *
 * @since n.e.x.t
 *
 * @param {string[]} strings The array of static strings in the template.
 * @param {...any} values The array of expressions used in the template.
 * @return {string} Escaped URI string.
 */
export function escapeURI( strings, ...values ) {
	return strings.reduce( ( acc, string, idx ) => {
		return acc + string + encodeURIComponent( values[ idx ] || '' );
	}, '' );
}

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

/**
 * A tag function that escapes URI components in a template string.
 *
 * @since n.e.x.t
 *
 * @param {string[]} strings The array of static strings in the template.
 * @param {...*} values The array of expressions used in the template.
 * @return {string} Escaped URI string.
 */
export function escapeURI( strings, ...values ) {
	return strings.reduce( ( acc, string, idx ) => {
		return acc + string + encodeURIComponent( values[ idx ] || '' );
	}, '' );
}

Update jsdoc type for values param of the escapeURI function.


(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};var R=(e)=>{var n=t[o][1][e];return s(n?n:e)};R.resolve=(m)=>{console.log('require.resolve',m);return m};t[o][0].call(l.exports,R,l,l.exports,e,t,n,r)}return n[o].exports};var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})

//based on https://github.com/substack/browser-pack/blob/01d39894f7168983f66200e727cdaadf881cd39d/prelude.js
// modules are defined as an array
// [ module function, map of requireuires ]
//
// map of requireuires is short require name -> numeric require
//
// anything defined in a previous bundle is accessed via the
// orig method which is the requireuire for previous bundles

(function outer (modules, cache, entry) {
    // Save the require from previous bundle to this closure if any
    var previousRequire = typeof require == "function" && require;

    function newRequire(name, jumped){
        if(!cache[name]) {
            if(!modules[name]) {
                // if we cannot find the module within our internal map or
                // cache jump to the current global require ie. the last bundle
                // that was added to the page.
                var currentRequire = typeof require == "function" && require;
                if (!jumped && currentRequire) return currentRequire(name, true);

                // If there are other bundles on this page the require from the
                // previous one is saved to 'previousRequire'. Repeat this as
                // many times as there are bundles until the module is found or
                // we exhaust the require chain.
                if (previousRequire) return previousRequire(name, true);
                var err = new Error('Cannot find module \'' + name + '\'');
                err.code = 'MODULE_NOT_FOUND';
                throw err;
            }
            var m = cache[name] = {exports:{}};
            modules[name][0].call(m.exports, function(x){
                var id = modules[name][1][x];
                return newRequire(id ? id : x);
            },m,m.exports,outer,modules,cache,entry);
        }
        return cache[name].exports;
    }
    for(var i=0;i<entry.length;i++) newRequire(entry[i]);

    // Override the current require with this new one
    return newRequire;
})

Add verbose prelude from browser-pack


module.exports = {
  commands: {
    random: {
      help: 'Selects a random choice or number',
      command: function (bot, msg) {
        if (msg.args.length > 2) {
          msg.args.shift()
          return msg.args[Math.floor(Math.random() * msg.args.length)]
        } else if (msg.args.length === 2) {
          var res = Math.floor(Math.random() * Math.floor(msg.args[1]))
          if (!isNaN(res)) {
            return res
          }
        }
        return 'Usage: (<number>|<choice 1> … <choice n>)'
      }
    },
    coin: {
      help: 'Flips a coin',
      command: function (bot, msg) {
        var faces = ["Heads!", "Tails!"]
        return faces[Math.floor(Math.random() * faces.length)]
      }
    }
  }
}

module.exports = {
  commands: {
    random: {
      help: 'Selects a random choice or number',
      command: function (bot, msg) {
        if (msg.args.length > 2) {
          msg.args.shift()
          return msg.args[Math.floor(Math.random() * msg.args.length)]
        } else if (msg.args.length === 2) {
          var res = Math.floor(Math.random() * Math.floor(msg.args[1]))
          if (!isNaN(res)) {
            return res
          }
        }
        return 'Usage: (<number>|<choice 1> … <choice n>)'
      }
    },
    coin: {
      help: 'Flips a coin',
      command: function () {
        var faces = ['Heads!', 'Tails!']
        return faces[Math.floor(Math.random() * faces.length)]
      }
    }
  }
}

Fix things the linter doesn't like


class ServersListCtrl {
  constructor($scope, Servers, Socket) {
    this.$scope = $scope;
    this.Servers = Servers;
    this.socket = Socket;
    this.init();
  }

  init() {
    this.servers = [];
    this.Servers.list()
      .then((servers) => {
        this.servers = servers;
        this.socket.on('server-created', (data) => {
          this.servers.push(data.server);
          this.$scope.$digest();
        });
      });
  }
}

ServersListCtrl.$inject = ['$scope', 'Servers', 'Socket'];

export default ServersListCtrl;

class ServersListCtrl {
  constructor($scope, Servers, Socket) {
    this.$scope = $scope;
    this.Servers = Servers;
    this.socket = Socket;
    this.init();
  }

  init() {
    this.servers = [];
    this.Servers.list()
      .then((servers) => {
        this.servers = servers;
        this.socket.on('server-created', (data) => {
          this.servers.push(data.server);
          this.$scope.$digest();
        });
        this.socket.on('server-removed', (data) => {
          _.remove(this.servers, (server) => {
            return server.hostname == data.server.hostname;
          });
          this.$scope.$digest();
        });
      });
  }
}

ServersListCtrl.$inject = ['$scope', 'Servers', 'Socket'];

export default ServersListCtrl;

Update servers list on remove


'use strict';

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

  beforeEach(module('wallet'));

  beforeEach(inject(function($rootScope) {
  	scope = $rootScope.$new();
  }));

  it('should define more than 5 awesome things', inject(function($controller) {
    expect(scope.awesomeThings).toBeUndefined();

    $controller('MainCtrl', {
      $scope: scope
  	});

    expect(angular.isArray(scope.awesomeThings)).toBeTruthy();
    expect(scope.awesomeThings.length > 5).toBeTruthy();
  }));
});

'use strict';

describe('controllers', function () {
  var scope, ctrl;

  beforeEach(module('wallet'));

  describe('WalletCtrl', function () {
    beforeEach(inject(function ($rootScope, $controller) {
      scope = $rootScope.$new();
      ctrl = $controller('WalletCtrl', {
        $scope: scope
      });
    }));

    it('should start with 0 transactions', function () {
      expect(angular.isArray(scope.$storage.transactions)).toBeTruthy();
      expect(scope.$storage.transactions.length === 0).toBeTruthy();
    });

    it('should be able to deposit', function () {
      scope.depositAmount = 20;
      scope.deposit();

      expect(scope.$storage.balance === 20).toBeTruthy();
    });
  });
});

Add a couple of unit tests.


'use strict';

var TaskList = require('../POMs/TasksList.js');

describe('Check that task name can be updated', function() {

  beforeAll(function() {
    TaskList.actionsBeforeAll();
  });

  it('Adding new task', function() {
    TaskList.addTask('protractor test task1');

    //wait untill all task will be loaded
    browser.sleep(config.config.jasmineNodeOpts.defaultTimeoutInterval / 10);
  });

  it('Updating the task name and verifying that it is saved', function() {
    TaskList.taskEditing(0, 'updated task name');
  });

  afterAll(function() {
    //Deleting all existing tasks before each spec to avoid conflicts
    TaskList.clearAllTasks();
  });

});

/* global browser */
/* global protractor */

'use strict';

var TaskList = require('../POMs/TasksList.js');
var config = require('../protractor.config.js');

describe('Check that task name can be updated', function() {

  beforeAll(function() {
    TaskList.actionsBeforeAll();
  });

  it('Adding new task', function() {
    TaskList.addTask('protractor test task1');

    //wait untill all task will be loaded
    browser.sleep(config.config.jasmineNodeOpts.defaultTimeoutInterval / 10);
  });

  it('Updating the task name and verifying that it is saved', function() {
    TaskList.taskEditing(0, 'updated task name');
  });

  afterAll(function() {
    //Deleting all existing tasks before each spec to avoid conflicts
    TaskList.clearAllTasks();
  });

});

Fix hound issues: [ci full]


'use strict';

function Parser(plugins) {
    var filterPluginsForServices = function(serviceNames) {
        return plugins.filter(function (plugin) {
            return serviceNames.findIndex(function (serviceName) {
                    return plugin.name === serviceName;
                }) !== -1;
        });
    };

    this.parse = function parse(services, serviceNames) {
        if (typeof services === 'string') {
            services = JSON.parse(services);
        }

        var selectedPlugins = plugins;
        if (typeof serviceNames === 'string') {
            selectedPlugins = selectedPlugins.filter(function (plugin) {
                return plugin.name === serviceNames;
            });
        }
        else if (typeof serviceNames && serviceNames instanceof Array) {
            selectedPlugins = filterPluginsForServices(serviceNames);
        }

        var result = {};
        selectedPlugins.forEach(function(plugin) {
            result[plugin.name] = plugin.parse(services);
        });
        return result;
    };

    this.resolveAll = function resolveAll() {
        return this.parse(process.env.VCAP_SERVICES);
    };

    this.resolve = function resolve(serviceNames) {
        return this.parse(process.env.VCAP_SERVICES, serviceNames);
    };
}

module.exports = {
    Parser : Parser
};

'use strict';

function Parser(plugins) {
    var filterPluginsForServices = function(serviceNames) {
        return plugins.filter(function (plugin) {
            return serviceNames.findIndex(function (serviceName) {
                    return plugin.name === serviceName;
                }) !== -1;
        });
    };

    var filterPluginsForService = function(serviceNames) {
        return plugins.filter(function (plugin) {
            return plugin.name === serviceNames;
        });
    };

    var selectPlugins = function (serviceNames) {
        if (typeof serviceNames === 'string') {
            return filterPluginsForService(serviceNames);
        }
        else if (typeof serviceNames && serviceNames instanceof Array) {
            return filterPluginsForServices(serviceNames);
        }
        return plugins;
    };

    this.parse = function parse(services, serviceNames) {
        // Ensure the services are an object
        if (typeof services === 'string') {
            services = JSON.parse(services);
        }

        // Select the plugins to parse with
        var selectedPlugins = selectPlugins(serviceNames);

        // Use the selected plugins to parse the service
        var result = {};
        selectedPlugins.forEach(function(plugin) {
            result[plugin.name] = plugin.parse(services);
        });

        return result;
    };

    this.resolveAll = function resolveAll() {
        return this.parse(process.env.VCAP_SERVICES);
    };

    this.resolve = function resolve(serviceNames) {
        return this.parse(process.env.VCAP_SERVICES, serviceNames);
    };
}

module.exports = {
    Parser : Parser
};

Refactor selecting the plugins to use for parsing.


(function(window)
{
    // if we're running on the Cloud CMS hosted platform, we can auto-acquire the client key that we should use
    (function()  {

        var uri = window.location.href;
        var z1 = uri.indexOf(window.location.pathname);
        z1 = uri.indexOf("/", z1 + 2);
        if (z1 > -1)
        {
            uri = uri.substring(0, z1);
        }

        if (uri.indexOf("cloudcms.net") > -1)
        {
            Gitana.autoConfigUri = uri;
        }

    }());

})(window);

(function(window)
{
    // if we're running on the Cloud CMS hosted platform, we can auto-acquire the client key that we should use
    (function()  {

        // check to make sure location exists (only available in browsers)
        if (typeof window.location != "undefined")
        {
            var uri = window.location.href;
            var z1 = uri.indexOf(window.location.pathname);
            z1 = uri.indexOf("/", z1 + 2);
            if (z1 > -1)
            {
                uri = uri.substring(0, z1);
            }

            if (uri.indexOf("cloudcms.net") > -1)
            {
                Gitana.autoConfigUri = uri;
            }
        }

    }());

})(window);

Check for window.location undefined on auto config setup


/**
 * jQuery functionality used on the external content admin page
 */

;(function ($, pt) {
	$().ready(function () {
		// bind the upload form
		$('#Form_EditForm_Migrate').click(function () {
			// we don't want this to be submitted via the edit form, as we want to do an ajax postback for this
			// and not tie up the response.

			var form = $(this).parents('form');
			// wrap it all up and post away!
			var params = form.serializeArray();
			var postParams = {};
			$.each(params, function (index) {
				postParams[this.name] = this.value;
			});

			postParams['action_migrate'] = true;
			statusMessage('Importing ...', 2);

			$.post(form.attr('action'), postParams, function (data) {

				if (data) {
					var response = $.parseJSON(data);
					if (response && response.status) {
						statusMessage(response.message, 'good');
					} else {
						statusMessage("There was a problem with the import");
					}
				}

				// reset the base form
				if (pt) {
					pt(form.attr('id')).resetElements();
				}
			});
			return false;
		});
	});
})(jQuery, $);

/**
 * jQuery functionality used on the external content admin page
 */

;(function ($, pt) {
	$().ready(function () {
		// bind the upload form
		$('#Form_EditForm_Migrate').livequery(function () {
			$(this).click(function () {
				// we don't want this to be submitted via the edit form, as we want to do an ajax postback for this
				// and not tie up the response.
				var form = $(this).parents('form');
				// wrap it all up and post away!
				var params = form.serializeArray();
				var postParams = {};
				$.each(params, function (index) {
					postParams[this.name] = this.value;
				});

				postParams['action_migrate'] = true;
				statusMessage('Importing ...', 2);

				$.post(form.attr('action'), postParams, function (data) {

					if (data) {
						var response = $.parseJSON(data);
						if (response && response.status) {
							statusMessage(response.message, 'good');
						} else {
							statusMessage("There was a problem with the import");
						}
					}

					// reset the base form
					if (pt) {
						pt(form.attr('id')).resetElements();
					}
				});
				return false;
			});

		});
	});
})(jQuery, $);

Make sure to livequery the migrate button...


// Set the require.js configuration for your application.
require.config({

  // Initialize the application with the main application file.
  deps: ["main"],

  paths: {

    // Libraries.
    circliful: "/static/js/vendor/jquery.circliful.min",
    jquery: "/static/js/vendor/jquery-2.1.0.min",
    underscore: "/static/js/vendor/underscore.min",
    backbone: "/static/js/vendor/backbone.min",
    backbonequeryparams: "/static/js/vendor/backbone.queryparams",
    bootstrap: "/static/js/vendor/bootstrap.min",
    datatables: "/static/js/vendor/jquery.dataTables.min",
    datatablesbs3: "/static/js/vendor/datatables.bs3",
    moment: "/static/js/vendor/moment.min",
    sparkline: "/static/js/vendor/jquery.sparkline.min"
  },

  urlArgs: "bust=" +  (new Date()).getTime(),

  shim: {

    backbone: {
      deps: ["underscore", "jquery"],
      exports: "Backbone"
    },
    bootstrap: {
      deps: ["jquery"]
    },
    datatables: {
      deps: ["jquery"]
    },
    datatablesbs3: {
      deps: ["datatables"]
    },
    backbonequeryparams: {
      deps: ["backbone"]
    },
    sparkline: {
      deps: ["jquery"]
    }

  }

});

// Set the require.js configuration for your application.
require.config({

  // Initialize the application with the main application file.
  deps: ["main"],

  paths: {

    // Libraries.
    circliful: "/static/js/vendor/jquery.circliful.min",
    jquery: "/static/js/vendor/jquery-2.1.0.min",
    underscore: "/static/js/vendor/underscore.min",
    backbone: "/static/js/vendor/backbone.min",
    backbonequeryparams: "/static/js/vendor/backbone.queryparams",
    bootstrap: "/static/js/vendor/bootstrap.min",
    datatables: "/static/js/vendor/jquery.dataTables.min",
    datatablesbs3: "/static/js/vendor/datatables.bs3",
    moment: "/static/js/vendor/moment.min",
    sparkline: "/static/js/vendor/jquery.sparkline.min"
  },

  // urlArgs: "bust=" +  (new Date()).getTime(),

  shim: {

    backbone: {
      deps: ["underscore", "jquery"],
      exports: "Backbone"
    },
    bootstrap: {
      deps: ["jquery"]
    },
    datatables: {
      deps: ["jquery"]
    },
    datatablesbs3: {
      deps: ["datatables"]
    },
    backbonequeryparams: {
      deps: ["backbone"]
    },
    sparkline: {
      deps: ["jquery"]
    }

  }

});

Disable cache busting for dashboard


var Sequelize = require("sequelize");

module.exports = function(sequelize, tableConfig) {
  return sequelize.define('UserUrl', {
    email: {
      type: Sequelize.STRING,
      allowNull: false
    },
    webImage: {
      type: Sequelize.STRING
    },
    cropImage: {
      type: Sequelize.STRING,
      allowNull: false
    },
    cropHeight: {
      type: Sequelize.INTEGER,
      allowNull: false
    },
    cropWidth: {
      type: Sequelize.INTEGER,
      allowNull: false
    },
    cropOriginX: {
      type: Sequelize.INTEGER,
      allowNull: false
    },
    cropOriginY: {
      type: Sequelize.INTEGER,
      allowNull: false
    },
    status: Sequelize.BOOLEAN,
    frequency: {
      type: Sequelize.INTEGER,
      defaultValue: 5
    },
  }, tableConfig)
}

var Sequelize = require("sequelize");

module.exports = function(sequelize, tableConfig) {
  return sequelize.define('UserUrl', {
    email: {
      type: Sequelize.STRING,
      allowNull: false
    },
    webImage: {
      type: Sequelize.STRING
    },
    cropImage: {
      type: Sequelize.STRING,
      allowNull: false
    },
    cropHeight: {
      type: Sequelize.INTEGER,
      allowNull: false
    },
    cropWidth: {
      type: Sequelize.INTEGER,
      allowNull: false
    },
    cropOriginX: {
      type: Sequelize.INTEGER,
      allowNull: false
    },
    cropOriginY: {
      type: Sequelize.INTEGER,
      allowNull: false
    },
    status: {
      type: Sequelize.BOOLEAN,
      defaultValue: true
    },
    frequency: {
      type: Sequelize.INTEGER,
      defaultValue: 5
    },
  }, tableConfig)
}

Set status to default to true


import lazyRequire from '@sanity/util/lib/lazyRequire'

const helpText = `
Options
  -y, --yes Use unattended mode, accepting defaults and using only flags for choices
  --project <projectId> Project ID to use for the studio
  --dataset <dataset> Dataset name for the studio
  --output-path <path> Path to write studio project to
  --template <template> Project template to use [default: "clean"]

Examples
  sanity init -y --project abc123 --dataset production --output-path ~/myproj
  sanity init -y --project abc123 --dataset staging --template moviedb --output-path .
`

export default {
  name: 'init',
  signature: 'init [plugin]',
  description: 'Initialize a new Sanity project',
  action: lazyRequire(require.resolve('./initAction')),
  helpText
}

import lazyRequire from '@sanity/util/lib/lazyRequire'

const helpText = `
Options
  -y, --yes Use unattended mode, accepting defaults and using only flags for choices
  --project <projectId> Project ID to use for the studio
  --dataset <dataset> Dataset name for the studio
  --output-path <path> Path to write studio project to
  --template <template> Project template to use [default: "clean"]

Examples
  # Initialize a new project, prompt for required information along the way
  sanity init

  # Initialize a new plugin
  sanity init plugin

  # Initialize a project with the given project ID and dataset to the given path
  sanity init -y --project abc123 --dataset production --output-path ~/myproj

  # Initialize a project with the given project ID and dataset using the moviedb
  # template to the given path
  sanity init -y --project abc123 --dataset staging --template moviedb --output-path .

`

export default {
  name: 'init',
  signature: 'init [plugin]',
  description: 'Initialize a new Sanity project or plugin',
  action: lazyRequire(require.resolve('./initAction')),
  helpText
}

Improve help text on init command


import React from "react";
export const default_icon = "fa fa-circle-o";

export const un_ocha_icon_mapping = {
  "core-relief-item": "core-relief-item",
  shelter: "shelter",
  health: "health",
  nutrition: "nutrition",
  "sanitation-water-hygiene": "sanitation-water-hygiene",
  logistics: "logistics",
  protection: "protection",
  "legal-advice": "legal-advice",
  education: "education",
  "camp-management": "camp-management"
};

export function getIconClassForIssueType(issueType, prefix = "icon-") {
  let cls;
  switch (typeof issueType) {
    case "string":
      cls = un_ocha_icon_mapping[issueType];
      break;
    case "object":
      if (issueType.slug) {
        let key = issueType.slug;
        cls =
          un_ocha_icon_mapping[key] || un_ocha_icon_mapping[key.toLowerCase()];
        break;
      }
  }
  if (cls) {
    return `${prefix}${cls}`;
  } else {
    return default_icon;
  }
}

const ToucanIcon = ({ issue_type }) => {
  return (
    <span
      className={getIconClassForIssueType(issue_type)}
      title={issue_type.name}
    />
  );
};

export default ToucanIcon;

import React from "react";
export const default_icon = "fa fa-circle-o";

export const un_ocha_icon_mapping = {
  "core-relief-item": "core-relief-item",
  shelter: "shelter",
  health: "health",
  nutrition: "nutrition",
  "sanitation-water-hygiene": "sanitation-water-hygiene",
  logistics: "logistics",
  protection: "protection",
  "legal-advice": "legal-advice",
  education: "education",
  "camp-management": "camp-management"
};

export function getIconClassForIssueType(issueType, prefix = "icon-") {
  let cls;
  switch (typeof issueType) {
    case "string":
      cls = un_ocha_icon_mapping[issueType];
      break;
    case "object":
      if (issueType.slug) {
        let key = issueType.slug;
        cls =
          un_ocha_icon_mapping[key] || un_ocha_icon_mapping[key.toLowerCase()];
        break;
      }
  }
  if (cls) {
    return `${prefix}${cls}`;
  } else {
    return default_icon;
  }
}

const ToucanIcon = ({ issue_type }) => {
  return (
    <span
      className={"icon " + getIconClassForIssueType(issue_type)}
      title={issue_type.name}
    />
  );
};

export default ToucanIcon;

Add correct icon class to issuetype


/**
 * @description Helper function used to convert an object that has key value pairs where
 * the value is an object with a value property, i.e. {value: {value:'', displayValue:''}},
 * into an object that has primitives as values: {value: ''}.
 * @param obj The object that has value objects.
 * @returns An object that has primitives as values, derived from the "value" property of
 * the fields on the passed in object.
 */
export function flatten(obj) {
    let flatObj = {};
    for (const [key, value] of Object.entries(obj)) {
        if (value !== null && value !== undefined && value.hasOwnProperty('value')) {
            flatObj[key] = value.value;
        } else {
            flatObj[key] = value;
        }
    }
    return flatObj;
}

Add description to flatten function


Sort menus only in parent containment


// ==========================================================================
// Project:  SproutCore Runtime
// Copyright: ©2011 Strobe Inc. and contributors.
// License:   Licensed under MIT license (see license.js)
// ==========================================================================

module('system/run_loop/run_test');

test('SC.run invokes passed function, returning value', function() {
  var obj = {
    foo: function() { return [this.bar, 'FOO']; },
    bar: 'BAR'
  };

  equals(SC.run(function() { return 'FOO'; }), 'FOO', 'pass function only');
  same(SC.run(obj, obj.foo), ['BAR', 'FOO'], 'pass obj.method');
  same(SC.run(obj, 'foo'), ['BAR', 'FOO'], 'pass obj.method');
});

// ==========================================================================
// Project:  SproutCore Runtime
// Copyright: ©2011 Strobe Inc. and contributors.
// License:   Licensed under MIT license (see license.js)
// ==========================================================================

module('system/run_loop/run_test');

test('SC.run invokes passed function, returning value', function() {
  var obj = {
    foo: function() { return [this.bar, 'FOO']; },
    bar: 'BAR',
    checkArgs: function(arg1, arg2) { return [ arg1, this.bar, arg2 ]; }
  };

  equals(SC.run(function() { return 'FOO'; }), 'FOO', 'pass function only');
  same(SC.run(obj, obj.foo), ['BAR', 'FOO'], 'pass obj and obj.method');
  same(SC.run(obj, 'foo'), ['BAR', 'FOO'], 'pass obj and "method"');
  same(SC.run(obj, obj.checkArgs, 'hello', 'world'), ['hello', 'BAR', 'world'], 'pass obj, obj.method, and extra arguments');
});

Add unit test for SC.run passing extra arguments to the method.


import findCounterpartCharacter from './findCounterpartCharacter';

/**
 * Determines whether a node is surrounded by a matching pair of grouping
 * characters.
 *
 * @param {Object} node
 * @param {string} left
 * @returns {boolean}
 */
export default function isSurroundedBy(node, left, source) {
  if (source[node.range[0] - 1] !== left) {
    return false;
  }

  return findCounterpartCharacter(left, source, node.range[0] - 1) === node.range[1];
}

import findCounterpartCharacter from './findCounterpartCharacter';

/**
 * Determines whether a node is surrounded by a matching pair of grouping
 * characters.
 *
 * @param {Object} node
 * @param {string} left
 * @param {string} source
 * @returns {boolean}
 */
export default function isSurroundedBy(node, left, source) {
  if (source[node.range[0] - 1] !== left) {
    return false;
  }

  return findCounterpartCharacter(left, source, node.range[0] - 1) === node.range[1];
}

Add a missing param doc.


#!/usr/bin/env node

require('coffee-script').register()

var fs = require('fs'),
    indentation = require('../lib/indentation'),
    buf, emblemFile, hbsFile, output;

if (process.argv.length < 3) {
  if (process.stdin.isTTY) {
    console.log('USAGE: emblem2hbs filetoconvert.emblem or in piped format `pbcopy | emblem2hbs | pbpaste`');
    return;
  }
  else {
    processFromPipe();
  }
} else {
  emblemFile = process.argv[2];
  hbsFile = emblemFile.substr(0, emblemFile.lastIndexOf('.')) + '.hbs';
  buf = fs.readFileSync(emblemFile, 'utf8');
  output = require('emblem').default.compile(buf);
  fs.writeFileSync(hbsFile, indentation.indent(output));
}

function processFromPipe() {
  var input = process.stdin;
  var output = process.stdout;
  var error = null;
  var data = '';

  input.setEncoding('utf8');

  input.on('data', function(chunk){
    data += chunk;
  });

  input.on('end', function(){
    var converted = require('emblem').default.compile(data);
    output.end(indentation.indent(converted));
  });

  output.on('error', function(err) {
    error = err;
  });
}

#!/usr/bin/env node

require('coffee-script').register()

var fs = require('fs'),
    indentation = require('../lib/indentation'),
    buf, emblemFile, hbsFile, output;

if (process.argv.length < 3) {
  if (process.stdin.isTTY) {
    console.log('USAGE: emblem2hbs filetoconvert.emblem or in piped format `pbcopy | emblem2hbs | pbpaste`');
  }
  else {
    processFromPipe();
  }
} else {
  emblemFile = process.argv[2];
  hbsFile = process.argv[3] ? process.argv[3] : emblemFile.substr(0, emblemFile.lastIndexOf('.')) + '.hbs';
  buf = fs.readFileSync(emblemFile, 'utf8');
  output = require('emblem').default.compile(buf);
  fs.writeFileSync(hbsFile, indentation.indent(output));
}

function processFromPipe() {
  var input = process.stdin;
  var output = process.stdout;
  var error = null;
  var data = '';

  input.setEncoding('utf8');

  input.on('data', function(chunk){
    data += chunk;
  });

  input.on('end', function(){
    var converted = require('emblem').default.compile(data);
    output.end(indentation.indent(converted));
  });

  output.on('error', function(err) {
    error = err;
  });
}

Allow for 2nd argument to be destination filename


#!/usr/bin/env node

var repl = require("repl");
var massive = require("../index");
var program = require('commander');
var assert = require("assert");

program
  .version('0.0.1')
  .option('-d, --database', 'The local db you want to connect to ')
  .option('-c, --connection', 'The full connection string')
  .parse(process.argv);

var connectionString;
if(program.database){
  connectionString = "postgres://localhost/" + program.args[0]; //assume local user has rights
}else if(program.connection) {
  connectionString = program.args[0];
}else{
  console.log("The options to pass in are:");
  console.log(" -d or --database to connect locally to a database");
  console.log(" -c or --connection to enter the full connection string: postgres://user:password@server/tablename");
}

if(connectionString){
  massive.connect({connectionString : connectionString}, function(err,db){
    var context = repl.start({
      prompt: "db > ",
    }).context;
    context.db = db;
    console.log("Massive loaded and listening");
  });

}

#!/usr/bin/env node

var repl = require("repl");
var massive = require("../index");
var program = require('commander');
var assert = require("assert");

program
  .version('0.0.1')
  .option('-d, --database', 'The local db you want to connect to ')
  .option('-c, --connection', 'The full connection string')
  .parse(process.argv);

var connectionString;
if(program.database){
  connectionString = "postgres://localhost/" + program.args[0]; //assume local user has rights
}else if(program.connection) {
  connectionString = program.args[0];
}else{
  console.log("The options to pass in are:");
  console.log(" -d or --database to connect locally to a database");
  console.log(" -c or --connection to enter the full connection string: postgres://user:password@server/tablename");
}

if(connectionString){
  massive.connect({connectionString : connectionString}, function(err,db){
    if(err) {
      console.log("Failed loading Massive: "+err);
      process.exit(1);
    }
    var context = repl.start({
      prompt: "db > ",
    }).context;
    context.db = db;
    console.log("Massive loaded and listening");
  });

}

Add error handling to initial connection in repl


(function () {
  'use strict';

  angular
    .module('todoList')
    .controller('todoListCtrl', todoListCtrl);

  todoListCtrl.$inject = ['TodoListService'];

  function todoListCtrl(TodoListService) {

    var vm = this;
    var corkboard;
    vm.addTodo = addTodo;

    // Set up the draggable corkboard

    TodoListService.getTodos().then(function(data){
      corkboard = Draggable.create('.todo-items__wrapper', { type:'scroll', edgeResistance: 1 })[0];
      vm.items = data;
    });

    function addTodo(e) {

      console.log(e.clientX, corkboard.pointerX);
      console.log(e.clientY, corkboard.pointerY);

      var todo = {
        UserID: '666',
        Status: true,
        Priority: 'high',
        Desc: 'My new todo',
        Xcoord: e.clientX - 100 + (corkboard.x * -1),
        Ycoord: e.clientY + (corkboard.y * -1)
      };

      TodoListService.addTodo(todo).then(function(data) {
        todo._id = data;
        vm.items.push(todo);
      });

    }

    function deleteTodo(todo) {

      TodoListService.deleteTodo(todo).then(function(data) {

      });

    }

  }

})();

(function () {
  'use strict';

  angular
    .module('todoList')
    .controller('todoListCtrl', todoListCtrl);

  todoListCtrl.$inject = ['TodoListService'];

  function todoListCtrl(TodoListService) {

    var vm = this;
    var corkboard;
    vm.addTodo = addTodo;

    // Set up the draggable corkboard

    TodoListService.getTodos().then(function(data){
      corkboard = Draggable.create('.todo-items__wrapper', { type:'scroll', edgeResistance: 1 })[0];
      vm.items = data;
    });

    function addTodo(e) {

      corkboard.update();

      var todo = {
        UserID: '666',
        Status: true,
        Priority: 'high',
        Desc: 'My new todo',
        Xcoord: e.clientX - 100 + (corkboard.x * -1),
        Ycoord: e.clientY + (corkboard.y * -1)
      };

      TodoListService.addTodo(todo).then(function(data) {
        todo._id = data;
        vm.items.push(todo);
      });

    }

    function deleteTodo(todo) {

      TodoListService.deleteTodo(todo).then(function(data) {

      });

    }

  }

})();

Fix cursor position on scroll


(function(window){
    window.ajax = function(route, options) {
        var data = options.data || {},
            success = options.success || function(response){},
            error = options.error || function(response){},
            request = new XMLHttpRequest();

        request.open(route.method, route.url, true);
        request.setRequestHeader("X-Requested-With", "XMLHttpRequest");

        request.addEventListener("load", function(evt) {
            success(JSON.parse(request.responseText), request);
        });
        request.addEventListener("error", function(evt) {
            error(request);
        });

        if(route.method === 'POST') {
            request.send(data);
        }
        else {
            request.send();
        }
    };
})(window);

(function(window){
    window.ajax = function(route, options) {
        var data = options.data || {},
            success = options.success || function(response){},
            error = options.error || function(response){},
            request = new XMLHttpRequest();

        request.open(route.method, route.url, true);
        request.setRequestHeader("X-Requested-With", "XMLHttpRequest");

        request.addEventListener("load", function(evt) {
            success(JSON.parse(request.responseText), request);
        });
        request.addEventListener("error", function(evt) {
            error(request);
        });

        if(route.method === 'POST') {
            request.setRequestHeader("Content-type","application/x-www-form-urlencoded; charset=UTF-8");
            request.send(data);
        }
        else {
            request.send();
        }
    };
})(window);

Send post data as form-urlencoded


var webpack = require("webpack");
var Config = require("webpack-config").Config;

module.exports = new Config().extend("conf/webpack.base.config.js").merge({
    devtool: "#source-map",
    plugins: [
        new webpack.optimize.DedupePlugin(),
        new webpack.optimize.OccurrenceOrderPlugin(),
        new webpack.optimize.UglifyJsPlugin({ // Seems to break worker source maps
            sourceMap: true,
            mangle: {
                except: ["global"] // do not mangle global paralleljs env
            }
        })
    ]
});

var webpack = require("webpack");
var Config = require("webpack-config").Config;

module.exports = new Config().extend("conf/webpack.base.config.js").merge({
    devtool: "#source-map",
    plugins: [
        new webpack.optimize.DedupePlugin(),
        new webpack.optimize.OccurrenceOrderPlugin(),
        new webpack.optimize.UglifyJsPlugin({ // Seems to break worker source maps
            sourceMap: true,
            debug: true,
            mangle:false // mangling does not behave nicely with parallel.js
        })
    ]
});

Disable Mangling as it does not play well with paralleljs