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

'use strict';

// MODULES //

var defineProperty = require( './define_property.js' );

// MAIN //

/**
* Tests for `Object.defineProperty` support.
*
* @private
* @returns {boolean} boolean indicating if an environment has `Object.defineProperty` support
*
* @example
* var bool = hasDefinePropertySupport();
* // returns <boolean>
*/
function hasDefinePropertySupport() {
	var bool;
	if ( defineProperty === null ) {
		return false;
	}
	// Test basic support...
	try {
		defineProperty( {}, 'x', {} );
		bool = true;
	} catch ( err ) { // eslint-disable-line no-unused-vars
		bool = false;
	}
	return bool;
}

// EXPORTS //

module.exports = hasDefinePropertySupport;

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

'use strict';

// MODULES //

var defineProperty = require( './define_property.js' );

// MAIN //

/**
* Tests for `Object.defineProperty` support.
*
* @private
* @returns {boolean} boolean indicating if an environment has `Object.defineProperty` support
*
* @example
* var bool = hasDefinePropertySupport();
* // returns <boolean>
*/
function hasDefinePropertySupport() {
	// Test basic support...
	try {
		defineProperty( {}, 'x', {} );
		return true;
	} catch ( err ) { // eslint-disable-line no-unused-vars
		return false;
	}
}

// EXPORTS //

module.exports = hasDefinePropertySupport;

Simplify implementation and achieve full test coverage


'use strict';

var RSVP = require('rsvp');
var tinylr = require('tiny-lr');
var ui = require('../ui');

exports.start = function(options) {
  var liveReloadServer = new tinylr.Server();

  var listen = RSVP.denodeify(liveReloadServer.listen.bind(liveReloadServer));

  // Reload on file changes
  options.watcher.on('change', function() {
    console.log('built');
    liveReloadServer.changed({body: {files: ['LiveReload files']}});
  });

  // Start LiveReload server
  return listen(options.liveReloadPort)
    .then(function(value) {
      ui.write('Livereload server on port ' + options.liveReloadPort + '\n');
      return value;
    });
};

'use strict';

var RSVP = require('rsvp');
var tinylr = require('tiny-lr');
var ui = require('../ui');

exports.start = function(options) {
  var liveReloadServer = new tinylr.Server();

  var listen = RSVP.denodeify(liveReloadServer.listen.bind(liveReloadServer));

  // Reload on file changes
  options.watcher.on('change', function() {
    liveReloadServer.changed({body: {files: ['LiveReload files']}});
  });

  // Start LiveReload server
  return listen(options.liveReloadPort)
    .then(function(value) {
      ui.write('Livereload server on port ' + options.liveReloadPort + '\n');
      return value;
    });
};

Remove logging of 'build' after each broccoli build


angular.module('WatchTimer')
  .directive('resizeTextToFill', function($document, $timeout) {
    return {
      link: function(scope, element, attrs, controller, transcludeFn) {

        var initial_font_size = 10;
        var font_size_increment = 20;

        var safety_counter_max = 100;

        function set_size(font_size) {
          element.css({
            fontSize: font_size + "px",
            lineHeight: font_size + "px" // digits don't need more than this
          });
        }

        function resize_to_fit() {
          var font_size = initial_font_size;
          var max_height = $document.height() - font_size_increment;
          var max_width = $document.width() - font_size_increment;
          var safety_counter = 0;

          set_size(font_size);

          while (
            element.height() <= max_height && element.width() <= max_width && safety_counter < safety_counter_max
          ) {
            safety_counter++;
            font_size += font_size_increment;
            set_size(font_size);
          }

          // go back down one size so we know it fits
          font_size -= font_size_increment;
          set_size(font_size);
        }

        $timeout(function() {
          resize_to_fit();
        });

        // 'resize' as the chrome dev toolkit does not trigger an
        // 'orientationchange' event when switching between oriantations.
        $(window).on("resize orientationchange", resize_to_fit);

      }
    };
  })

angular.module('WatchTimer')
  .directive('resizeTextToFill', function($window, $timeout) {
    return {
      link: function(scope, element, attrs, controller, transcludeFn) {

        var initial_font_size = 10;
        var font_size_increment = 20;

        var safety_counter_max = 100;

        function set_size(font_size) {
          element.css({
            fontSize: font_size + "px",
            lineHeight: font_size + "px" // digits don't need more than this
          });
        }

        function resize_to_fit() {
          var font_size = initial_font_size;
          var max_height = $($window).height() - font_size_increment;
          var max_width = $($window).width() - font_size_increment;
          var safety_counter = 0;

          set_size(font_size);

          while (
            element.height() <= max_height && element.width() <= max_width && safety_counter < safety_counter_max
          ) {
            safety_counter++;
            font_size += font_size_increment;
            set_size(font_size);
          }

          // go back down one size so we know it fits
          font_size -= font_size_increment;
          set_size(font_size);
        }

        $timeout(function() {
          resize_to_fit();
        });

        // 'resize' as the chrome dev toolkit does not trigger an
        // 'orientationchange' event when switching between oriantations.
        $($window).on("resize orientationchange", resize_to_fit);

      }
    };
  })

Use window dimensions rather than document dimensions so the sizing is more stable


// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.

// npm compatibility
if (typeof define !== 'function') { var define = require('./requirejs-shim')(module); }

define(["base/js/namespace"], function(Jupyter) {
    "use strict";

    var save_state = function() {
        Jupyter.WidgetManager._managers[0].get_state().then(function(state) {
            var data = "text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(state, null, "    "));
            var a = document.createElement("a");
            a.download = "state.json";
            a.href = "data:" + data;
            a.click();
        });
    };

    var load_extension = function() {
        Jupyter.toolbar.add_buttons_group([{
            id : 'widget_manager_state',
            label : 'Download Widget State',
            icon : 'fa-sliders',
            callback : save_state
        }]);
    };

    load_extension();

});

// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.

define(["base/js/namespace"], function(Jupyter) {
    "use strict";

    var save_state = function() {
        Jupyter.WidgetManager._managers[0].get_state().then(function(state) {
            var data = "text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(state, null, "    "));
            var a = document.createElement("a");
            a.download = "state.json";
            a.href = "data:" + data;
            a.click();
        });
    };

    var action = {
        help: 'Download Widget State',
        icon: 'fa-sliders',
        help_index : 'zz',
        handler : save_state
    };

    var action_name = 'save-widget-state';
    var prefix = '';
    Jupyter.notebook.keyboard_manager.actions.register(action, action_name, prefix);

});

Create action rather than toolbar button


Parse.Cloud.beforeSave("Bill", function (request, response) {
    request.object.get("Family").fetch().then(function (family) {
        family.get("Writers").fetch().then(function (writers) {
            writers.relation("users").query().find().then(function (results) {
                var found = false;
                for (var i = 0; i < results.length; ++i) {
                    if (results[i].id === request.user.id) {
                        found = true;
                        break;
                    }
                }
                if (found)
                    response.success();
                else
                    response.error("You cannot write to the family");
            });
        });
    });
});

Parse.Cloud.beforeSave("Bill", function (request, response) {
    request.object.get("Family").fetch().then(function (family) {
        family.get("Writers").fetch().then(function (writers) {
            writers.relation("users").query().find().then(function (results) {
                var found = false;
                for (var i = 0; i < results.length; ++i) {
                    if (results[i].id === request.user.id) {
                        found = true;
                        break;
                    }
                }
                if (found)
                    response.success();
                else
                    response.error("You cannot write to the family");
            });

            family.get("Readers").fetch().then(function (readers) {
                var writersQuery = writers.relation("users").query();
                var readersQuery = readers.relation("users").query();
                var usersQuery = Parse.Query.or(
                    writersQuery, readersQuery);

                var pushQuery = new Parse.Query(Parse.Installation);
                pushQuery.matchesQuery('user', usersQuery);

                Parse.Push.send({
                    where: pushQuery,
                    data: {
                        alert: "Bill added for " +
                            request.object.get("Payee")
                    }
                });
            });

        });
    });
});

Send push notifications via cloud code.


(function ($) {

    $.fn.animateCSS = function (effect, delay, callback) {

        // Return this to maintain chainability
        return this.each(function () {

            // Cache $(this) for speed
            var $this = $(this);

            // Check if delay exists or if it's a callback
            if (!delay || typeof delay == 'function') {

                // If it's a callback, move it to callback so we can call it later
                callback = delay;

                // Set the delay to 0 for the setTimeout
                delay = 0;
            }

            // Start a counter so we can delay the animation if required
            var animation = setTimeout(function () {

                // Add the animation effect with classes
                $this.addClass('animated ' + effect);

                // Check if the elemenr has been hidden to start with
                if ($this.css('visibility') == 'hidden') {

                    // If it has, show it (after the class has been added)
                    $this.css({
                        'visibility': 'visible'
                    });

                }

                // If the element is hidden
                if ($this.is(':hidden')) {

                    // Show it
                    $this.show();

                }

                // Event triggered when the animation has finished
                $this.bind('animationend webkitAnimationEnd oAnimationEnd', function () {

                    // Add a callback event
                    if (typeof callback == 'function') {

                        // Execute the callback
                        callback.call(this);

                    }

                });

            // Specify the delay
            }, delay);

        });

    };

})(jQuery);

(function ($) {

    $.fn.animateCSS = function (effect, delay, callback) {

        // Return this to maintain chainability
        return this.each(function () {

            // Cache $(this) for speed
            var $this = $(this);

            // Check if delay exists or if it's a callback
            if (!delay || typeof delay == 'function') {

                // If it's a callback, move it to callback so we can call it later
                callback = delay;

                // Set the delay to 0 for the setTimeout
                delay = 0;
            }

            // Start a counter so we can delay the animation if required
            var animation = setTimeout(function () {

                // Add the animation effect with classes
                $this.addClass('animated ' + effect);

                // Check if the elemenr has been hidden to start with
                if ($this.css('visibility') == 'hidden') {

                    // If it has, show it (after the class has been added)
                    $this.css({
                        'visibility': 'visible'
                    });

                }

                // If the element is hidden
                if ($this.is(':hidden')) {

                    // Show it
                    $this.show();

                }

                // Event triggered when the animation has finished
                $this.bind('animationend webkitAnimationEnd oAnimationEnd', function () {

                    // Remove the classes so they can be added again later
                    $this.removeClass('animated ' + effect);

                    // Add a callback event
                    if (typeof callback == 'function') {

                        // Execute the callback
                        callback.call(this);

                    }

                });

            // Specify the delay
            }, delay);

        });

    };

})(jQuery);

Remove effect classes after animations complete so they can be added again if needed


//DateQuestionView Component Constructor
function DateQuestionView(question, content) {

	var self = Ti.UI.createPicker({
		type : Ti.UI.PICKER_TYPE_DATE,
		value : new Date(),
		color : '#336699',
		right : 5,
		left : 5,
	});
	self.addEventListener('change', function(e) {
		this.value = e.value;
	});
	self.getValue = function() {
		var val = this.value.toISOString();
		return val.substr(0, val.indexOf('T')).replace(/-/g, '/');
	};
	if (content) self.setValue(new Date(content));

	return self;
}

