(function () {
  'use strict';

  function cancelSessionInviteCtrl($scope, $state, $stateParams, currentUser, $translate, alertService, usSpinnerService, cdEventsService) {

    usSpinnerService.spin('session-spinner');

    var applicationId = $stateParams.applicationId;
    var ticketId = $stateParams.eventId;
    currentUser = currentUser.data;

    if (_.isEmpty(currentUser)) {
        return $state.go('error-404-no-headers');
    }

    var application = {
      id: applicationId,
      ticketId: ticketId,
      updateAction: 'delete',
      deleted: true
    };

    $scope.showAlertBanner = function (application, successMessage) {
      return (application.status === 'approved' || application.attended || application.deleted) && successMessage !== undefined;
    }

    cdEventsService.bulkApplyApplications([application], function (applications) {

        var successMessage = $translate.instant('Thank you for confirming you will not be in attendance; your ticket has now been cancelled.');

        if (_.isEmpty(applications)) {
            return;
        }
        if ($scope.showAlertBanner(applications[0], successMessage)) {
          alertService.showAlert(successMessage);
        } else if (applications.ok === true) {
            alertService.showAlert(successMessage);
        } else {
            alertService.showAlert($translate.instant(applications.why));
        }
        $state.go('home');

    }, function (err) {
        if (err) {
            console.error(err);
            alertService.showError($translate.instant('Invalid ticket cancellation link.'));
        }
    })
  }

  angular.module('cpZenPlatform')
    .controller('cancel-session-invite-controller', ['$scope', '$state', '$stateParams', 'currentUser', '$translate', 'alertService', 'usSpinnerService', 'cdEventsService', cancelSessionInviteCtrl]);
})();

(function () {
  'use strict';

  function cancelSessionInviteCtrl($scope, $state, $stateParams, currentUser, $translate, alertService, usSpinnerService, cdEventsService) {

    usSpinnerService.spin('session-spinner');

    var applicationId = $stateParams.applicationId;
    var eventId = $stateParams.eventId;

    currentUser = currentUser.data;
    if (_.isEmpty(currentUser)) {
        return $state.go('error-404-no-headers');
    }

    var application = {
      id: applicationId,
      eventId: eventId
    };

    cdEventsService.removeApplicant(application, function (response) {

        var successMessage = $translate.instant('Thank you for confirming you will not be in attendance; your application has now been cancelled.');

        if (response.ok === true) {
          alertService.showAlert(successMessage);
        } else {
            alertService.showAlert($translate.instant(response.why));
        }
        $state.go('home');

    }, function (err) {
        if (err) {
            console.error(err);
            alertService.showError($translate.instant('Invalid application cancellation link.'));
        }
        $state.go('home');
    })
  }

  angular.module('cpZenPlatform')
    .controller('cancel-session-invite-controller', ['$scope', '$state', '$stateParams', 'currentUser', '$translate', 'alertService', 'usSpinnerService', 'cdEventsService', cancelSessionInviteCtrl]);
})();

Fix cancel session invite so that it doesn't require admin rights


(function() {
    var SirTrevor = window.SirTrevor;
    var BlockMixins = SirTrevor.BlockMixins;
    var Block = SirTrevor.Block;
    Block.prototype.availableMixins.push('clonable');

    BlockMixins.Clonable = {
        mixinName: 'Clonable',

        initializeClonable: function() {
            // For this mixin to work we need to also have the controllable mixin available.
            // Lets do some security checks
            if ((this.controllable === true && !this.$control_ui) || // controllable is enabled here but not yet initialized
                !this.controllable) { // we were not even marked as controllable
                this.controls = this.controls || {};
                this.withMixin(BlockMixins.Controllable);
                this.controllable = false; // This will prevent from double initing the controllable ui
            }

            var stInstance = SirTrevor.getInstance(this.instanceID);

            this.addUiControl('clone', function(e) {
                var blockData = this.getData();
                var blocks = stInstance.block_manager.blocks;
                stInstance.block_controls.currentContainer = this.$el; // This will determine where the new block is added
                stInstance.block_manager.createBlock(blockData.type, blockData.data);
                SirTrevor.EventBus.trigger('block:cloned', blocks[blocks.length - 1]);
            }.bind(this))
        },
    }
})();

(function() {
    var _ = window._;
    var SirTrevor = window.SirTrevor;
    var BlockMixins = SirTrevor.BlockMixins;
    var Block = SirTrevor.Block;
    Block.prototype.availableMixins.push('clonable');

    BlockMixins.Clonable = {
        mixinName: 'Clonable',

        initializeClonable: function() {
            // For this mixin to work we need to also have the controllable mixin available.
            // Lets do some security checks
            if ((this.controllable === true && !this.$control_ui) || // controllable is enabled here but not yet initialized
                !this.controllable) { // we were not even marked as controllable
                this.controls = this.controls || {};
                this.withMixin(BlockMixins.Controllable);
                this.controllable = false; // This will prevent from double initing the controllable ui
            }

            var stInstance = SirTrevor.getInstance(this.instanceID);

            this.addUiControl('clone', function(e) {
                var blockData = _.cloneDeep(this.getData());
                var blocks = stInstance.block_manager.blocks;
                stInstance.block_controls.currentContainer = this.$el; // This will determine where the new block is added
                stInstance.block_manager.createBlock(blockData.type, blockData.data);
                SirTrevor.EventBus.trigger('block:cloned', blocks[blocks.length - 1]);
            }.bind(this))
        },
    }
})();

Create a deep clone of block data when cloning


module("About Assignment (topics/about_assignment.js)");

test("local variables", function() {
    var temp = __;
    equal(temp, 1, "Assign a value to the variable temp");
});

test("global variables", function() {
    temp = 1; // Not using var is an example. Always use var in practise.
    equal(window.__, temp, 'global variables are assigned to the window object');
});

module("About Assignment (topics/about_assignment.js)");

test("local variables", function() {
    var temp = 1;
    equal(temp, 1, "Assign a value to the variable temp");
});

test("global variables", function() {
    temp = 1; // Not using var is an example. Always use var in practise.
    equal(window.temp, temp, 'global variables are assigned to the window object');
});

Complete assignment tests to pass


export default function getElementOverride (element, overrides) {
  const parentNode = getTopMostElementParent(element)

  for (let override in overrides) {
    if (overrides.hasOwnProperty(override)) {
      try {
        const matches = parentNode.querySelectorAll(override)
        if (Array.from(matches).indexOf(element) > -1) {
          return overrides[override]
        }
      } catch (e) {
        // At least we tried.
      }
    }
  }
}

function getTopMostElementParent (element) {
  let parentNode = element.parentNode
  while (!parentNode) {
    parentNode = parentNode.parentNode
  }
  return parentNode
}

export default function getElementOverride (element, overrides) {
  const parentNode = getTopMostElementParent(element)

  for (let override in overrides) {
    if (overrides.hasOwnProperty(override)) {
      try {
        const matches = parentNode.querySelectorAll(override)
        if (Array.from(matches).indexOf(element) > -1) {
          return overrides[override]
        }
      } catch (e) {
        // At least we tried.
      }
    }
  }
}

function getTopMostElementParent (element) {
  let parentNode = element.parentNode
  // Traverse up the DOM tree until the last parent is found.
  while (parentNode.parentNode) {
    parentNode = parentNode.parentNode
  }
  return parentNode
}

Fix obvious bug in `getTopMostElementParent`


import SauceConnectLauncher from 'sauce-connect-launcher'

class SauceLaunchService {
    /**
     * modify config and launch sauce connect
     */
    onPrepare (config) {
        if (!config.sauceConnect) {
            return
        }

        config.host = 'localhost'
        config.port = 4445

        this.sauceConnectOpts = Object.assign({
            username: config.user,
            accessKey: config.key
        }, config.sauceConnectOpts)

        return new Promise((resolve, reject) => SauceConnectLauncher(this.sauceConnectOpts, (err, sauceConnectProcess) => {
            if (err) {
                return reject(err)
            }

            this.sauceConnectProcess = sauceConnectProcess
            resolve()
        }))
    }

    /**
     * shut down sauce connect
     */
    onComplete () {
        if (!this.sauceConnectProcess) {
            return
        }

        return new Promise((r) => this.sauceConnectProcess.close(r))
    }
}

export default SauceLaunchService

import SauceConnectLauncher from 'sauce-connect-launcher'

class SauceLaunchService {
    /**
     * modify config and launch sauce connect
     */
    onPrepare (config) {
        if (!config.sauceConnect) {
            return
        }

        this.sauceConnectOpts = Object.assign({
            username: config.user,
            accessKey: config.key
        }, config.sauceConnectOpts)

        config.host = 'localhost'
        config.port = this.sauceConnectOpts.port || 4445

        return new Promise((resolve, reject) => SauceConnectLauncher(this.sauceConnectOpts, (err, sauceConnectProcess) => {
            if (err) {
                return reject(err)
            }

            this.sauceConnectProcess = sauceConnectProcess
            resolve()
        }))
    }

    /**
     * shut down sauce connect
     */
    onComplete () {
        if (!this.sauceConnectProcess) {
            return
        }

        return new Promise((r) => this.sauceConnectProcess.close(r))
    }
}

export default SauceLaunchService

Remove hardcoded sauce connect port


export const compose = (...fns) => (...args) => {
  fns.forEach(fn => fn && fn(...args));
};

export const pick = (obj, ...props) => {
  return props.reduce((newObj, prop) => {
    if (obj.hasOwnProperty(prop)) {
      newObj[prop] = obj[prop];
    }
    return newObj;
  }, {});
};

export const compose = (...fns) => (...args) => {
  fns.forEach(fn => fn && fn(...args));
};

export const pick = (obj, ...props) => {
  return props.reduce((newObj, prop) => {
    if (obj && obj.hasOwnProperty(prop)) {
      newObj[prop] = obj[prop];
    }
    return newObj;
  }, {});
};

Add null-safe property access in helper function


/* global console, Calendar, vis */

(function() {
    "use strict";

    var calendar = new Calendar();
    calendar.init(document.getElementById('visualization'),
    {
      height: "100vh",
      orientation: "top",
      zoomKey: 'shiftKey',
      zoomMax: 315360000000,
      zoomMin: 600000,
      editable: {
        add: false,
        updateTime: true,
        updateGroup: false,
        remove: true
      },
      groupOrder: function (a, b) {
          console.log("Ordenar!!");
          console.log(a.id);
          console.log(b.id);
          if (a.id === "demos") {
            return -1;
          }
          if (b.id === "demos") {
            return 1;
          }

          var groups = [a.id, b.id];
          groups.sort();
          if (a.id === groups[0]) {
            return -1;
          } else {
            return 1;
          }
        }

    });

    $(document).ready(function(){
      $('.vis-center>.vis-content').on('scroll', function () {
          $('.vis-left>.vis-content').scrollTop($(this).scrollTop());
      });
      $('.vis-left>.vis-content').on('scroll', function () {
          $('.vis-center>.vis-content').scrollTop($(this).scrollTop());
      });
    });

})();

/* global console, Calendar, vis */

(function() {
    "use strict";

    var calendar = new Calendar();
    calendar.init(document.getElementById('visualization'),
    {
      height: "100vh",
      orientation: "top",
      zoomKey: 'shiftKey',
      zoomMax: 315360000000,
      zoomMin: 600000,
      editable: {
        add: false,
        updateTime: true,
        updateGroup: false,
        remove: true
      },
      groupOrder: function (a, b) {
          if (a.id === "Demos") {
            return -1;
          }
          if (b.id === "Demos") {
            return 1;
          }

          var groups = [a.id, b.id];
          groups.sort();
          if (a.id === groups[0]) {
            return -1;
          } else {
            return 1;
          }
        }

    });

    $(document).ready(function(){
      $('.vis-center>.vis-content').on('scroll', function () {
          $('.vis-left>.vis-content').scrollTop($(this).scrollTop());
      });
      $('.vis-left>.vis-content').on('scroll', function () {
          $('.vis-center>.vis-content').scrollTop($(this).scrollTop());
      });
    });

})();

