<commit_before>/*!
 * PicturaIO
 * Licensed under the Apache License, Version 2.0
 * http://www.apache.org/licenses/LICENSE-2.0
 */

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

if (!window['__PicturaIO_Cookie__']) {

    // Prevent double script running on same page. Also note, we NEED to use
    // string as closure compiler would otherwise compile this statement badly.

    "use strict";

    (function (window, document) {

        var con = '__picturaio__',
            pfx = ';expires=0;path=/',
            val = 'dpr=' + (window.devicePixelRatio || 1) +
                  ',dw=' + Math.max(document.documentElement.clientWidth, window.innerWidth || 0) +
                  ',dh=' + Math.max(document.documentElement.clientHeight, window.innerHeight || 0);

        document.cookie = con + '=' + val + pfx;

        function canReadImage(fmt, fn) {
            var img = new Image();
            img.onload = function () {
                fn();
            };
            img.src = fmt;
        }

        var fmtList = {
            jp2: 'data:image/jp2;base64,/0//UQAyAAAAAAABAAAAAgAAAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAEBwEBBwEBBwEBBwEB/1IADAAAAAEAAAQEAAH/XAAEQED/ZAAlAAFDcmVhdGVkIGJ5IE9wZW5KUEVHIHZlcnNpb24gMi4wLjD/kAAKAAAAAABYAAH/UwAJAQAABAQAAf9dAAUBQED/UwAJAgAABAQAAf9dAAUCQED/UwAJAwAABAQAAf9dAAUDQED/k8+kEAGvz6QQAa/PpBABr994EAk//9k=',
            webp: 'data:image/webp;base64,UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA=='
        };

        canReadImage(fmtList.jp2, function () {
            val += ',jp2=1';
            document.cookie = con + '=' + val + pfx;
        });
        canReadImage(fmtList.webp, function () {
            val += ',webp=1';
            document.cookie = con + '=' + val + pfx;
        });

        window['__PicturaIO_Cookie__'] = val;

    })(this, document);

}<commit_msg>Added version attribute<commit_after>/*!
 * PicturaIO
 * Licensed under the Apache License, Version 2.0
 * http://www.apache.org/licenses/LICENSE-2.0
 */

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

if (!window['__PicturaIO_Cookie__']) {

    // Prevent double script running on same page. Also note, we NEED to use
    // string as closure compiler would otherwise compile this statement badly.

    "use strict";

    (function (window, document) {

        /**
         * The current PicturaIO client side Cookie JavaScript implementation version.
         */
        PicturaIO.Cookie.version = "${script.version}";

        var con = '__picturaio__',
            pfx = ';expires=0;path=/',
            val = 'dpr=' + (window.devicePixelRatio || 1) +
                  ',dw=' + Math.max(document.documentElement.clientWidth, window.innerWidth || 0) +
                  ',dh=' + Math.max(document.documentElement.clientHeight, window.innerHeight || 0);

        document.cookie = con + '=' + val + pfx;

        function canReadImage(fmt, fn) {
            var img = new Image();
            img.onload = function () {
                fn();
            };
            img.src = fmt;
        }

        var fmtList = {
            jp2: 'data:image/jp2;base64,/0//UQAyAAAAAAABAAAAAgAAAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAEBwEBBwEBBwEBBwEB/1IADAAAAAEAAAQEAAH/XAAEQED/ZAAlAAFDcmVhdGVkIGJ5IE9wZW5KUEVHIHZlcnNpb24gMi4wLjD/kAAKAAAAAABYAAH/UwAJAQAABAQAAf9dAAUBQED/UwAJAgAABAQAAf9dAAUCQED/UwAJAwAABAQAAf9dAAUDQED/k8+kEAGvz6QQAa/PpBABr994EAk//9k=',
            webp: 'data:image/webp;base64,UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA=='
        };

        canReadImage(fmtList.jp2, function () {
            val += ',jp2=1';
            document.cookie = con + '=' + val + pfx;
        });
        canReadImage(fmtList.webp, function () {
            val += ',webp=1';
            document.cookie = con + '=' + val + pfx;
        });

        window['__PicturaIO_Cookie__'] = val;

    })(this, document);

}<|endoftext|>


<commit_before>import {isAstMatch, extract} from '../../../utils/matchesAst';
import RequireUtilDetector from './RequireUtilDetector';
import RequireUtilInheritsDetector from './RequireUtilInheritsDetector';

/**
 * Processes nodes to detect super classes and return information for later
 * transformation.
 *
 * Detects:
 *
 *   var util = require('util');
 *   ...
 *   util.inherits(Class1, Class2);
 */
export default class UtilInherits {
  constructor() {
    this.inheritsNode = undefined;
  }

  /**
   * Process a node and return inheritance details if found.
   * @param {Object} node
   * @param {Object} parent
   * @returns {Object/undefined} m
   *                    {String}   m.className
   *                    {Node}     m.superClass
   *                    {Object[]} m.relatedExpressions
   */
  process(node, parent) {
    let m;
    if ((m = new RequireUtilDetector().detect(node)) && parent.type === 'Program') {
      this.inheritsNode = m;
    }
    else if ((m = new RequireUtilInheritsDetector().detect(node)) && parent.type === 'Program') {
      this.inheritsNode = m;
    }
    else if (this.inheritsNode && (m = this.matchUtilInherits(node))) {
      return {
        className: m.className,
        superClass: m.superClass,
        relatedExpressions: [{node, parent}]
      };
    }
  }

  // Discover usage of this.inheritsNode
  //
  // Matches: <this.utilInherits>(<className>, <superClass>);
  matchUtilInherits(node) {
    return isAstMatch(node, {
      type: 'ExpressionStatement',
      expression: {
        type: 'CallExpression',
        callee: this.inheritsNode,
        arguments: [
          {
            type: 'Identifier',
            name: extract('className')
          },
          extract('superClass')
        ]
      }
    });
  }
}
<commit_msg>Loop over all possible detectors<commit_after>import {isAstMatch, extract} from '../../../utils/matchesAst';
import RequireUtilDetector from './RequireUtilDetector';
import RequireUtilInheritsDetector from './RequireUtilInheritsDetector';

/**
 * Processes nodes to detect super classes and return information for later
 * transformation.
 *
 * Detects:
 *
 *   var util = require('util');
 *   ...
 *   util.inherits(Class1, Class2);
 */
export default class UtilInherits {
  constructor() {
    this.inheritsNode = undefined;
    this.detectors = [
      new RequireUtilDetector(),
      new RequireUtilInheritsDetector(),
    ];
  }

  /**
   * Process a node and return inheritance details if found.
   * @param {Object} node
   * @param {Object} parent
   * @returns {Object/undefined} m
   *                    {String}   m.className
   *                    {Node}     m.superClass
   *                    {Object[]} m.relatedExpressions
   */
  process(node, parent) {
    let m;
    if (parent && parent.type === 'Program' && (m = this.detectInheritsNode(node))) {
      this.inheritsNode = m;
    }
    else if (this.inheritsNode && (m = this.matchUtilInherits(node))) {
      return {
        className: m.className,
        superClass: m.superClass,
        relatedExpressions: [{node, parent}]
      };
    }
  }

  detectInheritsNode(node) {
    for (const detector of this.detectors) {
      let inheritsNode;
      if ((inheritsNode = detector.detect(node))) {
        return inheritsNode;
      }
    }
  }

  // Discover usage of this.inheritsNode
  //
  // Matches: <this.utilInherits>(<className>, <superClass>);
  matchUtilInherits(node) {
    return isAstMatch(node, {
      type: 'ExpressionStatement',
      expression: {
        type: 'CallExpression',
        callee: this.inheritsNode,
        arguments: [
          {
            type: 'Identifier',
            name: extract('className')
          },
          extract('superClass')
        ]
      }
    });
  }
}
<|endoftext|>


<commit_before>"use strict";

var Inflector = require('inflected');
var Cape = require('./utilities');

// Cape.ResourceAgent
//
// public properties:
//   form: the component that utilize this agent
//   id: the id of the resource
//   object: the object that represents the resource
//   errors: the object that holds error messages
// private properties:
//   _: the object that holds internal methods and properties of this class.
var ResourceAgent = function ResourceAgent(form, afterInitialize) {
  this.form = form;
  this.id = form.id;
  this.object = {};
  this.errors = {};
  this.headers = {
    'Accept': 'application/json',
    'Content-Type': 'application/json'
  }

  // Following codes are specific to Ruby on Rails.
  var metaElements = document.getElementsByTagName('meta');
  for (var i = metaElements.length - 1; i >= 0; i--) {
    if (metaElements[i].getAttribute('name') === 'csrf-token') {
      this.headers['X-CSRF-Token'] = metaElements[i].getAttribute('content');
      break;
    }
  }

  this._ = new _Internal(this);
  if (typeof this.init === 'function')
    this.init(afterInitialize);
  else if (typeof afterInitialize === 'function')
    afterInitialize.call(this.form, this);
};

Cape.extend(ResourceAgent.prototype, {
  init: function(afterInitialize) {
    var self = this;
    if (this.id) {
      fetch(this.memberPath(), {
        credentials: 'same-origin'
      })
      .then(function(response) {
        return response.json()
      })
      .then(function(json) {
        self.object = json[self.resourceName()];
        if (typeof afterInitialize === 'function') {
          afterInitialize.call(self.form, self);
        }
      });
    }
  },

  create: function(afterCreate) {
    if (typeof afterCreate !== 'function') {
      throw new Error("The first argument must be a function.");
    }

    var self = this;
    fetch(this.collectionPath(), {
      method: 'POST',
      headers: this.headers,
      body: JSON.stringify(this.form.paramsFor(this.resourceName())),
      credentials: 'same-origin'
    })
    .then(function(response) {
      return response.json()
    })
    .then(function(json) {
      afterCreate.call(self.form, json);
    });

    return false;
  },

  update: function(afterUpdate) {
    if (typeof afterUpdate !== 'function') {
      throw new Error("The first argument must be a function.");
    }

    var self = this;
    fetch(this.memberPath(), {
      method: 'PATCH',
      headers: this.headers,
      body: JSON.stringify(this.form.paramsFor(this.resourceName())),
      credentials: 'same-origin'
    })
    .then(function(response) {
      return response.json()
    })
    .then(function(json) {
      afterUpdate.call(self.form, json);
    });

    return false;
  },

  collectionPath: function() {
    var resources = Inflector.pluralize(this.resourceName());
    return this.pathPrefix() + resources;
  },

  memberPath: function() {
    var resources = Inflector.pluralize(this.resourceName());
    return this.pathPrefix() + resources + '/' + this.id;
  },

  pathPrefix: function() {
    return '/';
  },

  resourceName: function() {
    return Inflector.underscore(
      this.constructor.name.replace(/Agent$/, ''));
  }
});

// Internal properties of Cape.ResourceAgent
var _Internal = function _Internal(main) {
  this.main = main;
}

module.exports = ResourceAgent;
<commit_msg>Simplify fetch API usage of Cape.ResourceAgent<commit_after>"use strict";

var Inflector = require('inflected');
var Cape = require('./utilities');

// Cape.ResourceAgent
//
// public properties:
//   form: the component that utilize this agent
//   object: the object that represents the resource
//   errors: the object that holds error messages
// private properties:
//   _: the object that holds internal methods and properties of this class.
var ResourceAgent = function ResourceAgent(form) {
  this.form = form;
  this.object = {};
  this.errors = {};
  this.headers = {
    'Accept': 'application/json',
    'Content-Type': 'application/json'
  }

  // Following codes are specific to Ruby on Rails.
  var metaElements = document.getElementsByTagName('meta');
  for (var i = metaElements.length - 1; i >= 0; i--) {
    if (metaElements[i].getAttribute('name') === 'csrf-token') {
      this.headers['X-CSRF-Token'] = metaElements[i].getAttribute('content');
      break;
    }
  }

  this._ = new _Internal(this);
};