module.exports = DateQuestionView;

//DateQuestionView Component Constructor
function DateQuestionView(question, content) {

	var self = Ti.UI.createPicker({
		type : Ti.UI.PICKER_TYPE_DATE,
		value : content ? new Date(content) : new Date(),
		color : '#336699',
		right : 5,
		left : 5,
	});
	self.addEventListener('change', function(e) {
		this.value = e.value;
	});
	self.getValue = function() {
		var val = self.value;
		return val.getFullYear() + '/' + (val.getMonth() + 1) + '/' + val.getDate()
	};

	return self;
}

module.exports = DateQuestionView;

Fix the date-picker edit flow in Android.


import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
import Ember from 'ember';

const localeStub = Ember.Service.extend({
  locale: null
});

moduleForComponent('language-picker', 'Integration | Component | language picker', {
  integration: true,

  beforeEach: function() {
    this.register('service:i18n', localeStub);
    this.inject.service('i18n', { as: 'i18n' });
  }
});

test('it sets the locale', function(assert) {

  this.set('selectLanguage', (language, code) => {
    assert.deepEqual(language, 'English');
    assert.deepEqual(code, 'en-US');
  });

  this.render(hbs`{{language-picker onPick=(action selectLanguage)}}`);

  this.$('.flag-icon-us').parent().click();
  assert.equal(this.get('i18n.locale'), 'en-US');
  assert.expect(3);
});

import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
import Ember from 'ember';

const localeStub = Ember.Service.extend({
  locale: null
});

moduleForComponent('language-picker', 'Integration | Component | language picker', {
  integration: true,

  beforeEach: function() {
    this.register('service:i18n', localeStub);
    this.inject.service('i18n', { as: 'i18n' });
  }
});

test('it sets the locale', function(assert) {

  this.set('selectLanguage', (language, code) => {
    assert.deepEqual(language, 'English');
    assert.deepEqual(code, 'en-US');
  });

  this.render(hbs`{{language-picker onPick=(action selectLanguage)}}`);

  this.$('.flag-icon-us').first().parent().click();
  assert.equal(this.get('i18n.locale'), 'en-US');
  assert.expect(3);
});

Fix failing test when two locales used same flag icon


define({
  RoundCount: 2,
  MinPlayers: 4,
  RoundDuration: 20000
});

define({
  RoundCount: 2,
  MinPlayers: 4,
  RoundDuration: 90000
});

Increase round duration to 90 seconds


Application.Directives.directive('visNetwork', visNetworkDirective);

function visNetworkDirective() {
    return {
        restrict: 'E',
        require: '^ngModel',
        scope: {
            ngModel: '=',
            onSelect: '&',
            options: '='
        },
        link: function($scope, $element, $attrs, ngModel) {
           $scope.$watch('ngModel', function(newValue, oldValue) {
                if (newValue !== oldValue) {
                    // You actions here
                    var network = new vis.Network($element[0], $scope.ngModel, $scope.options || {});

                    var onSelect = $scope.onSelect() || function(prop) {};
                    network.on('select', function(properties) {
                        onSelect(properties);
                    });
                }
            }, true);
            var network = new vis.Network($element[0], $scope.ngModel, $scope.options || {});

            var onSelect = $scope.onSelect() || function(prop) {};
            network.on('select', function(properties) {
                onSelect(properties);
            });

        }
    };
}

Application.Directives.directive('visNetwork', visNetworkDirective);

function visNetworkDirective() {
    return {
        restrict: 'E',
        require: '^ngModel',
        scope: {
            ngModel: '=',
            onSelect: '&',
            options: '=',
            onLoaded: '&'
        },
        link: function($scope, $element, $attrs, ngModel) {
           $scope.$watch('ngModel', function(newValue, oldValue) {
                if (newValue !== oldValue) {
                    network.setData($scope.ngModel);
                }
            }, true);
            var network = new vis.Network($element[0], $scope.ngModel, $scope.options || {});

            var onLoaded = $scope.onLoaded() || function(){};
            onLoaded(network);

            var onSelect = $scope.onSelect() || function(prop) {};
            network.on('select', function(properties) {
                onSelect(properties);
            });
        }
    };
}

Add on-loaded argument so applications can get ahold of the network. Fix up the watch so that it sets the data for the network rather than creating a new network.


// Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt

frappe.require("assets/erpnext/js/financial_statements.js", function() {
	frappe.query_reports["Cash Flow"] = $.extend({},
		erpnext.financial_statements);

	// The last item in the array is the definition for Presentation Currency
	// filter. It won't be used in cash flow for now so we pop it. Please take
	// of this if you are working here.
	frappe.query_reports["Cash Flow"]["filters"].pop();

	frappe.query_reports["Cash Flow"]["filters"].push({
		"fieldname": "accumulated_values",
		"label": __("Accumulated Values"),
		"fieldtype": "Check"
	});

	frappe.query_reports["Cash Flow"]["filters"].push({
		"fieldname": "include_default_book_entries",
		"label": __("Include Default Book Entries"),
		"fieldtype": "Check"
	});
});

// Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt

frappe.require("assets/erpnext/js/financial_statements.js", function() {
	frappe.query_reports["Cash Flow"] = $.extend({},
		erpnext.financial_statements);

	// The last item in the array is the definition for Presentation Currency
	// filter. It won't be used in cash flow for now so we pop it. Please take
	// of this if you are working here.

	frappe.query_reports["Cash Flow"]["filters"].splice(5, 1);

	frappe.query_reports["Cash Flow"]["filters"].push(
		{
			"fieldname": "accumulated_values",
			"label": __("Accumulated Values"),
			"fieldtype": "Check"
		},
		{
			"fieldname": "include_default_book_entries",
			"label": __("Include Default Book Entries"),
			"fieldtype": "Check"
		}
	);
});

Add missing Cost Center filter in cash flow statement


/* eslint-env node */
const Reporter = require('ember-test-utils/reporter')

module.exports = {
  disable_watching: true,
  framework: 'mocha',
  launch_in_ci: [
    'Chrome',
    'Firefox'
  ],
  launch_in_dev: [
    'Chrome'
  ],
  reporter: new Reporter(),
  test_page: 'tests/index.html?hidepassed'
}

/* eslint-env node */

module.exports = {
  disable_watching: true,
  framework: 'mocha',
  launch_in_ci: [
    'Chrome',
    'Firefox'
  ],
  launch_in_dev: [
    'Chrome'
  ],
  test_page: 'tests/index.html?hidepassed'
}

Remove ember-test-utils/reporter to see if addresses build issue


var rc = require('rc');

/*eslint-disable camelcase */

module.exports = rc('number_switcher_3000', {
  aws_region: 'us-east-1',
  aws_s3_bucket: 'let-me-in-dev',
  brennens_number: '222-222-2223',
  jeffs_number: '222-222-2222',
  login_token: 'login_token',
  user_id: 'user_id'
});

/*eslint-enable camelcase */

var rc = require('rc');

/*eslint-disable camelcase */

module.exports = rc('number_switcher_3000', {
  aws_region: 'us-east-1',
  aws_s3_bucket: 'number-switcher-3000-dev',
  brennens_number: '222-222-2223',
  jeffs_number: '222-222-2222',
  login_token: 'login_token',
  user_id: 'user_id'
});

/*eslint-enable camelcase */

Update testing s3 bucket name


'use strict';

var express = require('express');
var kraken = require('kraken-js');
var db = require('./lib/db');
var crypto = require('./lib/crypto');

var passport = require('passport');

var localStrategy = require('./lib/registration');//??

var options, app;

/*
 * Create and configure application. Also exports application instance for use by tests.
 * See https://github.com/krakenjs/kraken-js#options for additional configuration options.
 */

options = {
    onconfig: function (config, next) {
        /*
         * Add any additional config setup or overrides here. `config` is an initialized
         * `confit` (https://github.com/krakenjs/confit/) configuration object.
         */
		db.config(config.get('databaseConfig'));

		localStrategy(passport); //???

		var cryptConfig = config.get('bcrypt');
		crypto.setCryptoLevel(cryptConfig.difficulty);
		//userLib.addUsers();

		next(null, config);
    }
};

app = module.exports = express();
//
app.use(passport.initialize());
app.use(passport.session());
//
app.use(kraken(options));
app.on('start', function () {
    console.log('Application ready to serve requests.');
    console.log('Environment: %s', app.kraken.get('env:env'));
});

'use strict';

var express = require('express');
var kraken = require('kraken-js');
var db = require('./lib/db');
var crypto = require('./lib/crypto');

var passport = require('passport');

var signUpStrategy= require('./lib/registration');//??
//var signInStrategy = require('./lib/login');

var options, app;

/*
 * Create and configure application. Also exports application instance for use by tests.
 * See https://github.com/krakenjs/kraken-js#options for additional configuration options.
 */

options = {
    onconfig: function (config, next) {
        /*
         * Add any additional config setup or overrides here. `config` is an initialized
         * `confit` (https://github.com/krakenjs/confit/) configuration object.
         */
		db.config(config.get('databaseConfig'));

		signUpStrategy(passport); //???
		//signInStrategy(passport);

		var cryptConfig = config.get('bcrypt');
		crypto.setCryptoLevel(cryptConfig.difficulty);
		//userLib.addUsers();

		next(null, config);
    }
};

app = module.exports = express();
//
app.use(passport.initialize());
app.use(passport.session());
//
app.use(kraken(options));
app.on('start', function () {
    console.log('Application ready to serve requests.');
    console.log('Environment: %s', app.kraken.get('env:env'));
});

Include signInStrategy for future updates


define(function (require) {
	var $ = require("jquery");

	var documentKeyboardHandler = function (handler) {
		// keeps track of which keys have already been pressed,
		// so we can keep track of which key is a repeated event.
		var keysDown = [];

		this.keyboardListener = function (event) {
			var keyIsDown = event.type === "keydown",
			    keyIsRepeated = keyIsDown ? keysDown[event.which] : false;

			keysDown[event.which] = keyIsDown;

			if (handler(event, keyIsDown, keyIsRepeated) === false) {
				event.preventDefault();

				return false;
			}
		}

		$(document.documentElement).keyup(keyboardListener);
		$(document.documentElement).keydown(keyboardListener);
	};

	return documentKeyboardHandler;
});

define(function (require) {
	var $ = require("jquery");

	var shiftKey = 16;

	var dummyShiftKeyboardEvent = {
		type: "keyup",
		preventDefault: function () { },
		which: shiftKey
	};

	var documentKeyboardHandler = function (handler) {
		// keeps track of which keys have already been pressed,
		// so we can keep track of which key is a repeated event.
		var keysDown = [];

		var keyboardListener = function (event) {
			var keyIsDown = event.type === "keydown",
			    keyIsRepeated = keyIsDown ? keysDown[event.which] : false;

			keysDown[event.which] = keyIsDown;

			if (handler(event, keyIsDown, keyIsRepeated) === false) {
				event.preventDefault();

				return false;
			}
		};

		// if the user releases the shift key after mousing out of the
		// document, the event will not be captured. if this occurs,
		// simulate the shift key being released when the mouse re-enters
		// the document.
		var mouseEnterListener = function (event) {
			if (keysDown[shiftKey] && !event.shiftKey) {
				keyboardListener(dummyShiftKeyboardEvent);
			}
		};

		$(document.documentElement).keyup(keyboardListener);
		$(document.documentElement).keydown(keyboardListener);
		$(document.documentElement).mouseenter(mouseEnterListener);
	};

	return documentKeyboardHandler;
});