Remove unnecesary logs from order function


'use strict';

module.exports = {
  clientOptions: {
    apiKey: process.env.DS_NORTHSTAR_API_KEY,
    baseURI: process.env.DS_NORTHSTAR_API_BASEURI,
  },
  getUserFields: {
    id: 'id',
    email: 'email',
    mobile: 'mobile',
  },
};

'use strict';

module.exports = {
  clientOptions: {
    apiKey: process.env.DS_NORTHSTAR_API_KEY,
    baseURI: process.env.DS_NORTHSTAR_API_BASEURI,
    // TODO: Use this in the routes integration test helper to intercept calls to get credentials
    oauth: {
      host: process.env.DS_NORTHSTAR_API_OAUTH_TOKEN_HOST,
      path: process.env.DS_NORTHSTAR_API_OAUTH_TOKEN_PATH,
    },
  },
  getUserFields: {
    id: 'id',
    email: 'email',
    mobile: 'mobile',
  },
};

Add TODO to intercept calls to get credentials for integration tests


(function () {
    'use strict';

    var factoriesModul;
    try {
        factoriesModul = angular.module('dcc.factories');
    } catch (e) {
        factoriesModul = angular.module('dcc.factories', []);
    }

    factoriesModul.factory('durationValidationFactory', [function () {
        var factory = {};

        factory.isValidTime = function (time) {
            var timeRegex = /^\d\d:\d\d$/;
            return !!timeRegex.test(time);
        };

        factory.isValidDay = function (day) {
            var dayRegex = /^\d\d-\d\d-\d\d\d\d$/;
            return !!dayRegex.test(day);
        };

        factory.isToAfterFrom = function (day, to, from) {
            return Date.parse(day + ' ' +  to) > Date.parse(day + ' ' +  from);
        };

        return factory;
    }]);
}());

(function () {
    'use strict';

    var factoriesModul;
    try {
        factoriesModul = angular.module('dcc.factories');
    } catch (e) {
        factoriesModul = angular.module('dcc.factories', []);
    }

    factoriesModul.factory('durationValidationFactory', [function () {
        var factory = {};

        factory.isValidTime = function (time) {
            var timeRegex = /^\d\d:\d\d$/;
            return !!timeRegex.test(time);
        };

        factory.isValidDay = function (day) {
            var dayRegex = /^\d\d-\d\d-\d\d\d\d$/;
            return !!dayRegex.test(day);
        };

        factory.isToAfterFrom = function (day, to, from) {
            day = day.replace("-", "/").replace("-", "/");
            return Date.parse(day + ' ' +  to) > Date.parse(day + ' ' +  from);
        };

        return factory;
    }]);
}());

Fix bug, appering only in Firefox (Date.parse("xx-xx-xxxx xx:xx"))


require('../app').directive('previewPictures', /* @ngInject */function () {
  return {
    restrict: 'EA',
    templateUrl: '/views/directives/previewpictures.html',
    scope: {
      pictures: '='
    },
    bindToController: true,
    link: function ($scope, $element, $attrs, $ctrl) {
      $element.on('click', $ctrl.preview)
    },
    controller: /* @ngInject */function ($filter, Lightbox) {
      var $ctrl = this
      var authurl = $filter('authurl')

      $ctrl.preview = function (event) {
        event.preventDefault()
        event.stopImmediatePropagation()

        Lightbox.openModal($ctrl.pictures.map(function (picture) {
          picture.url = authurl(picture.url)
          return picture
        }), 0)
      }
    },
    controllerAs: '$ctrl'
  }
})

var angular = require('angular')

require('../app').directive('previewPictures', /* @ngInject */function () {
  return {
    restrict: 'EA',
    templateUrl: '/views/directives/previewpictures.html',
    scope: {
      pictures: '='
    },
    bindToController: true,
    link: function ($scope, $element, $attrs, $ctrl) {
      $element.on('click', $ctrl.preview)
    },
    controller: /* @ngInject */function ($filter, Lightbox) {
      var $ctrl = this
      var authurl = $filter('authurl')

      $ctrl.preview = function (event) {
        event.preventDefault()
        event.stopImmediatePropagation()

        Lightbox.openModal($ctrl.pictures.map(function (picture) {
          return angular.extend({}, picture, { url: authurl(picture.url) })
        }), 0)
      }
    },
    controllerAs: '$ctrl'
  }
})

Return new object instead of modifying input objects


'use strict';

const config = require('config');
const sw = require('../lib/tasks/sw');
const task = require('../lib/helpers/task');
const sync = require('browser-sync');

module.exports = gulp => {
  const watch = `${config.folders.output}/**/*`;

  gulp.task('sw', 'Generates a dynamic Service Worker', cb => {
    sw((err) => {
      sync.reload();

      cb(err);
    });
  });

  task.watch('sw', watch, 'sw', gulp);
};

'use strict';

const config = require('config');
const sw = require('../lib/tasks/sw');
const task = require('../lib/helpers/task');

module.exports = gulp => {
  const watch = [
    `${config.folders.output}/**/*`,
    `!${config.folders.output}/${config.sw.file}`,
  ];

  gulp.task('sw', 'Generates a dynamic Service Worker', cb => {
    sw(cb);
  });

  task.watch('sw', watch, 'sw', gulp);
};

Fix SW watching for SW changes


import React              from 'react';
import Router             from 'react-router';
import routes             from './routes';
// import SettingsActions    from '../../actions/settings';
// import UserActions        from '../actions/user';

// var Route = Router.Route;

// describe('Logged in', function () {

//  helpStubAjax(SettingsActions);

//   UserActions.login({
//     data: {
//       body: {
//         email: "joe@example.com",
//         displayName: "joe",
//         jwt_token: "asdf"
//       }
//     }
//   });
//   jasmine.clock().tick(); // Advance the clock to the next tick
  localStorage.setItem('jwt', "asdfsd");

  describe('default route', function () {
    it('renders home', function (done) {
      Router.run(routes, '/', function (Handler, state){
        var html = React.renderToString(<Handler params={state.params} />);
        expect(html).toMatch(/Home/);
        done();
      });
    });
  });

});

Remove extra '})' from javscript.


// ELMO.Views.MissionChangeDropdown
(function(ns, klass) {

  // constructor
  ns.MissionChangeDropdown = klass = function(params) {
    $(params.el).on('change', function(e){
      var new_mission_name = $(e.currentTarget).find('option:selected').val();
      if (new_mission_name == '') {
        // Redirect straight to root if chose 'None' from dropdown.
        window.location.href = ELMO.app.url_builder.build('/', {mode: 'basic'}) + '?missionchange=1';
      } else {
        // If currently in basic mode, we automatically redirect to mission root.
        // This is because it doesn't currently make sense to use a path from basic mode in mission mode,
        // whereas sharing paths between missions or mission<->admin can make sense.
        var path = ELMO.app.params.mode == 'basic' ? '' : window.location.pathname;

        // Preserve query string and add missionchange param to it.
        var qs = window.location.search;
        qs += (qs == '' ? '?' : '&') + 'missionchange=1';

        window.location.href = ELMO.app.url_builder.build(path, {mode: 'mission', mission_name: new_mission_name}) + qs;
      }
    });
  };

})(ELMO.Views);

// ELMO.Views.MissionChangeDropdown
(function(ns, klass) {

  // constructor
  ns.MissionChangeDropdown = klass = function(params) {
    $(params.el).on('change', function(e){
      var new_mission_name = $(e.currentTarget).find('option:selected').val();
      if (new_mission_name == '') {
        // Redirect straight to root if chose 'None' from dropdown.
        window.location.href = ELMO.app.url_builder.build('/', {mode: 'basic'}) + '?missionchange=1';
      } else {
        // If currently in basic mode, we automatically redirect to mission root.
        // This is because it doesn't currently make sense to use a path from basic mode in mission mode,
        // whereas sharing paths between missions or mission<->admin can make sense.
        var path = ELMO.app.params.mode == 'basic' ? '' : window.location.pathname;

        // Preserve query string and add missionchange param to it.
        var params = new URLSearchParams(window.location.search);
        params.append('missionchange', '1');
        // Remove the 'search' param for search filters.
        params.delete('search');
        var qs = '?' + params.toString();

        window.location.href = ELMO.app.url_builder.build(path, {mode: 'mission', mission_name: new_mission_name}) + qs;
      }
    });
  };

})(ELMO.Views);

Stop preserving search query between missions


var locks = require('locks');

//Map lock only used to create new partitionLocks entries
var mapLock = locks.createMutex();
var partitionLocks = new Object();
var timeoutSeconds = 2;

//key is unique for the namespace & partition.
module.exports.lockedOperation = function lockedOperation (key, callback) {
	var keyStr = JSON.stringify(key);
    var keyLock = partitionLocks[keyStr];

    // If lock does not exist - acquire the map lock and create new lock for this key
  if (keyLock == null) {
      mapLock.timedLock(timeoutSeconds * 1000, function (error) {
        if (error) {
            console.log('Could not get the partition map mutex lock within ' + timeoutSeconds + ' seconds, so gave up');
        } else {
            if (keyLock == null) {
                 keyLock = locks.createMutex();
                 partitionLocks[keyStr] = keyLock;
                 console.log("Created partition lock object for " + keyStr);
                 mapLock.unlock();
              }
        }
       });
  }

  if (keyLock != null) {
     keyLock.timedLock(timeoutSeconds * 1000, function (error) {
       callback();
       keyLock.unlock();
     });
  }
};

var locks = require('locks');

//Map lock only used to create new partitionLocks entries
var mapLock = locks.createMutex();
var partitionLocks = new Object();
var timeoutSeconds = 2;

//key is unique for the namespace & partition.
module.exports.lockedOperation = function lockedOperation (key, callback) {
	var keyStr = JSON.stringify(key);
    var keyLock = partitionLocks[keyStr];

    // If lock does not exist - acquire the map lock and create new lock for this key
  if (keyLock == null) {
      mapLock.timedLock(timeoutSeconds * 1000, function (error) {
        if (error) {
            console.log('Could not get the partition map mutex lock within ' + timeoutSeconds + ' seconds, so gave up');
        } else {
            if (keyLock == null) {
                 keyLock = locks.createMutex();
                 partitionLocks[keyStr] = keyLock;
                 console.log("Created partition lock object for " + keyStr);
                 mapLock.unlock();
              }
        }
       });
  }

  if (keyLock != null) {
     keyLock.timedLock(timeoutSeconds * 1000, function (error) {
       console.log("Locked partition " + keyStr);
       callback();
       keyLock.unlock();
       console.log("Unlocked partition " + keyStr);
     });
  }
};

Revert "Remove unnecessary lock logging"


var child = require('child_process');
var http = require('http');
var test = require('tape');
var fs = require('fs');
var path = require('path');
var url = require('url');

test('bin print version', function (test) {
  test.plan(4);
  var options = [
    '-V',
    '--version'
  ];

  options.forEach(function (option) {
    var args = ['bin/amok.js', option];
    test.comment(args.join(' '));

    var cli = child.spawn('node', args);

    cli.stdout.on('data', function (data) {
      var message = data.toString();
      test.equal(message, require('../package.json').version + '\n');
    });

    cli.on('close', function (code) {
      test.equal(code, 0);
    });
  });
});