Cape.extend(ResourceAgent.prototype, {
  init: function(afterInitialize) {
    var self = this;
    if (this.form.id) {
      fetch(this.memberPath(), {
        credentials: 'same-origin'
      })
      .then(function(response) {
        var json = response.json()
        self.object = json[self.resourceName()];
        if (typeof afterInitialize === 'function') {
          afterInitialize.call(self.form, self);
        }
      })
      .catch(function(ex) {
        console.log(ex);
      });
    }
  },

  create: function(afterCreate) {
    if (typeof afterCreate !== 'function') {
      throw new Error("The first argument must be a function.");
    }

    var self = this;
    fetch(this.collectionPath(), {
      method: 'POST',
      headers: this.headers,
      body: JSON.stringify(this.form.paramsFor(this.resourceName())),
      credentials: 'same-origin'
    })
    .then(function(response) {
      afterCreate.call(self.form, response.json())
    })
    .catch(function(ex) {
      console.log(ex)
    });

    return false;
  },

  update: function(afterUpdate) {
    if (typeof afterUpdate !== 'function') {
      throw new Error("The first argument must be a function.");
    }

    var self = this;
    fetch(this.memberPath(), {
      method: 'PATCH',
      headers: this.headers,
      body: JSON.stringify(this.form.paramsFor(this.resourceName())),
      credentials: 'same-origin'
    })
    .then(function(response) {
      afterUpdate.call(self.form, response.json());
    })
    .catch(function(ex) {
      console.log(ex)
    });

    return false;
  },

  collectionPath: function() {
    var resources = Inflector.pluralize(this.resourceName());
    return this.pathPrefix() + resources;
  },

  memberPath: function() {
    var resources = Inflector.pluralize(this.resourceName());
    return this.pathPrefix() + resources + '/' + this.form.id;
  },

  pathPrefix: function() {
    return '/';
  },

  resourceName: function() {
    return Inflector.underscore(
      this.constructor.name.replace(/Agent$/, ''));
  }
});

// Internal properties of Cape.ResourceAgent
var _Internal = function _Internal(main) {
  this.main = main;
}

module.exports = ResourceAgent;
<|endoftext|>


<commit_before>/*global describe, it, expect, example, before, after, beforeEach, afterEach, mocha, sinon*/
'use strict';
var assert = require('chai').assert;
var utils = require('tests/utils');
var faker = require('faker');
var moment = require('moment');

console.log('UTC offset: ' + moment().utcOffset());

var RegistrationModal = require('js/registrationModal').ViewModel;

// Add sinon asserts to chai.assert, so we can do assert.calledWith instead of sinon.assert.calledWith
sinon.assert.expose(assert, {
    prefix: ''
});

describe('registrationModal', () => {
    sinon.collection.restore();
    var MAKE_PUBLIC = 'immediate';
    var MAKE_EMBARGO = 'embargo';

    var confirm = sinon.stub();
    var vm = new RegistrationModal(
        confirm,
        [],
        null
    );
    beforeEach(() => {
        vm = new RegistrationModal(
            confirm,
            [],
            null
        );
    });
    afterEach(() => {
        confirm.reset();
    });

    describe('#constructor', () => {
        it('takes a confirm method as a callback for bootbox success', () => {
            var args = {
                registrationChoice: vm.registrationChoice(),
                embargoEndDate: vm.embargoEndDate(),
                minimumTimeValidation: vm.minimumTimeValidation(),
                maximumTimeValidation: vm.maximumTimeValidation()
            };
            vm.register();
            assert.isTrue(confirm.calledWith(args));
        });
        it('takes an optional knockout validator', () => {
            var validate = sinon.spy();
            var instance = new RegistrationModal(
                function() {},
                [],
                {
                    validator: validate,
                    message: 'Bad, bad, bad.'
                }
            );
            var d = new Date();
            d.setDate(d.getDate() + 3);
            instance.pikaday(d);
            instance.pikaday.isValid();
            assert.isTrue(validate.called);
        });
    });
    describe('#embargoEndDate', () => {
        it('returns moment from user input', () => {
            vm.pikaday('2015-01-01');
            var date = vm.embargoEndDate();
            assert.isTrue(date instanceof moment);
        });
    });
    describe('#requestingEmbargo', () => {
        it('is true if the current registrationChoice is embargo', () => {
            vm.registrationChoice(MAKE_EMBARGO);
            assert.isTrue(vm.requestingEmbargo());
            vm.registrationChoice(MAKE_PUBLIC);
            assert.isFalse(vm.requestingEmbargo());
        });
    });
    describe('#pikaday.isValid', () => {
        it('returns true for date more than 2 days but less than 4 years in the future', () => {
            var validDate = new Date();
            validDate.setDate(validDate.getDate() + 3);
            vm.pikaday(validDate);
            assert.isTrue(vm.pikaday.isValid());
        });
        it('returns false for date less than 2 days in the future', () => {
            var invalidPastDate = new Date();
            invalidPastDate.setDate(invalidPastDate.getDate() - 2);
            vm.pikaday(invalidPastDate);
            assert.isFalse(vm.pikaday.isValid());
        });
        it('returns false for date more than 4 years in the future', () => {
            var invalidFutureDate = new Date();
            invalidFutureDate.setDate(invalidFutureDate.getDate() + 1462);
            vm.pikaday(invalidFutureDate);
            assert.isFalse(vm.pikaday.isValid());
        });
    });
    describe('#requestingEmbargo', () => {
        it('returns false if user chose make public', () => {
            vm.registrationChoice(MAKE_PUBLIC);
            assert.isFalse(vm.requestingEmbargo());
        });
        it('returns true if user chose requests embargo', () => {
            vm.registrationChoice(MAKE_EMBARGO);
            assert.isTrue(vm.requestingEmbargo());
        });
    });
    describe('#timeValidation', () => {
        it('returns true for date more than 2 days in the future', () => {
            var validDateTwoDays = new Date();
            vm.pikaday(new Date (validDateTwoDays.getTime() + 259200000)); //+3 Days
            assert.isTrue(vm.minimumTimeValidation(null, null, validDateTwoDays));
        });
        it('returns true for date less than 4 years in the future', () => {
            var validDateFourYears = new Date();
            vm.pikaday(new Date (validDateFourYears.getTime() + 126057600000)); //+1459 Days
            assert.isTrue(vm.maximumTimeValidation(null, null, validDateFourYears));
        });
        it('returns false for date less than 2 days in the future', () => {
            var invalidDateTwoDays = new Date();
            vm.pikaday(new Date (invalidDateTwoDays.getTime() + 86400000)); //+1 Day
            assert.isFalse(vm.minimumTimeValidation(null, null, invalidDateTwoDays));
        });
        it('returns false for date at least 4 years in the future', () => {
            var invalidDateFourYears = new Date();
            vm.pikaday(new Date (invalidDateFourYears.getTime() + 126144000000)); //+1460 Days
            assert.isFalse(vm.maximumTimeValidation(null, null, invalidDateFourYears));
        });
        it('returns true for date more than 2 days in the future, in a western timezone', () => {
            var validDateTwoDaysTZWest = moment().utcOffset(-4).toDate();
            validDateTwoDaysTZWest.setMinutes(validDateTwoDaysTZWest.getMinutes() - validDateTwoDaysTZWest.getTimezoneOffset());
            var validDateTZFutureWest = new Date(validDateTwoDaysTZWest);
            validDateTZFutureWest.setDate(validDateTwoDaysTZWest.getDate() + 3);
            vm.pikaday(validDateTZFutureWest);
            assert.isTrue(vm.minimumTimeValidation(null, null, validDateTwoDaysTZWest));
        });
        it('returns true for date more than 2 days in the future, in an eastern timezone', () => {
            var validDateTwoDaysTZEast = moment().utcOffset(4).toDate();
            validDateTwoDaysTZEast.setMinutes(validDateTwoDaysTZEast.getMinutes() - validDateTwoDaysTZEast.getTimezoneOffset());
            var validDateTZFutureEast = new Date(validDateTwoDaysTZEast);
            validDateTZFutureEast.setDate(validDateTwoDaysTZEast.getDate() + 3);
            vm.pikaday(validDateTZFutureEast);
            assert.isTrue(vm.minimumTimeValidation(null, null, validDateTwoDaysTZEast));
        });
        it('returns true for date less than 4 years in the future, in a western timezone', () => {
            var validDateFourYearsTZWest = moment().utcOffset(-4).toDate();
            validDateFourYearsTZWest.setMinutes(validDateFourYearsTZWest.getMinutes() - validDateFourYearsTZWest.getTimezoneOffset());
            var validDateTZFutureWest = new Date(validDateFourYearsTZWest);
            validDateTZFutureWest.setDate(validDateFourYearsTZWest.getDate() + 1459);
            vm.pikaday(validDateTZFutureWest);
            assert.isTrue(vm.maximumTimeValidation(null, null, validDateFourYearsTZWest));
        });
        it('returns true for date less than 4 years in the future, in an eastern timezone', () => {
            var validDateFourYearsTZEast = moment().utcOffset(4).toDate();
            validDateFourYearsTZEast.setMinutes(validDateFourYearsTZEast.getMinutes() - validDateFourYearsTZEast.getTimezoneOffset());
            var validDateTZFutureEastFY = new Date(validDateFourYearsTZEast);
            validDateTZFutureEastFY.setDate(validDateFourYearsTZEast.getDate() + 1459);
            vm.pikaday(validDateTZFutureEastFY);
            assert.isTrue(vm.maximumTimeValidation(null, null, validDateFourYearsTZEast));
        });
    });
});
<commit_msg>Remove debugging aid<commit_after>/*global describe, it, expect, example, before, after, beforeEach, afterEach, mocha, sinon*/
'use strict';
var assert = require('chai').assert;
var utils = require('tests/utils');
var faker = require('faker');
var moment = require('moment');

var RegistrationModal = require('js/registrationModal').ViewModel;

// Add sinon asserts to chai.assert, so we can do assert.calledWith instead of sinon.assert.calledWith
sinon.assert.expose(assert, {
    prefix: ''
});