Handle the shift key being released outside of the document by simulating the keyup event when the mouse returns.


define([
],
function (
) {
  var File = function (name, contents) {
    this.name = name || 'Example';
    this.contents = contents;
    this.selected = true;
  };
  File.prototype.findNumberAt = function (index) {
    var match = this.contents.substring(index).match(/-?\d+\.?\d*/);
    if (match) { return match[0]; }
  };
  File.prototype.spinNumber = function (number, direction, amount) {
    if (number.indexOf('.') === -1) {
      return (parseInt(number, 10) + direction * amount).toString();
    }
    else {
      return (parseFloat(number) + direction * amount).toFixed(2);
    }
  };
  File.prototype.spinNumberAt = function (
    index, direction, amount, originalNumber
  ) {
    var number = this.findNumberAt(index);
    originalNumber = originalNumber || number;
    var newNumber = this.spinNumber(originalNumber, direction, amount);
    this.contents = (
      this.contents.substring(0, index) +
      newNumber +
      this.contents.substring(index + number.length)
    );
  };
  File.prototype.recordOriginalNumberAt = function (index) {
    this.originalIndex = index;
    this.originalNumber = this.findNumberAt(index);
  };
  File.prototype.offsetOriginalNumber = function (offset) {
    this.spinNumberAt(this.originalIndex, 1, offset, this.originalNumber);
  };
  return File;
});

define([
],
function (
) {
  var File = function (name, contents) {
    this.name = name || 'Example';
    this.contents = contents;
    this.selected = true;
  };
  File.prototype.findNumberAt = function (index) {
    var match = this.contents.substring(index).match(/-?\d+\.?\d*/);
    if (match) { return match[0]; }
  };
  File.prototype.spinNumber = function (number, direction, amount) {
    if (number.indexOf('.') === -1) {
      return (parseInt(number, 10) + direction * amount).toString();
    }
    else {
      return (parseFloat(number) + direction * amount).toFixed(2);
    }
  };
  File.prototype.spinNumberAt = function (
    index, direction, amount, originalNumber
  ) {
    var number = this.findNumberAt(index);
    if (number === undefined) { return; }
    originalNumber = originalNumber || number;
    var newNumber = this.spinNumber(originalNumber, direction, amount);
    this.contents = (
      this.contents.substring(0, index) +
      newNumber +
      this.contents.substring(index + number.length)
    );
  };
  File.prototype.recordOriginalNumberAt = function (index) {
    var number = this.findNumberAt(index);
    if (number === undefined) { return; }
    this.originalNumber = number;
    this.originalIndex = index;
  };
  File.prototype.offsetOriginalNumber = function (offset) {
    this.spinNumberAt(this.originalIndex, 1, offset, this.originalNumber);
  };
  return File;
});

Fix error when theres no number at the cursor


export default class RocExportPlugin {
    constructor(resolveRequest) {
        this.resolveRequest = resolveRequest;
    }

    apply(compiler) {
        // We assume that loaders will be managed in Webpack-land, that is we will not manage them in exports
        compiler.plugin('normal-module-factory', (normalModuleFactory) => {
            normalModuleFactory.plugin('before-resolve', (result, callback) => {
                // We do not want to process Webpack special paths
                if (result.request.match(/(\?|!)/)) {
                    return callback(null, result);
                }

                // Try to resolve the dependency against the roc dependency context
                result.request = this.resolveRequest(result.request, result.context); // eslint-disable-line

                // Test to see if we can resolve the dependency
                return compiler.resolvers.normal.resolve(result.context, result.request, (err) => {
                    if (err) {
                        // We got an error and will try again with fallback enabled
                        result.request = this.resolveRequest(result.request, result.context, true); // eslint-disable-line
                        return callback(null, result);
                    }

                    return callback(null, result);
                });
            });
        });
    }
}

export default class RocExportPlugin {
    constructor(resolveRequest) {
        this.resolveRequest = resolveRequest;
    }

    apply(compiler) {
        // We assume that loaders will be managed in Webpack-land, that is we will not manage them in exports
        compiler.plugin('normal-module-factory', (normalModuleFactory) => {
            normalModuleFactory.plugin('before-resolve', (result, callback) => {
                // We do not want to process Webpack special paths
                if (!result || result.request.match(/(\?|!)/)) {
                    return callback(null, result);
                }

                // Try to resolve the dependency against the roc dependency context
                result.request = this.resolveRequest(result.request, result.context); // eslint-disable-line

                // Test to see if we can resolve the dependency
                return compiler.resolvers.normal.resolve(result.context, result.request, (err) => {
                    if (err) {
                        // We got an error and will try again with fallback enabled
                        result.request = this.resolveRequest(result.request, result.context, true); // eslint-disable-line
                        return callback(null, result);
                    }

                    return callback(null, result);
                });
            });
        });
    }
}

Handle the case when result is undefined


$(document).ready(function (){
  // smooth scrolling
  $(".smoothie").on('click', function(event) {
    var target = $(this.hash)
      , navOffset = $('#navbar').height()
	;

	event.preventDefault();

    return $('html, body')
		.animate(
			{ scrollTop: target.offset().top - navOffset },
			600,
			function() {
				return window.history.pushState(null, null, target.selector);
			}
		);
  });

  // Shuffle the mentor gallery
  // see: http://stackoverflow.com/a/9641564/1968883
  var parent = document.getElementById("mentor-gallery");
  var divs = parent.children;
  var frag = document.createDocumentFragment();
  while (divs.length) {
  	      frag.appendChild(divs[Math.floor(Math.random() * divs.length)]);
  }
  parent.appendChild(frag);
});

$(document).ready(function (){
  // smooth scrolling
  $(".smoothie").on('click', function(event) {
    var target = $(this.hash)
      , navOffset = $('#navbar').height()
	;

	event.preventDefault();

    return $('html, body')
		.animate(
			{ scrollTop: target.offset().top - navOffset },
			600,
			function() {
				return window.history.pushState(null, null, target.selector);
			}
		);
  });
});

Remove the randomization of mentors -- they are alphabetical by surname


var updatePreviewTarget = function(data, status) {
  $('.spinner').hide();
  $('#preview-target').html(data);
  renderLatexExpressions();
}

var renderLatexExpressions = function() {
  $('.latex-container').each(
    function(index) {
      katex.render(
        this.textContent,
        this,
        { displayMode: $(this).data('displaymode') == 'block' }
      );
    }
  );
}

var main = function() {
  $('div code').each(function(i, block) {
    hljs.highlightBlock(block);
  });

  $('button#preview').click(
    function() {
      $('#preview-target').html('');
      $('.spinner').show();
      $('h1.preview').text($('input[name=title]').val())
      $.post('/note_preview', {text: $('#note-txt').val()}, updatePreviewTarget);
    }
  );

  renderLatexExpressions();

  $("textarea[name='footnotes']").focusin(function(){
    $(this).height($(this).height() + 300);
  }).focusout(function(){
    $(this).height($(this).height() - 300);
  });
};

function searchRedirect(event, searchpage) {

  var term = (arguments.length == 2) ? document.getElementById('mainsearch').value : document.getElementById('searchbox').value;

  window.location = '/search/' + term + '/';

  event.preventDefault(event);

}

$(main);

var updatePreviewTarget = function(data, status) {
  $('.spinner').hide();
  $('#preview-target').html(data);
  $('#preview-target code').each(function(i, block) {
    hljs.highlightBlock(block);
  });
  renderLatexExpressions();
}

var renderLatexExpressions = function() {
  $('.latex-container').each(
    function(index) {
      katex.render(
        this.textContent,
        this,
        { displayMode: $(this).data('displaymode') == 'block' }
      );
    }
  );
}

var main = function() {
  $('div code').each(function(i, block) {
    hljs.highlightBlock(block);
  });

  $('button#preview').click(
    function() {
      $('#preview-target').html('');
      $('.spinner').show();
      $('h1.preview').text($('input[name=title]').val())
      $.post('/note_preview', {text: $('#note-txt').val()}, updatePreviewTarget);
    }
  );

  renderLatexExpressions();

  $("textarea[name='footnotes']").focusin(function(){
    $(this).height($(this).height() + 300);
  }).focusout(function(){
    $(this).height($(this).height() - 300);
  });
};

function searchRedirect(event, searchpage) {

  var term = (arguments.length == 2) ? document.getElementById('mainsearch').value : document.getElementById('searchbox').value;

  window.location = '/search/' + term + '/';

  event.preventDefault(event);

}

$(main);

Fix code highlighting in note previews


/*globals define*/
define([
  'geometry/views/object2d-view'
], function( Object2DView ) {
  'use strict';

  var PathView = Object2DView.extend({
  });

  return PathView;
});

/*globals define*/
define([
  'jquery',
  'geometry/views/object2d-view'
], function( $, Object2DView ) {
  'use strict';

  var PathView = Object2DView.extend({
    events: {
      'mousedown .handler': 'onMouseDown',
      'mousemove  .handler': 'onMousemMove',
      'mouseup .handler': 'onMouseUp'
    },

    initialize: function() {
      this.handlers = [];

      var pointCount = this.model.pointCount();
      var points = this.model.get( 'points' );

      var handlerEl;
      var transform;
      var point;
      var x, y;
      for ( var i = 0; i < pointCount; i++ ) {
        x = points[ 2 * i ];
        y = points[ 2 * i + 1 ];

        point = this.toWorld( x, y );
        x = point.x;
        y = point.y;

        transform = 'translate3d(' + x + 'px, ' + y + 'px, 0)';

        handlerEl = $( '<div>', {
          'class': 'handler',
          id: 'handler-' + i
        }).css({
          '-webkit-transform': transform,
          transform: transform
        });

        this.handlers.push( handlerEl );
      }
    },

    onMouseDown: function() {},
    onMouseMove: function() {},
    onMouseUp: function() {}
  });

  return PathView;
});

Add incomplete handler implementation to PathEditView.


/**
 * @flow
 */
'use strict';

let { Easing } = require('react-native');

// See Apple's "CAMediaTimingFunction Class Reference" for the Bezier control
// points
exports.easeIn = Easing.bezier(0.42, 0, 1, 1);
exports.easeOut = Easing.bezier(0, 0, 0.58, 1);
exports.easeInOut = Easing.bezier(0.42, 0, 0.58, 1);
exports.default = Easing.bezier(0.25, 0.1, 0.25, 1);

/**
 * @flow
 */
'use strict';

let { Easing } = require('react-native');

const EPSILON = 1e-9;

// See Apple's "CAMediaTimingFunction Class Reference" for the Bezier control
// points
exports.easeIn = Easing.bezier(0.42, 0, 1, 1, EPSILON);
exports.easeOut = Easing.bezier(0, 0, 0.58, 1, EPSILON);
exports.easeInOut = Easing.bezier(0.42, 0, 0.58, 1, EPSILON);
exports.default = Easing.bezier(0.25, 0.1, 0.25, 1, EPSILON);