test('bin print help', function (test) {
  test.plan(4);
  var options = [
    '-h',
    '--help'
  ];

  options.forEach(function (option) {
    var args = ['./bin/amok.js', option];
    test.comment(args.join(' '));

    var cli = child.spawn('node', args);

    cli.stdout.on('data', function (data) {
      var message = data.toString();
      test.ok(message.indexOf('Usage:') > -1);
    });

    cli.on('close', function (code) {
      test.equal(code, 0);
    });
  });
});

var child = require('child_process');
var http = require('http');
var test = require('tape');
var fs = require('fs');
var path = require('path');
var url = require('url');

var version = [
  '-V',
  '--version',
];

version.forEach(function (arg) {
  var args = ['bin/amok.js', arg];
  test(args.join(' '), function (test) {
    test.plan(2);

    var cli = child.spawn('node', args);

    cli.stdout.on('data', function (data) {
      var message = data.toString();
      test.equal(message, require('../package.json').version + '\n');
    });

    cli.on('close', function (code) {
      test.equal(code, 0);
    });
  });
});

var help = [
  '-h',
  '--help'
];

help.forEach(function (arg) {
  var args = ['bin/amok.js', arg];

  test('bin print help', function (test) {
    test.plan(2);

    test.comment(args.join(' '));
    var cli = child.spawn('node', args);

    cli.stdout.on('data', function (data) {
      var message = data.toString();
      test.ok(message.indexOf('Usage:') > -1);
    });

    cli.on('close', function (code) {
      test.equal(code, 0);
    });
  });
});

Move short option tests into top level tests


define('parsley/factory/constraint', [
  'parsley/utils'
], function (ParsleyUtils) {
  var ConstraintFactory = function (parsleyField, name, requirements, priority, isDomConstraint) {

    if (!new RegExp('ParsleyField').test(ParsleyUtils.get(parsleyField, '__class__')))
      throw new Error('ParsleyField or ParsleyFieldMultiple instance expected');

    if ('function' !== typeof window.ParsleyValidator.validators[name] ||
      'Assert' !== window.ParsleyValidator.validators[name](requirements).__parentClass__)
      throw new Error('Valid validator expected');

    var getPriority = function () {
      if ('undefined' !== typeof parsleyField.options[name + 'Priority'])
        return parsleyField.options[name + 'Priority'];

      return ParsleyUtils.get(window.ParsleyValidator.validators[name](requirements), 'priority') || 2;
    };

    priority = priority || getPriority();

    // If validator have a requirementsTransformer, execute it
    if ('function' === typeof window.ParsleyValidator.validators[name](requirements).requirementsTransformer)
      requirements = window.ParsleyValidator.validators[name](requirements).requirementsTransformer();

    return $.extend(window.ParsleyValidator.validators[name](requirements), {
      name: name,
      requirements: requirements,
      priority: priority,
      groups: [priority],
      isDomConstraint: isDomConstraint || ParsleyUtils.attr(parsleyField.$element, parsleyField.options.namespace, name)
    });
  };

  return ConstraintFactory;
});

define('parsley/factory/constraint', [
  'parsley/utils'
], function (ParsleyUtils) {
  var ConstraintFactory = function (parsleyField, name, requirements, priority, isDomConstraint) {
    var assert = {};

    if (!new RegExp('ParsleyField').test(ParsleyUtils.get(parsleyField, '__class__')))
      throw new Error('ParsleyField or ParsleyFieldMultiple instance expected');

    if ('function' === typeof window.ParsleyValidator.validators[name])
      assert = window.ParsleyValidator.validators[name](requirements);
    if ('Assert' !== assert.__parentClass__)
      throw new Error('Valid validator expected');

    var getPriority = function () {
      if ('undefined' !== typeof parsleyField.options[name + 'Priority'])
        return parsleyField.options[name + 'Priority'];

      return ParsleyUtils.get(assert, 'priority') || 2;
    };

    priority = priority || getPriority();

    // If validator have a requirementsTransformer, execute it
    if ('function' === typeof assert.requirementsTransformer) {
      requirements = assert.requirementsTransformer();
      // rebuild assert with new requirements
      assert = window.ParsleyValidator.validators[name](requirements);
    }

    return $.extend(assert, {
      name: name,
      requirements: requirements,
      priority: priority,
      groups: [priority],
      isDomConstraint: isDomConstraint || ParsleyUtils.attr(parsleyField.$element, parsleyField.options.namespace, name)
    });
  };

  return ConstraintFactory;
});

Store created assert and reuse. This makes it clearer when asserts need to be created and is more efficient.


exports.ensureAuthenticated = function(req, res, next) {
  if (req.isAuthenticated()) {
    next();
  } else {
    res.sendStatus(401);
  }
};

exports.ensureAuthenticated = function(req, res, next) {
  if (req.isAuthenticated()) {
    next();
  } else {
    res.status(401).send({ msg: 'Unauthorized' });
  }
};

Send valid json message back when used is unauthorized


/*
This file is part of Archivematica.

Copyright 2010-2013 Artefactual Systems Inc. <http://artefactual.com>

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

Archivematica 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 General Public License for more details.

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

$(function() {
  // add job viewer to generate dialogs with
  var job = new Job();
  job.set({'type': 'Approve Normalization'});
  job.sip = new Sip();
  job.sip.set({'directory': '{{ sipname }}'});
  var jobView = new BaseJobView({model: job});

  // add popovers
  $($.find('a.file-location'))
    .popover({
      trigger: 'hover',
      content: function()
        {
            return $(this).attr('data-location').replace(/%.*%/gi, '');
        }
    })
    .click(function(event) {
      event.preventDefault();
    });
});

/*
This file is part of Archivematica.

Copyright 2010-2013 Artefactual Systems Inc. <http://artefactual.com>

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

Archivematica 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 General Public License for more details.

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

$(function() {
  // add popovers
  $($.find('a.file-location'))
    .popover({
      trigger: 'hover',
      content: function()
        {
            return $(this).attr('data-location').replace(/%.*%/gi, '');
        }
    })
    .click(function(event) {
      event.preventDefault();
    });
});

Remove old JS code in the normalization report


function transitionToParams(params) {
  const location = this.props.location;
  const allParams = Object.assign({}, location.query, params);
  const keys = Object.keys(allParams);

  let url = location.pathname;
  if (keys.length) {
    url += `?${keys.map(key => `${key}=${encodeURIComponent(allParams[key])}`).join('&')}`;
  }

  this.context.router.transitionTo(url);
}

export default transitionToParams;

function transitionToParams(params) {
  const location = this.props.location;
  const allParams = Object.assign({}, location.query, params);
  const keys = Object.keys(allParams);

  let url = location.pathname;
  if (keys.length) {
    url += `?${keys.map(key => `${key}=${encodeURIComponent(allParams[key])}`).join('&')}`;
  }

  this.props.history.push(url);
}

export default transitionToParams;

Use API from released version of react-router v4 STRIPES-173


/* eslint-env mocha */
const Graph = require('../../src').DataStructures.Graph;
const assert = require('assert');

describe('Graph', () => {
  it('should be empty when initialized', () => {
    const inst = new Graph();

    assert(inst.isEmpty());
    assert.equal(inst.size, 0);
    assert.deepStrictEqual(inst.vertices, []);
  });
  });
});

/* eslint-env mocha */
const Graph = require('../../src').DataStructures.Graph;
const assert = require('assert');

describe('Graph', () => {
  it('should be empty when initialized', () => {
    const inst = new Graph();

    assert(inst.isEmpty());
    assert.equal(inst.size, 0);
    assert.deepStrictEqual(inst.vertices, []);
  });
  it('should get neighbours', () => {
    const inst = new Graph();

    inst.addVertex(1);
    inst.addVertex(2);
    inst.addVertex(3);
    inst.addVertex(4);
    inst.addVertex(5);

    inst.addEdge(1, 2, 1);
    inst.addEdge(1, 3, 1);
    inst.addEdge(1, 5, 1);
    inst.addEdge(3, 4, 1);
    inst.addEdge(1, 5, 1);

    assert.deepStrictEqual(inst.getNeighbours(1), ['2', '3', '5']);
  });
});

Test Graph: Adds test to check for neighbours


const html = require('choo/html');
const find = require('lodash/find');

module.exports = (state, prev, send) => {
  function enterLocation(e) {
    e.preventDefault();
    send('enterLocation');
  }

  function noContactsMessage(state) {
    if (state.splitDistrict && (state.address || state.cachedCity)) {
      return html`

The location you provided could be in one of two congressional districts.

 enterLocation(e)}>Enter your address or zip+4 to identify your representative in the House.

`
    }
    else {
      return html`<h2><a onclick=${(e) => enterLocation(e)}>Set your location</a> to find your representatives</h2>`
    }
  }

	return html`

		  ${noContactsMessage(state)}

`
}

const html = require('choo/html');
const find = require('lodash/find');

module.exports = (state, prev, send) => {
  function enterLocation(e) {
    e.preventDefault();
    send('enterLocation');
  }

  function noContactsMessage(state) {
    if (state.splitDistrict && (state.address || state.cachedCity)) {
      return html`

The location you provided could be in one of two congressional districts.

 enterLocation(e)}>Enter your address to identify your representative in the House.

`
    }
    else {
      return html`<h2><a onclick=${(e) => enterLocation(e)}>Set your location</a> to find your representatives</h2>`
    }
  }

	return html`

		  ${noContactsMessage(state)}

`
}

Remove zip+4 mention, it's not working great in google civic.


/* eslint-env node */

module.exports = function(deployTarget) {
  const API_VERSION = 'v1.12';
  var ENV = {
    build: {},
    exclude: ['.DS_Store', '*-test.js'],
    'revision-data': {
      type: 'git-commit',
    }
  };

  ENV.s3 = {
    acl: 'public-read',
    region: 'us-west-2',
    bucket: 'ilios-frontend-assets'
  };

  ENV['s3-index'] = {
    region: 'us-west-2',
    filePattern: 'index.json',
    bucket: 'frontend-json-config',
  };

  ENV.gzip = {
    //dont gzip JSON files
    filePattern: '**/*.{js,css,ico,map,xml,txt,svg,eot,ttf,woff,woff2}'
  };

  if (deployTarget === 'staging') {
    ENV.build.environment = 'production';
    ENV['s3-index'].prefix = 'stage-' + API_VERSION;
  }

  if (deployTarget === 'production') {
    ENV.build.environment = 'production';
    ENV['s3-index'].prefix = 'prod-' + API_VERSION;
  }

  // Note: if you need to build some configuration asynchronously, you can return
  // a promise that resolves with the ENV object instead of returning the
  // ENV object synchronously.
  return ENV;
};

/* eslint-env node */

module.exports = function(deployTarget) {
  const API_VERSION = 'v1.12';
  var ENV = {
    build: {},
    exclude: ['.DS_Store', '*-test.js'],
    'revision-data': {
      type: 'git-commit',
    }
  };

  ENV.s3 = {
    acl: 'public-read',
    region: 'us-west-2',
    bucket: 'ilios-frontend-assets'
  };

  ENV['s3-index'] = {
    region: 'us-west-2',
    filePattern: 'index.json',
    bucket: 'frontend-json-config',
  };

  ENV.gzip = {
    //dont gzip the json index file
    ignorePattern: '**/index.json'
  };

  if (deployTarget === 'staging') {
    ENV.build.environment = 'production';
    ENV['s3-index'].prefix = 'stage-' + API_VERSION;
  }

  if (deployTarget === 'production') {
    ENV.build.environment = 'production';
    ENV['s3-index'].prefix = 'prod-' + API_VERSION;
  }

  // Note: if you need to build some configuration asynchronously, you can return
  // a promise that resolves with the ENV object instead of returning the
  // ENV object synchronously.
  return ENV;
};