describe('registrationModal', () => {
    sinon.collection.restore();
    var MAKE_PUBLIC = 'immediate';
    var MAKE_EMBARGO = 'embargo';

    var confirm = sinon.stub();
    var vm = new RegistrationModal(
        confirm,
        [],
        null
    );
    beforeEach(() => {
        vm = new RegistrationModal(
            confirm,
            [],
            null
        );
    });
    afterEach(() => {
        confirm.reset();
    });

    describe('#constructor', () => {
        it('takes a confirm method as a callback for bootbox success', () => {
            var args = {
                registrationChoice: vm.registrationChoice(),
                embargoEndDate: vm.embargoEndDate(),
                minimumTimeValidation: vm.minimumTimeValidation(),
                maximumTimeValidation: vm.maximumTimeValidation()
            };
            vm.register();
            assert.isTrue(confirm.calledWith(args));
        });
        it('takes an optional knockout validator', () => {
            var validate = sinon.spy();
            var instance = new RegistrationModal(
                function() {},
                [],
                {
                    validator: validate,
                    message: 'Bad, bad, bad.'
                }
            );
            var d = new Date();
            d.setDate(d.getDate() + 3);
            instance.pikaday(d);
            instance.pikaday.isValid();
            assert.isTrue(validate.called);
        });
    });
    describe('#embargoEndDate', () => {
        it('returns moment from user input', () => {
            vm.pikaday('2015-01-01');
            var date = vm.embargoEndDate();
            assert.isTrue(date instanceof moment);
        });
    });
    describe('#requestingEmbargo', () => {
        it('is true if the current registrationChoice is embargo', () => {
            vm.registrationChoice(MAKE_EMBARGO);
            assert.isTrue(vm.requestingEmbargo());
            vm.registrationChoice(MAKE_PUBLIC);
            assert.isFalse(vm.requestingEmbargo());
        });
    });
    describe('#pikaday.isValid', () => {
        it('returns true for date more than 2 days but less than 4 years in the future', () => {
            var validDate = new Date();
            validDate.setDate(validDate.getDate() + 3);
            vm.pikaday(validDate);
            assert.isTrue(vm.pikaday.isValid());
        });
        it('returns false for date less than 2 days in the future', () => {
            var invalidPastDate = new Date();
            invalidPastDate.setDate(invalidPastDate.getDate() - 2);
            vm.pikaday(invalidPastDate);
            assert.isFalse(vm.pikaday.isValid());
        });
        it('returns false for date more than 4 years in the future', () => {
            var invalidFutureDate = new Date();
            invalidFutureDate.setDate(invalidFutureDate.getDate() + 1462);
            vm.pikaday(invalidFutureDate);
            assert.isFalse(vm.pikaday.isValid());
        });
    });
    describe('#requestingEmbargo', () => {
        it('returns false if user chose make public', () => {
            vm.registrationChoice(MAKE_PUBLIC);
            assert.isFalse(vm.requestingEmbargo());
        });
        it('returns true if user chose requests embargo', () => {
            vm.registrationChoice(MAKE_EMBARGO);
            assert.isTrue(vm.requestingEmbargo());
        });
    });
    describe('#timeValidation', () => {
        it('returns true for date more than 2 days in the future', () => {
            var validDateTwoDays = new Date();
            vm.pikaday(new Date (validDateTwoDays.getTime() + 259200000)); //+3 Days
            assert.isTrue(vm.minimumTimeValidation(null, null, validDateTwoDays));
        });
        it('returns true for date less than 4 years in the future', () => {
            var validDateFourYears = new Date();
            vm.pikaday(new Date (validDateFourYears.getTime() + 126057600000)); //+1459 Days
            assert.isTrue(vm.maximumTimeValidation(null, null, validDateFourYears));
        });
        it('returns false for date less than 2 days in the future', () => {
            var invalidDateTwoDays = new Date();
            vm.pikaday(new Date (invalidDateTwoDays.getTime() + 86400000)); //+1 Day
            assert.isFalse(vm.minimumTimeValidation(null, null, invalidDateTwoDays));
        });
        it('returns false for date at least 4 years in the future', () => {
            var invalidDateFourYears = new Date();
            vm.pikaday(new Date (invalidDateFourYears.getTime() + 126144000000)); //+1460 Days
            assert.isFalse(vm.maximumTimeValidation(null, null, invalidDateFourYears));
        });
        it('returns true for date more than 2 days in the future, in a western timezone', () => {
            var validDateTwoDaysTZWest = moment().utcOffset(-4).toDate();
            validDateTwoDaysTZWest.setMinutes(validDateTwoDaysTZWest.getMinutes() - validDateTwoDaysTZWest.getTimezoneOffset());
            var validDateTZFutureWest = new Date(validDateTwoDaysTZWest);
            validDateTZFutureWest.setDate(validDateTwoDaysTZWest.getDate() + 3);
            vm.pikaday(validDateTZFutureWest);
            assert.isTrue(vm.minimumTimeValidation(null, null, validDateTwoDaysTZWest));
        });
        it('returns true for date more than 2 days in the future, in an eastern timezone', () => {
            var validDateTwoDaysTZEast = moment().utcOffset(4).toDate();
            validDateTwoDaysTZEast.setMinutes(validDateTwoDaysTZEast.getMinutes() - validDateTwoDaysTZEast.getTimezoneOffset());
            var validDateTZFutureEast = new Date(validDateTwoDaysTZEast);
            validDateTZFutureEast.setDate(validDateTwoDaysTZEast.getDate() + 3);
            vm.pikaday(validDateTZFutureEast);
            assert.isTrue(vm.minimumTimeValidation(null, null, validDateTwoDaysTZEast));
        });
        it('returns true for date less than 4 years in the future, in a western timezone', () => {
            var validDateFourYearsTZWest = moment().utcOffset(-4).toDate();
            validDateFourYearsTZWest.setMinutes(validDateFourYearsTZWest.getMinutes() - validDateFourYearsTZWest.getTimezoneOffset());
            var validDateTZFutureWest = new Date(validDateFourYearsTZWest);
            validDateTZFutureWest.setDate(validDateFourYearsTZWest.getDate() + 1459);
            vm.pikaday(validDateTZFutureWest);
            assert.isTrue(vm.maximumTimeValidation(null, null, validDateFourYearsTZWest));
        });
        it('returns true for date less than 4 years in the future, in an eastern timezone', () => {
            var validDateFourYearsTZEast = moment().utcOffset(4).toDate();
            validDateFourYearsTZEast.setMinutes(validDateFourYearsTZEast.getMinutes() - validDateFourYearsTZEast.getTimezoneOffset());
            var validDateTZFutureEastFY = new Date(validDateFourYearsTZEast);
            validDateTZFutureEastFY.setDate(validDateFourYearsTZEast.getDate() + 1459);
            vm.pikaday(validDateTZFutureEastFY);
            assert.isTrue(vm.maximumTimeValidation(null, null, validDateFourYearsTZEast));
        });
    });
});
<|endoftext|>