Adjust epsilon, setting it to 1e-9 for smoother animations


/* @flow */

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

import AbstractContainer from '../AbstractContainer';

/**
 * Represents a container of React Native/mobile {@link Component} children.
 *
 * @extends AbstractContainer
 */
export default class Container extends AbstractContainer {
    /**
     * {@code Container} component's property types.
     *
     * @static
     */
    static propTypes = AbstractContainer.propTypes;

    /**
     * Implements React's {@link Component#render()}.
     *
     * @inheritdoc
     * @returns {ReactElement}
     */
    render() {
        const {
            onClick,
            touchFeedback = onClick,
            visible = true,
            ...props
        } = this.props;

        // visible
        if (!visible) {
            // Intentionally hide this Container without destroying it.
            // TODO Replace with display: 'none' supported in RN >= 0.43.
            props.style = {
                ...props.style,
                height: 0,
                width: 0
            };
        }

        let element = super._render(View, props);

        // onClick & touchFeedback
        if (visible && (onClick || touchFeedback)) {
            element = React.createElement(
                touchFeedback ? TouchableHighlight : TouchableWithoutFeedback,
                { onPress: onClick },
                element
            );
        }

        return element;
    }
}

/* @flow */

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

import { Platform } from '../../';
import AbstractContainer from '../AbstractContainer';

/**
 * Represents a container of React Native/mobile {@link Component} children.
 *
 * @extends AbstractContainer
 */
export default class Container extends AbstractContainer {
    /**
     * {@code Container} component's property types.
     *
     * @static
     */
    static propTypes = AbstractContainer.propTypes;

    /**
     * Implements React's {@link Component#render()}.
     *
     * @inheritdoc
     * @returns {ReactElement}
     */
    render() {
        const {
            onClick,
            touchFeedback = onClick,
            visible = true,
            ...props
        } = this.props;

        // visible
        if (!visible) {
            // FIXME: Whatever I try ends up failing somehow on Android, give up
            // for now, hoping display: 'none' solves this.
            if (Platform.OS === 'android') {
                return null;
            }

            // Intentionally hide this Container without destroying it.
            // TODO Replace with display: 'none' supported in RN >= 0.43.
            props.style = {
                ...props.style,
                height: 0,
                width: 0
            };
        }

        let element = super._render(View, props);

        // onClick & touchFeedback
        if (visible && (onClick || touchFeedback)) {
            element = React.createElement(
                touchFeedback ? TouchableHighlight : TouchableWithoutFeedback,
                { onPress: onClick },
                element
            );
        }

        return element;
    }
}

Destroy containes when they are not visible


// Copyright © 2017 Michael Howell. All rights reserved.
// The following code is covered by the AGPL-3.0 license.

const selfapi = require('selfapi');

const blog = require('../lib/blog');
const log = require('../lib/log');

// API resource to manage Janitor's Discourse-backed news section.
const blogAPI = module.exports = selfapi({
  title: 'Blog'
});

blogAPI.get('/synchronize', {
  title: 'Synchronize Blog',
  description: 'Pull the blog section from Discourse.',

  handler: async (_request, response) => {
    try {
      const { count } = await blog.synchronize();
      log('synchronized blog', { count });
      response.json({ count }, null, 2);
    } catch (error) {
      log('[fail] synchronized blog', error);
      response.statusCode = 500; // Internal Server Error
      response.json({ error: 'Could not synchronize' }, null, 2);
    }
  },

  examples: [{
    response: {
      body: JSON.stringify({ count: 1 }, null, 2)
    }
  }]
});

// Copyright © 2017 Michael Howell. All rights reserved.
// The following code is covered by the AGPL-3.0 license.

const selfapi = require('selfapi');

const blog = require('../lib/blog');
const log = require('../lib/log');

// API resource to manage Janitor's Discourse-backed news section.
const blogAPI = module.exports = selfapi({
  title: 'Blog'
});

blogAPI.post('/synchronize', {
  title: 'Synchronize Blog',
  description: 'Pull the blog section from Discourse.',

  handler: async (_request, response) => {
    try {
      const { count } = await blog.synchronize();
      log('synchronized blog', { count });
      response.json({ count }, null, 2);
    } catch (error) {
      log('[fail] synchronized blog', error);
      response.statusCode = 500; // Internal Server Error
      response.json({ error: 'Could not synchronize' }, null, 2);
    }
  },

  examples: [{
    response: {
      body: JSON.stringify({ count: 13 }, null, 2)
    }
  }]
});

Switch the blog handler over to POST


import template from './chat-page.html';
import { addMessage, selectThread } from '../../shared/threads/threads.actions';
import { getChannels, getDirectMessages, getCurrentThread } from '../../shared/threads/threads.selectors';
import { getCurrentUser } from '../../shared/users/users.selectors';

const ChatPageComponent = {
  bindings: {},
  templateUrl: template,
  controller: class ChatPageController {
    /* @ngInject */
    constructor($ngRedux) {
      this.$ngRedux = $ngRedux;
      this.unsubscribe = $ngRedux.connect(this.mapStateToThis, {})(this);
    }

    $onDestroy() {
      this.unsubscribe();
    }

    mapStateToThis(state) {
      return {
        channels: getChannels(state),
        directMessages: getDirectMessages(state),
        activeThread: getCurrentThread(state),
        currentUser: getCurrentUser(state),
      }
    }

    threadSelected(thread) {
      this.$ngRedux.dispatch(selectThread(thread));
    }

    sendMessage(message) {
      if (messageText.length > 0) {
        this.$ngRedux.dispatch(addMessage(
          this.activeThread,
          {
            author: this.currentUser,
            text: messageText
          }
        ));
      }
    }
  }
};

export default ChatPageComponent;

import template from './chat-page.html';
import { addMessage, selectThread } from '../../shared/threads/threads.actions';
import { getChannels, getDirectMessages, getCurrentThread } from '../../shared/threads/threads.selectors';
import { getCurrentUser } from '../../shared/users/users.selectors';

const ChatPageComponent = {
  bindings: {},
  templateUrl: template,
  controller: class ChatPageController {
    /* @ngInject */
    constructor($ngRedux) {
      this.$ngRedux = $ngRedux;
      this.unsubscribe = $ngRedux.connect(this.mapStateToThis, {})(this);
    }

    $onDestroy() {
      this.unsubscribe();
    }

    mapStateToThis(state) {
      return {
        channels: getChannels(state),
        directMessages: getDirectMessages(state),
        activeThread: getCurrentThread(state),
        currentUser: getCurrentUser(state),
      }
    }

    threadSelected(thread) {
      this.$ngRedux.dispatch(selectThread(thread));
    }

    sendMessage(messageText) {
      if (messageText.length > 0) {
        this.$ngRedux.dispatch(addMessage(
          this.activeThread,
          {
            author: this.currentUser,
            text: messageText
          }
        ));
      }
    }
  }
};

export default ChatPageComponent;

Fix param in sendMessage function


var exec = require('child_process').exec;
function puts(error, stdout, stderr) { console.log(stdout) }

var os = require('os');
//control OS
//then run command depengin on the OS

if (os.type() === 'Linux')
   exec("dist/package.sh", puts);
else if (os.type() === 'Darwin')
   exec("dist/package.sh", puts);
else if (os.type() === 'Windows_NT')
   exec("powershell dist/package.sh", puts);
else
   throw new Error("Unsupported OS found: " + os.type());

var spawn = require('child_process').spawn;
var os = require('os');

if (os.type() === 'Linux') {
   spawn('bash', ['dist/package.sh'], {stdio: 'inherit'});
} else if (os.type() === 'Darwin') {
   spawn('bash', ['dist/package.sh'], {stdio: 'inherit'});
} else if (os.type() === 'Windows_NT') {
   spawn('powershell', ['dist/package.sh'], {stdio: 'inherit'});
} else {
   throw new Error("Unsupported OS found: " + os.type());
}

Build script: Replace `exec` with `spawn`


/*
Public API for the container is still in flux.
The public API, specified on the application namespace should be considered the stable API.
// @module container
  @private
*/

import Registry from 'container/registry';
import Container from 'container/container';
import { getOwner, setOwner } from 'container/owner';

export { Registry, Container, getOwner, setOwner };

/*
Public API for the container is still in flux.
The public API, specified on the application namespace should be considered the stable API.
// @module container
  @private
*/

import Registry from './registry';
import Container from './container';
import { getOwner, setOwner } from './owner';

export { Registry, Container, getOwner, setOwner };

Use relative imports for container.


export class Notification {
  /**
   * @typedef NotificationData
   * @property {boolean} [read]
   * @property {string} title
   * @property {string} message
   * @property {('basic'|'sms')} [type='basic']
   * @property {string} [date]
   * @property {object} [metadata]
   */

  /**
   *
   * @param {NotificationData} data
   */
  constructor(data) {
    this.read = 'read' in data ? data.read : false;
    this.title = data.title;
    this.message = data.message;
    this.date = 'date' in data ? data.date : null;
    this.metadata = 'metadata' in data ? data.metadata : '';
    this.id = this.title + this.message + this.date + JSON.stringify(this.metadata);
  }

  /**
   * Converts the notification to a JSON object
   * @return {object}
   */
  toJSON() {
    return {
      read: this.read,
      title: this.title,
      message: this.message,
      date: this.date,
      metadata: this.metadata,
    };
  }

  static fromJSON(json) {
    return new Notification(json);
  }
}

export class Notification {
  /**
   * @typedef NotificationData
   * @property {boolean} [read]
   * @property {string} title
   * @property {string} message
   * @property {('basic'|'sms')} [type='basic']
   * @property {number} [date]
   * @property {object} [metadata]
   */

  /**
   *
   * @param {NotificationData} data
   */
  constructor(data) {
    this.read = 'read' in data ? data.read : false;
    this.title = data.title;
    this.message = data.message;
    this.date = 'date' in data ? data.date : null;
    this.metadata = 'metadata' in data ? data.metadata : '';
    this.id = this.title + this.message + this.date + JSON.stringify(this.metadata);
  }

  /**
   * Converts the notification to a JSON object
   * @return {object}
   */
  toJSON() {
    return {
      read: this.read,
      title: this.title,
      message: this.message,
      date: this.date,
      metadata: this.metadata,
    };
  }

  static fromJSON(json) {
    return new Notification(json);
  }
}

Change date to unix timestamp


import Axios from 'axios'
import PathBuilder from './path-builder'

class RailsRouteBuilder {
  constructor (configs = {}) {
    // TODO
    // Make an option for switching to GET for destroy actions
    this.pathBuilder = new PathBuilder
  }

  //
  // RESTful Actions
  //
  index (...args) {
    this.list(args)
  }

  list (resource, params) {
    let path = resource
    return this.pathBuilder.get(path, params)
  }

  show (resource, params) {
    let path = `${resource}/:id`
    return this.pathBuilder.get(path, params)
  }

  destroy (resource, params) {
    let path = `${resource}/:id`
    return this.pathBuilder.delete(path, params)
  }