Use ignorePattern to not gzip index.json


var expect = require("expect.js");
var AWS = require('aws-sdk');
var AwsQ = require("../index.js");

describe('entry point',function(){
	it('should inject/hack \'then\' into aws.response.prototype',function(){
		AwsQ.InjectMagic(AWS);

		expect(AWS.Request.prototype).to.have.property("then");
		expect(AWS.Request.prototype.then).to.be.a('function');

	});
});

var expect = require("expect.js");
var AwsQ = require("../index.js");

var AwsMock;
beforeEach(function(){
	AwsMock = {
		Request:{
			prototype:{

			}
		}
	};
});

describe('AwsQ entry point',function(){
	it('should inject/hack \'then\' into aws.response.prototype',function(){
		AwsQ.InjectMagic(AwsMock);

		expect(AwsMock.Request.prototype).to.have.property("then");
		expect(AwsMock.Request.prototype.then).to.be.a('function');
	});
});

describe('Aws.Request.prototype.then function',function(){
	it('should return a Q promise',function(){
		var AWS = require('aws-sdk');
		AwsQ.InjectMagic(AWS);

		var ec2 = new AWS.EC2();
		var promise = ec2.describeAccountAttributes({}).then();
		expect(promise).to.be.a('promise');
	});
});

Expand tests to prove Request.then returns a promise


var srcFolder = './src';
var distFolder = './dist';
var docsFolder = './docs';
var docsSrc = docsFolder + '/src';
var docsDist = docsFolder + '/dist';

var dirs = {
  srcJS: srcFolder,
  docs: {
    src: docsSrc,
    dist: docsDist,
    srcJS: docsSrc,
    distJS: docsDist,
    srcCSS: docsFolder + '/**',
    distCSS: docsDist + '/',
    srcImg: srcFolder + '/img',
    distImg: distFolder + '/img'
  }
};

var files = {
  docs: {
    srcJS: [
      dirs.docs.srcJS + '/List/index.js'
    ],
    distJS: dirs.docs.distJS + '/index.js',
    srcCSS: dirs.docs.srcCSS + '/*.less',
    distCSS: dirs.docs.distCSS + '/index.css',
    srcHTML: dirs.docs.src + '/index.html',
    distHTML: dirs.docs.dist + '/index.html'
  }
};

module.exports = {
  dirs: dirs,
  files: files
};

var srcFolder = './src';
var distFolder = './dist';
var docsFolder = './docs';
var docsSrc = docsFolder + '/src';
var docsDist = docsFolder + '/dist';

var dirs = {
  srcJS: srcFolder,
  docs: {
    src: docsSrc,
    dist: docsDist,
    srcJS: docsSrc,
    distJS: docsDist,
    srcCSS: docsFolder + '/**',
    distCSS: docsDist + '/'
  }
};

var files = {
  docs: {
    srcJS: [
      dirs.docs.srcJS + '/List/index.js'
    ],
    distJS: dirs.docs.distJS + '/index.js',
    srcCSS: dirs.docs.srcCSS + '/*.less',
    distCSS: dirs.docs.distCSS + '/index.css',
    srcHTML: dirs.docs.src + '/index.html',
    distHTML: dirs.docs.dist + '/index.html'
  }
};

module.exports = {
  dirs: dirs,
  files: files
};

Remove reference to images in Config file


/**
 * Helper module for tests
 */

const path = require('path');
exports.appPath = path.resolve(__dirname, '../');
exports.electronPath = path.resolve(__dirname, '../node_modules/.bin/electron');

process.env.NODE_ENV = 'test'; // suppress logging

/**
 * Helper module for tests
 */

const path = require('path');
exports.appPath = path.resolve(__dirname, '../');

var exe_suffix = "";
if(process.platform === 'win32') {
  exe_suffix = '.exe';
}
exports.electronPath = path.resolve(__dirname, '../node_modules/.bin/electron' + exe_suffix);

process.env.NODE_ENV = 'test'; // suppress logging

Make electron executable path platform dependent


(function () {
  'use strict';

  angular.module('core')
    .directive('pageTitle', pageTitle);

  pageTitle.$inject = ['$rootScope', '$timeout', '$interpolate', '$state'];

  function pageTitle($rootScope, $timeout, $interpolate, $state) {
    var directive = {
      restrict: 'A',
      link: link
    };

    return directive;

    function link(scope, element) {
      $rootScope.$on('$stateChangeSuccess', listener);

      function listener(event, toState) {
        var title = (getTitle($state.$current));
        $timeout(function () {
          element.text(title);
        }, 0, false);
      }

      function getTitle(currentState) {
        var applicationCoreTitle = 'Pythia';
        var workingState = currentState;
        if (currentState.data) {
          workingState = (typeof workingState.locals !== 'undefined') ? workingState.locals.globals : workingState;
          var stateTitle = $interpolate(currentState.data.pageTitle)(workingState);
          return applicationCoreTitle + ' | ' + stateTitle;
        } else {
          return applicationCoreTitle;
        }
      }
    }
  }
}());

(function () {
  'use strict';

  angular.module('core')
    .directive('pageTitle', pageTitle);

  pageTitle.$inject = ['$rootScope', '$timeout', '$interpolate', '$state'];

  function pageTitle($rootScope, $timeout, $interpolate, $state) {
    var directive = {
      restrict: 'A',
      link: link
    };

    return directive;

    function link(scope, element) {
      $rootScope.$on('$stateChangeSuccess', listener);

      function listener(event, toState) {
        var title = (getTitle($state.$current));
        $timeout(function () {
          element.text(title);
        }, 0, false);
      }

      function getTitle(currentState) {
        var applicationCoreTitle = 'OpenSM';
        var workingState = currentState;
        if (currentState.data) {
          workingState = (typeof workingState.locals !== 'undefined') ? workingState.locals.globals : workingState;
          var stateTitle = $interpolate(currentState.data.pageTitle)(workingState);
          return applicationCoreTitle + ' | ' + stateTitle;
        } else {
          return applicationCoreTitle;
        }
      }
    }
  }
}());

Change application name in page title


module.exports = {
  plugins: [
    require('postcss-import'),
    require('postcss-css-reset'),
    require('postcss-custom-properties'),
    require('postcss-extend'),
    require('postcss-nested'),
    require('postcss-color-function'),
    require('postcss-button'),
    require('postcss-inline-svg'),
    require('postcss-svgo'),
    require('postcss-flexbox'),
    require('postcss-neat')({
      neatMaxWidth: '960px'
    }),
    require('postcss-extend'),
    require('postcss-custom-media'),
    require('postcss-media-minmax'),
    require('postcss-cssnext')({
      browsers: [
        'last 2 versions',
        'ie >= 10'
      ]
    }),
    require('cssnano')({
      autoprefixer: false, // already prefixed w/ cssnext
      save: true,
      core: true,
      sourcemap: true
    })
  ]
};

module.exports = {
  plugins: [
    require('postcss-import'),
    require('postcss-css-reset'),
    require('postcss-custom-properties'),
    require('postcss-extend'),
    require('postcss-nested'),
    require('postcss-color-function'),
    require('postcss-button'),
    require('postcss-inline-svg'),
    require('postcss-svgo'),
    require('postcss-flexbox'),
    require('postcss-neat')({
      neatMaxWidth: '1200px'
    }),
    require('postcss-extend'),
    require('postcss-custom-media'),
    require('postcss-media-minmax'),
    require('postcss-cssnext')({
      browsers: [
        'last 2 versions',
        'ie >= 10'
      ]
    }),
    require('cssnano')({
      autoprefixer: false, // already prefixed w/ cssnext
      save: true,
      core: true,
      sourcemap: true
    })
  ]
};

Fix value of max. width ('neatMaxWidth') used by postcss-neat


define(function (require) {
  var app = {
      // Application Constructor
      initialize: function() {
          this.bindEvents();
      },
      // Bind Event Listeners
      //
      // Bind any events that are required on startup. Common events are:
      // 'load', 'deviceready', 'offline', and 'online'.
      bindEvents: function() {
          document.addEventListener('deviceready', this.onDeviceReady, false);
      },
      // deviceready Event Handler
      //
      // The scope of 'this' is the event. In order to call the 'receivedEvent'
      // function, we must explicity call 'app.receivedEvent(...);'
      onDeviceReady: function() {
          app.receivedEvent('deviceready');
      },
      // Update DOM on a Received Event
      receivedEvent: function(id) {
          var parentElement = document.getElementById(id);
          var listeningElement = parentElement.querySelector('.listening');
          var receivedElement = parentElement.querySelector('.received');

          listeningElement.setAttribute('style', 'display:none;');
          receivedElement.setAttribute('style', 'display:block;');

          console.log('Received Event: ' + id);
      }
  };
  return app;
});

define(function (require) {
  var app = {
      // Application Constructor
      initialize: function() {
          this.bindEvents();
      },
      // Bind Event Listeners
      //
      // Bind any events that are required on startup. Common events are:
      // 'load', 'deviceready', 'offline', and 'online'.
      bindEvents: function() {
          document.addEventListener('deviceready', this.onDeviceReady, false);
      },
      // deviceready Event Handler
      //
      // The scope of 'this' is the event. In order to call the 'receivedEvent'
      // function, we must explicity call 'app.receivedEvent(...);'
      onDeviceReady: function() {
          app.receivedEvent('deviceready');
      },
      // Update DOM on a Received Event
      receivedEvent: function(id) {
          var parentElement = document.getElementById(id);

          console.log('Received Event: ' + id);
      }
  };
  return app;
});

Fix unknown error for android/emulator.


// @flow
export const roundToDecimalPlace = (number: number, place: number = 1): number =>
  Math.round(number * 10 * place) / (10 * place);

// @flow
export const roundToDecimalPlace = (number: number, place: number = 2): number =>
  Math.round(number * 10 * place) / (10 * place);

Change default decimal place to 2


module.exports = {
  'Does Chat Exist' : function (browser) {
    browser
      .url(browser.launchUrl)
      .waitForElementVisible('body', 1000)
      .waitForElementVisible('iframe#drift-widget', 1000)
      .frame('drift-widget')
      .click('button.CHAT')
      .end();
  },
  'Does Chat Open' : function (browser) {
    browser
      .url(browser.launchUrl)
      .waitForElementVisible('body', 1000)
      .waitForElementVisible('iframe#drift-widget', 1000)
      .frame('drift-widget')
      .click('button.CHAT')
      .pause(1000)
      .verify.elementPresent('div._1b8HzQM8nmpP_VSMq7OnIz')
      .end();
  }
};

module.exports = {
  'Does Chat Exist' : function (browser) {
    browser
      .url(browser.launchUrl)
      .waitForElementVisible('body', 1000)
      .waitForElementVisible('iframe#drift-widget', 1000)
      .frame('drift-widget')
      .click('button.CHAT')
      .end();
  },
  'Does Chat Open' : function (browser) {
    browser
      .url(browser.launchUrl)
      .waitForElementVisible('body', 1000)
      .waitForElementVisible('iframe#drift-widget', 1000)
      .frame('drift-widget')
      .click('button.CHAT')
      .pause(1000)
      .verify.elementPresent('div._1b8HzQM8nmpP_VSMq7OnIz')
      .end();
  },
  'Check Chat Name' : function (browser) {
    browser
      .url(browser.launchUrl)
      .waitForElementVisible('body', 1000)
      .waitForElementVisible('iframe#drift-widget', 1000)
      .frame('drift-widget')
      .click('button.CHAT')
      .pause(1000)
      .assert.containsText("div._3LgOTzOP49NkcKVg-VuAeA", "Jake Kinsella")
      .end();
  },
  'Check Chat Message' : function (browser) {
    browser
      .url(browser.launchUrl)
      .waitForElementVisible('body', 1000)
      .waitForElementVisible('iframe#drift-widget', 1000)
      .frame('drift-widget')
      .click('button.CHAT')
      .pause(1000)
      .assert.containsText("div.dp2Cn7rCPUkibY9nD2WFj", "This is a test")
      .end();
  }
};