<commit_before>var yaja = typeof yaja === 'undefined' ? {} : yaja;
(function (window, yaja, undefined) {

// Intials: ㄱ ㄲ ㄴ ㄷ ㄸ ㄹ ㅁ ㅂ ㅃ ㅅ ㅆ ㅇ ㅈ ㅉ ㅊ ㅋ ㅌ ㅍ ㅎ
// Command codes
var NULL = 11,       // ㅇ
    TERMINATE = 18,  // ㅎ
    ADD = 3,         // ㄷ
    MULTIPLY = 4,    // ㄸ
    DIVIDE = 2,      // ㄴ
    SUBTRACT = 16,   // ㅌ
    MODULO = 5,      // ㄹ
    POP = 6,         // ㅁ
    PUSH = 7,        // ㅂ
    DUPLICATE = 8,   // ㅃ
    SWAP = 17,       // ㅍ
    SELECT = 9,      // ㅅ
    TRANSFER = 10,   // ㅆ
    COMPARE = 12,    // ㅈ
    DECIDE = 14,     // ㅊ
    COMMAND_ARITY = [0, 0, 2, 2, 2, 2, 1, 0, 1, 0, 1, 0, 2, 0, 1, 0, 2, 2, 0];

// Medials: ㅏ ㅐ ㅑ ㅒ ㅓ ㅔ ㅕ ㅖ ㅗ ㅘ ㅙ ㅚ ㅛ ㅜ ㅝ ㅞ ㅟ ㅠ ㅡ ㅢ ㅣ
// Directions (bits 0,1)
var UP = 0,
    RIGHT = 1,
    DOWN = 3,
    LEFT = 2,
    DIRECTION = 3;  // 0b000011
// Inertia and deflection (bits 2,3,4)
var FORCE = 0,
    SAME = 4,
    REFLECT = 24,
    SAME_V_REFLECT_H = 8,
    REFLECT_V_SAME_H = 20,
    INERTIA = 28;  // 0b011100
// Speed (bit 5)
var ONE = 0,
    TWO = 32;
    SPEED = 32;  // 0b100000
// Movements
var MOVEMENTS = [RIGHT | ONE, SAME, RIGHT | TWO, SAME, LEFT | ONE, SAME,
      LEFT | TWO, SAME, UP | ONE, SAME, SAME, SAME, UP | TWO, DOWN | ONE,
      SAME, SAME, SAME, DOWN | TWO, REFLECT_V_SAME_H, REFLECT, SAME_V_REFLECT_H
    ];

// Finals: null ㄱ ㄲ ㄳ ㄴ ㄵ ㄶ ㄷ ㄹ ㄺ ㄻ ㄼ ㄽ ㄾ ㄿ ㅀ ㅁ ㅂ ㅄ ㅅ ㅆ ㅇ ㅈ ㅊ ㅋ ㅌ ㅍ ㅎ
// Arguments for POP and PUSH
var INTEGER = 21,    // ㅇ
    CHARACTER = 27,  // ㅎ
    VALUES = [0, 2, 4, 4, 2, 5, 5, 3, 5, 7, 9, 9, 7, 9, 9, 8, 4, 4, 6, 2, 4,
      undefined, 3, 4, 3, 4, 4, undefined];

// Storage
var QUEUE = 21;

var NULL_INSTRUCTION = [NULL, 1, 0];

function Interpreter() {
  var self = this;
  this._buffer = [];
  this._bufferingMode = 'line-bufferred';
  this._out = {print: function () {}};
}

Interpreter.prototype.setOut = function (out, bufferingMode) {
  this._out = out;
  if (bufferingMode !== undefined) this._bufferingMode = bufferingMode;
};

Interpreter.prototype.setProgram = function (program) {
  var code = this._parse(program);
  this._process = {
    code: code,
    height: code.length,
    instructionIndex: [0, 0],
    direction: DOWN,
    speed: ONE,
    storage: this._createStorage(),
    storageIndex: 0,
    instructionCount: 0
  };
};

Interpreter.prototype.run = function (maxInstructions) {
  var process = this._process,
      code = process.code,
      data = process.storage[process.storageIndex],
      index = process.instructionIndex,
      currentInstructionCount = 0;
  if (process.height == 0) return;
  while (maxInstructions === undefined ||
         currentInstructionCount <= maxInstructions) {
    var row = code[index[0]];
        instruction = row[index[1]];
    if (instruction !== undefined) {
      var commandCode = instruction[0],
          movement = MOVEMENTS[instruction[1]],
          direction = movement & DIRECTION,
          speed = movement & SPEED,
          argumentCode = instruction[2],
          arity = COMMAND_ARITY[commandCode],
          first,
          second;
      // Update current direction
      if ((movement & INERTIA) == FORCE) {
        // Simple movement
        process.direction = direction;
        process.speed = speed;
      } else if (movement != SAME) {
        var d = process.direction;
        if (movement == REFLECT ||
            (movement == SAME_V_REFLECT_H && (d == RIGHT || d == LEFT)) ||
            (movement == REFLECT_V_SAME_H && (d == UP || d == DOWN))) {
          process.direction = ~d & DIRECTION;
        }
      }
      // Check if current storage has enough values
      if (data.length < 2 &&
          (arity == 2 || data.length < 1 && arity == 1)) {
        commandCode = NULL;
        process.direction = ~process.direction & DIRECTION;
      }
      // Get values
      if (arity >= 1) {
        if (commandCode != DUPLICATE) first = data.pop();
        if (arity == 2) second = data.pop();
      }
      // Perform command
      switch (commandCode) {
        // Arithmetic commands
        case ADD:      data.push(second + first); break;
        case MULTIPLY: data.push(second * first); break;
        case DIVIDE:   data.push(~~(second / first)); break;
        case SUBTRACT: data.push(second - first); break;
        case MODULO:   data.push(second % first); break;
        // Storage commands
        case POP:
          if (argumentCode == INTEGER) {
            this._write(first);
          } else if (argumentCode == CHARACTER) {
            this._write(String.fromCharCode(first));
          } else {
            // Invalid argument; discard value
          }
          break;
        case PUSH:
          var value;
          if (argumentCode == INTEGER) {
            value = ~~window.prompt('Enter an integer:');
          } else if (argumentCode == CHARACTER) {
            value = window.prompt('Enter a character:').charCodeAt(0);
          } else {
            value = VALUES[argumentCode];
          }
          data.push(value);
          break;
        case DUPLICATE:
          data.duplicate();
          break;
        case SWAP:
          data.push(first);
          data.push(second);
          break;
        // Miscellaneous commands
        case SELECT:
          data = process.storage[process.storageIndex = argumentCode];
          break;
        case TRANSFER:
          process.storage[argumentCode].push(first);
          break;
        case COMPARE:
          data.push(second >= first ? 1 : 0);
          break;
        case DECIDE:
          if (first == 0) process.direction = ~process.direction & DIRECTION;
          break;
        case TERMINATE:
          this._flush();
          return;
        case NULL: default:
          break;
      }
      ++process.instructionCount;
      ++currentInstructionCount;
    }
    // Move to the next instruction
    var inc = process.speed == ONE ? 1 : 2,
        width = row.length,
        x;
    switch (process.direction) {
      case UP:    x = index[0] - inc; index[0] = x < 0 ? height - 1 : x; break;
      case DOWN:  x = index[0] + inc; index[0] = x >= height ? 0 : x; break;
      case RIGHT: x = index[1] + inc; index[1] = x >= width ? 0 : x; break;
      case LEFT:  x = index[1] - inc; index[1] = x < 0 ? width - 1 : x; break;
    }
  }
  this._flush();
};

Interpreter.prototype._parse = function (program) {
  var lines = program.split('\n')
      height = lines.length,
      rows = [];
  for (var i = 0; i < height; ++i) {
    var line = lines[i],
        width = line.length,
        row = [];
    for (var j = 0; j < width; ++j) {
      row.push(this._makeInstruction(line.charCodeAt(j)));
    }
    rows.push(row);
  }
  return rows;
};

Interpreter.prototype._makeInstruction = function (charCode) {
  // '가'.charCodeAt(0) == 0xac00
  // '힣'.charCodeAt(0) == 0xd7a3
  if (charCode < 0xac00 || charCode > 0xd7a3) return NULL_INSTRUCTION;
  // ~~ (double bitwise NOT) is a fast alternative to Math.floor
  var x = charCode - 0xac00,
      argumentCode = x % 28,  // final (종성)
      y = ~~(x / 28),
      movementCode = y % 21,  // medial (중성)
      commandCode = ~~(y / 21);  // initial (초성)
  return [commandCode, movementCode, argumentCode];
};

Interpreter.prototype._createStorage = function () {
  var storage = [];
  for (var i = 0; i < 28; ++i) {
    storage[i] = i == QUEUE ? new yaja.Queue() : new yaja.Stack();
  }
  return storage;
};

Interpreter.prototype._write = function (c) {
  var bufferingMode = this._bufferingMode,
      buffer = this._buffer,
      out = this._out;
  if (bufferingMode == 'unbuffered') {
    out.print(c);
  } else {
    buffer.push(c);
    if (bufferingMode == 'line-buffered' && c == '\n') this._flush();
  }
};

Interpreter.prototype._flush = function () {
  this._out.print(this._buffer.splice(0).join(''));
};

yaja.Interpreter = Interpreter;

})(window, yaja);
<commit_msg>Use zero length instead of splice<commit_after>var yaja = typeof yaja === 'undefined' ? {} : yaja;
(function (window, yaja, undefined) {

// Intials: ㄱ ㄲ ㄴ ㄷ ㄸ ㄹ ㅁ ㅂ ㅃ ㅅ ㅆ ㅇ ㅈ ㅉ ㅊ ㅋ ㅌ ㅍ ㅎ
// Command codes
var NULL = 11,       // ㅇ
    TERMINATE = 18,  // ㅎ
    ADD = 3,         // ㄷ
    MULTIPLY = 4,    // ㄸ
    DIVIDE = 2,      // ㄴ
    SUBTRACT = 16,   // ㅌ
    MODULO = 5,      // ㄹ
    POP = 6,         // ㅁ
    PUSH = 7,        // ㅂ
    DUPLICATE = 8,   // ㅃ
    SWAP = 17,       // ㅍ
    SELECT = 9,      // ㅅ
    TRANSFER = 10,   // ㅆ
    COMPARE = 12,    // ㅈ
    DECIDE = 14,     // ㅊ
    COMMAND_ARITY = [0, 0, 2, 2, 2, 2, 1, 0, 1, 0, 1, 0, 2, 0, 1, 0, 2, 2, 0];

// Medials: ㅏ ㅐ ㅑ ㅒ ㅓ ㅔ ㅕ ㅖ ㅗ ㅘ ㅙ ㅚ ㅛ ㅜ ㅝ ㅞ ㅟ ㅠ ㅡ ㅢ ㅣ
// Directions (bits 0,1)
var UP = 0,
    RIGHT = 1,
    DOWN = 3,
    LEFT = 2,
    DIRECTION = 3;  // 0b000011
// Inertia and deflection (bits 2,3,4)
var FORCE = 0,
    SAME = 4,
    REFLECT = 24,
    SAME_V_REFLECT_H = 8,
    REFLECT_V_SAME_H = 20,
    INERTIA = 28;  // 0b011100
// Speed (bit 5)
var ONE = 0,
    TWO = 32;
    SPEED = 32;  // 0b100000
// Movements
var MOVEMENTS = [RIGHT | ONE, SAME, RIGHT | TWO, SAME, LEFT | ONE, SAME,
      LEFT | TWO, SAME, UP | ONE, SAME, SAME, SAME, UP | TWO, DOWN | ONE,
      SAME, SAME, SAME, DOWN | TWO, REFLECT_V_SAME_H, REFLECT, SAME_V_REFLECT_H
    ];

// Finals: null ㄱ ㄲ ㄳ ㄴ ㄵ ㄶ ㄷ ㄹ ㄺ ㄻ ㄼ ㄽ ㄾ ㄿ ㅀ ㅁ ㅂ ㅄ ㅅ ㅆ ㅇ ㅈ ㅊ ㅋ ㅌ ㅍ ㅎ
// Arguments for POP and PUSH
var INTEGER = 21,    // ㅇ
    CHARACTER = 27,  // ㅎ
    VALUES = [0, 2, 4, 4, 2, 5, 5, 3, 5, 7, 9, 9, 7, 9, 9, 8, 4, 4, 6, 2, 4,
      undefined, 3, 4, 3, 4, 4, undefined];

// Storage
var QUEUE = 21;

var NULL_INSTRUCTION = [NULL, 1, 0];

function Interpreter() {
  var self = this;
  this._buffer = [];
  this._bufferingMode = 'line-bufferred';
  this._out = {print: function () {}};
}

Interpreter.prototype.setOut = function (out, bufferingMode) {
  this._out = out;
  if (bufferingMode !== undefined) this._bufferingMode = bufferingMode;
};

Interpreter.prototype.setProgram = function (program) {
  var code = this._parse(program);
  this._process = {
    code: code,
    height: code.length,
    instructionIndex: [0, 0],
    direction: DOWN,
    speed: ONE,
    storage: this._createStorage(),
    storageIndex: 0,
    instructionCount: 0
  };
};

Interpreter.prototype.run = function (maxInstructions) {
  var process = this._process,
      code = process.code,
      data = process.storage[process.storageIndex],
      index = process.instructionIndex,
      currentInstructionCount = 0;
  if (process.height == 0) return;
  while (maxInstructions === undefined ||
         currentInstructionCount <= maxInstructions) {
    var row = code[index[0]];
        instruction = row[index[1]];
    if (instruction !== undefined) {
      var commandCode = instruction[0],
          movement = MOVEMENTS[instruction[1]],
          direction = movement & DIRECTION,
          speed = movement & SPEED,
          argumentCode = instruction[2],
          arity = COMMAND_ARITY[commandCode],
          first,
          second;
      // Update current direction
      if ((movement & INERTIA) == FORCE) {
        // Simple movement
        process.direction = direction;
        process.speed = speed;
      } else if (movement != SAME) {
        var d = process.direction;
        if (movement == REFLECT ||
            (movement == SAME_V_REFLECT_H && (d == RIGHT || d == LEFT)) ||
            (movement == REFLECT_V_SAME_H && (d == UP || d == DOWN))) {
          process.direction = ~d & DIRECTION;
        }
      }
      // Check if current storage has enough values
      if (data.length < 2 &&
          (arity == 2 || data.length < 1 && arity == 1)) {
        commandCode = NULL;
        process.direction = ~process.direction & DIRECTION;
      }
      // Get values
      if (arity >= 1) {
        if (commandCode != DUPLICATE) first = data.pop();
        if (arity == 2) second = data.pop();
      }
      // Perform command
      switch (commandCode) {
        // Arithmetic commands
        case ADD:      data.push(second + first); break;
        case MULTIPLY: data.push(second * first); break;
        case DIVIDE:   data.push(~~(second / first)); break;
        case SUBTRACT: data.push(second - first); break;
        case MODULO:   data.push(second % first); break;
        // Storage commands
        case POP:
          if (argumentCode == INTEGER) {
            this._write(first);
          } else if (argumentCode == CHARACTER) {
            this._write(String.fromCharCode(first));
          } else {
            // Invalid argument; discard value
          }
          break;
        case PUSH:
          var value;
          if (argumentCode == INTEGER) {
            value = ~~window.prompt('Enter an integer:');
          } else if (argumentCode == CHARACTER) {
            value = window.prompt('Enter a character:').charCodeAt(0);
          } else {
            value = VALUES[argumentCode];
          }
          data.push(value);
          break;
        case DUPLICATE:
          data.duplicate();
          break;
        case SWAP:
          data.push(first);
          data.push(second);
          break;
        // Miscellaneous commands
        case SELECT:
          data = process.storage[process.storageIndex = argumentCode];
          break;
        case TRANSFER:
          process.storage[argumentCode].push(first);
          break;
        case COMPARE:
          data.push(second >= first ? 1 : 0);
          break;
        case DECIDE:
          if (first == 0) process.direction = ~process.direction & DIRECTION;
          break;
        case TERMINATE:
          this._flush();
          return;
        case NULL: default:
          break;
      }
      ++process.instructionCount;
      ++currentInstructionCount;
    }
    // Move to the next instruction
    var inc = process.speed == ONE ? 1 : 2,
        width = row.length,
        x;
    switch (process.direction) {
      case UP:    x = index[0] - inc; index[0] = x < 0 ? height - 1 : x; break;
      case DOWN:  x = index[0] + inc; index[0] = x >= height ? 0 : x; break;
      case RIGHT: x = index[1] + inc; index[1] = x >= width ? 0 : x; break;
      case LEFT:  x = index[1] - inc; index[1] = x < 0 ? width - 1 : x; break;
    }
  }
  this._flush();
};

Interpreter.prototype._parse = function (program) {
  var lines = program.split('\n')
      height = lines.length,
      rows = [];
  for (var i = 0; i < height; ++i) {
    var line = lines[i],
        width = line.length,
        row = [];
    for (var j = 0; j < width; ++j) {
      row.push(this._makeInstruction(line.charCodeAt(j)));
    }
    rows.push(row);
  }
  return rows;
};

Interpreter.prototype._makeInstruction = function (charCode) {
  // '가'.charCodeAt(0) == 0xac00
  // '힣'.charCodeAt(0) == 0xd7a3
  if (charCode < 0xac00 || charCode > 0xd7a3) return NULL_INSTRUCTION;
  // ~~ (double bitwise NOT) is a fast alternative to Math.floor
  var x = charCode - 0xac00,
      argumentCode = x % 28,  // final (종성)
      y = ~~(x / 28),
      movementCode = y % 21,  // medial (중성)
      commandCode = ~~(y / 21);  // initial (초성)
  return [commandCode, movementCode, argumentCode];
};

Interpreter.prototype._createStorage = function () {
  var storage = [];
  for (var i = 0; i < 28; ++i) {
    storage[i] = i == QUEUE ? new yaja.Queue() : new yaja.Stack();
  }
  return storage;
};

Interpreter.prototype._write = function (c) {
  var bufferingMode = this._bufferingMode,
      buffer = this._buffer,
      out = this._out;
  if (bufferingMode == 'unbuffered') {
    out.print(c);
  } else {
    buffer.push(c);
    if (bufferingMode == 'line-buffered' && c == '\n') this._flush();
  }
};

Interpreter.prototype._flush = function () {
  this._out.print(this._buffer.join(''));
  this._buffer.length = 0;
};

yaja.Interpreter = Interpreter;

})(window, yaja);
<|endoftext|>


<commit_before>var _ = require('lodash');
var Store = require('store-prototype');
var request = require('superagent');

var RSVPStore = new Store();

var loaded = false;
var busy = false;
var meetup = {};
var rsvp = {};
var attendees = [];

var REFRESH_INTERVAL = 5000; // 5 seconds

var refreshTimeout = null;
function cancelRefresh() {
	clearTimeout(refreshTimeout);
}

RSVPStore.extend({

	getMeetup: function() {
		return meetup;
	},

	getRSVP: function() {
		return rsvp;
	},

	getAttendees: function(callback) {
		return attendees;
	},

	rsvp: function(attending, callback) {
		if (busy) return;
		cancelRefresh();
		busy = true;
		RSVPStore.notifyChange();
		request
			.post('/api/me/meetup')
			.send({ data: {
				meetup: SydJS.currentMeetupId,
				attending: attending
			}})
			.end(function(err, res) {
				if (err) {
					console.log('Error with the AJAX request: ', err)
					return;
				}
				RSVPStore.getMeetupData();
			});
	},

	isLoaded: function() {
		return loaded;
	},

	isBusy: function() {
		return busy;
	},

	getMeetupData: function(callback) {
		// ensure any scheduled refresh is stopped,
		// in case this was called directly
		cancelRefresh();
		// request the update from the API
		busy = true;
		request
			.get('/api/meetup/' + SydJS.currentMeetupId)
			.end(function(err, res) {
				if (err) {
					console.log('Error with the AJAX request: ', err)
				}
				if (!err && res.body) {
					loaded = true;
					meetup = res.body.meetup;
					rsvp = res.body.rsvp;
					attendees = res.body.attendees;
					RSVPStore.notifyChange();
				}
				busy = false;
				RSVPStore.queueMeetupRefresh();
				return callback && callback(err, res.body);
			});
	},

	queueMeetupRefresh: function() {
		refreshTimeout = setTimeout(RSVPStore.getMeetupData, REFRESH_INTERVAL);
	}

});