  create (resource, params) {
    let path = resource
    return this.pathBuilder.post(path, params)
  }

  update (resource, params) {
    let path = `${resource}/:id`
    return this.pathBuilder.patch(path, params)
  }

  new (resource, params) {
    let path = `${resource}/:id/new`
    return this.pathBuilder.get(path, params)
  }

  edit (resource, params) {
    let path = `${resource}/:id/edit`
    return this.pathBuilder.get(path, params)
  }
}

export default RailsRouteBuilder

import PathBuilder from './path-builder'

class RailsRouteBuilder {
  constructor (configs = {}) {
    // TODO
    // Make an option for switching to GET for destroy actions
    this.pathBuilder = new PathBuilder()
  }

  //
  // RESTful Actions
  //
  index (...args) {
    this.list(args)
  }

  list (resource, params) {
    let path = resource
    return this.pathBuilder.get(path, params)
  }

  show (resource, params) {
    let path = `${resource}/:id`
    return this.pathBuilder.get(path, params)
  }

  destroy (resource, params) {
    let path = `${resource}/:id`
    return this.pathBuilder.delete(path, params)
  }

  create (resource, params) {
    let path = resource
    return this.pathBuilder.post(path, params)
  }

  update (resource, params) {
    let path = `${resource}/:id`
    return this.pathBuilder.patch(path, params)
  }

  new (resource, params) {
    let path = `${resource}/:id/new`
    return this.pathBuilder.get(path, params)
  }

  edit (resource, params) {
    let path = `${resource}/:id/edit`
    return this.pathBuilder.get(path, params)
  }
}

export default RailsRouteBuilder

Fix js style on RailsRouteBuilder


var Promise = require('es6-promise').Promise;

function fetch(options) {

  return new Promise(function(resolve, reject) {
    var xhr = new XMLHttpRequest();

    xhr.onload = function() {

      if(xhr.getResponseHeader('content-type').search('application/json') >= 0) {
        try {
          var response = JSON.parse(xhr.responseText);
          return resolve(response);
        } catch(e) {
          reject(e);
        }
      }

      resolve(xhr.responseText);
    };

    xhr.onerror = function() {
      reject();
    };

    xhr.open(options.method, options.url);
    xhr.send(options.body);
  });
}

module.exports = fetch;

var Promise = require('es6-promise').Promise;

function fetch(options) {

  return new Promise(function(resolve, reject) {
    var xhr = new XMLHttpRequest();

    xhr.onload = function() {

      if(xhr.getResponseHeader('content-type').search('application/json') >= 0) {
        try {
          var response = JSON.parse(xhr.responseText);
          return resolve(response);
        } catch(e) {
          return reject(e);
        }
      }

      if(xhr.status !== 200) {
        return reject();
      }

      resolve(xhr.responseText);
    };

    xhr.onerror = function(e) {
      reject(e);
    };

    xhr.open(options.method, options.url);
    xhr.send(options.body);
  });
}

module.exports = fetch;

Fix Fetch util reject case


$(document).ready(function() {
  $(document).on('click', '.accept-btn', function(e) {
    e.preventDefault();
    answer = $(this).closest('.delegate-text-answer').find('textarea').val();
    text_answer = $(this).closest('.answer_fields_wrapper').find('.text-answer')
    $(text_answer).find('.text-answer-field').val(answer);
  });
});

$(document).ready(function() {
  $(document).on('click', '.accept-btn', function(e) {
    e.preventDefault();
    answer = $(this).closest('.delegate-text-answer').find('textarea').val();
    text_answer = $(this).closest('.answer_fields_wrapper').find('.text-answer')
    text_answer_field = $(text_answer).find('.text-answer-field')
    $(text_answer_field).val(answer);
    $(text_answer_field).addClass('dirty')
  });
});

Add dirty class to text answer to trigger save afterwards


Taskar.Sections.Feedback = function(element){
  var appear = Taskar.UI.WindowForm("feedback_form");

  $("feedback_form").observe('ajax:after', function(e) {
    this.fade();
  });

  element.observe("click", function(e){
    e.stop();
    $("feedback_form").setStyle({
      top:  (e.pointerY() +  20) + "px",
      left: (e.pointerX() - 380) + "px"
    });
    appear();
  });
};

Taskar.Sections.Feedback = function(element){
  var appear = Taskar.UI.WindowForm("feedback_form");

  $("feedback_form").observe('ajax:post', function(e) { this.fade(); });

  element.observe("click", function(e){
    e.stop();
    $("feedback_form").setStyle({
      top:  (e.pointerY() +  20) + "px",
      left: (e.pointerX() - 380) + "px"
    });
    appear();
  });
};

Remove unneeded instance of ajax:after event


var http = require('http');
var util = require('./util');
var file = require('.file_system.file');
var counter=0;

http.createServer(function (req, res) {
    var answer =  util.helloWorld();

    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end(answer+' '+counter);
    counter = counter + 1;
    console.log(counter);

}).listen(1337, '127.0.0.1' );
//counter = 0;
console.log('Server running at http://127.0.0.1:1337/');

var http = require('http');
var util = require('./util');
var counter=0;

http.createServer(function (req, res) {
    var answer =  util.helloWorld();

    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end(answer+' '+counter);
    counter = counter + 1;
    console.log(counter);

}).listen(1337, '127.0.0.1' );
//counter = 0;
console.log('Server running at http://127.0.0.1:1337/');

FIX Delete require of non-existing file


import { usingConnect, sql } from './pg-helpers'
import Promise from 'bluebird';
import crypto from 'crypto';
import scmp from 'scmp'

const pbkdf2 = Promise.promisify(crypto.pbkdf2, crypto);

class User {
  constructor(userData) {
    this.id = userData.id;
    this.email = userData.email;
    this.passwordHash = userData.passwordhash;
    this.salt = userData.salt;
    this.isAdmin = userData.isadmin;
  }

  validatePassword(password) {
    return pbkdf2(password, this.salt, 25000, 512, 'sha256')
      .then(hashed => new Buffer(hashed, 'binary').toString('hex'))
      .then(hash => scmp(hash, this.passwordHash));
  }
}

export class UserRepository {
  constructor(connectionString) {
    this.connectionString = connectionString;
  }

  findById(id) {
    return usingConnect(this.connectionString, client =>
      client.queryAsync(sql`SELECT * FROM users WHERE id=${id}`)
        .then(result => result.rows.length === 0
          ? undefined
          : new User(result.rows[0])));
  }

  findByEmail(email) {
    return usingConnect(this.connectionString, client =>
      client.queryAsync(sql`SELECT * FROM users WHERE email=${email}`)
        .then(result => result.rows.length === 0
          ? undefined
          : new User(result.rows[0])));
  }
}

import { usingConnect, sql } from './pg-helpers'
import Promise from 'bluebird';
import crypto from 'crypto';
import scmp from 'scmp'

const pbkdf2 = Promise.promisify(crypto.pbkdf2, crypto);

class User {
  constructor(userData) {
    this.id = userData.id;
    this.email = userData.email;
    this.passwordHash = userData.passwordhash;
    this.salt = userData.salt;
    this.isAdmin = userData.admin;
  }

  validatePassword(password) {
    return pbkdf2(password, this.salt, 25000, 512, 'sha256')
      .then(hashed => new Buffer(hashed, 'binary').toString('hex'))
      .then(hash => scmp(hash, this.passwordHash));
  }
}

export class UserRepository {
  constructor(connectionString) {
    this.connectionString = connectionString;
  }

  findById(id) {
    return usingConnect(this.connectionString, client =>
      client.queryAsync(sql`SELECT * FROM users WHERE id=${id}`)
        .then(result => result.rows.length === 0
          ? undefined
          : new User(result.rows[0])));
  }

  findByEmail(email) {
    return usingConnect(this.connectionString, client =>
      client.queryAsync(sql`SELECT * FROM users WHERE email=${email}`)
        .then(result => result.rows.length === 0
          ? undefined
          : new User(result.rows[0])));
  }
}

Fix reading of admin status for users


// Shared styles of highcharts-chart for polymer 3.0
import { html } from '@polymer/lit-element';

export const SharedStyles = html`
<style>
    :host {width: 100%;display: inline-block;min-height: var(--highcharts-min-height, 26em)}
    :host[height-responsive] {min-height: initial}
    :host[height-responsive] #Chart {height: 100%}
    #Chart {@apply --highcharts-container}
</style>`;

// Shared styles of highcharts-chart for polymer 3.0
import { html } from '@polymer/polymer/polymer-element.js';

export const SharedStyles = html`
<style>
    :host {width: 100%;display: inline-block;min-height: var(--highcharts-min-height, 26em)}
    :host[height-responsive] {min-height: initial}
    :host[height-responsive] #Chart {height: 100%}
    #Chart {@apply --highcharts-container}
</style>`;

Use html from polymer-element.js instead


requirejs.config({
    baseUrl: '/static',

    paths: {
        antibodies: 'modules/antibodies',
        app: 'modules/app',
        assert: 'modules/assert',
        base: 'modules/base',
        home: 'modules/home',
        navbar: 'modules/navbar',

        // Plugins
        text: 'libs/text',

        // Libraries
        backbone: 'libs/backbone',
        bootstrap: 'libs/bootstrap.min',
        jquery: 'libs/jquery.min',
        modernizr: 'libs/modernizr.min',
        underscore: 'libs/underscore.min'
    },

    shim: {
        backbone: {
            deps: ['underscore', 'jquery'],
            exports: 'Backbone'
        },

        bootstrap: {
            deps: ['jquery']
        },

        // Modernizr loads later than normal with Require.js, so don't use the
        // new HTML5 elements in the HTML file itself.
        modernizr: {
            exports: 'Modernizr'
        },

        underscore: {
            exports: '_'
        }
    }
});

if (!window.TESTRUNNER) require(['jquery', 'app', 'bootstrap', 'modernizr'],
function main($, app) {

    // Treat the jQuery ready function as the entry point to the application.
    // Inside this function, kick-off all initialization, everything up to this
    // point should be definitions.
    $(function ready() {
        app.start();

        // Simplify debugging
        window.app = app;
    });
});

requirejs.config({
    baseUrl: '/static',

    paths: {
        antibodies: 'modules/antibodies',
        app: 'modules/app',
        assert: 'modules/assert',
        base: 'modules/base',
        home: 'modules/home',
        navbar: 'modules/navbar',

        // Plugins
        text: 'libs/text',

        // Libraries
        backbone: 'libs/backbone',
        'backbone.hal': 'libs/backbone.hal',
        bootstrap: 'libs/bootstrap.min',
        jquery: 'libs/jquery.min',
        modernizr: 'libs/modernizr.min',
        underscore: 'libs/underscore.min'
    },

    shim: {
        backbone: {
            deps: ['underscore', 'jquery'],
            exports: 'Backbone'
        },

        bootstrap: {
            deps: ['jquery']
        },

        // Modernizr loads later than normal with Require.js, so don't use the
        // new HTML5 elements in the HTML file itself.
        modernizr: {
            exports: 'Modernizr'
        },

        underscore: {
            exports: '_'
        }
    }
});