Add chat name and message tests


/* jshint strict:false */
/* global QUnit:false, Backbone:false, window:false */

// Taken from Backbone 1.1.2 test suite

(function() {

  var sync = Backbone.sync;
  var ajax = Backbone.ajax;
  var emulateHTTP = Backbone.emulateHTTP;
  var emulateJSON = Backbone.emulateJSON;
  var history = window.history;
  var pushState = history.pushState;
  var replaceState = history.replaceState;

  QUnit.testStart(function() {
    var env = this.config.current.testEnvironment;

    // We never want to actually call these during tests.
    history.pushState = history.replaceState = function(){};

    // Capture ajax settings for comparison.
    Backbone.ajax = function(settings) {
      env.ajaxSettings = settings;
    };

    // Capture the arguments to Backbone.sync for comparison.
    Backbone.sync = function(method, model, options) {
      env.syncArgs = {
        method: method,
        model: model,
        options: options
      };
      sync.apply(this, arguments);
    };

  });

  QUnit.testDone(function() {
    Backbone.sync = sync;
    Backbone.ajax = ajax;
    Backbone.emulateHTTP = emulateHTTP;
    Backbone.emulateJSON = emulateJSON;
    history.pushState = pushState;
    history.replaceState = replaceState;
  });

})();

/* jshint strict:false */
/* global QUnit:false, Backbone:false */

// Taken from Backbone 1.1.2 test suite
// Routing-specific stuff commented out

(function() {

  var sync = Backbone.sync;
  var ajax = Backbone.ajax;
  var emulateHTTP = Backbone.emulateHTTP;
  var emulateJSON = Backbone.emulateJSON;
  // var history = window.history;
  // var pushState = history.pushState;
  // var replaceState = history.replaceState;

  QUnit.testStart(function() {
    var env = this.config.current.testEnvironment;

    // We never want to actually call these during tests.
    // history.pushState = history.replaceState = function(){};

    // Capture ajax settings for comparison.
    Backbone.ajax = function(settings) {
      env.ajaxSettings = settings;
    };

    // Capture the arguments to Backbone.sync for comparison.
    Backbone.sync = function(method, model, options) {
      env.syncArgs = {
        method: method,
        model: model,
        options: options
      };
      sync.apply(this, arguments);
    };

  });

  QUnit.testDone(function() {
    Backbone.sync = sync;
    Backbone.ajax = ajax;
    Backbone.emulateHTTP = emulateHTTP;
    Backbone.emulateJSON = emulateJSON;
    // history.pushState = pushState;
    // history.replaceState = replaceState;
  });

})();

Remove route-related stuff from test-bb/env


module.exports = function(grunt) {

  require('google-closure-compiler').grunt(grunt);

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    concat: {
      options: {
        separator: '\n\n'
      },
      dist: {
        src: [
          'src/start.js',
          'src/simple2d.js',
          'src/shapes.js',
          'src/image.js',
          'src/sprite.js',
          'src/text.js',
          'src/sound.js',
          'src/music.js',
          'src/input.js',
          'src/window.js',
          'src/gl.js',
          'src/end.js'
        ],
        dest: 'build/simple2d.js'
      }
    },
    eslint: {
      dist: ['build/simple2d.js']
    },
    'closure-compiler': {
      dist: {
        files: {
          'build/simple2d.min.js': ['build/simple2d.js']
        },
        options: {
          compilation_level: 'SIMPLE'
          // ,formatting: 'pretty_print'
        }
      }
    }
  });

  grunt.loadNpmTasks('grunt-eslint');
  grunt.loadNpmTasks('grunt-contrib-concat');

  // Define tasks
  grunt.registerTask('default', [
    'concat',
    'eslint',
    'closure-compiler'
  ]);
};

module.exports = function(grunt) {

  require('google-closure-compiler').grunt(grunt);

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    concat: {
      options: {
        banner: '// Simple2D.js — v<%= pkg.version %>, built <%= grunt.template.today("mm-dd-yyyy") %>\n\n',
        separator: '\n\n'
      },
      dist: {
        src: [
          'src/start.js',
          'src/simple2d.js',
          'src/shapes.js',
          'src/image.js',
          'src/sprite.js',
          'src/text.js',
          'src/sound.js',
          'src/music.js',
          'src/input.js',
          'src/window.js',
          'src/gl.js',
          'src/end.js'
        ],
        dest: 'build/simple2d.js'
      }
    },
    eslint: {
      dist: ['build/simple2d.js']
    },
    'closure-compiler': {
      dist: {
        files: {
          'build/simple2d.min.js': ['build/simple2d.js']
        },
        options: {
          compilation_level: 'SIMPLE'
          // ,formatting: 'pretty_print'
        }
      }
    }
  });

  grunt.loadNpmTasks('grunt-eslint');
  grunt.loadNpmTasks('grunt-contrib-concat');

  // Define tasks
  grunt.registerTask('default', [
    'concat',
    'eslint',
    'closure-compiler'
  ]);
};

Add banner to Grunt banner


var winston = require('winston'),
    utils = require('../utils');

module.exports = function(radio, command, logger) {
  logger = logger || winston;

  function boundVolume(vol) {
    if(vol < 0) {
      vol = 0;
    } else if(vol > 100) {
      vol = 100;
    }
    return vol;
  }

  var commandPromise;

  if(command.hasOwnProperty('value')) {
    var absoluteVol = parseInt(command.value);

    commandPromise = radio.sendCommands([
      ['setvol', boundVolume(absoluteVol)]
    ]);
  } else if(command.hasOwnProperty('diff')) {
    commandPromise = radio.status()
      .then(function(statusString) {
        var volume = statusString.match(/volume: (\d{1,3})$/m)[1];
        var newVol = parseInt(volume)+parseInt(command.diff);

        return radio.sendCommands([['setvol', boundVolume(newVol)]]);
      }, utils.failedPromiseHandler());
  }

  return commandPromise;
};

var winston = require('winston'),
    utils = require('../utils');

function boundVolume(vol) {
  if(vol < 0) {
    vol = 0;
  } else if(vol > 100) {
    vol = 100;
  }
  return vol;
}

module.exports = function(radio, command, logger) {
  logger = logger || winston;

  var commandPromise;

  if(command.hasOwnProperty('value')) {
    var absoluteVol = parseInt(command.value);

    commandPromise = radio.sendCommands([
      ['setvol', boundVolume(absoluteVol)]
    ]);
  } else if(command.hasOwnProperty('diff')) {
    commandPromise = radio.status()
      .then(function(statusString) {
        var volume = statusString.match(/volume: (\d{1,3})$/m)[1];
        var newVol = parseInt(volume)+parseInt(command.diff);

        return radio.sendCommands([['setvol', boundVolume(newVol)]]);
      }, utils.failedPromiseHandler());
  }

  return commandPromise;
};

Move private function to outside of exported scope


var path = require('path');

var BACKENDS_PATH = __dirname;

function getDatabaseBackend(name, options) {
  var module, databaseBackend;

  module = require(path.join(BACKENDS_PATH, name.replace(/\.js$/, '')));
  databaseBackend = new module.DatabaseBackend(name, options);
  return databaseBackend;
}

exports.getDatabaseBackend = getDatabaseBackend;

var path = require('path');

var sprintf = require('sprintf').sprintf;

var BACKENDS_PATH = __dirname;

function getDatabaseBackend(name, options) {
  var module, databaseBackend;

  try {
    module = require(path.join(BACKENDS_PATH, name.replace(/\.js$/, '')));
  }
  catch (err) {
    throw new Error(sprintf('Failed loading "%s" backend: %s', name, err.message));
  }

  databaseBackend = new module.DatabaseBackend(name, options);
  return databaseBackend;
}

exports.getDatabaseBackend = getDatabaseBackend;

Throw a custom error if a db backend cannot be loaded.


const path = require('path')
const fs = require('fs')

const { readFile } = require('./fs.utils')
const { log } = require('./log.utils')

module.exports = exports = configFilename => {
  return (modules, options) => {
    readFile(configFilename)
      .then(data => JSON.parse(data).folder_path)
      .then(folderPath => {
        return modules
          .map(name => {
            const addr = path.join(folderPath, 'files', name)
            const file = fs.readFileSync(path.join(addr, 'settings.json'))

            let settings = []
            try {
              settings = JSON.parse(file)
            } catch(e) {
              log({ type: 'warn', message: `Not able to load settings file for "${name}" module.` })
            }

            return { name, folder: addr, settings }
          })
          .filter(moduleConf => moduleConf.settings.length > 0)
      })
      .then(data => console.log(JSON.stringify(data)))
  }
}

const path = require('path')
const fs = require('fs')

const { readFile } = require('./fs.utils')
const { log } = require('./log.utils')

module.exports = exports = configFilename => {
  return (modules, options) => {
    readFile(configFilename)
      .then(data => JSON.parse(data).folder_path)
      .then(processModuleData(modules))
      .then(data => console.log(JSON.stringify(data, null, 2)))
  }
}

function processModuleData(moduleNames) {
  return folderPath => {
    return moduleNames
      .map(name => {
        const addr = path.join(folderPath, 'files', name)
        const file = fs.readFileSync(path.join(addr, 'settings.json'))

        let settings = []
        try {
          settings = JSON.parse(file)
        } catch(e) {
          log({ type: 'warn', message: `Not able to load settings file for "${name}" module.` })
        }

        return { name, folder: addr, settings }
      })
      .filter(moduleConf => moduleConf.settings.length > 0)
  }
}

Change callback into external function


var path = require("path");
var webpack = require("webpack");
var libraryName = 'lc-form-validation';
var CopyWebpackPlugin = require('copy-webpack-plugin');

var basePath = __dirname;

var env = process.env.NODE_ENV;
var production = 'production';

var config = {
  context: path.join(basePath, "src"),
  resolve: {
      // .js is required for react imports.
      // .tsx is for our app entry point.
      // .ts is optional, in case you will be importing any regular ts files.
      extensions: ['', '.js', '.ts', '.tsx']
  },

  entry: [
    './index.ts'
  ],

  output: {
    library: libraryName,
    libraryTarget: 'umd',
    umdNamedDefine: true
  },

  module: {
		loaders: [
      {
        test: /\.(ts|tsx)$/,
        exclude: /node_modules/,
        loader: 'ts-loader'
      }
		]
	},

  plugins:[
    new webpack.optimize.OccurenceOrderPlugin(),
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(env)
    }),
    new CopyWebpackPlugin([
      {from: '../package.json', to:'dist/package.json'}
    ])
  ]
};

if (env === 'production') {
  config.plugins.push(
    new webpack.optimize.UglifyJsPlugin({
      compressor: {
        pure_getters: true,
        unsafe: true,
        unsafe_comps: true,
        warnings: false
      }
    })
  )
}

module.exports = config;

var path = require("path");
var webpack = require("webpack");
var libraryName = 'lc-form-validation';
var CopyWebpackPlugin = require('copy-webpack-plugin');

var basePath = __dirname;