RSVPStore.getMeetupData();
module.exports = RSVPStore;
<commit_msg>Busy needs to be reset before notifyChange() is called<commit_after>var _ = require('lodash');
var Store = require('store-prototype');
var request = require('superagent');

var RSVPStore = new Store();

var loaded = false;
var busy = false;
var meetup = {};
var rsvp = {};
var attendees = [];

var REFRESH_INTERVAL = 5000; // 5 seconds

var refreshTimeout = null;
function cancelRefresh() {
	clearTimeout(refreshTimeout);
}

RSVPStore.extend({

	getMeetup: function() {
		return meetup;
	},

	getRSVP: function() {
		return rsvp;
	},

	getAttendees: function(callback) {
		return attendees;
	},

	rsvp: function(attending, callback) {
		if (busy) return;
		cancelRefresh();
		busy = true;
		RSVPStore.notifyChange();
		request
			.post('/api/me/meetup')
			.send({ data: {
				meetup: SydJS.currentMeetupId,
				attending: attending
			}})
			.end(function(err, res) {
				if (err) {
					console.log('Error with the AJAX request: ', err)
					return;
				}
				RSVPStore.getMeetupData();
			});
	},

	isLoaded: function() {
		return loaded;
	},

	isBusy: function() {
		return busy;
	},

	getMeetupData: function(callback) {
		// ensure any scheduled refresh is stopped,
		// in case this was called directly
		cancelRefresh();
		// request the update from the API
		busy = true;
		request
			.get('/api/meetup/' + SydJS.currentMeetupId)
			.end(function(err, res) {
				if (err) {
					console.log('Error with the AJAX request: ', err)
				}
				busy = false;
				if (!err && res.body) {
					loaded = true;
					meetup = res.body.meetup;
					rsvp = res.body.rsvp;
					attendees = res.body.attendees;
					RSVPStore.notifyChange();
				}
				RSVPStore.queueMeetupRefresh();
				return callback && callback(err, res.body);
			});
	},

	queueMeetupRefresh: function() {
		refreshTimeout = setTimeout(RSVPStore.getMeetupData, REFRESH_INTERVAL);
	}

});

RSVPStore.getMeetupData();
module.exports = RSVPStore;
<|endoftext|>


<commit_before>var levelup = require('levelup')
var sublevel = require('sublevelup')
var transaction = require('level-transactions')
var ginga = require('ginga')
var H = require('highland')
var xtend = require('xtend')
var ltgt = require('ltgt')
var inherits = require('util').inherits
var EventEmitter = require('events').EventEmitter
var extract = require('./util/extract')
var sort = require('./util/sort')
var group = require('./util/group')
var similarity = require('./util/similarity')

var END = '\uffff'

var defaults = {
  db: process.browser ? require('level-js') : require('leveldown'),
  ttl: 60 * 1000, // transaction ttl
  fields: { '*': true }
}
var override = {
  keyEncoding: 'utf8',
  valueEncoding: 'json'
}

function Levi (dir, opts) {
  if (!(this instanceof Levi)) return new Levi(dir, opts)
  opts = xtend(defaults, opts, override)

  var db = typeof dir === 'string'
    ? sublevel(levelup(dir, opts))
    : sublevel(dir, opts)

  this.options = db.options

  // meta: size -> N
  // store: key -> value
  // tokens: key -> tokens
  // weight: token! -> nt
  // weight: token!key -> tf

  this.store = db
  this.meta = db.sublevel('mt')
  this.tokens = db.sublevel('tk')
  this.weight = db.sublevel('wt')

  EventEmitter.call(this)
  this.setMaxListeners(Infinity)
}

// Pipeline plugins
Levi.tokenizer = require('./tokenizer')
Levi.stemmer = require('./stemmer')
Levi.stopword = require('./stopword')

function countTokens (tokens) {
  var counts = {}
  for (var i = 0, l = tokens.length; i < l; i++) {
    // avoid conflict with obj reserved words
    var key = '@' + tokens[i]
    counts[key] = (counts[key] || 0) + 1
  }
  return counts
}
// ginga params middleware factory
function params () {
  var names = Array.prototype.slice.call(arguments)
  var len = names.length
  return function (ctx) {
    var l = Math.min(ctx.args.length, len)
    for (var i = 0; i < l; i++) ctx[names[i]] = ctx.args[i]
  }
}

inherits(Levi, EventEmitter)
Levi.fn = ginga(Levi.prototype)

Levi.fn.define('get', params('key', 'options'), function (ctx, done) {
  this.store.get(ctx.key, xtend(this.options, ctx.options), done)
})

var RESERVED = /!/g

Levi.fn.define('pipeline', params('tokens', 'field'), function (ctx, next) {
  // init with highland stream
  try {
    ctx.tokens = H(extract(ctx.tokens))
    next()
  } catch (e) {
    next(e)
  }
}, function (ctx, done) {
  // finalize with trimming and padding
  H(ctx.tokens)
  .reject(function (token) {
    return typeof token !== 'string' || token.trim() === ''
  })
  .map(function (token) {
    // prevent conflict with reserved words
    return token.replace(RESERVED, '')
  })
  .collect()
  .pull(done)
})

function pre (ctx, next) {
  ctx.options = xtend(this.options, ctx.options)

  // prepare transaction
  ctx.tx = transaction(this.store)
  // tx increment
  ctx.tx.incr = function (key, options, cb) {
    this.get(key, options, function (err, val) {
      if (err && !err.notFound) return next(err)
      val = (val || 0) + 1
      this.put(key, val, options)
      if (cb) cb(null, val)
    })
  }
  // tx decrement
  ctx.tx.decr = function (key, options, cb) {
    this.get(key, options, function (err, val) {
      if (err && !err.notFound) return next(err)
      val = (val || 0) - 1
      if (val === 0) this.del(key, options)
      else this.put(key, val, options)
      if (cb) cb(null, val)
    })
  }
  ctx.on('end', function (err) {
    if (err) ctx.tx.rollback(err)
  })

  // key check
  if (
    ctx.key === '' ||
    ctx.key === null ||
    ctx.key === undefined
  ) return next(new Error('Key required.'))
  ctx.key = String(ctx.key)

  next()
}

// clean up old index
function del (ctx, next) {
  var self = this
  if (!ctx.value) {
    ctx.on('end', function (err) {
      if (err) return
      self.emit('del', { key: ctx.key })
    })
  }
  ctx.tx.get(ctx.key, function (err, value) {
    if (err && !err.notFound) return next(err)
    // skip if not exists
    if (!value) return next()
    // keep value if reindexing
    if (!ctx.value) ctx.value = value
    // del store item
    ctx.tx.del(ctx.key)
    // decrement size
    ctx.tx.decr('size', { prefix: self.meta })
    // delete all tfs that contains key
    ctx.tx.get(ctx.key, { prefix: self.tokens }, function (err, tokens) {
      if (err) return next(err)
      tokens.forEach(function (token) {
        // decrement nt
        ctx.tx.decr(token + '!', { prefix: self.weight })
        // del tf
        ctx.tx.del(token + '!' + ctx.key, { prefix: self.weight, unsafe: true })
      })
      // delete tokens
      ctx.tx.del(ctx.key, { prefix: self.tokens, unsafe: true })
      next()
    })
  })
}

// put new index
function put (ctx, next) {
  var self = this

  if (!ctx.value) return next(new Error('Value required.'))
  if (Buffer.isBuffer(ctx.value)) {
    ctx.value = ctx.value.toString('binary')
  }

  // result for emitter
  var result = {
    key: ctx.key,
    value: ctx.value,
    nts: {}
  }
  ctx.on('end', function (err) {
    if (err) return
    self.emit('put', { key: ctx.key, value: ctx.value })
    self.emit('_write', result)
  })

  // increment size
  ctx.tx.incr('size', { prefix: self.meta }, function (err, val) {
    if (err) return next(err)
    result.size = val
  })
  // put store item
  ctx.tx.put(ctx.key, ctx.value)

  var value
  if (typeof ctx.value === 'string' || Array.isArray(ctx.value)) {
    // string means no field boost
    value = { '*': ctx.value }
  } else {
    value = ctx.value
  }

  var tfs = {}

  // init tf all fields zero
  var tfInit = {}
  for (var field in value) tfInit[field] = 0

  H(Object.keys(value))
  .map(H.wrapCallback(function (field, cb) {
    self.pipeline(value[field], field, function (err, tokens) {
      if (err) return cb(err)
      var total = tokens.length
      var counts = countTokens(tokens)
      for (var token in counts) {
        tfs[token] = tfs[token] || xtend(tfInit)
        tfs[token][field] = counts[token] / total
      }
      cb(null, field)
    })
  }))
  .series()
  .collect()
  .pull(function (err, fields) {
    if (err) return next(err)
    var uniqs = Object.keys(tfs)
    uniqs.forEach(function (token) {
      // increment nt
      ctx.tx.incr(token + '!', { prefix: self.weight }, function (err, val) {
        if (err) return next(err)
        result.nts[token] = val
      })
      // put tf object
      ctx.tx.put(token + '!' + ctx.key, tfs[token], {
        prefix: self.weight, unsafe: true
      })
    })
    result.tfs = tfs
    // put tokens
    ctx.tx.put(ctx.key, uniqs, { prefix: self.tokens, unsafe: true })

    next()
  })
}

// commit index write
function write (ctx, done) {
  ctx.tx.commit(done)
}

Levi.fn.define('del', params('key', 'options'), pre, del, write)
Levi.fn.define('put', params('key', 'value', 'options'), pre, del, put, write)
Levi.fn.define('reindex', params('key', 'options'), pre, del, put, write)

Levi.fn.createReadStream =
Levi.fn.readStream = function (opts) {
  return H(this.store.createReadStream(
    xtend(this.options, opts)
  ))
}

Levi.fn.createSearchStream =
Levi.fn.searchStream = function (q, opts) {
  opts = xtend(this.options, opts)
  var self = this
  var values = opts.values !== false
  var fields = opts.fields || { '*': 1 }
  var offset = Number(opts.offset) > 0 ? opts.offset : 0
  var limit = Number(opts.limit) > 0 ? opts.limit : Infinity

  var N
  var query = {}

  return H(function (push, next) {
    // pipeline query
    self.pipeline(q, function (err, tokens) {
      if (err) return push(err)
      var total = tokens.length
      var counts = countTokens(tokens)
      var uniqs = Object.keys(counts)
      uniqs.forEach(function (token) {
        // calculate tf for query
        query[token] = counts[token] / total
      })
      // get N
      self.meta.get('size', function (err, size) {
        if (err && !err.notFound) return push(err)
        if (!size) return next(H([]))
        N = size
        next(H(uniqs))
      })
    })
  })
  .map(function (token) {
    var idf
    var len = token.length + 1 // token! length
    var startKey = token + '!'

    var ltgtOpts = {}
    if ('gt' in opts) ltgtOpts.gt = startKey + String(opts.gt)
    else if ('gte' in opts) ltgtOpts.gte = startKey + String(opts.gte)
    else ltgtOpts.gte = startKey

    if ('lt' in opts) ltgtOpts.lt = startKey + String(opts.lt)
    else if ('lte' in opts) ltgtOpts.lte = startKey + String(opts.lte)
    else ltgtOpts.lt = startKey + END

    // scan through nt and tf of token.
    return H(function (push, next) {
      if (ltgt.lowerBound(ltgtOpts) === startKey && !ltgt.lowerBoundExclusive(ltgtOpts)) {
        // grab nt and tf in 1 scan
        next(H(
          self.weight.createReadStream(ltgtOpts)
        ))
      } else {
        // custom lower bound exists, have to grab nt separately
        self.weight.get(startKey, function (err, nt) {
          if (err && !err.notFound) return push(err)

          if (nt) push(null, {key: startKey, value: nt})

          next(H(
            self.weight.createReadStream(ltgtOpts)
          ))
        })
      }
    })
    .map(function (data) {
      if (data.key === startKey) {
        // retrieve nt, tfidf for query
        var nt = data.value || 0
        // idf smooth
        idf = Math.log(1 + N / nt)

        // tfidf for query
        query[token] *= idf
      } else {
        // retrieve tf, tfidf with field boost for result
        // tfidf group by fields
        var tfidf = {}
        for (var field in data.value) {
          var tf = data.value[field]
          tfidf[field] = tf * idf
        }
        return {
          token: token,
          key: data.key.slice(len),
          tfidf: tfidf
        }
      }
    })
    .filter(function (data) {
      return !!data
    })
  })
  .reduce1(sort) // merge sort since weight sorted by key
  .series()
  .through(group) // group by key
  .map(function (data) {
    // tfidf group by fields, tokens
    // tfidf[field] becomes term vector
    var tfidf = {}
    data.forEach(function (item) {
      for (var field in item.tfidf) {
        tfidf[field] = tfidf[field] || {}
        tfidf[field][item.token] = item.tfidf[field]
      }
    })
    var boostTotal = 0
    var score = 0
    for (var field in tfidf) {
      var boost = Number(fields[field] || fields['*']) || 0
      score += similarity(query, tfidf[field]) * boost
      boostTotal += boost
    }
    if (boostTotal > 0) {
      score /= boostTotal // normalize
    }
    return {
      key: data[0].key,
      score: score
    }
  })
  .filter(function (data) {
    return data.score > 0
  })
  .sortBy(function (a, b) {
    // sort by score desc
    // todo optimize this
    return b.score - a.score
  })
  .drop(offset)
  .take(limit)
  .map(H.wrapCallback(function (doc, cb) {
    if (values) {
      // fetch value if values true
      self.get(doc.key, function (err, val) {
        if (err && !err.notFound) return cb(err)
        doc.value = val
        cb(null, doc)
      })
    } else {
      cb(null, doc)
    }
  }))
  .series()
}