if (!window.TESTRUNNER) require(['jquery', 'app', 'bootstrap', 'modernizr'],
function main($, app) {

    // Treat the jQuery ready function as the entry point to the application.
    // Inside this function, kick-off all initialization, everything up to this
    // point should be definitions.
    $(function ready() {
        app.start();

        // Simplify debugging
        window.app = app;
    });
});

Add backbone.hal to js module config.


import { version as nodeVersion } from 'process'

import readPkgUp from 'read-pkg-up'

// Caches it.
// TODO: use async instead
const {
  pkg: { version: autoserverVersion },
} = readPkgUp.sync()

// Retrieve environment-specific versions
export const getVersionsInfo = function() {
  const autoserver = `v${autoserverVersion}`

  return { node: nodeVersion, autoserver }
}

import { version as nodeVersion } from 'process'

import readPkgUp from 'read-pkg-up'

// Caches it.
// TODO: use async instead
const {
  package: { version: autoserverVersion },
} = readPkgUp.sync()

// Retrieve environment-specific versions
export const getVersionsInfo = function() {
  const autoserver = `v${autoserverVersion}`

  return { node: nodeVersion, autoserver }
}

Rename property for upgrade of read-pkg-up


// @flow

import { Meeting } from '../actions/ActionTypes';
import { createSelector } from 'reselect';
import createEntityReducer from 'app/utils/createEntityReducer';

export default createEntityReducer({
  key: 'meetings',
  types: {
    fetch: Meeting.FETCH,
    mutate: Meeting.CREATE
  },
  mutate(state, action) {
    switch (action.type) {
      case Meeting.DELETE.SUCCESS:
        return {
          ...state,
          items: state.items.filter(id => action.meta.meetingId != id)
        };
      case Meeting.SET_INVITATION_STATUS.SUCCESS: {
        const { meetingId, status, user } = action.meta;
        return {
          ...state,
          byId: {
            ...state.byId,
            [meetingId]: {
              ...state.byId[meetingId],
              invitations: state.byId[meetingId].invitations.map(invitation => {
                if (invitation.user.id === user) {
                  invitation.status = status;
                }
                return invitation;
              })
            }
          }
        };
      }
      default:
        return state;
    }
  }
});

export const selectMeetings = createSelector(
  state => state.meetings.byId,
  state => state.meetings.items,
  (meetingsById, meetingIds) => meetingIds.map(id => meetingsById[id])
);

export const selectMeetingById = createSelector(
  state => state.meetings.byId,
  (state, props) => props.meetingId,
  (meetingsById, meetingId) => meetingsById[meetingId]
);

// @flow

import { Meeting } from '../actions/ActionTypes';
import { createSelector } from 'reselect';
import createEntityReducer from 'app/utils/createEntityReducer';

export default createEntityReducer({
  key: 'meetings',
  types: {
    fetch: Meeting.FETCH,
    mutate: Meeting.CREATE
  },
  mutate(state, action) {
    switch (action.type) {
      case Meeting.DELETE.SUCCESS:
        return {
          ...state,
          items: state.items.filter(id => action.meta.meetingId !== id)
        };
      case Meeting.SET_INVITATION_STATUS.SUCCESS: {
        const { meetingId, status, user } = action.meta;
        return {
          ...state,
          byId: {
            ...state.byId,
            [meetingId]: {
              ...state.byId[meetingId],
              invitations: state.byId[meetingId].invitations.map(invitation => {
                if (invitation.user.id === user) {
                  invitation.status = status;
                }
                return invitation;
              })
            }
          }
        };
      }
      default:
        return state;
    }
  }
});

export const selectMeetings = createSelector(
  state => state.meetings.byId,
  state => state.meetings.items,
  (meetingsById, meetingIds) => meetingIds.map(id => meetingsById[id])
);

export const selectMeetingById = createSelector(
  state => state.meetings.byId,
  (state, props) => props.meetingId,
  (meetingsById, meetingId) => meetingsById[meetingId]
);

Use js `equal value and equal type` when comparing meetingId


// locations.js
// caches location information from foursquare

// TO-DO: Foursquare ToS says data can be stored for 30 days before it must be updated.
// Any data that is older than 30 days should be removed.

module.exports = function (sequelize, DataTypes) {
  var Location = sequelize.define('Location', {
    locationId: DataTypes.INTEGER,
    fourSquareId: DataTypes.STRING(24),
    name: DataTypes.STRING,
    address: DataTypes.JSON,
    price: DataTypes.INTEGER,
    longitude: DataTypes.DOUBLE,
    latitude: DataTypes.DOUBLE,
    tags: DataTypes.ARRAY(DataTypes.TEXT),
  });

  return Location;
};

// locations.js
// caches location information from foursquare

// TO-DO: Foursquare ToS says data can be stored for 30 days before it must be updated.
// Any data that is older than 30 days should be removed.

module.exports = function (sequelize, DataTypes) {
  var Location = sequelize.define('Location', {
    fourSquareId: DataTypes.STRING(24),
    name: DataTypes.STRING,
    address: DataTypes.JSON,
    price: DataTypes.INTEGER,
    longitude: DataTypes.DOUBLE,
    latitude: DataTypes.DOUBLE,
    tags: DataTypes.ARRAY(DataTypes.TEXT),
  });

  return Location;
};

Remove unnecessary locationId field from Locations table


ckan.module('orgportals_goto_next_section', function ($, _) {
  'use strict';

  return {
    initialize: function() {
      var nextSectionBtn = this.el;

      nextSectionBtn.on('click', function onNextSectionBtnClick() {
        var section = nextSectionBtn.closest('div[data-section]');
        var nextSection, topOffset;

        if (section.length === 1) {
          nextSection = section.next();
          topOffset = nextSection.offset().top;

          window.scrollTo(0, topOffset - 60);
        }
      });
    }
  }
});

ckan.module('orgportals_goto_next_section', function ($, _) {
  'use strict';

  return {
    initialize: function() {
      //We don't need to go to the next section if there is only one section
      if ($("[data-section]").length === 1) {
        $(".next-section-container").hide()
      }

      var nextSectionBtn = this.el;

      nextSectionBtn.on('click', function onNextSectionBtnClick() {
        var section = nextSectionBtn.closest('div[data-section]');
        var nextSection, topOffset;

        if (section.length === 1) {
          nextSection = section.next();
          topOffset = nextSection.offset().top;

          window.scrollTo(0, topOffset - 60);
        }
      });
    }
  }
});

Hide next section buttons if there is only one section


myApp.service('breadCrumbService', function($location) {

    this.crumbs = [
    ];

    this.current = {
        path: './',
        display:'Inventories'
    };

    this.update = function(crumb) {
        console.log("Updating crumbs:", crumb);
        var index = this.crumbs.findIndex(function(c) {
            return c.path === crumb.path;
        });

        if(index == -1) {
            this.crumbs.push(this.current);
            this.current = crumb;
        }
        else {
            this.current = this.crumbs[index];
            this.crumbs = this.crumbs.slice(0, index);
        }
    };

    this.get = function(i) {
        var index = i || 0;
        return this.crumbs[index];
    };
});

myApp.service('breadCrumbService', function($location) {

    this.crumbs = [
    ];

    this.current = {
        path: './',
        display:'Inventories'
    };

    this.update = function(crumb) {
        console.log("Updating crumbs:", crumb);
        if(this.current.path === crumb.path || crumb.path === '/')
            return;

        var index = this.crumbs.findIndex(function(c) {
            return c.path === crumb.path;
        });

        if(index == -1) {
            this.crumbs.push(this.current);
            this.current = crumb;
        }
        else {
            this.current = this.crumbs[index];
            this.crumbs = this.crumbs.slice(0, index);
        }
    };

    this.get = function(i) {
        var index = i || 0;
        return this.crumbs[index];
    };
});

Add guard clause to beginning of update method


import * as Backbone from 'backbone';
import SearchView from '../search/views/search-view';
import Messages from './messenger';

class AppRouter extends Backbone.Router {

  get routes() {
    return {
      'search/(?q=*queryString)': 'search'
    };
  }

  initialize() {
    this.listenTo(Messages, 'router:go', this.go);
  }

  go(route) {
    this.navigate(route, {trigger: true});
  }

  search(queryString = '') {
    new SearchView({query: queryString});
  }

}

export default AppRouter;

import * as Backbone from 'backbone';
import SearchAppView from '../search/views/searchApp-view';
import Messages from './messenger';

class AppRouter extends Backbone.Router {

  get routes() {
    return {
      'search/(?q=*queryString)': 'search'
    };
  }

  initialize() {
    this.listenTo(Messages, 'router:go', this.go);
  }

  go(route) {
    this.navigate(route, {trigger: true});
  }

  search(queryString = '') {
    new SearchAppView({query: queryString});
  }

}

export default AppRouter;

Add 'app' to name of main view in search module


$(document).ready(function() {
    function initTinyMceTextarea(event) {
        var targetRow = $(event.target).parents('tr');
        if (targetRow.find('td:contains(".help"),td:contains(".html"),td:contains(".information.")').length) {
            var textarea = targetRow.find('textarea');
            if (!textarea.hasClass('tinymce')) {
                textarea.addClass('tinymce');
            }

            initTinyMCE();

            var tinymceInstance = tinymce.get(textarea.attr('id'));
            tinymceInstance.on('Change', function() {
                angular.element(textarea)
                    .triggerHandler('input');
            });
        }
    }

    function removeTinyMceTextarea(event) {
        var textarea = $(event.target).parents('tr').find('textarea');

        tinymce.get(textarea.attr('id')).remove();
    }

    $('.translation-ui').on('click', 'tbody td .actions .btn-primary', initTinyMceTextarea);
    $('.translation-ui').on('click', 'tbody td .actions .btn-success', removeTinyMceTextarea);
});

$(document).ready(function() {
    function initTinyMceTextarea(event) {
        var targetRow = $(event.target).parents('tr');
        if (targetRow.find('td:contains(".help"),td:contains(".html"),td:contains(".information.")').length) {
            var textarea = targetRow.find('textarea');
            if (!textarea.hasClass('tinymce')) {
                textarea.addClass('tinymce');
            }

            initTinyMCE();

            var tinymceInstance = tinymce.get(textarea.attr('id'));
            tinymceInstance.on('Change', function() {
                textarea.val(
                    tinymceInstance.getContent()
                );

                angular.element(textarea)
                    .triggerHandler('input');
            });
        }
    }

    function removeTinyMceTextarea(event) {
        var textarea = $(event.target).parents('tr').find('textarea');

        tinymce.get(textarea.attr('id')).remove();
    }

    $('.translation-ui').on('click', 'tbody td .actions .btn-primary', initTinyMceTextarea);
    $('.translation-ui').on('click', 'tbody td .actions .btn-success', removeTinyMceTextarea);
});

Fix saving of HTML translation messages