var env = process.env.NODE_ENV;
var production = 'production';

var config = {
  context: path.join(basePath, "src"),
  resolve: {
      // .js is required for react imports.
      // .tsx is for our app entry point.
      // .ts is optional, in case you will be importing any regular ts files.
      extensions: ['', '.js', '.ts', '.tsx']
  },

  entry: [
    './index.ts'
  ],

  output: {
    library: libraryName,
    libraryTarget: 'umd',
    umdNamedDefine: true
  },

  module: {
		loaders: [
      {
        test: /\.(ts|tsx)$/,
        exclude: /node_modules/,
        loader: 'ts-loader'
      }
		]
	},

  plugins:[
    new webpack.optimize.OccurenceOrderPlugin(),
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(env)
    }),
    new CopyWebpackPlugin([
      {from: '../package.json', to:'dist/package.json'},
      {from: '../../README.md', to:'dist/README.md'}
    ])
  ]
};

if (env === production) {
  config.plugins.push(
    new webpack.optimize.UglifyJsPlugin({
      compressor: {
        pure_getters: true,
        unsafe: true,
        unsafe_comps: true,
        warnings: false
      }
    })
  )
}

module.exports = config;

Copy README.md to dist folder


$(document).ready(
  function(){

    // Local copy of jQuery selectors, for performance.
    var jpPlayTime = $("#jplayer_play_time");
    var jpTotalTime = $("#jplayer_total_time");
    var jpStatus = $("#demo_status"); // For displaying information about jPlayer's status in the demo page

    $("#jquery_jplayer").jPlayer({ volume: 50,
                                   oggSupport: false,
                                   preload: 'none'
                                 })
      .jPlayer("onProgressChange", function(loadPercent, playedPercentRelative, playedPercentAbsolute, playedTime, totalTime) {
                 jpPlayTime.text($.jPlayer.convertTime(playedTime));
                 jpTotalTime.text($.jPlayer.convertTime(totalTime));

               })
      .jPlayer("onSoundComplete", function() {
               });

    var contentFlow = new ContentFlow('contentFlow', { circularFlow: false } ) ;
    if (file) play(file);
  });

function play(file) {
  $("#jquery_jplayer").jPlayer("setFile", file).jPlayer("play");
};

$(document).ready(
  function(){

    // Local copy of jQuery selectors, for performance.
    var jpPlayTime = $("#jplayer_play_time");
    var jpTotalTime = $("#jplayer_total_time");
    var jpStatus = $("#demo_status"); // For displaying information about jPlayer's status in the demo page

    $("#jquery_jplayer").jPlayer({ volume: 50,
                                   oggSupport: false,
                                   preload: 'none'
                                 })
      .jPlayer("onProgressChange", function(loadPercent, playedPercentRelative, playedPercentAbsolute, playedTime, totalTime) {
                 jpPlayTime.text($.jPlayer.convertTime(playedTime));
                 jpTotalTime.text($.jPlayer.convertTime(totalTime));

               })
      .jPlayer("onSoundComplete", function() {
               });

    var contentFlow = new ContentFlow('contentFlow', { circularFlow: false ,
                                                       onclickInactiveItem: function (item) {
                                                         this.conf.onclickActiveItem(item);
                                                       },

                                                       onclickActiveItem: function (item) {
                                                         console.log(item.item);
                                                         if ($(item.item).hasClass('active')) console.log('clicked');
                                                       }
                                                     } ) ;
    if (file) play(file);
  });

function play(file) {
  $("#jquery_jplayer").jPlayer("setFile", file).jPlayer("play");
};

Set the click behavior in preparation of loading artist/album content.


onload = function() {
  var canvas = document.getElementById('canvas');
  var socket = io();

  socket.on('state', function(state) {
    // console.log('update state');
  });

  var game = new Game();

  (function animate(){ // Recursive animation call
    requestAnimationFrame(animate);
  })();
  date = Date.now();
  socket.emit('test', {});
  socket.on('test', function() {
    console.log('delay (2 way): ', Date.now()-date);
  })
};

onload = function() {
  var canvas = document.getElementById('canvas');
  var socket = io();

  socket.on('state', function(state) {
    // console.log('update state');
  });

  // var game = new Game();

  (function animate(){ // Recursive animation call
    requestAnimationFrame(animate);
  })();
  date = Date.now();
  socket.emit('test', {});
  socket.on('test', function() {
    console.log('delay (2 way): ', Date.now()-date);
  })
};

Remove failing line for tests


import { Linking, StyleSheet, Text } from 'react-native';
import React, { PureComponent } from 'react';
import Example from '../../shared/example';

const url = 'https://mathiasbynens.github.io/rel-noopener/malicious.html';

export default class LinkingPage extends PureComponent {
  handlePress() {
    Linking.canOpenURL(url).then((supported) => {
      return Linking.openURL(url);
    });
  }

  render() {
    return (
      <Example title="Linking">
        <Text onPress={this.handlePress} style={styles.text}>
          Linking.openURL
        </Text>
        <Text
          accessibilityRole="link"
          href="https://mathiasbynens.github.io/rel-noopener/malicious.html"
          style={styles.text}
          target="_blank"
        >
          target="_blank"
        </Text>
      </Example>
    );
  }
}

const styles = StyleSheet.create({
  text: {
    borderRadius: 5,
    borderStyle: 'solid',
    borderWidth: 1,
    marginVertical: 10,
    padding: 10
  }
});

import { Linking, StyleSheet, Text } from 'react-native';
import React, { PureComponent } from 'react';
import Example from '../../shared/example';

const url = 'https://mathiasbynens.github.io/rel-noopener/malicious.html';

export default class LinkingPage extends PureComponent {
  handlePress() {
    Linking.canOpenURL(url).then((supported) => {
      return Linking.openURL(url);
    });
  }

  render() {
    return (
      <Example title="Linking">
        <Text onPress={this.handlePress} style={styles.text}>
          Linking.openURL
        </Text>
        <Text
          accessibilityRole="link"
          href="https://mathiasbynens.github.io/rel-noopener/malicious.html"
          hrefAttrs={{
            target: '_blank'
          }}
          style={styles.text}
        >
          target="_blank"
        </Text>
      </Example>
    );
  }
}

const styles = StyleSheet.create({
  text: {
    borderRadius: 5,
    borderStyle: 'solid',
    borderWidth: 1,
    marginVertical: 10,
    padding: 10
  }
});

Fix one of the Linking code examples


/*!
 * wef.cssParser tests
 * Copyright (c) 2011 Pablo Escalada
 * MIT Licensed
 */
module("cssParser");
test("namespace", function() {
    notEqual(wef.cssParser , undefined, "is wef.cssParser namespace defined?");
    equal(typeof wef.cssParser, "function", "is wef.cssParser a function?");
});

test("constructor", function() {
    equal(typeof wef.cssParser(), "object", "empty constructor returns an object");
});

test("public properties", function() {
    equal(typeof wef.cssParser().version, "string", "wef.cssParser().version");
});

test("pubic methods", function() {
    var text = 'body { height: 100%; display: "a.b.c"  /2em "....."  /1em "d.e.f" "....."  /1em "g.h.i"  /2em 5em 1em  *  1em 10em}';
    ok(wef.cssParser().parse(text) != null, "parse()");
});

/*!
 * wef.cssParser tests
 * Copyright (c) 2011 Pablo Escalada
 * MIT Licensed
 */
module("cssParser");
test("namespace", function() {
    notEqual(wef.cssParser , undefined, "is wef.cssParser namespace defined?");
    equal(typeof wef.cssParser, "function", "is wef.cssParser a function?");
});

test("constructor", function() {
    equal(typeof wef.cssParser(), "object", "empty constructor returns an object");
});

test("public properties", function() {
    equal(typeof wef.cssParser().version, "string", "wef.cssParser().version");
});

test("pubic methods", function() {
    var text = 'body { height: 100%; display: "a.b.c"  /2em "....."  /1em "d.e.f" "....."  /1em "g.h.i"  /2em 5em 1em  *  1em 10em}';
    ok(wef.cssParser().parse(text) != null, "parse(string) ok");
    ok(wef.cssParser().parse(444) == null, "parse(number) returns null");
});

Add parse param type test


"use strict"

cordova.commandProxy.add("CsDeviceInfo", {
    getAppId: function (successCb, failCb)
    {
        setTimeout(function () {
            try {
                successCb(Windows.ApplicationModel.Package.current.id.ProductId)
            } catch (e) {
                failCb(e)
            }
        }, 0)
    },

    getVersionName: function (successCb, failCb)
    {
        setTimeout(function () {
            try {
                var version = Windows.ApplicationModel.Package.current.id.version
                successCb(
                    ""  +version.major
                    +"."+version.minor
                    +"."+version.build
                    +"."+version.revision
                )
            } catch (e) {
                failCb(e)
            }
        }, 0)
    },

    isHackedDevice: function (successCb, failCb)
    {
        // Assume Windows devices are not "hacked"...
        setTimeout(function () { successCb(false) }, 0)
    },
})

"use strict"

cordova.commandProxy.add("CsDeviceInfo", {
    getAppId: function (successCb, failCb)
    {
        setTimeout(function () {
            try {
                successCb(Windows.ApplicationModel.Package.current.id.name)
            } catch (e) {
                failCb(e)
            }
        }, 0)
    },

    getVersionName: function (successCb, failCb)
    {
        setTimeout(function () {
            try {
                var version = Windows.ApplicationModel.Package.current.id.version
                successCb(
                    ""  +version.major
                    +"."+version.minor
                    +"."+version.build
                    +"."+version.revision
                )
            } catch (e) {
                failCb(e)
            }
        }, 0)
    },

    isHackedDevice: function (successCb, failCb)
    {
        // Assume Windows devices are not "hacked"...
        setTimeout(function () { successCb(false) }, 0)
    },
})

Use `Windows.ApplicationModel.Package.current.id.name` instead of `.ProductId` as we get "permission denied" error when trying to retrieve productId Unable to find any documentation on required permissions...


const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const WebpackBundleTracker = require('webpack-bundle-tracker')

module.exports = {
  mode: 'development',
  entry: {
    base: './static/javascript/base',
    pages: './static/javascript/pages',
    home: './static/javascript/home',
    queries: './static/javascript/queries',
  },
  output: {
    filename: '[name]_[hash].js',
    path: path.resolve(__dirname, 'staticfiles/bundles'),
    publicPath: 'http://localhost:8080/static/bundles/',
  },
  devtool: 'eval-source-map',
  devServer: {
    compress: true,
    hot: true,
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
          }
        },
      },
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
          },
          {
            loader: 'css-loader',
          },
          {
            loader: 'sass-loader',
            options: {
              implementation: require('sass'),
            }
          },
        ]
      },
      {
        test: /\.(png|jpe?g|gif|svg)$/,
        use: [
          {
            loader: 'file-loader',
          },
        ]
      },
    ]
  },
  plugins: [
    new WebpackBundleTracker({
      filename: './webpack-stats.json',
    }),
    new MiniCssExtractPlugin({
      filename: '[name]_[hash].css',
    }),
  ],
}

const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const WebpackBundleTracker = require('webpack-bundle-tracker')

module.exports = {
  mode: 'development',
  entry: {
    base: './static/javascript/base',
    pages: './static/javascript/pages',
    home: './static/javascript/home',
    queries: './static/javascript/queries',
  },
  output: {
    filename: '[name]_[hash].js',
    path: path.resolve(__dirname, 'staticfiles/bundles'),
    publicPath: 'http://localhost:8080/static/bundles/',
  },
  devtool: 'eval-source-map',
  devServer: {
    compress: true,
    hot: true,
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
          }
        },
      },
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
          },
          {
            loader: 'css-loader',
          },
          {
            loader: 'sass-loader',
            options: {
              implementation: require('sass'),
            }
          },
        ]
      },
    ]
  },
  plugins: [
    new WebpackBundleTracker({
      filename: './webpack-stats.json',
    }),
    new MiniCssExtractPlugin({
      filename: '[name]_[hash].css',
    }),
  ],
}