Levi.fn.createLiveStream =
Levi.fn.liveStream = function (q, opts) {
  opts = xtend(this.options, opts)
  var self = this
  var values = opts.values !== false
  var fields = opts.fields || { '*': 1 }

  var live = H('_write', this)
  var query = {}
  var nts = {}

  return H(function (push, next) {
    // pipeline query
    self.pipeline(q, function (err, tokens) {
      if (err) return push(err)
      var total = tokens.length
      var counts = countTokens(tokens)
      var uniqs = Object.keys(counts)
      uniqs.forEach(function (token) {
        // calculate tf for query
        query[token] = counts[token] / total
      })
      // cache initial nt for query
      H(uniqs)
      .map(H.wrapCallback(function (token, cb) {
        self.weight.get(token + '!', function (err, nt) {
          if (err && !err.notFound) return push(err)
          nts[token] = nt || 0
          cb()
        })
      }))
      .series()
      .done(function () {
        next(live)
      })
    })
  })
  .map(function (data) {
    // clone query coz idfs are different on every updates
    var xquery = xtend(query)
    var field
    // tfidf group by fields, tokens
    // tfidf[field] becomes term vector
    var tfidf = {}
    for (var token in query) {
      var idf, nt
      var N = data.size
      if (token in data.nts) {
        nt = data.nts[token]
        nts[token] = nt // update cached nt
        idf = Math.log(1 + N / nt)
        for (field in data.tfs[token]) {
          var tf = data.tfs[token][field]
          tfidf[field] = tfidf[field] || {}
          tfidf[field][token] = tf * idf
        }
        xquery[token] *= idf
      } else {
        // use cached nt if not exists
        nt = nts[token]
        idf = Math.log(1 + N / nt)
        xquery[token] *= idf
      }
    }

    if (!ltgt.contains(opts, data.key)) return null

    var boostTotal = 0
    var score = 0
    for (field in tfidf) {
      var boost = Number(fields[field] || fields['*']) || 0
      score += similarity(xquery, tfidf[field]) * boost
      boostTotal += boost
    }
    if (boostTotal > 0) {
      score /= boostTotal // normalize
    }
    return {
      key: data.key,
      score: score,
      value: values ? data.value : undefined
    }
  })
  .filter(function (data) {
    return data && data.score > 0
  })
}

module.exports = Levi
<commit_msg>prepare unsafe param<commit_after>var levelup = require('levelup')
var sublevel = require('sublevelup')
var transaction = require('level-transactions')
var ginga = require('ginga')
var H = require('highland')
var xtend = require('xtend')
var ltgt = require('ltgt')
var inherits = require('util').inherits
var EventEmitter = require('events').EventEmitter
var extract = require('./util/extract')
var sort = require('./util/sort')
var group = require('./util/group')
var similarity = require('./util/similarity')

var END = '\uffff'

var defaults = {
  db: process.browser ? require('level-js') : require('leveldown'),
  ttl: 60 * 1000, // transaction ttl
  fields: { '*': true }
}
var override = {
  keyEncoding: 'utf8',
  valueEncoding: 'json'
}

function Levi (dir, opts) {
  if (!(this instanceof Levi)) return new Levi(dir, opts)
  opts = xtend(defaults, opts, override)

  var db = typeof dir === 'string'
    ? sublevel(levelup(dir, opts))
    : sublevel(dir, opts)

  this.options = db.options

  // meta: size -> N
  // store: key -> value
  // tokens: key -> tokens
  // weight: token! -> nt
  // weight: token!key -> tf

  this.store = db
  this.meta = db.sublevel('mt')
  this.tokens = db.sublevel('tk')
  this.weight = db.sublevel('wt')

  EventEmitter.call(this)
  this.setMaxListeners(Infinity)
}

// Pipeline plugins
Levi.tokenizer = require('./tokenizer')
Levi.stemmer = require('./stemmer')
Levi.stopword = require('./stopword')

function countTokens (tokens) {
  var counts = {}
  for (var i = 0, l = tokens.length; i < l; i++) {
    // avoid conflict with obj reserved words
    var key = '@' + tokens[i]
    counts[key] = (counts[key] || 0) + 1
  }
  return counts
}
// ginga params middleware factory
function params () {
  var names = Array.prototype.slice.call(arguments)
  var len = names.length
  return function (ctx) {
    var l = Math.min(ctx.args.length, len)
    for (var i = 0; i < l; i++) ctx[names[i]] = ctx.args[i]
  }
}

inherits(Levi, EventEmitter)
Levi.fn = ginga(Levi.prototype)

Levi.fn.define('get', params('key', 'options'), function (ctx, done) {
  this.store.get(ctx.key, xtend(this.options, ctx.options), done)
})

var RESERVED = /!/g

Levi.fn.define('pipeline', params('tokens', 'field'), function (ctx, next) {
  // init with highland stream
  try {
    ctx.tokens = H(extract(ctx.tokens))
    next()
  } catch (e) {
    next(e)
  }
}, function (ctx, done) {
  // finalize with trimming and padding
  H(ctx.tokens)
  .reject(function (token) {
    return typeof token !== 'string' || token.trim() === ''
  })
  .map(function (token) {
    // prevent conflict with reserved words
    return token.replace(RESERVED, '')
  })
  .collect()
  .pull(done)
})

function pre (ctx, next) {
  ctx.options = xtend(this.options, ctx.options)

  // prepare transaction
  ctx.tx = transaction(this.store)
  // tx increment
  ctx.tx.incr = function (key, options, cb) {
    this.get(key, options, function (err, val) {
      if (err && !err.notFound) return next(err)
      options = xtend(options, {unsafe: true})
      val = (val || 0) + 1
      this.put(key, val, options)
      if (cb) cb(null, val)
    })
  }
  // tx decrement
  ctx.tx.decr = function (key, options, cb) {
    this.get(key, options, function (err, val) {
      if (err && !err.notFound) return next(err)
      options = xtend(options, {unsafe: true})
      val = (val || 0) - 1
      if (val === 0) this.del(key, options)
      else this.put(key, val, options)
      if (cb) cb(null, val)
    })
  }
  ctx.on('end', function (err) {
    if (err) ctx.tx.rollback(err)
  })

  // key check
  if (
    ctx.key === '' ||
    ctx.key === null ||
    ctx.key === undefined
  ) return next(new Error('Key required.'))
  ctx.key = String(ctx.key)

  next()
}

// clean up old index
function del (ctx, next) {
  var self = this
  if (!ctx.value) {
    ctx.on('end', function (err) {
      if (err) return
      self.emit('del', { key: ctx.key })
    })
  }
  ctx.tx.get(ctx.key, function (err, value) {
    if (err && !err.notFound) return next(err)
    // skip if not exists
    if (!value) return next()
    // keep value if reindexing
    if (!ctx.value) ctx.value = value
    // del store item
    ctx.tx.del(ctx.key)
    // decrement size
    ctx.tx.decr('size', { prefix: self.meta })
    // delete all tfs that contains key
    ctx.tx.get(ctx.key, { prefix: self.tokens }, function (err, tokens) {
      if (err) return next(err)
      tokens.forEach(function (token) {
        // decrement nt
        ctx.tx.decr(token + '!', { prefix: self.weight })
        // del tf
        ctx.tx.del(token + '!' + ctx.key, { prefix: self.weight, unsafe: true })
      })
      // delete tokens
      ctx.tx.del(ctx.key, { prefix: self.tokens, unsafe: true })
      next()
    })
  })
}

// put new index
function put (ctx, next) {
  var self = this

  if (!ctx.value) return next(new Error('Value required.'))
  if (Buffer.isBuffer(ctx.value)) {
    ctx.value = ctx.value.toString('binary')
  }

  // result for emitter
  var result = {
    key: ctx.key,
    value: ctx.value,
    nts: {}
  }
  ctx.on('end', function (err) {
    if (err) return
    self.emit('put', { key: ctx.key, value: ctx.value })
    self.emit('_write', result)
  })

  // increment size
  ctx.tx.incr('size', { prefix: self.meta }, function (err, val) {
    if (err) return next(err)
    result.size = val
  })
  // put store item
  ctx.tx.put(ctx.key, ctx.value)

  var value
  if (typeof ctx.value === 'string' || Array.isArray(ctx.value)) {
    // string means no field boost
    value = { '*': ctx.value }
  } else {
    value = ctx.value
  }

  var tfs = {}

  // init tf all fields zero
  var tfInit = {}
  for (var field in value) tfInit[field] = 0

  H(Object.keys(value))
  .map(H.wrapCallback(function (field, cb) {
    self.pipeline(value[field], field, function (err, tokens) {
      if (err) return cb(err)
      var total = tokens.length
      var counts = countTokens(tokens)
      for (var token in counts) {
        tfs[token] = tfs[token] || xtend(tfInit)
        tfs[token][field] = counts[token] / total
      }
      cb(null, field)
    })
  }))
  .series()
  .collect()
  .pull(function (err, fields) {
    if (err) return next(err)
    var uniqs = Object.keys(tfs)
    uniqs.forEach(function (token) {
      // increment nt
      ctx.tx.incr(token + '!', { prefix: self.weight }, function (err, val) {
        if (err) return next(err)
        result.nts[token] = val
      })
      // put tf object
      ctx.tx.put(token + '!' + ctx.key, tfs[token], {
        prefix: self.weight, unsafe: true
      })
    })
    result.tfs = tfs
    // put tokens
    ctx.tx.put(ctx.key, uniqs, { prefix: self.tokens, unsafe: true })

    next()
  })
}

// commit index write
function write (ctx, done) {
  ctx.tx.commit(done)
}

Levi.fn.define('del', params('key', 'options'), pre, del, write)
Levi.fn.define('put', params('key', 'value', 'options'), pre, del, put, write)
Levi.fn.define('reindex', params('key', 'options'), pre, del, put, write)

Levi.fn.createReadStream =
Levi.fn.readStream = function (opts) {
  return H(this.store.createReadStream(
    xtend(this.options, opts)
  ))
}