/*
    The Conflict of Interest (COI) module of Kuali Research
    Copyright © 2005-2016 Kuali, Inc.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

import fs from 'fs';
import path from 'path';

const filePath = process.env.LOCAL_FILE_DESTINATION || 'uploads/';

export function getFile(dbInfo, key, callback) {
  const stream = fs.createReadStream(path.join(filePath, key));
  stream.on('error', err => {
    callback(err);
  });
  return stream;
}

export function deleteFile(key, callback) {
  fs.unlink(path.join(filePath, key), err => {
    callback(err);
  });
}

/*
    The Conflict of Interest (COI) module of Kuali Research
    Copyright © 2005-2016 Kuali, Inc.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

import fs from 'fs';
import path from 'path';

const filePath = process.env.LOCAL_FILE_DESTINATION || 'uploads/';

function cleanKey(key) {
  return key.replace(/(\.\.)|\/|\\/g, '');
}

export function getFile(dbInfo, key, callback) {
  const stream = fs.createReadStream(path.join(filePath, cleanKey(key)));
  stream.on('error', err => {
    callback(err);
  });
  return stream;
}

export function deleteFile(key, callback) {
  fs.unlink(path.join(filePath, cleanKey(key)), err => {
    callback(err);
  });
}

Check for characters in file path


(function ( $ ) {
	"use strict";

	$(function () {

		// catch the upvote/downvote action
		$( 'div.karma' ).on( 'click', 'span > a', function( e ){
			e.preventDefault();
			var value = 0;
			var comment_id = $(this).data('commentId');
			if ( $(this).hasClass( 'add-karma' ) ) {
				value = 1;
			} else if( $(this).hasClass( 'remove-karma' ) ) {
				value = -1;
			}

			$.post( comment_popularity.ajaxurl, {action: 'comment_vote', vote: value, comment_id: comment_id, hmn_vote_nonce: comment_popularity.hmn_vote_nonce}, function( data ){
				// update karma
				$( 'div#comment-' + data.data.comment_id + ' span.comment-karma' ).text( data.data.karma );
			});
		});

	});

}(jQuery));

(function ( $ ) {
	"use strict";

	$(function () {

		// catch the upvote/downvote action
		$( 'div.comment-weight-container' ).on( 'click', 'span > a', function( e ){
			e.preventDefault();
			var value = 0;
			var comment_id = $(this).data('commentId');
			if ( $(this).hasClass( 'vote-up' ) ) {
				value = 1;
			} else if( $(this).hasClass( 'vote-down' ) ) {
				value = -1;
			}

			$.post( comment_popularity.ajaxurl, {action: 'comment_vote', vote: value, comment_id: comment_id, hmn_vote_nonce: comment_popularity.hmn_vote_nonce}, function( data ){
				// update karma
				$( 'div#comment-' + data.data.comment_id + ' span.comment-weight' ).text( data.data.weight );
			});
		});

	});

}(jQuery));

Use weight terminology instead of karma


import { Controller } from 'stimulus'
import '@ap-spectrum-web-components/slider'
import '@spectrum-web-components/theme/lib/theme-lightest'
import '@spectrum-web-components/theme/lib/scale-large'
import '@spectrum-web-components/theme/lib/theme'

export default class extends Controller {
  static targets = ['image']

  connect() {
    this.changeQuality()
    this.scale = this.data.get('scale').split(',')
  }

  changeQuality(event) {
    let quality = this.data.get('quality')
    let newQuality

    if (event) {
      newQuality = this.scale[Number.parseInt(event.target.value, 10)]
    } else {
      newQuality = quality
    }

    if (event && quality === newQuality) {
      return
    } else {
      this.data.set('quality', newQuality)
    }

    this.imageTarget.src = this.baseSrc().replace('??', newQuality)
  }

  baseSrc() {
    return this.imageTarget.dataset.src
  }
}

import { Controller } from 'stimulus'
// import '@ap-spectrum-web-components/slider'
// import '@spectrum-web-components/theme/lib/theme-lightest'
// import '@spectrum-web-components/theme/lib/scale-large'
// import '@spectrum-web-components/theme/lib/theme'

export default class extends Controller {
  static targets = ['image']

  connect() {
    this.changeQuality()
    this.scale = this.data.get('scale').split(',')
  }

  changeQuality(event) {
    let quality = this.data.get('quality')
    let newQuality

    if (event) {
      newQuality = this.scale[Number.parseInt(event.target.value, 10)]
    } else {
      newQuality = quality
    }

    if (event && quality === newQuality) {
      return
    } else {
      this.data.set('quality', newQuality)
    }

    this.imageTarget.src = this.baseSrc().replace('??', newQuality)
  }

  baseSrc() {
    return this.imageTarget.dataset.src
  }
}

Disable troublesome imports for now


Kpcc.ArticleView = Ember.View.extend({
    didInsertElement: function() {
        $('body').scrollTop(0).addClass('article');
    }
});

Kpcc.ArticleView = Ember.View.extend({
    didInsertElement: function() {
        this.scrollToTop()
        this.addBodyClass()
    },

    modelChanged: function() {
        this.scrollToTop()
    }.observes('controller.model'),

    scrollToTop: function() {
        $('body').scrollTop(0)
    },

    addBodyClass: function() {
        $('body').addClass('article')
    }
});

Make page scroll to top when switching between articles


var Client    = require('client'),
    Utils     = require('utils'),
    ZAFClient = {};

/// ### ZAFClient API
///
/// When you include the ZAF SDK on your website, you get access to the `ZAFClient` object.
///
/// #### ZAFClient.init([callback(context)])
///
/// Returns a [`client`](#client-object) object.
///
/// ##### Arguments
///
///   * `callback(context)` (optional) a function called as soon as communication with
///     the Zendesk app is established. The callback receives a context object with
///     data related to the Zendesk app, including `currentAccount`, `currentUser`, and `location`
///
/// Example:
///
/// ```javascript
/// var client = ZAFClient.init(function(context) {
///   var currentUser = context.currentUser;
///   console.log('Hi ' + currentUser.name);
/// });
/// ```
ZAFClient.init = function(callback) {
  var params = Utils.queryParameters(),
      client;

  if (!params.origin || !params.app_guid) { return false; }

  client = new Client({ origin: params.origin, appGuid: params.app_guid });

  if (typeof callback === 'function') {
    client.on('app.registered', callback.bind(client));
  }

  return client;
};

module.exports = ZAFClient;

var Client    = require('client'),
    Utils     = require('utils'),
    ZAFClient = {};

/// ### ZAFClient API
///
/// When you include the ZAF SDK on your website, you get access to the `ZAFClient` object.
///
/// #### ZAFClient.init([callback(context)])
///
/// Returns a [`client`](#client-object) object.
///
/// ##### Arguments
///
///   * `callback(context)` (optional) a function called as soon as communication with
///     the Zendesk app is established. The callback receives a context object with
///     data related to the Zendesk app, including `currentAccount`, `currentUser`, and `location`
///
/// Example:
///
/// ```javascript
/// var client = ZAFClient.init(function(context) {
///   var currentUser = context.currentUser;
///   console.log('Hi ' + currentUser.name);
/// });
/// ```
ZAFClient.init = function(callback) {
  var queryParams = Utils.queryParameters(),
      hashParams = Utils.queryParameters(( document.location.hash || '' ).slice(1)),
      origin = queryParams.origin || hashParams.origin,
      app_guid = queryParams.app_guid || hashParams.app_guid,
      client;

  if (!origin || !app_guid) { return false; }

  client = new Client({ origin: origin, appGuid: app_guid });

  if (typeof callback === 'function') {
    client.on('app.registered', callback.bind(client));
  }

  return client;
};

module.exports = ZAFClient;

Support hash params for origin and app guid


$(function () {
    $('#btn-add-gadget').click(function () {
        var elem = $(this);
        asset.process(elem.data('type'), elem.data('aid'), location.href, elem);
    });

    $('#btn-remove-subscribe').click(function () {
        var elem = $(this);
        asset.unsubscribeBookmark(elem.data('type'), elem.data('aid'), location.href, elem);
    });

    $('#btn-remove-subscribe').hover(
        function () {
            $(this).find("i").removeClass().addClass("fa fa-remove");
        },
        function () {
            $(this).find("i").removeClass().addClass("fa fa-star");
        });

    $("a[data-toggle='tooltip']").tooltip();

    $('.embed-snippet').hide();

    $('.btn-embed').click(function () {
        $('.embed-snippet').toggle(400);
        return false;
    });

    var el = $('.user-rating'),
        rating = el.data('rating');
    $($('input', el)[rating - 1]).attr('checked', 'checked');

    $('.auto-submit-star').rating({
        callback: function (value, link) {
            if (value == undefined) {
                value = 0;
            }
            $('.rate-num-assert').html('(' + value + ')');
            caramel.post('/apis/rate', {
                asset: $('#assetp-tabs').data('aid'),
                value: value || 0
            }, function (data) {

            });
        }
    });

});

$(function () {
    $('.action-container').on(
        {
            mouseenter: function () {
                $(this).find("i").removeClass().addClass("fa fa-remove");
            },
            mouseleave: function () {
                $(this).find("i").removeClass().addClass("fa fa-star");
            },
            click: function () {
                var elem = $(this);
                asset.unsubscribeBookmark(elem.data('type'), elem.data('aid'), location.href, elem);
            }
        },'#btn-remove-subscribe').on(
        {
            click: function () {
                var elem = $(this);
                asset.process(elem.data('type'), elem.data('aid'), location.href, elem);
            }
        },'#btn-add-gadget');

    $("a[data-toggle='tooltip']").tooltip();

    $('.embed-snippet').hide();

    $('.btn-embed').click(function () {
        $('.embed-snippet').toggle(400);
        return false;
    });

    var el = $('.user-rating'),
        rating = el.data('rating');
    $($('input', el)[rating - 1]).attr('checked', 'checked');

    $('.auto-submit-star').rating({
        callback: function (value, link) {
            if (value == undefined) {
                value = 0;
            }
            $('.rate-num-assert').html('(' + value + ')');
            caramel.post('/apis/rate', {
                asset: $('#assetp-tabs').data('aid'),
                value: value || 0
            }, function (data) {

            });
        }
    });

});

Fix for STORE-1330: Remove direct binding of `click` API to `on`


import DS from 'ember-data';

export default DS.RESTSerializer.extend({
  _shouldSerializeHasMany: function() {
    return true;
  },

  // This fixes a failure in Ember Data 1.13 where an empty hasMany
  // was saving as undefined rather than [].
  serializeHasMany(snapshot, json, relationship) {
    this._super.apply(this, arguments);

    const key = relationship.key;

    if (!json[key]) {
      json[key] = [];
    }
  }
});

import Ember from 'ember';
import DS from 'ember-data';

const {
  get
} = Ember;
const keys = Object.keys || Ember.keys;

export default DS.RESTSerializer.extend({
  _shouldSerializeHasMany: function() {
    return true;
  },

  // This fixes a failure in Ember Data 1.13 where an empty hasMany
  // was saving as undefined rather than [].
  serializeHasMany(snapshot, json, relationship) {
    this._super.apply(this, arguments);

    const key = relationship.key;

    if (!json[key]) {
      json[key] = [];
    }
  },

  _isAttachment(attribute) {
    return ['attachment', 'attachments'].indexOf(attribute.type) !== -1;
  },

  serializeAttribute(snapshot, json, key, attribute) {
    this._super(snapshot, json, key, attribute);
    if (this._isAttachment(attribute)) {
      // if provided, use the mapping provided by `attrs` in the serializer
      var payloadKey = this._getMappedKey(key, snapshot.type);
      if (payloadKey === key && this.keyForAttribute) {
        payloadKey = this.keyForAttribute(key, 'serialize');
      }
      // assign any attachments to the attachments property, so that relational-pouch
      // will put these in the special CouchDB _attachments property
      // this will conflict with any 'attachments' attr in the model
      // suggest that #toRawDoc in relational-pouch should allow _attachments to be specified
      json['attachments'] = Object.assign({}, json['attachments'], json[payloadKey]);
    }
  },

  extractAttributes(modelClass, resourceHash) {
    let attributes = this._super(modelClass, resourceHash);
    let modelAttrs = get(modelClass, 'attributes');
    modelClass.eachTransformedAttribute(key => {
      let attribute = modelAttrs.get(key);
      if (this._isAttachment(attribute)) {
        // put the corresponding _attachments entries from the response into the attribute
        let fileNames = keys(attributes[key]);
        fileNames.forEach(fileName => {
          attributes[key][fileName] = resourceHash.attachments[fileName];
        });
      }
    });
    return attributes;
  }
});

Change serializer so that attachments are passed to and from relational-couch correctly


(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:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},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})

(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})

Implement require.resolve(), log and return module name given


/* eslint-disable no-return-assign */
import { format, addHours, addDays } from 'date-fns';