Remove file loader from conf


"use strict";

exports.NOT_IMPLEMENTED = function (target, nameForErrorMessage) {
  return function () {
    var raise;

    var message = "Called NOT_IMPLEMENTED without an element to raise on" +
      (nameForErrorMessage ? ": " + nameForErrorMessage : "");

    target = target || this;
    if (target) {
      raise = target.raise;
    } else {
      if (typeof console !== "undefined" && console.log) {
        console.log(new Error(message));
      }

      return;
    }

    raise.call(this, "error", message);
  };
};

"use strict";

exports.NOT_IMPLEMENTED = function (target, nameForErrorMessage) {
  return function () {
    var raise;

    var message = "Called NOT_IMPLEMENTED without an element to raise on" +
      (nameForErrorMessage ? ": " + nameForErrorMessage : "");

    target = target || this;
    if (target) {
      raise = target.raise;
    } else {
      if (typeof console !== "undefined" && console.log) {
        console.log(new Error(message));
      }

      return;
    }

    raise.call(target, "error", message);
  };
};

Fix a global variable leak


var path = require('path');

module.exports = {
  extends: [
    require.resolve('ember-cli-eslint/coding-standard/ember-application.js')
  ]
};

Remove unused path require from eslint config


module.exports = {
  root: true,
  env: {
    node: true,
  },
  extends: ['eslint:recommended', 'plugin:prettier/recommended'],
  parserOptions: {
    ecmaVersion: 2021,
  },
  plugins: ['prettier'],
  rules: {
    'prettier/prettier': 'error',
    'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
  },
}

module.exports = {
  root: true,
  env: {
    node: true,
  },
  extends: ['eslint:recommended', 'plugin:prettier/recommended'],
  parserOptions: {
    ecmaVersion: 2021,
  },
  plugins: ['prettier'],
  rules: {
    'prettier/prettier': 'error',
    'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
  },
  overrides: [
    {
      files: ['src/*', 'src/**/*'],
      env: {
        browser: true,
      },
    },
  ],
}

Use browser eslint inside src


require.config({
  paths: {
    text: 'lib/require-text',
    css: 'lib/require-css',

    jquery: 'lib/jquery',

    underscore: 'lib/underscore',
    'underscore.crunch': 'lib/underscore.crunch',

    backbone: 'lib/backbone',

    marked: 'lib/marked',
    'marked.highlight': 'lib/marked.highlight',

    moment: 'lib/moment',

    highlight: 'lib/highlight',

    "disqus.embed": '//livingwithdjango.disqus.com/embed',
    "disqus.count": '//livingwithdjango.disqus.com/count'
  },

  shim: {
    highlight: {
      deps: ['css!style/highlight.css'],
      exports: 'hljs'
    }
  }
});

require(
  [
    'backbone', 'models', 'views', 'css!style/main.css'
  ], function(B, M, V) {
    new (B.Router.extend({
      routes: {'': 'list', '*src': 'entry'},
      list: function() { new V.List(); },
      entry: function(src) { new V.Entry({model: new M.Entry({src: src})}); }
    }));

    $('body').on('click', 'a', function() {window.location.reload();});

    B.history.start({root: '/living-with-django/'});
  }
);

require.config({
  paths: {
    text: 'lib/require-text',
    css: 'lib/require-css',

    jquery: 'lib/jquery',

    underscore: 'lib/underscore',
    'underscore.crunch': 'lib/underscore.crunch',

    backbone: 'lib/backbone',

    marked: 'lib/marked',
    'marked.highlight': 'lib/marked.highlight',

    moment: 'lib/moment',

    highlight: 'lib/highlight',

    "disqus.embed": '//livingwithdjango.disqus.com/embed',
    "disqus.count": '//livingwithdjango.disqus.com/count'
  },

  shim: {
    highlight: {
      deps: ['css!style/highlight.css'],
      exports: 'hljs'
    }
  }
});

require(
  [
    'backbone', 'models', 'views', 'css!style/main.css'
  ], function(B, M, V) {
    new (B.Router.extend({
      routes: {'': 'list', '*src': 'entry'},
      list: function() { new V.List(); },
      entry: function(src) { new V.Entry({model: new M.Entry({src: src})}); }
    }));

    // Disqus needs to be required once the "disqus_thread" element is on the page, so we need to
    // reload the page whenever that element is added.  Easier to just always reload the page.
    $('body').on('click', 'a', function() {window.location.reload();});

    B.history.start({root: '/living-with-django/'});
  }
);

Add comment explaining weird reload.


var appRoot = require('app-root-path');
//var mongoose = require('mongoose');
var Categories = require(appRoot + '/modules/catalogs/categories.js');
var Courses = require(appRoot + '/modules/catalogs/courses.js');
var async = require('asyncawait/async');
var await = require('asyncawait/await');

function CatCountSubNodes(){
}

CatCountSubNodes.prototype.init = function(params){
    this.slug = params.slug;
};

CatCountSubNodes.prototype.run = async ( function(){
    var self = this;

    var result = await( Categories.findOne({
        slug: self.slug
    }).exec());
    if (result._id) {
        var countCourses = await (Courses.find({category: result._id}).count().exec());
    }
    //var catId = result._id;

    self.result = countCourses;
} );

CatCountSubNodes.prototype.render = function(){
    var countCourses = this.result;

    return '
  Course Available: ' + countCourses + '
' ;
};

module.exports = CatCountSubNodes;

var appRoot = require('app-root-path');
//var mongoose = require('mongoose');
var Categories = require(appRoot + '/modules/catalogs/categories.js');
var Courses = require(appRoot + '/modules/catalogs/courses.js');
var async = require('asyncawait/async');
var await = require('asyncawait/await');

function CatCountSubNodes(){
}

CatCountSubNodes.prototype.init = function(params){
    this.slug = params.slug;
};

CatCountSubNodes.prototype.run = async ( function(){
    var self = this;

    var result = await( Categories.findOne({
        slug: self.slug
    }).exec());
    if (result._id) {
        var countAllCourses = await (Courses.find({category: result._id}).count().exec());
        var countDeletedCourses = await (Courses.find({category: result._id, isDeleted:{$exists: true}}).count().exec());
        var activeCourse = countAllCourses - countDeletedCourses;
    }
    //var catId = result._id;

    self.result = activeCourse;
} );

CatCountSubNodes.prototype.render = function(){
    var activeCourse = this.result;

    return '
  Course Available: ' + activeCourse + '
' ;
};

module.exports = CatCountSubNodes;

Check number of not deleted course to display in categories mind map


define(['itemObjectModel', 'mac/roman'], function(itemOM, macRoman) {

  'use strict';

  function open(item) {
    function onAtom(item, byteSource) {
      return byteSource.slice(0, 8).getBytes().then(function(headerBytes) {
        var length = new DataView(headerBytes.buffer, headerBytes.byteOffset, 4).getUint32(0, false);
        var name = macRoman(headerBytes, 4, 4);
        var atomItem = itemOM.createItem(name);
        if (length > 8) {
          atomItem.byteSource = byteSource.slice(8, length);
        }
        item.addItem(atomItem);
        if (byteSource.length > (length + 8)) {
          return onAtom(atomItem, byteSource.slice(length));
        }
      });
    }
    return onAtom(item, item.byteSource);
  }

  return open;

});

define(['itemObjectModel', 'mac/roman'], function(itemOM, macRoman) {

  'use strict';

  function open(item) {
    function onAtom(item, byteSource) {
      return byteSource.slice(0, 8).getBytes().then(function(headerBytes) {
        var length = new DataView(headerBytes.buffer, headerBytes.byteOffset, 4).getUint32(0, false);
        var name = macRoman(headerBytes, 4, 4);
        var atomItem = itemOM.createItem(name);
        if (length > 8) {
          atomItem.byteSource = byteSource.slice(8, length);
        }
        item.addItem(atomItem);
        if (byteSource.length > (length + 8)) {
          return onAtom(item, byteSource.slice(length));
        }
      });
    }
    return onAtom(item, item.byteSource);
  }

  return open;

});

Fix which atom to add subatoms to


var __ = 'Fill me in';

describe('koan.js', function () {
  describe('Strings', function () {

    it('should create Strings', function () {
      expect('a String').toBe(__);
      expect(new String('a String')).toBe(__);
    });

    it('should concatenate Strings', function () {
      expect('a ' + 'String').toBe(__);
      expect(new String('a ') + 'String').toBe(__);
    });

    it('should convert an Number to String', function () {
      expect((1).toString()).toBe(__);
      expect(new String(1)).toBe(__);
    });

    it('should convert an Array to String', function () {
      expect([1, 2, 3].toString()).toBe(__);
      expect(new String([1, 2, 3])).toBe(__);
    });

    it('should convert an Object to String', function () {
      expect({ 'abc': 1, '2': 'banana'}.toString()).toBe(__);
      expect(new String({ 'abc': 1, '2': 'banana'})).toBe(__);
    });

    it('should have a type', function () {
      expect(typeof '').toBe(__);
      expect(typeof new String()).toBe(__);
    });

  });
});

var __ = 'Fill me in';

describe('koan.js', function () {

  describe('Strings', function () {
    describe('literal', function () {
      it('should create Strings', function () {
        expect('a String').toBe(__);
      });

      it('should concatenate Strings', function () {
        expect('a ' + 'String').toBe(__);
      });

      it('should convert an Number to String', function () {
        expect((1).toString()).toBe(__);
      });

      it('should convert an Array to String', function () {
        expect([1, 2, 3].toString()).toBe(__);
      });

      it('should convert an Object to String', function () {
        expect({ 'abc': 1, '2': 'banana'}.toString()).toBe(__);
      });

      it('should have a type', function () {
        expect(typeof '').toBe(__);
      });
    });

    describe('object', function () {
      it('should create Strings', function () {
        expect(new String('a String')).toBe(__);
      });

      it('should concatenate Strings', function () {
        expect(new String('a ') + 'String').toBe(__);
      });

      it('should convert an Number to String', function () {
        expect(new String(1)).toBe(__);
      });

      it('should convert an Array to String', function () {
        expect(new String([1, 2, 3])).toBe(__);
      });

      it('should convert an Object to String', function () {
        expect(new String({ 'abc': 1, '2': 'banana'})).toBe(__);
      });

      it('should have a type', function () {
        expect(typeof new String()).toBe(__);
      });
    });
  });
});

Split string literals and string objects tests


'use strict'

var botQueryHandler = require('./storage/replyQuery');
var tg = require('./telegram/telegramAPI');
var replyQuery = require('./storage/replyQuery');
var lfm = require('./lfmController')

botQueryHandler.answer = function (query, queryData) {
    botQueryHandler[queryData.method](query, queryData);
};

botQueryHandler.youtube = function (query, queryData) {
    var replyData  = replyQuery.get(queryData.artist, queryData.object, 'tracks');
    lfm.getYouTubeLink(replyData.artist, replyData.object, function (response) {
        if (!response) {
            response = 'Sorry, this button is outdated';
        };
        tg.sendTextMessage(response, query.message.chat.id);
        tg.answerCallbackQuery(query.id);
    });
};

module.exports = botQueryHandler;

'use strict'