Levi.fn.createSearchStream =
Levi.fn.searchStream = function (q, opts) {
  opts = xtend(this.options, opts)
  var self = this
  var values = opts.values !== false
  var fields = opts.fields || { '*': 1 }
  var offset = Number(opts.offset) > 0 ? opts.offset : 0
  var limit = Number(opts.limit) > 0 ? opts.limit : Infinity

  var N
  var query = {}

  return H(function (push, next) {
    // pipeline query
    self.pipeline(q, function (err, tokens) {
      if (err) return push(err)
      var total = tokens.length
      var counts = countTokens(tokens)
      var uniqs = Object.keys(counts)
      uniqs.forEach(function (token) {
        // calculate tf for query
        query[token] = counts[token] / total
      })
      // get N
      self.meta.get('size', function (err, size) {
        if (err && !err.notFound) return push(err)
        if (!size) return next(H([]))
        N = size
        next(H(uniqs))
      })
    })
  })
  .map(function (token) {
    var idf
    var len = token.length + 1 // token! length
    var startKey = token + '!'

    var ltgtOpts = {}
    if ('gt' in opts) ltgtOpts.gt = startKey + String(opts.gt)
    else if ('gte' in opts) ltgtOpts.gte = startKey + String(opts.gte)
    else ltgtOpts.gte = startKey

    if ('lt' in opts) ltgtOpts.lt = startKey + String(opts.lt)
    else if ('lte' in opts) ltgtOpts.lte = startKey + String(opts.lte)
    else ltgtOpts.lt = startKey + END

    // scan through nt and tf of token.
    return H(function (push, next) {
      if (ltgt.lowerBound(ltgtOpts) === startKey && !ltgt.lowerBoundExclusive(ltgtOpts)) {
        // grab nt and tf in 1 scan
        next(H(
          self.weight.createReadStream(ltgtOpts)
        ))
      } else {
        // custom lower bound exists, have to grab nt separately
        self.weight.get(startKey, function (err, nt) {
          if (err && !err.notFound) return push(err)

          if (nt) push(null, {key: startKey, value: nt})

          next(H(
            self.weight.createReadStream(ltgtOpts)
          ))
        })
      }
    })
    .map(function (data) {
      if (data.key === startKey) {
        // retrieve nt, tfidf for query
        var nt = data.value || 0
        // idf smooth
        idf = Math.log(1 + N / nt)

        // tfidf for query
        query[token] *= idf
      } else {
        // retrieve tf, tfidf with field boost for result
        // tfidf group by fields
        var tfidf = {}
        for (var field in data.value) {
          var tf = data.value[field]
          tfidf[field] = tf * idf
        }
        return {
          token: token,
          key: data.key.slice(len),
          tfidf: tfidf
        }
      }
    })
    .filter(function (data) {
      return !!data
    })
  })
  .reduce1(sort) // merge sort since weight sorted by key
  .series()
  .through(group) // group by key
  .map(function (data) {
    // tfidf group by fields, tokens
    // tfidf[field] becomes term vector
    var tfidf = {}
    data.forEach(function (item) {
      for (var field in item.tfidf) {
        tfidf[field] = tfidf[field] || {}
        tfidf[field][item.token] = item.tfidf[field]
      }
    })
    var boostTotal = 0
    var score = 0
    for (var field in tfidf) {
      var boost = Number(fields[field] || fields['*']) || 0
      score += similarity(query, tfidf[field]) * boost
      boostTotal += boost
    }
    if (boostTotal > 0) {
      score /= boostTotal // normalize
    }
    return {
      key: data[0].key,
      score: score
    }
  })
  .filter(function (data) {
    return data.score > 0
  })
  .sortBy(function (a, b) {
    // sort by score desc
    // todo optimize this
    return b.score - a.score
  })
  .drop(offset)
  .take(limit)
  .map(H.wrapCallback(function (doc, cb) {
    if (values) {
      // fetch value if values true
      self.get(doc.key, function (err, val) {
        if (err && !err.notFound) return cb(err)
        doc.value = val
        cb(null, doc)
      })
    } else {
      cb(null, doc)
    }
  }))
  .series()
}

Levi.fn.createLiveStream =
Levi.fn.liveStream = function (q, opts) {
  opts = xtend(this.options, opts)
  var self = this
  var values = opts.values !== false
  var fields = opts.fields || { '*': 1 }

  var live = H('_write', this)
  var query = {}
  var nts = {}

  return H(function (push, next) {
    // pipeline query
    self.pipeline(q, function (err, tokens) {
      if (err) return push(err)
      var total = tokens.length
      var counts = countTokens(tokens)
      var uniqs = Object.keys(counts)
      uniqs.forEach(function (token) {
        // calculate tf for query
        query[token] = counts[token] / total
      })
      // cache initial nt for query
      H(uniqs)
      .map(H.wrapCallback(function (token, cb) {
        self.weight.get(token + '!', function (err, nt) {
          if (err && !err.notFound) return push(err)
          nts[token] = nt || 0
          cb()
        })
      }))
      .series()
      .done(function () {
        next(live)
      })
    })
  })
  .map(function (data) {
    // clone query coz idfs are different on every updates
    var xquery = xtend(query)
    var field
    // tfidf group by fields, tokens
    // tfidf[field] becomes term vector
    var tfidf = {}
    for (var token in query) {
      var idf, nt
      var N = data.size
      if (token in data.nts) {
        nt = data.nts[token]
        nts[token] = nt // update cached nt
        idf = Math.log(1 + N / nt)
        for (field in data.tfs[token]) {
          var tf = data.tfs[token][field]
          tfidf[field] = tfidf[field] || {}
          tfidf[field][token] = tf * idf
        }
        xquery[token] *= idf
      } else {
        // use cached nt if not exists
        nt = nts[token]
        idf = Math.log(1 + N / nt)
        xquery[token] *= idf
      }
    }

    if (!ltgt.contains(opts, data.key)) return null

    var boostTotal = 0
    var score = 0
    for (field in tfidf) {
      var boost = Number(fields[field] || fields['*']) || 0
      score += similarity(xquery, tfidf[field]) * boost
      boostTotal += boost
    }
    if (boostTotal > 0) {
      score /= boostTotal // normalize
    }
    return {
      key: data.key,
      score: score,
      value: values ? data.value : undefined
    }
  })
  .filter(function (data) {
    return data && data.score > 0
  })
}

module.exports = Levi
<|endoftext|>


<commit_before>var StyleCache = function (prefix) {
	this.prefix = prefix + 'C-'
	this.style = document.createElement('style')
	document.head.appendChild(this.style)
	this.total = 0
	this.stats = {}
	this.classes = {}
	this.classes_total = 0
}

StyleCache.prototype.constructor = StyleCache

StyleCache.prototype.add = function(rule) {
	this.stats[rule] = (this.stats[rule] || 0) + 1
	++this.total
}

StyleCache.prototype.register = function(rules) {
	var key = rules.join('+')
	var classes = this.classes
	var cls = classes[key]
	if (cls !== undefined)
		return cls

	var cls = classes[key] = this.prefix + this.classes_total++
	this.style.appendChild(document.createTextNode(cls + '{' + rules.join(';') + '}'))
	return cls
}

StyleCache.prototype.classify = function(rules) {
	var total = this.total
	if (total < 100) //fixme: initial population threshold
		return ''

	rules.sort() //mind vendor prefixes!
	var n = rules.length
	var classes = []
	var classified = []
	var hot = []
	var self = this
	rules.forEach(function(rule, idx) {
		var hits = self.stats[rule]
		var usage = hits
		if (usage > 100) { //fixme: usage threshold
			classified.push(rule)
			hot.push(idx)
		}
	})
	if (!hot.length)
		return ''
	hot.forEach(function(offset, idx) {
		rules.splice(offset - idx, 1)
	})
	return self.register(classified)
}

var registerGenericListener = function(target) {
	var copyArguments = _globals.core.copyArguments
	var prefix = '_domEventHandler_'
	target.onListener('',
		function(name) {
			//log('registering generic event', name)
			var pname = prefix + name
			var callback = target[pname] = function() {
				var args = copyArguments(arguments, 0, name)
				target.emit.apply(target, args)
			}
			target.dom.addEventListener(name, callback)
		},
		function(name) {
			//log('removing generic event', name)
			var pname = prefix + name
			target.dom.removeEventListener(name, target[pname])
		}
	)
}

/**
 * @constructor
 */

exports.Element = function(context, dom) {
	if (!context._styleCache)
		context._styleCache = new StyleCache(context._prefix)

	_globals.core.EventEmitter.apply(this)
	this._context = context
	this.dom = dom
	this._fragment = []
	this._styles = {}

	registerGenericListener(this)
}

exports.Element.prototype = Object.create(_globals.core.EventEmitter.prototype)
exports.Element.prototype.constructor = exports.Element

exports.Element.prototype.addClass = function(cls) {
	this.dom.classList.add(cls)
}

exports.Element.prototype.setHtml = function(html) {
	var dom = this.dom
	this._fragment.forEach(function(node) { dom.removeChild(node) })
	this._fragment = []

	if (html === '')
		return

	var fragment = document.createDocumentFragment()
	var temp = document.createElement('div')

	temp.innerHTML = html
	while (temp.firstChild) {
		this._fragment.push(temp.firstChild)
		fragment.appendChild(temp.firstChild)
	}
	dom.appendChild(fragment)
	return dom.children
}

exports.Element.prototype.width = function() {
	return this.dom.clientWidth
}

exports.Element.prototype.height = function() {
	return this.dom.clientHeight
}

exports.Element.prototype.fullWidth = function() {
	return this.dom.scrollWidth
}

exports.Element.prototype.fullHeight = function() {
	return this.dom.scrollHeight
}

exports.Element.prototype.style = function(name, style) {
	if (style !== undefined) {
		if (style !== '') //fixme: replace it with explicit 'undefined' syntax
			this._styles[name] = style
		else
			delete this._styles[name]
		this.updateStyle()
	} else if (name instanceof Object) { //style({ }) assignment
		for(var k in name) {
			var value = name[k]
			if (value !== '') //fixme: replace it with explicit 'undefined' syntax
				this._styles[k] = value
			else
				delete this._styles[k]
		}
		this.updateStyle()
	}
	else
		return this._styles[name]
}

exports.Element.prototype.setAttribute = function(name, value) {
	this.dom.setAttribute(name, value)
}

exports.Element.prototype.updateStyle = function() {
	var element = this.dom
	if (!element)
		return

	/** @const */
	var cssUnits = {
		'left': 'px',
		'top': 'px',
		'width': 'px',
		'height': 'px',

		'border-radius': 'px',
		'border-width': 'px',

		'margin-left': 'px',
		'margin-top': 'px',
		'margin-right': 'px',
		'margin-bottom': 'px',

		'padding-left': 'px',
		'padding-top': 'px',
		'padding-right': 'px',
		'padding-bottom': 'px'
	}

	var cache = this._context._styleCache
	var rules = []
	for(var name in this._styles) {
		var value = this._styles[name]

		var prefixedName = this._context.getPrefixedName(name)
		var ruleName = prefixedName !== false? prefixedName: name
		if (Array.isArray(value))
			value = value.join(',')

		var unit = (typeof value === 'number')? cssUnits[name] || '': ''
		value += unit

		//var prefixedValue = window.Modernizr.prefixedCSSValue(name, value)
		//var prefixedValue = value
		var rule = ruleName + ':' + value //+ (prefixedValue !== false? prefixedValue: value)
		cache.add(rule)

		rules.push(rule)
	}
	var cls = cache.classify(rules)
	if (cls.length > 0 && this._class !== cls) {
		var classList = this.dom.classList
		if (this._class !== undefined)
			classList.remove(this._class)
		this._class = cls
		classList.add(cls)
	}
	this.dom.setAttribute('style', rules.join(';'))
}

exports.Element.prototype.append = function(el) {
	this.dom.appendChild((el instanceof exports.Element)? el.dom: el)
}

exports.Element.prototype.discard = function() {
	_globals.core.EventEmitter.prototype.discard.apply(this)
	this.remove()
}

exports.Element.prototype.remove = function() {
	var dom = this.dom
	dom.parentNode.removeChild(dom)
}

exports.Window = function(context, dom) {
	_globals.core.EventEmitter.apply(this)
	this._context = context
	this.dom = dom

	registerGenericListener(this)
}

exports.Window.prototype = Object.create(_globals.core.EventEmitter.prototype)
exports.Window.prototype.constructor = exports.Window

exports.Window.prototype.width = function() {
	return this.dom.innerWidth
}

exports.Window.prototype.height = function() {
	return this.dom.innerHeight
}

exports.Window.prototype.scrollY = function() {
	return this.dom.scrollY
}