const formatUpcomingReviews = (data = []) => {
  let extraDays = 0;
  const getFutureDayName = (daysAhead = 0) => format(addDays(new Date(), daysAhead), 'dddd');
  const genDay = (hour) => (hour === '12am' ? getFutureDayName((extraDays += 1)) : '');
  const genHour = (index) => `${format(addHours(new Date(), index + 1), 'ha')}`;

  return data.reduce((list, value, index) => {
    const hour = genHour(index);
    return list.concat({
      day: genDay(hour),
      hour,
      value,
    });
  }, []);
};

export default formatUpcomingReviews;

/* eslint-disable no-return-assign */
import { format, addHours, addDays } from 'date-fns';

const formatUpcomingReviews = (data = []) => {
  let extraDays = 0;
  const getFutureDayName = (daysAhead = 0) => format(addDays(new Date(), daysAhead), 'dddd');
  const genDay = (hour) => (hour === '12am' ? getFutureDayName((extraDays += 1)) : '');
  const genHour = (index) => `${format(addHours(new Date(), index), 'ha')}`;

  return data.reduce((list, value, index) => {
    const hour = genHour(index);
    return list.concat({
      day: genDay(hour),
      hour,
      value,
    });
  }, []);
};

export default formatUpcomingReviews;

Remove extra hour in upcoming review formatter


import React from 'react';
import './SearchUrl.css';

function SearchUrl(props) {
  const searchUrl = decodeURI(props.searchUrl.substring(59));
  return (
    <div className="SearchUrl">
      {searchUrl}
    </div>
  );
}

export default SearchUrl;

import React from 'react';
import './SearchUrl.css';

function SearchUrl(props) {
  const searchUrl = decodeURI(props.searchUrl.substring(59));
  return (

      {searchUrl}

  );
}

export default SearchUrl;

Use fixed-width font to display urls


const merge = require('webpack-merge')
const webpack = require('webpack')
const WebpackAssetsManifest = require('webpack-assets-manifest')

const WEBPACK_DEV_SERVER = 'http://localhost:8080'

module.exports = function () {
  process.env.NODE_ENV = 'development'

  return merge(require('./config.base.js'), {
    mode: 'development',
    watch: true,

    output: {
      filename: 'js/main.js',
      publicPath: WEBPACK_DEV_SERVER + '/assets/module/web/'
    },

    module: {
      rules: [
        {
          test: /\.(sass|scss|css)$/,
          use: [
            'vue-style-loader',
            'css-loader',
            'sass-loader'
          ]
        }
      ]
    },

    plugins: [
      new webpack.HotModuleReplacementPlugin(),
      new WebpackAssetsManifest({
        output: 'assets.json',
        writeToDisk: true,
        publicPath: true,
        assets: { 'webpack-hot-reload': WEBPACK_DEV_SERVER + '/webpack-dev-server.js' }
      })
    ],

    devtool: '#cheap-module-eval-source-map',

    devServer: {
      contentBase: false,
      hot: true,
      headers: {
        'Access-Control-Allow-Origin': '*'
      }
    }
  })
}

const merge = require('webpack-merge')
const webpack = require('webpack')
const WebpackAssetsManifest = require('webpack-assets-manifest')

const WEBPACK_DEV_SERVER = 'http://localhost:8080'

module.exports = function () {
  process.env.NODE_ENV = 'development'

  return merge(require('./config.base.js'), {
    mode: 'development',
    watch: true,

    output: {
      filename: 'js/main.js',
      chunkFilename: 'js/[name].bundle.js',
      publicPath: WEBPACK_DEV_SERVER + '/assets/module/web/'
    },

    module: {
      rules: [
        {
          test: /\.(sass|scss|css)$/,
          use: [
            'vue-style-loader',
            'css-loader',
            'sass-loader'
          ]
        }
      ]
    },

    plugins: [
      new webpack.HotModuleReplacementPlugin(),
      new WebpackAssetsManifest({
        output: 'assets.json',
        writeToDisk: true,
        publicPath: true,
        assets: { 'webpack-hot-reload': WEBPACK_DEV_SERVER + '/webpack-dev-server.js' }
      })
    ],

    devtool: '#cheap-module-eval-source-map',

    devServer: {
      contentBase: false,
      hot: true,
      headers: {
        'Access-Control-Allow-Origin': '*'
      }
    }
  })
}

Add bundle name in dev mode


const os = require('os');
const TerserPlugin = require('terser-webpack-plugin');

/**
 * parallel doesn't work yet for WSL (GNU/Linux on Windows)
 * cf https://github.com/webpack-contrib/terser-webpack-plugin/issues/21
 * https://github.com/webpack-contrib/uglifyjs-webpack-plugin/issues/302
 * @return {Boolean} true if WSL
 */
const isWSL = () => {
    if (process.platform === 'linux' && os.release().includes('Microsoft')) {
        return true;
    }

    return false;
};

const minimizer = [
    new TerserPlugin({
        exclude: /\/node_modules\/(?!(asmcrypto\.js|pmcrypto))/,
        cache: true,
        parallel: !isWSL(),
        sourceMap: true,
        terserOptions: {
            mangle: true,
            compress: true
        }
    })
];

module.exports = ({ isProduction }) => ({
    splitChunks: {
        // Share all chunks between async and sync bundles https://webpack.js.org/plugins/split-chunks-plugin/#splitchunks-chunks
        chunks: 'all',
        name: true
    },
    minimize: isProduction,
    minimizer
});

const os = require('os');
const TerserPlugin = require('terser-webpack-plugin');

/**
 * parallel doesn't work yet for WSL (GNU/Linux on Windows)
 * cf https://github.com/webpack-contrib/terser-webpack-plugin/issues/21
 * https://github.com/webpack-contrib/uglifyjs-webpack-plugin/issues/302
 * @return {Boolean} true if WSL
 */
const isWSL = () => {
    if (process.platform === 'linux' && os.release().includes('Microsoft')) {
        return true;
    }

    return false;
};

const minimizer = [
    new TerserPlugin({
        exclude: /\/node_modules\/(?!(asmcrypto\.js|pmcrypto))/,
        cache: true,
        parallel: !isWSL(),
        sourceMap: true,
        terserOptions: {
            mangle: true,
            compress: true,
            safari10: true
        }
    })
];

module.exports = ({ isProduction }) => ({
    splitChunks: {
        // Share all chunks between async and sync bundles https://webpack.js.org/plugins/split-chunks-plugin/#splitchunks-chunks
        chunks: 'all',
        name: true
    },
    minimize: isProduction,
    minimizer
});

Add back support for safari10


'use strict';

import config      from '../config';
import gulp        from 'gulp';
import browserSync from 'browser-sync';

var reload_delay = 1000;

gulp.task('server', function() {
  console.log('BROWSER SYNC ENABLED ON PORT ' + config.server.port + ' & NAME: ' + config.server.name)
  browserSync.init({
    server: {
      baseDir: config.path.dist,
      directory: true,
      index: 'index.html'
    },
    ui: false,
    port: config.server.port,
    logPrefix: config.server.name,
    logConnections: true,
    notify: true
  })
});

'use strict';

import config      from '../config';
import gulp        from 'gulp';
import browserSync from 'browser-sync';

var reload_delay = 1000;

gulp.task('server', function() {
  console.log('----------------------------')
  console.log('BROWSER SYNC ENABLED ON PORT ' + config.server.port + ' & NAME: ' + config.server.name)
  console.log('----------------------------')
  browserSync.init({
    server: {
      baseDir: config.path.dist,
      directory: true,
      index: 'index.html'
    },
    ui: false,
    port: config.server.port,
    logPrefix: config.server.name,
    logConnections: true,
    notify: true
  })
});

ADD CONSOLE LOG / BROWSER SYNC


const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const CardSchema = new Schema({

  name: {
    type: String,
    unique: true,
    require: true,
  },

  idName: {
    type: String,
    unique: true,
    require: true,
  },

  rarity: {
    type: String,
    enum: 'Common Rare Epic Legendary'.split(' '),
    require: true,
  },

  type: {
    type: String,
    enum: 'Troop Building Spell'.split(' '),
    require: true,
  },

  description: {
    type: String,
    require: true,
  },

  arena: {
    type: Number,
    require: true,
  },

  elixirCost: {
    type: Number,
    require: true,
  },

  order: {
    type: Number,
    require: true,
  },

});

CardSchema.pre('save', function preSave(next) {
  if (!this.idName) {
    this.idName = JSON.parse(JSON.stringify(this.name.toLowerCase()));
    this.idName = this.idName.replace(/ /g, '-');
    this.idName = this.idName.replace(/\./g, '');
  }
  next();
});

module.exports = mongoose.model('Card', CardSchema);

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const CardSchema = new Schema({

  name: {
    type: String,
    unique: true,
    require: true,
  },

  idName: {
    type: String,
    unique: true,
    require: true,
  },

  rarity: {
    type: String,
    enum: 'Common Rare Epic Legendary'.split(' '),
    require: true,
  },

  type: {
    type: String,
    enum: 'Troop Building Spell'.split(' '),
    require: true,
  },

  description: {
    type: String,
    require: true,
  },

  arena: {
    type: Number,
    require: true,
  },

  elixirCost: {
    type: Number,
    require: true,
  },

  order: {
    type: Number,
    require: true,
  },

  copyId: {
    type: Number,
    require: true,
  },

});

CardSchema.pre('save', function preSave(next) {
  if (!this.idName) {
    this.idName = JSON.parse(JSON.stringify(this.name.toLowerCase()));
    this.idName = this.idName.replace(/ /g, '-');
    this.idName = this.idName.replace(/\./g, '');
  }
  next();
});

module.exports = mongoose.model('Card', CardSchema);

Add copyId to card schema