var botQueryHandler = require('./storage/replyQuery');
var tg = require('./telegram/telegramAPI');
var replyQuery = require('./storage/replyQuery');
var lfm = require('./lfmController')

botQueryHandler.answer = function (query, queryData) {
    botQueryHandler[queryData.method](query, queryData);
};

botQueryHandler.youtube = function (query, queryData) {
    var replyData  = replyQuery.get(queryData.artist, queryData.object, 'tracks');
    if (!replyData) {
        tg.answerCallbackQuery(query.id);
        return tg.sendTextMessage('Sorry, this button is outdated', query.message.chat.id);
    };
    lfm.getYouTubeLink(replyData.artist, replyData.object, function (response) {
        tg.sendTextMessage(response, query.message.chat.id);
        tg.answerCallbackQuery(query.id);
    });
};

module.exports = botQueryHandler;

Fix safe handling of old buttons


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

//Controller method - retrieve joined listing/user/category fields from DB
exports.getAll = function(category, callback) {
  // Listing.findAll({ order: ['createdAt', 'DESC'] })
  db.Listing.findAll({
    include:
    [{
      model: db.Category,
      attributes: ['categoryName'],
      where: {categoryName: category},
    },
    {
      model: db.User,
      attributes: ['username', 'phone', 'email']
    }]
  })
    .then(function(listings) {
      callback(200, listings);
    })
    .catch(function(error) {
      console.error(error);
      callback(404, error);
    });
};

//Controller method - add a listings to DB
exports.addOne = function(listing, callback) {
  db.Listing.create(listing)
    .then(function(listing) {
      callback(201, listing);
    })
    .catch(function(error) {
      console.error(error);
      callback(404, error);
    });
};

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

//Controller method - retrieve joined listing/user/category fields from DB
exports.getAll = function(category, callback) {
  // Listing.findAll({ order: ['createdAt', 'DESC'] })
  db.Listing.findAll({
    include:
    [{
      model: db.Category,
      attributes: ['categoryName'],
      where: {categoryName: category},
    },
    {
      model: db.User,
      attributes: ['username', 'phone', 'email']
    }],
    order: 'createdAt DESC'
  })
    .then(function(listings) {
      callback(200, listings);
    })
    .catch(function(error) {
      console.error(error);
      callback(404, error);
    });
};

//Controller method - add a listings to DB
exports.addOne = function(listing, callback) {
  db.Listing.create(listing)
    .then(function(listing) {
      callback(201, listing);
    })
    .catch(function(error) {
      console.error(error);
      callback(404, error);
    });
};

Implement sorting Listings in Descending order (most recent top of the page)


var expr = require('vega-expression'),
    args = ['datum', 'event', 'signals'];

module.exports = expr.compiler(args, {
  idWhiteList: args,
  fieldVar:    args[0],
  globalVar:   args[2],
  functions:   function(codegen) {
    var fn = expr.functions(codegen);
    fn.eventItem = function() { return 'event.vg.item'; };
    fn.eventGroup = 'event.vg.getGroup';
    fn.eventX = 'event.vg.getX';
    fn.eventY = 'event.vg.getY';
    fn.open = 'window.open';
    return fn;
  }
});

var expr = require('vega-expression'),
    args = ['datum', 'event', 'signals'];

module.exports = expr.compiler(args, {
  idWhiteList:  args,
  fieldVar:     args[0],
  globalVar:    args[2],
  functions:    function(codegen) {
    var fn = expr.functions(codegen);
    fn.eventItem = function() { return 'event.vg.item'; };
    fn.eventGroup = 'event.vg.getGroup';
    fn.eventX = 'event.vg.getX';
    fn.eventY = 'event.vg.getY';
    fn.open = 'window.open';
    fn.inrange = 'this.defs.inrange';
    return fn;
  },
  functionDefs: function(codegen) {
    return {
      'inrange': inrange
    };
  }
});

function inrange(val, a, b, exclusive) {
  var min = a, max = b;
  if (a > b) { min = b; max = a; }
  return exclusive ?
    (min < val && max > val) :
    (min <= val && max >= val);
}

Add inrange in vega proper


#!/usr/bin/env node

var sys = require('sys');

var logger = require('../lib/logger');
var getConfig = require('./init').getConfig;

var cfg = getConfig();

if (cfg.jsonrpc.password == null) {
  logger.error('No JSON RPC password specified in configuration.');
  logger.notice('Note that you can use the --rpcpassword command line parameter.');
  process.exit(1);
}

var RpcClient = require('jsonrpc2').Client;

var rpc = new RpcClient(cfg.jsonrpc.port, cfg.jsonrpc.host,
                        cfg.jsonrpc.username, cfg.jsonrpc.password);

if (process.argv.length < 3) {
  logger.error("No RPC method specified!");
  console.log("Usage: node daemon/cli.js <rpcmethod> [rpcparam1 rpcparam2 ...]");
  process.exit(1);
}

var params = process.argv.slice(3).map(function (param) {
  return JSON.parse(param);
});

rpc.call(process.argv[2], params, function (err, result) {
  if (err) {
    logger.error("RPC Error: "+
                 (err.stack ? err.stack : sys.inspect(err)));
    process.exit(1);
  }
  sys.puts(sys.inspect(result, false, null));
});

#!/usr/bin/env node

var sys = require('sys');

var logger = require('../lib/logger');
var getConfig = require('./init').getConfig;

var cfg = getConfig();

if (cfg.jsonrpc.password == null) {
  logger.error('No JSON RPC password specified in configuration.');
  logger.notice('Note that you can use the --rpcpassword command line parameter.');
  process.exit(1);
}

var RpcClient = require('jsonrpc2').Client;

var rpc = new RpcClient(cfg.jsonrpc.port, cfg.jsonrpc.host,
                        cfg.jsonrpc.username, cfg.jsonrpc.password);

if (process.argv.length < 3) {
  logger.error("No RPC method specified!");
  console.log("Usage: node daemon/cli.js <rpcmethod> [rpcparam1 rpcparam2 ...]");
  process.exit(1);
}

var params = process.argv.slice(3).map(function (param) {
  var value;
  try {
    value = JSON.parse(param);
  } catch (e) {
    value = param;
  }
  return value;
});

rpc.call(process.argv[2], params, function (err, result) {
  if (err) {
    logger.error("RPC Error: "+
                 (err.stack ? err.stack : sys.inspect(err)));
    process.exit(1);
  }
  sys.puts(sys.inspect(result, false, null));
});

Allow RPC client to accept string arguments.


var utils = require('./util'),
    util = require("util"),
    events = require("events");

function Expirer(collection, options){
	var defaults = {
			expire_after_ms: 10 * 60 * 1000,
			time_attribute: 'touched'
		},
		me = this,
		timer;

	options = utils.merge(defaults, options || {});
	options.cleanup_interval = options.check_interval || options.expire_after_ms;

	function check_expiration(){
		var i = 0,
		    expire_time = new Date() - options.expire_after_ms,
				time_attr = options.time_attribute,
				keep_if = options.keep_if;

		for(var k in collection){
			var obj = collection[k];
			if(obj[time_attr] < expire_time && !(keep_if && keep_if(obj))){
				me.emit('expired', obj);
				delete collection[k];
			}
		}
	}

	this.pause = function(){
		if(timer) clearInterval(timer);
	}

	this.start = function(){
		if(!timer) timer = setInterval(check_expiration, options.cleanup_interval);
	}

	this.start();
}

util.inherits(Expirer, events.EventEmitter);

module.exports = Expirer;

var utils = require('./util'),
    util = require("util"),
    events = require("events");

function Expirer(collection, options){
	var defaults = {
			expire_after_ms: 10 * 60 * 1000,
			time_attribute: 'touched'
		},
		self = this,
		timer;

	options = utils.merge(defaults, options || {});
	options.cleanup_interval = options.check_interval || options.expire_after_ms;

	function check_expiration(){
		var i = 0,
		    expire_time = new Date() - options.expire_after_ms,
				time_attr = options.time_attribute,
				keep_if = options.keep_if;

		for(var k in collection){
			var obj = collection[k];
			if(obj[time_attr] < expire_time && !(keep_if && keep_if(obj))){
				self.emit('expired', obj);
				delete collection[k];
			}
		}
	}

	timer = setInterval(check_expiration, options.cleanup_interval);
}

util.inherits(Expirer, events.EventEmitter);

module.exports = Expirer;

Remove unused functions in Expirer.js


module.exports = {
  root: true,
  extends: ['coderdojo', 'eslint:recommended', 'prettier'],
  plugins: ['prettier'],
  rules: {
    'prettier/prettier': [
      'error',
      {
        singleQuote: true,
        semi: true,
        trailingComma: 'es5',
      },
    ],
  },
};

module.exports = {
  root: true,
  extends: ['coderdojo', 'eslint:recommended', 'prettier'],
  plugins: ['prettier'],
  rules: {
    'prettier/prettier': [
      'warn',
      {
        singleQuote: true,
        semi: true,
        trailingComma: 'es5',
      },
    ],
  },
};

Set prettier to only create warnings


'use strict';

module.exports = {
  root: true,
  parser: 'babel-eslint',
  parserOptions: {
    ecmaVersion: 2018,
    sourceType: 'module',
  },
  plugins: ['ember', 'prettier'],
  extends: [
    'eslint:recommended',
    'plugin:ember/recommended',
    'plugin:prettier/recommended',
  ],
  env: {
    browser: true,
  },
  rules: {
    'ember/no-new-mixins': 'off',
  },
  overrides: [
    // node files
    {
      files: [
        '.eslintrc.js',
        '.template-lintrc.js',
        '.prettierrc.js',
        'ember-cli-build.js',
        'index.js',
        'testem.js',
        'blueprints/*/index.js',
        'config/**/*.js',
        'tests/dummy/config/**/*.js',
      ],
      excludedFiles: [
        'addon/**',
        'addon-test-support/**',
        'app/**',
        'tests/dummy/app/**',
      ],
      parserOptions: {
        sourceType: 'script',
      },
      env: {
        browser: false,
        node: true,
      },
      plugins: ['node'],
      rules: Object.assign(
        {},
        require('eslint-plugin-node').configs.recommended.rules,
        {
          // add your custom rules and overrides for node files here
        }
      ),
    },
  ],
};

'use strict';

module.exports = {
  root: true,
  parser: 'babel-eslint',
  parserOptions: {
    ecmaVersion: 2018,
    sourceType: 'module',
  },
  plugins: ['ember', 'prettier'],
  extends: [
    'eslint:recommended',
    'plugin:ember/recommended',
    'plugin:prettier/recommended',
  ],
  env: {
    browser: true,
  },
  rules: {
    'ember/no-new-mixins': 'off',
    'ember/no-mixins': 'off',
    'ember/no-legacy-test-waiters': 'off',
  },
  overrides: [
    // node files
    {
      files: [
        '.eslintrc.js',
        '.template-lintrc.js',
        '.prettierrc.js',
        'ember-cli-build.js',
        'index.js',
        'testem.js',
        'blueprints/*/index.js',
        'config/**/*.js',
        'tests/dummy/config/**/*.js',
      ],
      excludedFiles: [
        'addon/**',
        'addon-test-support/**',
        'app/**',
        'tests/dummy/app/**',
      ],
      parserOptions: {
        sourceType: 'script',
      },
      env: {
        browser: false,
        node: true,
      },
      plugins: ['node'],
      rules: Object.assign(
        {},
        require('eslint-plugin-node').configs.recommended.rules,
        {
          // add your custom rules and overrides for node files here
        }
      ),
    },
  ],
};

Disable eslint rules ember/no-legacy-test-waiters and ember/no-mixins