exports.getElement = function(tag) {
	var tags = document.getElementsByTagName(tag)
	if (tags.length != 1)
		throw new Error('no tag ' + tag + '/multiple tags')
	return new exports.Element(this, tags[0])
}
<commit_msg>require at least 2 rules for classification<commit_after>var StyleCache = function (prefix) {
	this.prefix = prefix + 'C-'
	this.style = document.createElement('style')
	document.head.appendChild(this.style)
	this.total = 0
	this.stats = {}
	this.classes = {}
	this.classes_total = 0
}

StyleCache.prototype.constructor = StyleCache

StyleCache.prototype.add = function(rule) {
	this.stats[rule] = (this.stats[rule] || 0) + 1
	++this.total
}

StyleCache.prototype.register = function(rules) {
	var key = rules.join('+')
	var classes = this.classes
	var cls = classes[key]
	if (cls !== undefined)
		return cls

	var cls = classes[key] = this.prefix + this.classes_total++
	this.style.appendChild(document.createTextNode(cls + '{' + rules.join(';') + '}'))
	return cls
}

StyleCache.prototype.classify = function(rules) {
	var total = this.total
	if (total < 100) //fixme: initial population threshold
		return ''

	rules.sort() //mind vendor prefixes!
	var n = rules.length
	var classes = []
	var classified = []
	var hot = []
	var self = this
	rules.forEach(function(rule, idx) {
		var hits = self.stats[rule]
		var usage = hits
		if (usage > 100) { //fixme: usage threshold
			classified.push(rule)
			hot.push(idx)
		}
	})
	if (hot.length < 2)
		return ''
	hot.forEach(function(offset, idx) {
		rules.splice(offset - idx, 1)
	})
	return self.register(classified)
}

var registerGenericListener = function(target) {
	var copyArguments = _globals.core.copyArguments
	var prefix = '_domEventHandler_'
	target.onListener('',
		function(name) {
			//log('registering generic event', name)
			var pname = prefix + name
			var callback = target[pname] = function() {
				var args = copyArguments(arguments, 0, name)
				target.emit.apply(target, args)
			}
			target.dom.addEventListener(name, callback)
		},
		function(name) {
			//log('removing generic event', name)
			var pname = prefix + name
			target.dom.removeEventListener(name, target[pname])
		}
	)
}

/**
 * @constructor
 */

exports.Element = function(context, dom) {
	if (!context._styleCache)
		context._styleCache = new StyleCache(context._prefix)

	_globals.core.EventEmitter.apply(this)
	this._context = context
	this.dom = dom
	this._fragment = []
	this._styles = {}

	registerGenericListener(this)
}

exports.Element.prototype = Object.create(_globals.core.EventEmitter.prototype)
exports.Element.prototype.constructor = exports.Element

exports.Element.prototype.addClass = function(cls) {
	this.dom.classList.add(cls)
}

exports.Element.prototype.setHtml = function(html) {
	var dom = this.dom
	this._fragment.forEach(function(node) { dom.removeChild(node) })
	this._fragment = []

	if (html === '')
		return

	var fragment = document.createDocumentFragment()
	var temp = document.createElement('div')

	temp.innerHTML = html
	while (temp.firstChild) {
		this._fragment.push(temp.firstChild)
		fragment.appendChild(temp.firstChild)
	}
	dom.appendChild(fragment)
	return dom.children
}

exports.Element.prototype.width = function() {
	return this.dom.clientWidth
}

exports.Element.prototype.height = function() {
	return this.dom.clientHeight
}

exports.Element.prototype.fullWidth = function() {
	return this.dom.scrollWidth
}

exports.Element.prototype.fullHeight = function() {
	return this.dom.scrollHeight
}

exports.Element.prototype.style = function(name, style) {
	if (style !== undefined) {
		if (style !== '') //fixme: replace it with explicit 'undefined' syntax
			this._styles[name] = style
		else
			delete this._styles[name]
		this.updateStyle()
	} else if (name instanceof Object) { //style({ }) assignment
		for(var k in name) {
			var value = name[k]
			if (value !== '') //fixme: replace it with explicit 'undefined' syntax
				this._styles[k] = value
			else
				delete this._styles[k]
		}
		this.updateStyle()
	}
	else
		return this._styles[name]
}

exports.Element.prototype.setAttribute = function(name, value) {
	this.dom.setAttribute(name, value)
}

exports.Element.prototype.updateStyle = function() {
	var element = this.dom
	if (!element)
		return

	/** @const */
	var cssUnits = {
		'left': 'px',
		'top': 'px',
		'width': 'px',
		'height': 'px',

		'border-radius': 'px',
		'border-width': 'px',

		'margin-left': 'px',
		'margin-top': 'px',
		'margin-right': 'px',
		'margin-bottom': 'px',

		'padding-left': 'px',
		'padding-top': 'px',
		'padding-right': 'px',
		'padding-bottom': 'px'
	}

	var cache = this._context._styleCache
	var rules = []
	for(var name in this._styles) {
		var value = this._styles[name]

		var prefixedName = this._context.getPrefixedName(name)
		var ruleName = prefixedName !== false? prefixedName: name
		if (Array.isArray(value))
			value = value.join(',')

		var unit = (typeof value === 'number')? cssUnits[name] || '': ''
		value += unit

		//var prefixedValue = window.Modernizr.prefixedCSSValue(name, value)
		//var prefixedValue = value
		var rule = ruleName + ':' + value //+ (prefixedValue !== false? prefixedValue: value)
		cache.add(rule)

		rules.push(rule)
	}
	var cls = cache.classify(rules)
	if (cls.length > 0 && this._class !== cls) {
		var classList = this.dom.classList
		if (this._class !== undefined)
			classList.remove(this._class)
		this._class = cls
		classList.add(cls)
	}
	this.dom.setAttribute('style', rules.join(';'))
}

exports.Element.prototype.append = function(el) {
	this.dom.appendChild((el instanceof exports.Element)? el.dom: el)
}

exports.Element.prototype.discard = function() {
	_globals.core.EventEmitter.prototype.discard.apply(this)
	this.remove()
}

exports.Element.prototype.remove = function() {
	var dom = this.dom
	dom.parentNode.removeChild(dom)
}

exports.Window = function(context, dom) {
	_globals.core.EventEmitter.apply(this)
	this._context = context
	this.dom = dom

	registerGenericListener(this)
}

exports.Window.prototype = Object.create(_globals.core.EventEmitter.prototype)
exports.Window.prototype.constructor = exports.Window

exports.Window.prototype.width = function() {
	return this.dom.innerWidth
}

exports.Window.prototype.height = function() {
	return this.dom.innerHeight
}

exports.Window.prototype.scrollY = function() {
	return this.dom.scrollY
}

exports.getElement = function(tag) {
	var tags = document.getElementsByTagName(tag)
	if (tags.length != 1)
		throw new Error('no tag ' + tag + '/multiple tags')
	return new exports.Element(this, tags[0])
}
<|endoftext|>


<commit_before>var categories = [];

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

function loadTable() {
    $('#overlay').show();
    var heskUrl = $('p#hesk-path').text();
    var $tableBody = $('#table-body');

    $.ajax({
        method: 'GET',
        url: heskUrl + 'api/index.php/v1/categories/all',
        headers: { 'X-Internal-Call': true },
        success: function(data) {
            $tableBody.html('');

            if (data.length === 0) {
                mfhAlert.error("I couldn't find any categories :(", "No categories found");
                $('#overlay').hide();
                return;
            }

            var totalNumberOfTickets = 0;
            $.each(data, function() {
                totalNumberOfTickets += this.numberOfTickets;
            });

            var first = true;
            var lastElement = null;
            $.each(data, function() {
                var $template = $($('#category-row-template').html());

                $template.find('span[data-property="id"]').text(this.id).attr('data-value', this.id);
                $template.find('span[data-property="category-name"]').text(this.name);
                var $priority = $template.find('span[data-property="priority"]');
                if (this.priority === 0) {
                    // Critical
                    $priority.text(mfhLang.text('critical')).addClass('critical');
                } else if (this.priority === 1) {
                    // High
                    $priority.text(mfhLang.text('high')).addClass('important');
                } else if (this.priority === 2) {
                    // Medium
                    $priority.text(mfhLang.text('medium')).addClass('medium');
                } else {
                    // Low
                    $priority.text(mfhLang.text('low')).addClass('normal');
                }
                $template.find('a[data-property="number-of-tickets"]')
                    .text(this.numberOfTickets)
                    .attr('href', '#' + this.numberOfTickets);
                var percentText = mfhLang.text('perat');
                var percentage = Math.round(this.numberOfTickets / totalNumberOfTickets * 100);
                $template.find('div.progress').attr('title', percentText.replace('%s', percentage + '%'));
                $template.find('div.progress-bar').attr('aria-value-now', percentage).css('width', percentage + '%');

                if (this.usage === 1) {
                    // Tickets only
                    $template.find('.fa-calendar').removeClass('fa-calendar');
                } else if (this.usage === 2) {
                    // Events only
                    $template.find('.fa-ticket').removeClass('fa-ticket');
                }

                // TODO Action buttons

                $tableBody.append($template);

                categories[this.id] = this;

                lastElement = this;
            });

            if (lastElement) {
                //-- Hide the down arrow on the last element
                $('[data-value="' + lastElement.id + '"]').parent().parent()
                    .find('[data-direction="down"]').css('visibility', 'hidden');
            }
        },
        error: function(data) {
            mfhAlert.errorWithLog(mfhLang.text('Something bad happened...'), data.responseJSON);
            console.error(data);
        },
        complete: function() {
            $('#overlay').hide();
        }
    });
}<commit_msg>Hide the up arrow for the top-most element<commit_after>var categories = [];

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

function loadTable() {
    $('#overlay').show();
    var heskUrl = $('p#hesk-path').text();
    var $tableBody = $('#table-body');

    $.ajax({
        method: 'GET',
        url: heskUrl + 'api/index.php/v1/categories/all',
        headers: { 'X-Internal-Call': true },
        success: function(data) {
            $tableBody.html('');

            if (data.length === 0) {
                mfhAlert.error("I couldn't find any categories :(", "No categories found");
                $('#overlay').hide();
                return;
            }

            var totalNumberOfTickets = 0;
            $.each(data, function() {
                totalNumberOfTickets += this.numberOfTickets;
            });

            var first = true;
            var lastElement = null;
            $.each(data, function() {
                var $template = $($('#category-row-template').html());

                $template.find('span[data-property="id"]').text(this.id).attr('data-value', this.id);
                $template.find('span[data-property="category-name"]').text(this.name);
                var $priority = $template.find('span[data-property="priority"]');
                if (this.priority === 0) {
                    // Critical
                    $priority.text(mfhLang.text('critical')).addClass('critical');
                } else if (this.priority === 1) {
                    // High
                    $priority.text(mfhLang.text('high')).addClass('important');
                } else if (this.priority === 2) {
                    // Medium
                    $priority.text(mfhLang.text('medium')).addClass('medium');
                } else {
                    // Low
                    $priority.text(mfhLang.text('low')).addClass('normal');
                }
                $template.find('a[data-property="number-of-tickets"]')
                    .text(this.numberOfTickets)
                    .attr('href', '#' + this.numberOfTickets);
                var percentText = mfhLang.text('perat');
                var percentage = Math.round(this.numberOfTickets / totalNumberOfTickets * 100);
                $template.find('div.progress').attr('title', percentText.replace('%s', percentage + '%'));
                $template.find('div.progress-bar').attr('aria-value-now', percentage).css('width', percentage + '%');

                if (this.usage === 1) {
                    // Tickets only
                    $template.find('.fa-calendar').removeClass('fa-calendar');
                } else if (this.usage === 2) {
                    // Events only
                    $template.find('.fa-ticket').removeClass('fa-ticket');
                }

                // TODO Action buttons

                $tableBody.append($template);

                categories[this.id] = this;

                lastElement = this;

                if (first) {
                    $template.find('[data-direction="up"]').css('visibility', 'hidden');
                    first = false;
                }
            });

            if (lastElement) {
                //-- Hide the down arrow on the last element
                $('[data-value="' + lastElement.id + '"]').parent().parent()
                    .find('[data-direction="down"]').css('visibility', 'hidden');
            }
        },
        error: function(data) {
            mfhAlert.errorWithLog(mfhLang.text('Something bad happened...'), data.responseJSON);
            console.error(data);
        },
        complete: function() {
            $('#overlay').hide();
        }
    });
}<|endoftext|>