<commit_before>/*
Copyright 2020 The Matrix.org Foundation C.I.C.

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.
*/

import RoomViewStore from "../../../../src/stores/RoomViewStore";
import {createMessageContent} from "../../../../src/components/views/rooms/SendMessageComposer";
import EditorModel from "../../../../src/editor/model";
import {createPartCreator, createRenderer} from "../../../editor/mock";

jest.mock("../../../../src/stores/RoomViewStore");

describe('<SendMessageComposer/>', () => {
    describe("createMessageContent", () => {
        RoomViewStore.getQuotingEvent.mockReturnValue(false);
        const permalinkCreator = jest.fn();

        it("sends plaintext messages correctly", () => {
            const model = new EditorModel([], createPartCreator(), createRenderer());
            model.update("hello world", "insertText", {offset: 11, atNodeEnd: true});

            const content = createMessageContent(model, permalinkCreator);

            expect(content).toEqual({
                body: "hello world",
                msgtype: "m.text",
            });
        });

        it("sends markdown messages correctly", () => {
            const model = new EditorModel([], createPartCreator(), createRenderer());
            model.update("hello *world*", "insertText", {offset: 13, atNodeEnd: true});

            const content = createMessageContent(model, permalinkCreator);

            expect(content).toEqual({
                body: "hello *world*",
                msgtype: "m.text",
                format: "org.matrix.custom.html",
                formatted_body: "hello world",
            });
        });

        it("strips /me from messages and marks them as m.emote accordingly", () => {
            const model = new EditorModel([], createPartCreator(), createRenderer());
            model.update("/me blinks __quickly__", "insertText", {offset: 22, atNodeEnd: true});

            const content = createMessageContent(model, permalinkCreator);

            expect(content).toEqual({
                body: "blinks __quickly__",
                msgtype: "m.emote",
                format: "org.matrix.custom.html",
                formatted_body: "blinks quickly",
            });
        });

        it("allows sending double-slash escaped slash commands correctly", () => {
            const model = new EditorModel([], createPartCreator(), createRenderer());
            model.update("//dev/null is my favourite place", "insertText", {offset: 32, atNodeEnd: true});

            const content = createMessageContent(model, permalinkCreator);

            expect(content).toEqual({
                body: "/dev/null is my favourite place",
                msgtype: "m.text",
            });
        });
    });
});

<commit_msg>Write Enzyme tests for SendMessageComposer state/history persistence behaviour<commit_after>/*
Copyright 2020 The Matrix.org Foundation C.I.C.

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.
*/

import Adapter from "enzyme-adapter-react-16";
import { configure, mount } from "enzyme";
import React from "react";
import {act} from "react-dom/test-utils";

import SendMessageComposer, {createMessageContent} from "../../../../src/components/views/rooms/SendMessageComposer";
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
import EditorModel from "../../../../src/editor/model";
import {createPartCreator, createRenderer} from "../../../editor/mock";
import {createTestClient, mkEvent, mkStubRoom} from "../../../test-utils";
import BasicMessageComposer from "../../../../src/components/views/rooms/BasicMessageComposer";
import {MatrixClientPeg} from "../../../../src/MatrixClientPeg";
import {sleep} from "../../../../src/utils/promise";
import SpecPermalinkConstructor from "../../../../src/utils/permalinks/SpecPermalinkConstructor";
import defaultDispatcher from "../../../../src/dispatcher/dispatcher";

jest.mock("../../../../src/stores/RoomViewStore");

configure({ adapter: new Adapter() });

describe('<SendMessageComposer/>', () => {
    describe("createMessageContent", () => {
        const permalinkCreator = jest.fn();

        it("sends plaintext messages correctly", () => {
            const model = new EditorModel([], createPartCreator(), createRenderer());
            model.update("hello world", "insertText", {offset: 11, atNodeEnd: true});

            const content = createMessageContent(model, permalinkCreator);

            expect(content).toEqual({
                body: "hello world",
                msgtype: "m.text",
            });
        });

        it("sends markdown messages correctly", () => {
            const model = new EditorModel([], createPartCreator(), createRenderer());
            model.update("hello *world*", "insertText", {offset: 13, atNodeEnd: true});

            const content = createMessageContent(model, permalinkCreator);

            expect(content).toEqual({
                body: "hello *world*",
                msgtype: "m.text",
                format: "org.matrix.custom.html",
                formatted_body: "hello world",
            });
        });

        it("strips /me from messages and marks them as m.emote accordingly", () => {
            const model = new EditorModel([], createPartCreator(), createRenderer());
            model.update("/me blinks __quickly__", "insertText", {offset: 22, atNodeEnd: true});

            const content = createMessageContent(model, permalinkCreator);

            expect(content).toEqual({
                body: "blinks __quickly__",
                msgtype: "m.emote",
                format: "org.matrix.custom.html",
                formatted_body: "blinks quickly",
            });
        });

        it("allows sending double-slash escaped slash commands correctly", () => {
            const model = new EditorModel([], createPartCreator(), createRenderer());
            model.update("//dev/null is my favourite place", "insertText", {offset: 32, atNodeEnd: true});

            const content = createMessageContent(model, permalinkCreator);

            expect(content).toEqual({
                body: "/dev/null is my favourite place",
                msgtype: "m.text",
            });
        });
    });

    describe("functions correctly mounted", () => {
        const mockClient = MatrixClientPeg.matrixClient = createTestClient();
        const mockRoom = mkStubRoom();
        const mockEvent = mkEvent({
            type: "m.room.message",
            content: "Replying to this",
            event: true,
        });
        mockRoom.findEventById = jest.fn(eventId => {
            return eventId === mockEvent.getId() ? mockEvent : null;
        });

        const spyDispatcher = jest.spyOn(defaultDispatcher, "dispatch");

        beforeEach(() => {
            localStorage.clear();
            spyDispatcher.mockReset();
        });

        it("renders text and placeholder correctly", () => {
            const wrapper = mount(<MatrixClientContext.Provider value={mockClient}>
                <SendMessageComposer
                    room={mockRoom}
                    placeholder="placeholder string"
                    permalinkCreator={new SpecPermalinkConstructor()}
                />
            </MatrixClientContext.Provider>);

            expect(wrapper.find('[aria-label="placeholder string"]')).toHaveLength(1);

            act(() => {
                wrapper.find(BasicMessageComposer).instance().insertText("Test Text");
                wrapper.update();
            });

            expect(wrapper.text()).toBe("Test Text");
        });

        it("correctly persists state to and from localStorage", () => {
            const wrapper = mount(<MatrixClientContext.Provider value={mockClient}>
                <SendMessageComposer
                    room={mockRoom}
                    placeholder=""
                    permalinkCreator={new SpecPermalinkConstructor()}
                    replyToEvent={mockEvent}
                />
            </MatrixClientContext.Provider>);

            act(() => {
                wrapper.find(BasicMessageComposer).instance().insertText("Test Text");
                wrapper.update();
            });

            const key = wrapper.find(SendMessageComposer).instance()._editorStateKey;

            expect(wrapper.text()).toBe("Test Text");
            expect(localStorage.getItem(key)).toBeNull();

            // ensure the right state was persisted to localStorage
            wrapper.unmount();
            expect(JSON.parse(localStorage.getItem(key))).toStrictEqual({
                parts: [{"type": "plain", "text": "Test Text"}],
                replyEventId: mockEvent.getId(),
            });

            // ensure the correct model is re-loaded
            wrapper.mount();
            expect(wrapper.text()).toBe("Test Text");
            expect(spyDispatcher).toHaveBeenCalledWith({
                action: "reply_to_event",
                event: mockEvent,
            });

            // now try with localStorage wiped out
            wrapper.unmount();
            localStorage.removeItem(key);
            wrapper.mount();
            expect(wrapper.text()).toBe("");
        });

        it("persists state correctly without replyToEvent onbeforeunload", () => {
            const wrapper = mount(<MatrixClientContext.Provider value={mockClient}>
                <SendMessageComposer
                    room={mockRoom}
                    placeholder=""
                    permalinkCreator={new SpecPermalinkConstructor()}
                />
            </MatrixClientContext.Provider>);

            act(() => {
                wrapper.find(BasicMessageComposer).instance().insertText("Hello World");
                wrapper.update();
            });

            const key = wrapper.find(SendMessageComposer).instance()._editorStateKey;

            expect(wrapper.text()).toBe("Hello World");
            expect(localStorage.getItem(key)).toBeNull();

            // ensure the right state was persisted to localStorage
            window.dispatchEvent(new Event('beforeunload'));
            expect(JSON.parse(localStorage.getItem(key))).toStrictEqual({
                parts: [{"type": "plain", "text": "Hello World"}],
            });
        });

        it("persists to session history upon sending", async () => {
            const wrapper = mount(<MatrixClientContext.Provider value={mockClient}>
                <SendMessageComposer
                    room={mockRoom}
                    placeholder="placeholder"
                    permalinkCreator={new SpecPermalinkConstructor()}
                    replyToEvent={mockEvent}
                />
            </MatrixClientContext.Provider>);

            act(() => {
                wrapper.find(BasicMessageComposer).instance().insertText("This is a message");
                wrapper.find(".mx_SendMessageComposer").simulate("keydown", { key: "Enter" });
                wrapper.update();
            });
            await sleep(10); // await the async _sendMessage
            wrapper.update();
            expect(spyDispatcher).toHaveBeenCalledWith({
                action: "reply_to_event",
                event: null,
            });

            expect(wrapper.text()).toBe("");
            const str = sessionStorage.getItem(`mx_cider_composer_history_${mockRoom.roomId}[0]`);
            expect(JSON.parse(str)).toStrictEqual({
                parts: [{"type": "plain", "text": "This is a message"}],
                replyEventId: mockEvent.getId(),
            });
        });
    });
});

<|endoftext|>


<commit_before>/**
 * Call a node.js generator with backoff and other customization
 *
 * Licensed under MIT.
 * Copyright (c) 2014 David Smith <https://github.com/codeactual/>
 */

/*jshint node:true*/
'use strict';

/**
 * Reference to Prankcall.
 */
exports.Prankcall = Prankcall;

/**
 * Create a new Prankcall.
 *
 * @return {object}
 */
exports.create = function() { return new Prankcall(); };

/**
 * Extend Prankcall.prototype.
 *
 * @param {object} ext
 * @return {object} Merge result.
 */
exports.extend = function(ext) { return extend(Prankcall.prototype, ext); };

var events = require('events');
var retry = require('retry');
var util = require('util');

var requireComponent = require('../component/require');
var extend = requireComponent('extend');
var configurable = requireComponent('configurable.js');

/**
 * Prankcall constructor.
 *
 * Usage:
 *
 *     var prankcall = require('prankcall').create();
 *     prankcall.retry({retries: 3}); // `retry` options
 *
 *     var send = function *() {
 *        // ... async read ...
 *        return data;
 *     };
 *     var recv = function *() {
 *        // ... async read ...
 *        return true; // keep reading
 *     };
 *
 *     yield prankcall.recv(recv).send(send);
 *
 * Configuration:
 *
 * - `{object} retry` Valid `retry` options
 *   - To disable: `prankcall.disable('retry')`
 *   - Default values below are from `retry`
 *   - `{number} [timeout=2000]`
 *   - `{number} [retries=10]`
 *   - `{number} [factor=2]`
 *   - `{number} [minTimeout=1000]`
 *   - `{number} [maxTimeout=Infinity]`
 *   - `{boolean} [randomize=false]`
 * - `{function *} [receive=none]` Optional consumer of each call's return value
 *   - `return true` to make another call
 *   - `return <falsey>` stop calling
 * - `{number} [sleep=1000]` Delay (in milliseconds) after successful call
 * @see tim-kos/node-retry https://github.com/tim-kos/node-retry/#api
 * @see Events https://github.com/codeactual/prankcall/blob/master/docs/events.md
 */
function Prankcall() {
  this.settings = {
    retry: {},
    recv: Prankcall.defaultReceive,
    sleep: 1000
  };
  events.EventEmitter.call(this);
}

util.inherits(Prankcall, events.EventEmitter);
configurable(Prankcall.prototype);

/**
 * Define a generator, or regular function, to receive the return values from
 * the generator passed to Prankcall.prototype.send.
 *
 * - Ex. to implement pagination by inspecting JSON responses from HTTP requests
 *   and then deciding whether to `send` should run again to fetch the next page.
 *   `recv` can signal `send` to continue/stop its loop by returning `true/false`.
 *
 * @param {function *} recv Generator or regular function.
 * - Receives the return value from `send` as the first argument.
 * @return {object} this
 */
Prankcall.prototype.recv = function(recv) {
  return this.set('recv', recv);
};

/**
 * Customize `retry` timeout options for backoff in `send`.
 *
 * @param {object} options Any valid `retry` timeout pairs
 * @return {object} this
 * @see tim-kos/node-retry https://github.com/tim-kos/node-retry/#api
 */
Prankcall.prototype.retry = function(options) {
  return this.set('retry', options);
};

/**
 * Sleep for a custom duration after each `send` iteration.
 *
 * @param {number} ms
 * @return {object} this
 */
Prankcall.prototype.sleep = function(ms) {
  return this.set('sleep', ms);
};

/**
 * Yield to `sender` and retry after an exception.
 *
 * - By default, collect only one result from `sender`.
 *   - Return `true` from a Prankcall.prototype.receive generator to collect another.
 * - By default, backoff according to stock `retry` settings.
 *
 * @param {function *} sender
 */
Prankcall.prototype.send = function *(sender) {
  var backoffTime;
  var callAgain = true;
  var callAgainSleep = this.get('sleep');
  var callReturn; // `return` value from `sender`
  var sleep = Prankcall.sleep;
  var stats = {
    calls: 0,
    currentRetries: 0,
    totalRetries: 0
  };
  var timeouts = this.calcTimeouts();

  // Block executes on:
  // - 1st run
  // - Retry
  // - Receiver function returned `true`, see `this.get('recv')` below
  while (callAgain) {
    try {
      this.emit('call', {calls: stats.calls});

      callReturn = yield sender();

      stats.calls++;
      this.emit('return', {calls: stats.calls}, callReturn);
    } catch (err) {
      backoffTime = timeouts.shift();
      if (backoffTime === undefined) { // Retries exhausted
        throw err;
      } else {
        stats.currentRetries++;
        stats.totalRetries++;
        this.emit(
          'retry',
          {
            backoffTime: backoffTime,
            remainRetries: timeouts.length,
            currentRetries: stats.currentRetries,
            err: err,
            totalRetries: stats.totalRetries
          },
          err
        );
        yield Prankcall.backoff(backoffTime);
        continue;
      }
    }

    var recv = this.get('recv');
    if (isGeneratorFunction(recv)) {
      callAgain = !!(yield recv(callReturn));
    } else {
      callAgain = !!recv(callReturn);
    }

    if (callAgain) {
      // Reset per-iteration states
      stats.currentRetries = 0;
      timeouts = this.calcTimeouts();

      this.emit('next', {sleepTime: callAgainSleep});

      if (callAgainSleep) {
        yield sleep(callAgainSleep);
      }
    }
  }
};

/**
 * Use `retry` to calculate timeout durations.
 *
 * @return {array} Ex. `[1000, 2000, 4000, ...]`
 * @api private
 */
Prankcall.prototype.calcTimeouts = function() {
  var retryOpts = this.get('retry');
  return typeof retryOpts === 'object' ? retry.timeouts(retryOpts) : [];
};

/**
 * Err on the side of halting the generator after one call instead of
 * unexpectedly running it forever.
 */
Prankcall.defaultReceive = function() {
  return false;
};

Prankcall.noOpCallback = function(done) {
  done();
};

/**
 * Separate from Prankcall.sleep to allow separate test doubles.
 */
Prankcall.backoff = function(ms) {
  return function prankCallBackoff(done) {
    setTimeout(done, ms);
  };
};

Prankcall.sleep = function(ms) {
  return function prankCallSleep(done) {
    setTimeout(done, ms);
  };
};

/**
 * Type check approach from `co`.
 *
 * @see co https://github.com/visionmedia/co
 * @api private
 */
function isGeneratorFunction(obj) {
  return obj && obj.constructor && 'GeneratorFunction' == obj.constructor.name;
}
<commit_msg>fix(send): Remove redundant `retry` event payload arg<commit_after>/**
 * Call a node.js generator with backoff and other customization
 *
 * Licensed under MIT.
 * Copyright (c) 2014 David Smith <https://github.com/codeactual/>
 */

/*jshint node:true*/
'use strict';

/**
 * Reference to Prankcall.
 */
exports.Prankcall = Prankcall;

/**
 * Create a new Prankcall.
 *
 * @return {object}
 */
exports.create = function() { return new Prankcall(); };

/**
 * Extend Prankcall.prototype.
 *
 * @param {object} ext
 * @return {object} Merge result.
 */
exports.extend = function(ext) { return extend(Prankcall.prototype, ext); };

var events = require('events');
var retry = require('retry');
var util = require('util');

var requireComponent = require('../component/require');
var extend = requireComponent('extend');
var configurable = requireComponent('configurable.js');

/**
 * Prankcall constructor.
 *
 * Usage:
 *
 *     var prankcall = require('prankcall').create();
 *     prankcall.retry({retries: 3}); // `retry` options
 *
 *     var send = function *() {
 *        // ... async read ...
 *        return data;
 *     };
 *     var recv = function *() {
 *        // ... async read ...
 *        return true; // keep reading
 *     };
 *
 *     yield prankcall.recv(recv).send(send);
 *
 * Configuration:
 *
 * - `{object} retry` Valid `retry` options
 *   - To disable: `prankcall.disable('retry')`
 *   - Default values below are from `retry`
 *   - `{number} [timeout=2000]`
 *   - `{number} [retries=10]`
 *   - `{number} [factor=2]`
 *   - `{number} [minTimeout=1000]`
 *   - `{number} [maxTimeout=Infinity]`
 *   - `{boolean} [randomize=false]`
 * - `{function *} [receive=none]` Optional consumer of each call's return value
 *   - `return true` to make another call
 *   - `return <falsey>` stop calling
 * - `{number} [sleep=1000]` Delay (in milliseconds) after successful call
 * @see tim-kos/node-retry https://github.com/tim-kos/node-retry/#api
 * @see Events https://github.com/codeactual/prankcall/blob/master/docs/events.md
 */
function Prankcall() {
  this.settings = {
    retry: {},
    recv: Prankcall.defaultReceive,
    sleep: 1000
  };
  events.EventEmitter.call(this);
}

util.inherits(Prankcall, events.EventEmitter);
configurable(Prankcall.prototype);

/**
 * Define a generator, or regular function, to receive the return values from
 * the generator passed to Prankcall.prototype.send.
 *
 * - Ex. to implement pagination by inspecting JSON responses from HTTP requests
 *   and then deciding whether to `send` should run again to fetch the next page.
 *   `recv` can signal `send` to continue/stop its loop by returning `true/false`.
 *
 * @param {function *} recv Generator or regular function.
 * - Receives the return value from `send` as the first argument.
 * @return {object} this
 */
Prankcall.prototype.recv = function(recv) {
  return this.set('recv', recv);
};

/**
 * Customize `retry` timeout options for backoff in `send`.
 *
 * @param {object} options Any valid `retry` timeout pairs
 * @return {object} this
 * @see tim-kos/node-retry https://github.com/tim-kos/node-retry/#api
 */
Prankcall.prototype.retry = function(options) {
  return this.set('retry', options);
};

/**
 * Sleep for a custom duration after each `send` iteration.
 *
 * @param {number} ms
 * @return {object} this
 */
Prankcall.prototype.sleep = function(ms) {
  return this.set('sleep', ms);
};

/**
 * Yield to `sender` and retry after an exception.
 *
 * - By default, collect only one result from `sender`.
 *   - Return `true` from a Prankcall.prototype.receive generator to collect another.
 * - By default, backoff according to stock `retry` settings.
 *
 * @param {function *} sender
 */
Prankcall.prototype.send = function *(sender) {
  var backoffTime;
  var callAgain = true;
  var callAgainSleep = this.get('sleep');
  var callReturn; // `return` value from `sender`
  var sleep = Prankcall.sleep;
  var stats = {
    calls: 0,
    currentRetries: 0,
    totalRetries: 0
  };
  var timeouts = this.calcTimeouts();

  // Block executes on:
  // - 1st run
  // - Retry
  // - Receiver function returned `true`, see `this.get('recv')` below
  while (callAgain) {
    try {
      this.emit('call', {calls: stats.calls});

      callReturn = yield sender();

      stats.calls++;
      this.emit('return', {calls: stats.calls}, callReturn);
    } catch (err) {
      backoffTime = timeouts.shift();
      if (backoffTime === undefined) { // Retries exhausted
        throw err;
      } else {
        stats.currentRetries++;
        stats.totalRetries++;
        this.emit(
          'retry',
          {
            backoffTime: backoffTime,
            remainRetries: timeouts.length,
            currentRetries: stats.currentRetries,
            err: err,
            totalRetries: stats.totalRetries
          }
        );
        yield Prankcall.backoff(backoffTime);
        continue;
      }
    }

    var recv = this.get('recv');
    if (isGeneratorFunction(recv)) {
      callAgain = !!(yield recv(callReturn));
    } else {
      callAgain = !!recv(callReturn);
    }

    if (callAgain) {
      // Reset per-iteration states
      stats.currentRetries = 0;
      timeouts = this.calcTimeouts();

      this.emit('next', {sleepTime: callAgainSleep});

      if (callAgainSleep) {
        yield sleep(callAgainSleep);
      }
    }
  }
};

/**
 * Use `retry` to calculate timeout durations.
 *
 * @return {array} Ex. `[1000, 2000, 4000, ...]`
 * @api private
 */
Prankcall.prototype.calcTimeouts = function() {
  var retryOpts = this.get('retry');
  return typeof retryOpts === 'object' ? retry.timeouts(retryOpts) : [];
};

/**
 * Err on the side of halting the generator after one call instead of
 * unexpectedly running it forever.
 */
Prankcall.defaultReceive = function() {
  return false;
};

Prankcall.noOpCallback = function(done) {
  done();
};

/**
 * Separate from Prankcall.sleep to allow separate test doubles.
 */
Prankcall.backoff = function(ms) {
  return function prankCallBackoff(done) {
    setTimeout(done, ms);
  };
};

Prankcall.sleep = function(ms) {
  return function prankCallSleep(done) {
    setTimeout(done, ms);
  };
};

/**
 * Type check approach from `co`.
 *
 * @see co https://github.com/visionmedia/co
 * @api private
 */
function isGeneratorFunction(obj) {
  return obj && obj.constructor && 'GeneratorFunction' == obj.constructor.name;
}
<|endoftext|>


<commit_before>/**
 * Template functions for convert one we.js attribute to one ember.js attribute string
 *
 */

var templates = {};

templates.model = function(data) {
  var str = '',
    emberName = toUp(data.modelName);

  str += data.appName + '.' + emberName + ' = DS.Model.extend({ \n';

  str += data.attrs + '\n';

  str += '}); \n';

  return str;
}

templates.attrs = {};

templates.attrs.clean = function(data) {
  var str = '';
  str += '\t' + data.name + ': DS.attr() \n';
  return str;
}

templates.attrs.simpleField = function(data, fieldType) {
  if (!fieldType) fieldType = 'string';

  var str = '';
  str += '\t' + data.name + ': DS.attr("'+fieldType+'", {';

  if (data.defaultValue) {
    str += '\n \t\t defaultValue: "' + data.defaultValue + '" \n';
  }

  str += '\t}) \n';
  return str;
}

templates.attrs.string = function(data) {
  return templates.attrs.simpleField(data, 'string');
}

templates.attrs.number = function(data) {
  return templates.attrs.simpleField(data, 'number');
}

templates.attrs.date = function(data) {
   return templates.attrs.simpleField(data, 'data');
}

templates.attrs.boolean = function(data) {
  return templates.attrs.simpleField(data, 'boolean');
}

// -- relationships

templates.attrs.belongsTo = function(data, joinFNName) {
  var str = '';

  if (!joinFNName) joinFNName = 'DS.belongsTo';

  str += '\t' + data.name + ': '+ joinFNName +'("' + data.relationName + '", {';

  if (data.inverse) {
    str += '\n \t\t inverse: "' + data.inverse + '", ';
  }

  if (data.async) {
    str += '\n \t\t async: true \n';
  }

  str += '\t}) \n';
  return str;
}

templates.attrs.hasMany = function(data) {
  return templates.attrs.belongsTo(data, 'DS.hasMany');
}

templates.attrs.term = function(data) {
  var str = '';
  str += '\t' + data.name + ': DS.attr("term", {';

  if (data.defaultValue) {
    str += '\n \t\t defaultValue: "' + data.defaultValue + '", \n';
  }

  str += '\n \t\t canCreate: ' + data.canCreate + ', \n';

  if (data.vocabularyId) {
    str += '\n \t\t vocabularyId: "' + data.vocabularyId + '" \n';
  } else {
    str += '\n \t\t vocabularyId: null \n';
  }

  str += '\t}) \n';
  return str;
}

/**
 * Convert te first letter of one string to uppercase
 *
 * @param  {sring} str
 * @return {string}
 */
function toUp(str) {
  return str[0].toUpperCase() + str.substring(1);
}

module.exports = templates;<commit_msg>fix database/templates.js date type<commit_after>/**
 * Template functions for convert one we.js attribute to one ember.js attribute string
 *
 */

var templates = {};

templates.model = function(data) {
  var str = '',
    emberName = toUp(data.modelName);

  str += data.appName + '.' + emberName + ' = DS.Model.extend({ \n';

  str += data.attrs + '\n';

  str += '}); \n';

  return str;
}

templates.attrs = {};

templates.attrs.clean = function(data) {
  var str = '';
  str += '\t' + data.name + ': DS.attr() \n';
  return str;
}

templates.attrs.simpleField = function(data, fieldType) {
  if (!fieldType) fieldType = 'string';

  var str = '';
  str += '\t' + data.name + ': DS.attr("'+fieldType+'", {';

  if (data.defaultValue) {
    str += '\n \t\t defaultValue: "' + data.defaultValue + '" \n';
  }

  str += '\t}) \n';
  return str;
}

templates.attrs.string = function(data) {
  return templates.attrs.simpleField(data, 'string');
}

templates.attrs.number = function(data) {
  return templates.attrs.simpleField(data, 'number');
}

templates.attrs.date = function(data) {
   return templates.attrs.simpleField(data, 'date');
}

templates.attrs.boolean = function(data) {
  return templates.attrs.simpleField(data, 'boolean');
}

// -- relationships

templates.attrs.belongsTo = function(data, joinFNName) {
  var str = '';

  if (!joinFNName) joinFNName = 'DS.belongsTo';

  str += '\t' + data.name + ': '+ joinFNName +'("' + data.relationName + '", {';

  if (data.inverse) {
    str += '\n \t\t inverse: "' + data.inverse + '", ';
  }

  if (data.async) {
    str += '\n \t\t async: true \n';
  }

  str += '\t}) \n';
  return str;
}

templates.attrs.hasMany = function(data) {
  return templates.attrs.belongsTo(data, 'DS.hasMany');
}

templates.attrs.term = function(data) {
  var str = '';
  str += '\t' + data.name + ': DS.attr("term", {';

  if (data.defaultValue) {
    str += '\n \t\t defaultValue: "' + data.defaultValue + '", \n';
  }

  str += '\n \t\t canCreate: ' + data.canCreate + ', \n';

  if (data.vocabularyId) {
    str += '\n \t\t vocabularyId: "' + data.vocabularyId + '" \n';
  } else {
    str += '\n \t\t vocabularyId: null \n';
  }

  str += '\t}) \n';
  return str;
}

/**
 * Convert te first letter of one string to uppercase
 *
 * @param  {sring} str
 * @return {string}
 */
function toUp(str) {
  return str[0].toUpperCase() + str.substring(1);
}

module.exports = templates;<|endoftext|>


<commit_before>(function(global) {
    var UNDEFINED,
        exportObject;

    if (typeof module !== "undefined" && module.exports) {
        exportObject = exports;
    } else {
        exportObject = global.jasmineReporters = global.jasmineReporters || {};
    }

    function elapsed(start, end) { return (end - start)/1000; }
    function isFailed(obj) { return obj.status === "failed"; }
    function isSkipped(obj) { return obj.status === "pending"; }
    function isDisabled(obj) { return obj.status === "disabled"; }
    function extend(dupe, obj) { // performs a shallow copy of all props of `obj` onto `dupe`
        for (var prop in obj) {
            if (obj.hasOwnProperty(prop)) {
                dupe[prop] = obj[prop];
            }
        }
        return dupe;
    }
    function log(str) {
        var con = global.console || console;
        if (con && con.log) {
            con.log(str);
        }
    }

    /**
     * Basic reporter that outputs spec results to the terminal.
     * Use this reporter in your build pipeline.
     *
     * Usage:
     *
     * jasmine.getEnv().addReporter(new jasmineReporters.TerminalReporter(options);
     *
     * @param {object} [options]
     * @param {number} [options.verbosity] meaningful values are 0 through 3; anything
     *   greater than 3 is treated as 3 (default: 2)
     * @param {boolean} [options.color] print in color or not (default: true)
     */
    var DEFAULT_VERBOSITY = 2,
        ATTRIBUTES_TO_ANSI = {
            "off": 0,
            "bold": 1,
            "red": 31,
            "green": 32,
            "yellow": 33
        };

    exportObject.TerminalReporter = function(options) {
        var self = this;
        self.started = false;
        self.finished = false;

        // sanitize arguments
        options = options || {};
        self.verbosity = typeof options.verbosity === "number" ? options.verbosity : DEFAULT_VERBOSITY;
        self.color = options.color;

        var indent_string = '  ',
            startTime,
            suites = [],
            currentSuite = null,
            totalSpecsExecuted = 0,
            totalSpecsSkipped = 0,
            totalSpecsDisabled = 0,
            totalSpecsFailed = 0,
            totalSpecsDefined;

        var __suites = {}, __specs = {};
        function getSuite(suite) {
            __suites[suite.id] = extend(__suites[suite.id] || {}, suite);
            return __suites[suite.id];
        }
        function getSpec(spec) {
            __specs[spec.id] = extend(__specs[spec.id] || {}, spec);
            return __specs[spec.id];
        }

        self.jasmineStarted = function(summary) {
            totalSpecsDefined = summary && summary.totalSpecsDefined || NaN;
            startTime = exportObject.startTime = new Date();
            self.started = true;
        };
        self.suiteStarted = function(suite) {
            suite = getSuite(suite);
            suite._specs = 0;
            suite._nestedSpecs = 0;
            suite._failures = 0;
            suite._nestedFailures = 0;
            suite._skipped = 0;
            suite._nestedSkipped = 0;
            suite._disabled = 0;
            suite._nestedDisabled = 0;
            suite._depth = currentSuite ? currentSuite._depth+1 : 1;
            suite._parent = currentSuite;
            currentSuite = suite;
            if (self.verbosity > 2) {
                log(indentWithLevel(suite._depth, inColor(suite.description, "bold")));
            }
        };
        self.specStarted = function(spec) {
            spec = getSpec(spec);
            spec._suite = currentSuite;
            spec._depth = currentSuite._depth+1;
            currentSuite._specs++;
            if (self.verbosity > 2) {
                log(indentWithLevel(spec._depth, spec.description + ' ...'));
            }
        };
        self.specDone = function(spec) {
            spec = getSpec(spec);
            var failed = false,
                skipped = false,
                disabled = false,
                color = 'green',
                resultText = '';
            if (isSkipped(spec)) {
                skipped = true;
                color = '';
                spec._suite._skipped++;
                totalSpecsSkipped++;
            }
            if (isFailed(spec)) {
                failed = true;
                color = 'red';
                spec._suite._failures++;
                totalSpecsFailed++;
            }
            if (isDisabled(spec)) {
                disabled = true;
                color = 'yellow';
                spec._suite._disabled++;
                totalSpecsDisabled++;
            }
            totalSpecsExecuted++;

            if (self.verbosity === 2) {
                resultText = failed ? 'F' : skipped ? 'S' : disabled ? 'D' : '.';
            } else if (self.verbosity > 2) {
                resultText = ' ' + (failed ? 'Failed' : skipped ? 'Skipped' : disabled ? 'Disabled' : 'Passed');
            }
            log(inColor(resultText, color));

            if (failed) {
                if (self.verbosity === 1) {
                    log(spec.fullName);
                } else if (self.verbosity === 2) {
                    log(' ');
                    log(indentWithLevel(spec._depth, spec.fullName));
                }

                for (var i = 0, failure; i < spec.failedExpectations.length; i++) {
                    log(inColor(indentWithLevel(spec._depth, indent_string + spec.failedExpectations[i].message), color));
                }
            }
        };
        self.suiteDone = function(suite) {
            suite = getSuite(suite);
            // disabled suite (xdescribe) -- suiteStarted was never called
            if (suite._parent === UNDEFINED) {
                self.suiteStarted(suite);
            }
            if (suite._parent) {
                suite._parent._specs += suite._specs + suite._nestedSpecs;
                suite._parent._failures += suite._failures + suite._nestedFailures;
                suite._parent._skipped += suite._skipped + suite._nestedSkipped;
                suite._parent._disabled += suite._disabled + suite._nestedDisabled;

            }
            currentSuite = suite._parent;
            if (self.verbosity < 3) {
                return;
            }

            var total = suite._specs + suite._nestedSpecs,
                failed = suite._failures + suite._nestedFailures,
                skipped = suite._skipped + suite._nestedSkipped,
                disabled = suite._disabled + suite._nestedDisabled,
                passed = total - failed - skipped,
                color = failed ? 'red+bold' : 'green+bold',
                str = passed + ' of ' + total + ' passed (' + skipped + ' skipped, ' + disabled + ' disabled)';
            log(indentWithLevel(suite._depth, inColor(str+'.', color)));
        };
        self.jasmineDone = function() {
            var now = new Date(),
                dur = elapsed(startTime, now),
                total = totalSpecsDefined || totalSpecsExecuted,
                disabled = total - totalSpecsExecuted + totalSpecsDisabled,
                skipped = totalSpecsSkipped,
                spec_str = total + (total === 1 ? " spec, " : " specs, "),
                fail_str = totalSpecsFailed + (totalSpecsFailed === 1 ? " failure, " : " failures, "),
                skip_str = skipped + " skipped, ",
                disabled_str = disabled + " disabled in ",
                summary_str = spec_str + fail_str + skip_str + disabled_str + dur + "s.",
                result_str = (totalSpecsFailed && "FAILURE: " || "SUCCESS: ") + summary_str,
                result_color = totalSpecsFailed && "red+bold" || "green+bold";

            if (self.verbosity === 2) {
                log('');
            }

            if (self.verbosity > 0) {
                log(inColor(result_str, result_color));
            }
            //log("Specs skipped but not reported (entire suite skipped or targeted to specific specs)", totalSpecsDefined - totalSpecsExecuted + totalSpecsDisabled);

            self.finished = true;
            // this is so phantomjs-testrunner.js can tell if we're done executing
            exportObject.endTime = now;
        };
        function indentWithLevel(level, string) {
            return new Array(level).join(indent_string) + string;
        }
        function inColor(string, color) {
            var color_attributes = color && color.split("+"),
                ansi_string = "",
                i, attr;

            if (!self.color || !color_attributes) {
                return string;
            }

            for(i = 0; i < color_attributes.length; i++) {
                ansi_string += "\033[" + ATTRIBUTES_TO_ANSI[color_attributes[i]] + "m";
            }
            ansi_string += string + "\033[" + ATTRIBUTES_TO_ANSI["off"] + "m";

            return ansi_string;
        }
    };
})(this);
<commit_msg>added showStack option. Shows stack when failed spectation<commit_after>(function(global) {
    var UNDEFINED,
        exportObject;

    if (typeof module !== "undefined" && module.exports) {
        exportObject = exports;
    } else {
        exportObject = global.jasmineReporters = global.jasmineReporters || {};
    }

    function elapsed(start, end) { return (end - start)/1000; }
    function isFailed(obj) { return obj.status === "failed"; }
    function isSkipped(obj) { return obj.status === "pending"; }
    function isDisabled(obj) { return obj.status === "disabled"; }
    function extend(dupe, obj) { // performs a shallow copy of all props of `obj` onto `dupe`
        for (var prop in obj) {
            if (obj.hasOwnProperty(prop)) {
                dupe[prop] = obj[prop];
            }
        }
        return dupe;
    }
    function log(str) {
        var con = global.console || console;
        if (con && con.log) {
            con.log(str);
        }
    }

    /**
     * Basic reporter that outputs spec results to the terminal.
     * Use this reporter in your build pipeline.
     *
     * Usage:
     *
     * jasmine.getEnv().addReporter(new jasmineReporters.TerminalReporter(options);
     *
     * @param {object} [options]
     * @param {number} [options.verbosity] meaningful values are 0 through 3; anything
     *   greater than 3 is treated as 3 (default: 2)
     * @param {boolean} [options.color] print in color or not (default: true)
     */
    var DEFAULT_VERBOSITY = 2,
        ATTRIBUTES_TO_ANSI = {
            "off": 0,
            "bold": 1,
            "red": 31,
            "green": 32,
            "yellow": 33,
            "blue": 34,
            "magenta": 35,
            "cyan": 36
        };

    exportObject.TerminalReporter = function(options) {
        var self = this;
        self.started = false;
        self.finished = false;

        // sanitize arguments
        options = options || {};
        self.verbosity = typeof options.verbosity === "number" ? options.verbosity : DEFAULT_VERBOSITY;
        self.color = options.color;
        self.showStack = options.showStack;

        var indent_string = '  ',
            startTime,
            suites = [],
            currentSuite = null,
            totalSpecsExecuted = 0,
            totalSpecsSkipped = 0,
            totalSpecsDisabled = 0,
            totalSpecsFailed = 0,
            totalSpecsDefined;

        var __suites = {}, __specs = {};
        function getSuite(suite) {
            __suites[suite.id] = extend(__suites[suite.id] || {}, suite);
            return __suites[suite.id];
        }
        function getSpec(spec) {
            __specs[spec.id] = extend(__specs[spec.id] || {}, spec);
            return __specs[spec.id];
        }

        self.jasmineStarted = function(summary) {
            totalSpecsDefined = summary && summary.totalSpecsDefined || NaN;
            startTime = exportObject.startTime = new Date();
            self.started = true;
        };
        self.suiteStarted = function(suite) {
            suite = getSuite(suite);
            suite._specs = 0;
            suite._nestedSpecs = 0;
            suite._failures = 0;
            suite._nestedFailures = 0;
            suite._skipped = 0;
            suite._nestedSkipped = 0;
            suite._disabled = 0;
            suite._nestedDisabled = 0;
            suite._depth = currentSuite ? currentSuite._depth+1 : 1;
            suite._parent = currentSuite;
            currentSuite = suite;
            if (self.verbosity > 2) {
                log(indentWithLevel(suite._depth, inColor(suite.description, "bold")));
            }
        };
        self.specStarted = function(spec) {
            spec = getSpec(spec);
            spec._suite = currentSuite;
            spec._depth = currentSuite._depth+1;
            currentSuite._specs++;
            if (self.verbosity > 2) {
                log(indentWithLevel(spec._depth, spec.description + ' ...'));
            }
        };
        self.specDone = function(spec) {
            spec = getSpec(spec);
            var failed = false,
                skipped = false,
                disabled = false,
                color = 'green',
                resultText = '';
            if (isSkipped(spec)) {
                skipped = true;
                color = '';
                spec._suite._skipped++;
                totalSpecsSkipped++;
            }
            if (isFailed(spec)) {
                failed = true;
                color = 'red';
                spec._suite._failures++;
                totalSpecsFailed++;
            }
            if (isDisabled(spec)) {
                disabled = true;
                color = 'yellow';
                spec._suite._disabled++;
                totalSpecsDisabled++;
            }
            totalSpecsExecuted++;

            if (self.verbosity === 2) {
                resultText = failed ? 'F' : skipped ? 'S' : disabled ? 'D' : '.';
            } else if (self.verbosity > 2) {
                resultText = ' ' + (failed ? 'Failed' : skipped ? 'Skipped' : disabled ? 'Disabled' : 'Passed');
            }
            log(inColor(resultText, color));

            if (failed) {
                if (self.verbosity === 1) {
                    log(spec.fullName);
                } else if (self.verbosity === 2) {
                    log(' ');
                    log(indentWithLevel(spec._depth, spec.fullName));
                }

                for (var i = 0, failure; i < spec.failedExpectations.length; i++) {
                    log(inColor(indentWithLevel(spec._depth, indent_string + spec.failedExpectations[i].message), color));
                    if (self.showStack){
                        var stackLines = spec.failedExpectations[i].stack.split('\n');
                        stackLines.forEach(function(line){
                            log( inColor( indentWithLevel(spec._depth, indent_string + line), 'magenta' ) );
                        });
                    }
                }
            }
        };
        self.suiteDone = function(suite) {
            suite = getSuite(suite);
            // disabled suite (xdescribe) -- suiteStarted was never called
            if (suite._parent === UNDEFINED) {
                self.suiteStarted(suite);
            }
            if (suite._parent) {
                suite._parent._specs += suite._specs + suite._nestedSpecs;
                suite._parent._failures += suite._failures + suite._nestedFailures;
                suite._parent._skipped += suite._skipped + suite._nestedSkipped;
                suite._parent._disabled += suite._disabled + suite._nestedDisabled;

            }
            currentSuite = suite._parent;
            if (self.verbosity < 3) {
                return;
            }

            var total = suite._specs + suite._nestedSpecs,
                failed = suite._failures + suite._nestedFailures,
                skipped = suite._skipped + suite._nestedSkipped,
                disabled = suite._disabled + suite._nestedDisabled,
                passed = total - failed - skipped,
                color = failed ? 'red+bold' : 'green+bold',
                str = passed + ' of ' + total + ' passed (' + skipped + ' skipped, ' + disabled + ' disabled)';
            log(indentWithLevel(suite._depth, inColor(str+'.', color)));
        };
        self.jasmineDone = function() {
            var now = new Date(),
                dur = elapsed(startTime, now),
                total = totalSpecsDefined || totalSpecsExecuted,
                disabled = total - totalSpecsExecuted + totalSpecsDisabled,
                skipped = totalSpecsSkipped,
                spec_str = total + (total === 1 ? " spec, " : " specs, "),
                fail_str = totalSpecsFailed + (totalSpecsFailed === 1 ? " failure, " : " failures, "),
                skip_str = skipped + " skipped, ",
                disabled_str = disabled + " disabled in ",
                summary_str = spec_str + fail_str + skip_str + disabled_str + dur + "s.",
                result_str = (totalSpecsFailed && "FAILURE: " || "SUCCESS: ") + summary_str,
                result_color = totalSpecsFailed && "red+bold" || "green+bold";

            if (self.verbosity === 2) {
                log('');
            }

            if (self.verbosity > 0) {
                log(inColor(result_str, result_color));
            }
            //log("Specs skipped but not reported (entire suite skipped or targeted to specific specs)", totalSpecsDefined - totalSpecsExecuted + totalSpecsDisabled);

            self.finished = true;
            // this is so phantomjs-testrunner.js can tell if we're done executing
            exportObject.endTime = now;
        };
        function indentWithLevel(level, string) {
            return new Array(level).join(indent_string) + string;
        }
        function inColor(string, color) {
            var color_attributes = color && color.split("+"),
                ansi_string = "",
                i, attr;

            if (!self.color || !color_attributes) {
                return string;
            }

            for(i = 0; i < color_attributes.length; i++) {
                ansi_string += "\033[" + ATTRIBUTES_TO_ANSI[color_attributes[i]] + "m";
            }
            ansi_string += string + "\033[" + ATTRIBUTES_TO_ANSI["off"] + "m";

            return ansi_string;
        }
    };
})(this);
<|endoftext|>


<commit_before>angular
    .module('app')
    .directive('decimalPlaces', function() {
        return {
            link: function(scope, ele, attrs) {
                ele.bind('keypress', function(e) {
                    var newVal = ele.val() + (e.charCode !== 0 ? String.fromCharCode(e.charCode) : '');

                    // get the actual letter user has entered
                    var theCharacter = String.fromCharCode(e.charCode);

                    console.log(ele.val().split(".").length);

                    if (theCharacter.search(/\d/) === -1) {
                        console.log('not a number, checking for period (.)');

                        // temp fix to allow '-' to indicate null value on rate sheets
                        if (theCharacter.search(/\-/) !== -1 && ele.val().split("-").length <= 1) {
                            return true;
                        }

                        if (theCharacter.search(/\./) === -1 || ele.val().split(".").length > 1) {
                            console.log('Too many periods (.)');
                            e.preventDefault();
                        }
                    }

                    if (theCharacter.indexOf('0') === 1 && theCharacter.search(/\./) === -1 && ele.val().split(".").length === 1) {
                        console.log('User is trying to enter a non-decimal number');
                        e.preventDefault();
                    }

                    if (ele.val().search(/\d+\.\d{3}/) === 0 && newVal.length > ele.val().length) {
                        //e.preventDefault();
                    }

                    if (ele.val().search(/\.\d{3}/) === 0 && newVal.length > ele.val().length) {
                        //e.preventDefault();
                    }

                });
            }
        };

    })
    .directive('numberOnly', function() {
        return {
            require: '?ngModel',
            link: function(scope, ele, attrs, ctrl) {
                var matcher, matchedString, input;

                if (!ctrl) return;

                // force truthy in case we are on non-input el
                attrs.numberOnly = true;

                var validator = function(value) {
                    if (attrs.numberOnly && (notNum(value) || value === false)) {
                        ctrl.$setValidity('numericality', true);
                    } else {
                        ctrl.$setValidity('numericality', false);
                    }
                    return value;
                };

                ctrl.$formatters.push(validator);
                ctrl.$parsers.unshift(validator);

                attrs.$observe('numberOnly', function() {
                    validator(ctrl.$viewValue);
                });

                function notNum(value) {
                    if (value || value === 0 || value === '0') {
                        // /regex/#match returns an array, the first item of which is
                        // the matched string (e.g. "0.123" in "0.123abc" if the user)
                        // input a string with letters in it, for whatever reason.
                        // The input attribute is the original input, so we check that
                        // the matched string ("0.123") matches the original input;
                        // if it doesn't, it's false. The !! operator converts to
                        // boolean, so the return statement will always return
                        // true or false.
                        matcher       = value.toString().match(/\d{1,}\.{0,1}\d{0,}/);
                        matchedString = matcher[0];
                        input         = matcher.input;

                        return matchedString == input;
                    } else {
                        return false;
                    }

                }
            }
        };

    })
    .directive('isUser', function(userService, Validator) {
            return {
                require: '?ngModel',
                link: function(scope, ele, attrs, ctrl) {
                    var matcher, matchedString, input, form;

                    if (!ctrl) return;

                    form = scope.$eval(ele[0].form.name);

                    // force truthy in case we are on non-input el
                    attrs.isUser = true;

                    var validator = function(value) {

                        if(!value) return;

                        userService.find({email: value || undefined}).then(function(response, err) {
                            if (response.data && response.data[0] && response.data[0].email) {
                                ctrl.$setValidity('isUser', true);
                            } else {
                                ctrl.$setValidity('isUser', false);
                            }
                            Validator.validateField(form.email, form);
                        });
                    };

                    $(ele).on('blur', function() {
                        validator(ctrl.$viewValue);
                    });

                }
            };

        })
        .directive('integerOnly', function() {
            return {
                require: '?ngModel',
                link: function(scope, ele, attrs, ctrl) {
                    var matcher, matchedString, input;

                    if (!ctrl) return;

                    // force truthy in case we are on non-input el
                    attrs.numberOnly = true;

                    var validator = function(value) {
                        if (attrs.numberOnly && (notInt(value) || value === false)) {
                            ctrl.$setValidity('integer', true);
                        } else {
                            ctrl.$setValidity('integer', false);
                        }
                        return value;
                    };

                    ctrl.$formatters.push(validator);
                    ctrl.$parsers.unshift(validator);

                    attrs.$observe('integerOnly', function() {
                        validator(ctrl.$viewValue);
                    });

                    function notInt(value) {
                        if (value || value === 0 || value === '0') {

                            matcher       = value.toString().match(/\d{1,}/);
                            matchedString = matcher[0];
                            input         = matcher.input;

                            return matchedString == input;
                        } else {
                            return false;
                        }

                    }
                }
            };

        });
<commit_msg>Fix number-only && integer-only validations<commit_after>angular
    .module('app')
    .directive('decimalPlaces', function() {
        return {
            link: function(scope, ele, attrs) {
                ele.bind('keypress', function(e) {
                    var newVal = ele.val() + (e.charCode !== 0 ? String.fromCharCode(e.charCode) : '');

                    // get the actual letter user has entered
                    var theCharacter = String.fromCharCode(e.charCode);

                    console.log(ele.val().split(".").length);

                    if (theCharacter.search(/\d/) === -1) {
                        console.log('not a number, checking for period (.)');

                        // temp fix to allow '-' to indicate null value on rate sheets
                        if (theCharacter.search(/\-/) !== -1 && ele.val().split("-").length <= 1) {
                            return true;
                        }

                        if (theCharacter.search(/\./) === -1 || ele.val().split(".").length > 1) {
                            console.log('Too many periods (.)');
                            e.preventDefault();
                        }
                    }

                    if (theCharacter.indexOf('0') === 1 && theCharacter.search(/\./) === -1 && ele.val().split(".").length === 1) {
                        console.log('User is trying to enter a non-decimal number');
                        e.preventDefault();
                    }

                    if (ele.val().search(/\d+\.\d{3}/) === 0 && newVal.length > ele.val().length) {
                        //e.preventDefault();
                    }

                    if (ele.val().search(/\.\d{3}/) === 0 && newVal.length > ele.val().length) {
                        //e.preventDefault();
                    }

                });
            }
        };

    })
    .directive('numberOnly', function() {
        return {
            require: '?ngModel',
            link: function(scope, ele, attrs, ctrl) {
                var matcher, matchedString, input;

                if (!ctrl) return;

                // force truthy in case we are on non-input el
                attrs.numberOnly = true;

                var validator = function(value) {
                    if (notNum(value)) {
                        // not valid if not number
                        ctrl.$setValidity('numericality', false);
                    } else {
                        ctrl.$setValidity('numericality', true);
                    }
                    return value;
                };

                ctrl.$formatters.push(validator);
                ctrl.$parsers.unshift(validator);

                attrs.$observe('numberOnly', function() {
                    validator(ctrl.$viewValue);
                });

                function notNum(value) {
                    if (value || value === 0 || value === '0') {
                        // /regex/#match returns an array, the first item of which is
                        // the matched string (e.g. "0.123" in "0.123abc" if the user)
                        // input a string with letters in it, for whatever reason.
                        // The input attribute is the original input, so we check that
                        // the matched string ("0.123") matches the original input;
                        // if it doesn't, it's false. The !! operator converts to
                        // boolean, so the return statement will always return
                        // true or false.
                        try {
                            value         = value.toString().replace(/\,/g, "");
                            matcher       = value.match(/\d{1,}\.{0,1}\d{0,}/);
                            matchedString = matcher[0];
                            input         = matcher.input;
                            console.log(matchedString == input);
                            return matchedString != input;
                        } catch(e) {
                            return false;
                        }
                    } else {
                        return false;
                    }

                }
            }
        };

    })
    .directive('isUser', function(userService, Validator) {
            return {
                require: '?ngModel',
                link: function(scope, ele, attrs, ctrl) {
                    var matcher, matchedString, input, form;

                    if (!ctrl) return;

                    form = scope.$eval(ele[0].form.name);

                    // force truthy in case we are on non-input el
                    attrs.isUser = true;

                    var validator = function(value) {

                        if(!value) return;

                        userService.find({email: value || undefined}).then(function(response, err) {
                            if (response.data && response.data[0] && response.data[0].email) {
                                ctrl.$setValidity('isUser', true);
                            } else {
                                ctrl.$setValidity('isUser', false);
                            }
                            Validator.validateField(form.email, form);
                        });
                    };

                    $(ele).on('blur', function() {
                        validator(ctrl.$viewValue);
                    });

                }
            };

        })
        .directive('integerOnly', function() {
            return {
                require: '?ngModel',
                link: function(scope, ele, attrs, ctrl) {
                    var matcher, matchedString, input;

                    if (!ctrl) return;

                    // force truthy in case we are on non-input el
                    attrs.numberOnly = true;

                    var validator = function(value) {
                        if (notInt(value)) {
                            // not valid if not Integer
                            ctrl.$setValidity('integer', false);
                        } else {
                            ctrl.$setValidity('integer', true);
                        }
                        return value;
                    };

                    ctrl.$formatters.push(validator);
                    ctrl.$parsers.unshift(validator);

                    attrs.$observe('integerOnly', function() {
                        validator(ctrl.$viewValue);
                    });

                    function notInt(value) {
                        if (value || value === 0 || value === '0') {
                            try {
                                value         = value.replace(/\,/g, "");
                                matcher       = value.toString().match(/\d{1,}\,{0,}/);
                                matchedString = matcher[0];
                                input         = matcher.input;

                                return matchedString != input;
                            } catch(e) {
                                return false;
                            }
                        } else {
                            return false;
                        }

                    }
                }
            };

        });
<|endoftext|>


<commit_before>/*
 * app.js: Client for the Nodejitsu apps API.
 *
 * (C) 2010, Nodejitsu Inc.
 *
 */

var util = require('util'),
    winston = require('winston'),
    jitsu = require('jitsu');

//
// ### function Apps (options)
// #### @options {Object} Options for this instance
// Constructor function for the Apps resource responsible
// with Nodejitsu's Apps API
//
var Apps = exports.Apps = function (options) {
  jitsu.api.Client.call(this, options);
};

// Inherit from Client base object
util.inherits(Apps, jitsu.api.Client);

//
// ### function list (callback)
// #### @callback {function} Continuation to pass control to when complete
// Lists all applications for the authenticated user
//
Apps.prototype.list = function (callback) {
  winston.info('Listing apps');
  this._request('GET', ['apps', jitsu.config.username], callback, function (res, result) {
    callback(null, result.apps);
  });
};

//
// ### function create (app, callback)
// #### @app {Object} Package.json manifest for the application.
// #### @callback {function} Continuation to pass control to when complete
// Creates an application with the specified package.json manifest in `app`.
//
Apps.prototype.create = function (app, callback) {
  this._request('POST', ['apps', jitsu.config.username, app.name], app, callback, function (res, result) {
    callback();
  });
};

//
// ### function view (name, callback)
// #### @name {string} Name of the application to view
// #### @callback {function} Continuation to pass control to when complete
// Views the application specified by `name`.
//
Apps.prototype.view = function (name, callback) {
  this._request('GET', ['apps', jitsu.config.username, name], callback, function (res, result) {
    callback(null, result.app);
  });
};

//
// ### function update (name, attrs, callback)
// #### @name {string} Name of the application to update
// #### @attrs {Object} Attributes to update for this application.
// #### @callback {function} Continuation to pass control to when complete
// Updates the application with `name` with the specified attributes in `attrs`
//
Apps.prototype.update = function (name, attrs, callback) {
  this._request('PUT', ['apps', jitsu.config.username, name], attrs, callback, function (res, result) {
    callback();
  });
};

//
// ### function destroy (name, callback)
// #### @name {string} Name of the application to destroy
// #### @callback {function} Continuation to pass control to when complete
// Destroys the application with `name` for the authenticated user.
//
Apps.prototype.destroy = function (name, callback) {
  this._request('DELETE', ['apps', jitsu.config.username, name], callback, function (res, result) {
    callback();
  });
};<commit_msg>[api] Added start/stop/restart to Apps API client<commit_after>/*
 * app.js: Client for the Nodejitsu apps API.
 *
 * (C) 2010, Nodejitsu Inc.
 *
 */

var util = require('util'),
    winston = require('winston'),
    jitsu = require('jitsu');

//
// ### function Apps (options)
// #### @options {Object} Options for this instance
// Constructor function for the Apps resource responsible
// with Nodejitsu's Apps API
//
var Apps = exports.Apps = function (options) {
  jitsu.api.Client.call(this, options);
};

// Inherit from Client base object
util.inherits(Apps, jitsu.api.Client);

//
// ### function list (callback)
// #### @callback {function} Continuation to pass control to when complete
// Lists all applications for the authenticated user
//
Apps.prototype.list = function (callback) {
  winston.info('Listing apps');
  this._request('GET', ['apps', jitsu.config.username], callback, function (res, result) {
    callback(null, result.apps);
  });
};

//
// ### function create (app, callback)
// #### @app {Object} Package.json manifest for the application.
// #### @callback {function} Continuation to pass control to when complete
// Creates an application with the specified package.json manifest in `app`.
//
Apps.prototype.create = function (app, callback) {
  this._request('POST', ['apps', jitsu.config.username, app.name], app, callback, function (res, result) {
    callback();
  });
};

//
// ### function view (name, callback)
// #### @name {string} Name of the application to view
// #### @callback {function} Continuation to pass control to when complete
// Views the application specified by `name`.
//
Apps.prototype.view = function (name, callback) {
  this._request('GET', ['apps', jitsu.config.username, name], callback, function (res, result) {
    callback(null, result.app);
  });
};

//
// ### function update (name, attrs, callback)
// #### @name {string} Name of the application to update
// #### @attrs {Object} Attributes to update for this application.
// #### @callback {function} Continuation to pass control to when complete
// Updates the application with `name` with the specified attributes in `attrs`
//
Apps.prototype.update = function (name, attrs, callback) {
  this._request('PUT', ['apps', jitsu.config.username, name], attrs, callback, function (res, result) {
    callback();
  });
};

//
// ### function destroy (name, callback)
// #### @name {string} Name of the application to destroy
// #### @callback {function} Continuation to pass control to when complete
// Destroys the application with `name` for the authenticated user.
//
Apps.prototype.destroy = function (name, callback) {
  this._request('DELETE', ['apps', jitsu.config.username, name], callback, function (res, result) {
    callback();
  });
};

//
// ### function start (name, callback)
// #### @name {string} Name of the application to start
// #### @callback {function} Continuation to pass control to when complete
// Starts the application with `name` for the authenticated user.
//
Apps.prototype.start = function (name, callback) {
  this._request('POST', ['apps', jitsu.config.username, name, 'start'], { start: name }, callback, function (res, result) {
    callback();
  });
};

//
// ### function restart (name, callback)
// #### @name {string} Name of the application to start
// #### @callback {function} Continuation to pass control to when complete
// Starts the application with `name` for the authenticated user.
//
Apps.prototype.restart = function (name, callback) {
  this._request('POST', ['apps', jitsu.config.username, name, 'restart'], { restart: name }, callback, function (res, result) {
    callback();
  });
};

//
// ### function stop (name, callback)
// #### @name {string} Name of the application to stop.
// #### @callback {function} Continuation to pass control to when complete
// Stops the application with `name` for the authenticated user.
//
Apps.prototype.stop = function (name, callback) {
  this._request('POST', ['apps', jitsu.config.username, name, 'stop'], { stop: name }, callback, function (res, result) {
    callback();
  });
};<|endoftext|>


<commit_before>const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const async = require('async');
const bitcore = require('bitcore-lib');

const Coin = mongoose.model('Coin');
const WalletAddress = mongoose.model('WalletAddress');
const workerService = require('../services/worker');

const TransactionSchema = new Schema({
  txid: String,
  network: String,
  chain: String,
  blockHeight: Number,
  blockHash: String,
  blockTime: Date,
  blockTimeNormalized: Date,
  coinbase: Boolean,
  fee: Number,
  size: Number,
  locktime: Number,
  wallets: { type: [Schema.Types.ObjectId] },
});

TransactionSchema.index({txid: 1});
TransactionSchema.index({blockHeight: 1});
TransactionSchema.index({blockHash: 1});
TransactionSchema.index({blockTimeNormalized: 1});
TransactionSchema.index({wallets: 1}, {sparse: true});

TransactionSchema.statics.batchImport = async function (params) {
  return new Promise(async (resolve) => {
    let partition = (array, n) => {
      return array.length ? [array.splice(0, n)].concat(partition(array, n)) : [];
    };

    async.eachLimit(params.txs, 8, async function(tx, cb){
      let mintParams = params;
      mintParams.tx = tx;
      try {
        await workerService.sendTask('mintCoins', mintParams);
      } catch (e){
        return cb(e);
      }
      cb();
    }, async function(){
      let spendOps = Transaction.spendCoins(params);
      if (spendOps.length) {
        spendOps = partition(spendOps, 100);
        spendOps = spendOps.map((spendBatch) => Coin.collection.bulkWrite(spendBatch, { ordered: false }));
        await Promise.all(spendOps);
      }

      let txOps = await Transaction.addTransactions(params);
      txOps = partition(txOps, 100);
      txOps = txOps.map((txBatch) => Transaction.collection.bulkWrite(txBatch, { ordered: false }));
      await Promise.all(txOps);
      resolve();
    });
  });
};

TransactionSchema.statics.addTransactions = async function(params){
  let { blockHash, blockTime, blockTimeNormalized, height, network, txs } = params;
  return new Promise(async (resolve) => {
    let txids = txs.map((tx) => tx.hash);
    let mintWallets = await Coin.collection.aggregate([
      {
        $match: { mintTxid: {$in: txids} }
      },
      { $unwind: '$wallets' },
      { $group: { _id: '$mintTxid', wallets: { $addToSet: '$wallets' } } }
    ]).toArray();
    let spentWallets = await Coin.collection.aggregate([
      {
        $match: { spentTxid: { $in: txids } }
      },
      { $unwind: '$wallets' },
      { $group: { _id: '$spentTxid', wallets: { $addToSet: '$wallets' } } }
    ]).toArray();

    let txOps = txs.map((tx) => {
      let wallets = [];
      for (let wallet of mintWallets.concat(spentWallets).filter((wallet) => wallet._id === tx.hash)){
        for (let walletMatch of wallet.wallets){
          if (!wallets.find((wallet) => wallet.toHexString() === walletMatch.toHexString())){
            wallets.push(walletMatch);
          }
        }
      }

      return {
        updateOne: {
          filter: {
            txid: tx.hash
          },
          update: {
            $set: {
              network: network,
              blockHeight: height,
              blockHash: blockHash,
              blockTime: blockTime,
              blockTimeNormalized: blockTimeNormalized,
              coinbase: tx.isCoinbase(),
              size: tx.toBuffer().length,
              locktime: tx.nLockTime,
              wallets: wallets
            }
          },
          upsert: true,
          forceServerObjectId: true
        }
      };
    });
    resolve(txOps);
  });
};

TransactionSchema.statics.mintCoins = async function (params) {
  let { height, network, tx } = params;
  tx = bitcore.Transaction(tx);
  let mintOps = [];
  for (let [index, output] of tx.outputs.entries()) {
    let address;
    let scriptBuffer = output.script && output.script.toBuffer();
    try {
      address = output.script.toAddress(network).toString();
      if (address === 'false' && output.script.classify() === 'Pay to public key') {
        let hash = bitcore.crypto.Hash.sha256ripemd160(output.script.chunks[0].buf);
        address = bitcore.Address(hash, network).toString();
      }
    } catch (e) {
      address = 'noAddress';
    }
    let op = {
      updateOne: {
        filter: {
          mintTxid: tx.hash,
          mintIndex: index,
          spentHeight: { $lt: 0 }
        },
        update: {
          $set: {
            network: network,
            mintHeight: height,
            coinbase: tx.isCoinbase(),
            value: output.satoshis,
            address: address,
            script: scriptBuffer,
            spentHeight: -2,
            wallets: []
          }
        },
        upsert: true,
        forceServerObjectId: true
      }
    };
    mintOps.push(op);
  }
  let mintOpsAddresses = mintOps.map((mintOp) => mintOp.updateOne.update.$set.address);
  let wallets = await WalletAddress.collection.find({ address: { $in: mintOpsAddresses } }, { batchSize: 100}).toArray();
  if (wallets.length){
    mintOps = mintOps.map((mintOp) => {
      mintOp.updateOne.update.$set.wallets = wallets.filter((wallet) => wallet.address === mintOp.updateOne.update.$set.address).map((wallet) => wallet.wallet);
      return mintOp;
    });
  }
  await Coin.collection.bulkWrite(mintOps, { ordered: false });
};

TransactionSchema.statics.spendCoins = function (params) {
  let { height, txs } = params;
  let spendOps = [];
  for (let tx of txs) {
    if (tx.isCoinbase()) {
      continue;
    }
    for (let input of tx.inputs) {
      input = input.toObject();
      spendOps.push({
        updateOne: {
          filter: {
            mintTxid: input.prevTxId,
            mintIndex: input.outputIndex,
            spentHeight: { $lt: 0 }
          },
          update: {
            $set: {
              spentTxid: tx.hash,
              spentHeight: height
            }
          }
        }
      });
    }
  }
  return spendOps;
};

TransactionSchema.statics.getTransactions = function (params) {
  let query = params.query;
  return this.collection.aggregate([
    { $match: query },
    {
      $lookup:
        {
          from: 'coins',
          localField: 'txid',
          foreignField: 'spentTxid',
          as: 'inputs'
        }
    },
    {
      $lookup:
        {
          from: 'coins',
          localField: 'txid',
          foreignField: 'mintTxid',
          as: 'outputs'
        }
    }
  ]);
};

TransactionSchema.statics._apiTransform = function(tx, options) {
  let transform = {
    txid: tx.txid,
    network: tx.network,
    blockHeight: tx.blockHeight,
    blockHash: tx.blockHash,
    blockTime: tx.blockTime,
    blockTimeNormalized: tx.blockTimeNormalized,
    coinbase: tx.coinbase,
    fee: tx.fee,
  };
  if(options && options.object) {
    return transform;
  }
  return JSON.stringify(transform);
};

var Transaction = module.exports = mongoose.model('Transaction', TransactionSchema);<commit_msg>Revert "cb for async"<commit_after>const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const async = require('async');
const bitcore = require('bitcore-lib');

const Coin = mongoose.model('Coin');
const WalletAddress = mongoose.model('WalletAddress');
const workerService = require('../services/worker');

const TransactionSchema = new Schema({
  txid: String,
  network: String,
  chain: String,
  blockHeight: Number,
  blockHash: String,
  blockTime: Date,
  blockTimeNormalized: Date,
  coinbase: Boolean,
  fee: Number,
  size: Number,
  locktime: Number,
  wallets: { type: [Schema.Types.ObjectId] },
});

TransactionSchema.index({txid: 1});
TransactionSchema.index({blockHeight: 1});
TransactionSchema.index({blockHash: 1});
TransactionSchema.index({blockTimeNormalized: 1});
TransactionSchema.index({wallets: 1}, {sparse: true});

TransactionSchema.statics.batchImport = async function (params) {
  return new Promise(async (resolve) => {
    let partition = (array, n) => {
      return array.length ? [array.splice(0, n)].concat(partition(array, n)) : [];
    };

    async.eachLimit(params.txs, 8, async function(tx){
      let mintParams = params;
      mintParams.tx = tx;
      await workerService.sendTask('mintCoins', mintParams);
    }, async function(){
      let spendOps = Transaction.spendCoins(params);
      if (spendOps.length) {
        spendOps = partition(spendOps, 100);
        spendOps = spendOps.map((spendBatch) => Coin.collection.bulkWrite(spendBatch, { ordered: false }));
        await Promise.all(spendOps);
      }

      let txOps = await Transaction.addTransactions(params);
      txOps = partition(txOps, 100);
      txOps = txOps.map((txBatch) => Transaction.collection.bulkWrite(txBatch, { ordered: false }));
      await Promise.all(txOps);
      resolve();
    });
  });
};

TransactionSchema.statics.addTransactions = async function(params){
  let { blockHash, blockTime, blockTimeNormalized, height, network, txs } = params;
  return new Promise(async (resolve) => {
    let txids = txs.map((tx) => tx.hash);
    let mintWallets = await Coin.collection.aggregate([
      {
        $match: { mintTxid: {$in: txids} }
      },
      { $unwind: '$wallets' },
      { $group: { _id: '$mintTxid', wallets: { $addToSet: '$wallets' } } }
    ]).toArray();
    let spentWallets = await Coin.collection.aggregate([
      {
        $match: { spentTxid: { $in: txids } }
      },
      { $unwind: '$wallets' },
      { $group: { _id: '$spentTxid', wallets: { $addToSet: '$wallets' } } }
    ]).toArray();

    let txOps = txs.map((tx) => {
      let wallets = [];
      for (let wallet of mintWallets.concat(spentWallets).filter((wallet) => wallet._id === tx.hash)){
        for (let walletMatch of wallet.wallets){
          if (!wallets.find((wallet) => wallet.toHexString() === walletMatch.toHexString())){
            wallets.push(walletMatch);
          }
        }
      }

      return {
        updateOne: {
          filter: {
            txid: tx.hash
          },
          update: {
            $set: {
              network: network,
              blockHeight: height,
              blockHash: blockHash,
              blockTime: blockTime,
              blockTimeNormalized: blockTimeNormalized,
              coinbase: tx.isCoinbase(),
              size: tx.toBuffer().length,
              locktime: tx.nLockTime,
              wallets: wallets
            }
          },
          upsert: true,
          forceServerObjectId: true
        }
      };
    });
    resolve(txOps);
  });
};

TransactionSchema.statics.mintCoins = async function (params) {
  let { height, network, tx } = params;
  tx = bitcore.Transaction(tx);
  let mintOps = [];
  for (let [index, output] of tx.outputs.entries()) {
    let address;
    let scriptBuffer = output.script && output.script.toBuffer();
    try {
      address = output.script.toAddress(network).toString();
      if (address === 'false' && output.script.classify() === 'Pay to public key') {
        let hash = bitcore.crypto.Hash.sha256ripemd160(output.script.chunks[0].buf);
        address = bitcore.Address(hash, network).toString();
      }
    } catch (e) {
      address = 'noAddress';
    }
    let op = {
      updateOne: {
        filter: {
          mintTxid: tx.hash,
          mintIndex: index,
          spentHeight: { $lt: 0 }
        },
        update: {
          $set: {
            network: network,
            mintHeight: height,
            coinbase: tx.isCoinbase(),
            value: output.satoshis,
            address: address,
            script: scriptBuffer,
            spentHeight: -2,
            wallets: []
          }
        },
        upsert: true,
        forceServerObjectId: true
      }
    };
    mintOps.push(op);
  }
  let mintOpsAddresses = mintOps.map((mintOp) => mintOp.updateOne.update.$set.address);
  let wallets = await WalletAddress.collection.find({ address: { $in: mintOpsAddresses } }, { batchSize: 100}).toArray();
  if (wallets.length){
    mintOps = mintOps.map((mintOp) => {
      mintOp.updateOne.update.$set.wallets = wallets.filter((wallet) => wallet.address === mintOp.updateOne.update.$set.address).map((wallet) => wallet.wallet);
      return mintOp;
    });
  }
  await Coin.collection.bulkWrite(mintOps, { ordered: false });
};

TransactionSchema.statics.spendCoins = function (params) {
  let { height, txs } = params;
  let spendOps = [];
  for (let tx of txs) {
    if (tx.isCoinbase()) {
      continue;
    }
    for (let input of tx.inputs) {
      input = input.toObject();
      spendOps.push({
        updateOne: {
          filter: {
            mintTxid: input.prevTxId,
            mintIndex: input.outputIndex,
            spentHeight: { $lt: 0 }
          },
          update: {
            $set: {
              spentTxid: tx.hash,
              spentHeight: height
            }
          }
        }
      });
    }
  }
  return spendOps;
};

TransactionSchema.statics.getTransactions = function (params) {
  let query = params.query;
  return this.collection.aggregate([
    { $match: query },
    {
      $lookup:
        {
          from: 'coins',
          localField: 'txid',
          foreignField: 'spentTxid',
          as: 'inputs'
        }
    },
    {
      $lookup:
        {
          from: 'coins',
          localField: 'txid',
          foreignField: 'mintTxid',
          as: 'outputs'
        }
    }
  ]);
};

TransactionSchema.statics._apiTransform = function(tx, options) {
  let transform = {
    txid: tx.txid,
    network: tx.network,
    blockHeight: tx.blockHeight,
    blockHash: tx.blockHash,
    blockTime: tx.blockTime,
    blockTimeNormalized: tx.blockTimeNormalized,
    coinbase: tx.coinbase,
    fee: tx.fee,
  };
  if(options && options.object) {
    return transform;
  }
  return JSON.stringify(transform);
};

var Transaction = module.exports = mongoose.model('Transaction', TransactionSchema);<|endoftext|>


<commit_before>// Micro BERT encoder/decoder
// Copyright (c) Maxim Sokhatsky (@5HT)

function atom(o) { return { type: "Atom", value: o, toString: function() { return this.value; } }; };
function bin(o) { return { type: "Binary", value: o, toString: function() { return "<<\'"+this.value+"'>>"; } }; };
function tuple() {
    return { type: "Tuple", value: arguments, toString: function() { var s = "";
        for (var i=0;i<this.value.length;i++) { if (s!=="") s+=","; s+=this.value[i]; }
        return "{" + s + "}"; } }; };
function dec(S) { return decode(ltoa(new Uint8Array(S))); };
function enc(s) {
    var ori = encode(s), buf = new Uint8Array(new ArrayBuffer(ori.length));
    for (var i=0; i < buf.length; i++) { buf[i] = ori.charCodeAt(i); }
    return new Blob([buf.buffer]); };

BERT = itoa(131);
SATOM = itoa(115);
ATOM = itoa(100);
BINARY = itoa(109);
SINT = itoa(97);
INT = itoa(98);
FLOAT = itoa(99);
STR = itoa(107);
LIST = itoa(108);
SBIG = itoa(110);
LBIG = itoa(111);
TUPLE = itoa(104);
LTUPLE = itoa(105);
NIL = itoa(106);
ZERO = itoa(0);

function itoa(x) { return String.fromCharCode(x); }
function ltoa(a) { for (var i = 0,s=""; i < a.length; i++) s += itoa(a[i]); return s; };
function itol(Int, Length) {
    var isNegative, OriginalInt, i, Rem, s = "";
    isNegative = (Int < 0);
    if (isNegative) { Int = Int * (0 - 1); }
    OriginalInt = Int;
    for (i = 0; i < Length; i++) {
        Rem = Int % 256;
        if (isNegative) { Rem = 255 - Rem; }
        s = String.fromCharCode(Rem) + s;
        Int = Math.floor(Int / 256);
    }
    if (Int > 0) { throw ("BERT: Range: " + OriginalInt); }
    return s; };
function ltoi(S, Length) {
    var isNegative, i, n, Num = 0;
    isNegative = (S.charCodeAt(0) > 128);
    for (i = 0; i < Length; i++) {
        n = S.charCodeAt(i);
        if (isNegative) { n = 255 - n; }
        if (Num === 0) { Num = n; }
        else { Num = Num * 256 + n; }
    }
    if (isNegative) { Num = Num * (0 - 1); }
    return Num; };
function ltobi(S, Count) {
    var isNegative, i, n, Num = 0;
    isNegative = (S.charCodeAt(0) === 1);
    S = S.substring(1);
    for (i = Count - 1; i >= 0; i--) {
        n = S.charCodeAt(i);
        if (Num === 0) { Num = n; } else { Num = Num * 256 + n; } }
    if (isNegative) { return Num * -1; }
    return Num; };

function encode(o) { return BERT + en_inner(o); };
function en_inner(Obj) { if(Obj === undefined) return NIL; var func = 'en_' + typeof(Obj); return eval(func)(Obj); };
function en_string(Obj) { return STR + itol(Obj.length, 2) + Obj; };
function en_boolean(Obj) { if (Obj) return en_inner(atom("true")); else return en_inner(atom("false")); };
function en_number(Obj) { var isi = (Obj % 1 === 0); if (!isi) { return en_float(Obj); }
    if (isi && Obj >= 0 && Obj < 256) { return SINT + itol(Obj, 1); }
    return INT + itol(Obj, 4); };
function en_float(Obj) { var s = Obj.toExponential(); while (s.length < 31) { s += ZERO; } return FLOAT + s; };
function en_object(Obj) {
    if (Obj.type === "Atom") return en_atom(Obj);
    if (Obj.type === "Binary") return en_bin(Obj);
    if (Obj.type === "Tuple") return en_tuple(Obj);
    if (Obj.constructor.toString().indexOf("Array") !== -1) return en_array(Obj);
    return en_associative_array(Obj); };
function en_atom(Obj) { return ATOM + itol(Obj.value.length, 2) + Obj.value; };
function en_bin(Obj) { return BINARY + itol(Obj.value.length, 4) + Obj.value; };
function en_tuple(Obj) {
    var i, s = "";
    if (Obj.value.length < 256) { s += TUPLE + itol(Obj.value.length, 1); }
    else { s += LTUPLE + itol(Obj.value.length, 4); }
    for (i = 0; i < Obj.value.length; i++) { s += en_inner(Obj.value[i]); }
    return s; };
function en_array(Obj) {
    var i, s = LIST + itol(Obj.length, 4);
    for (i = 0; i < Obj.length; i++) { s += en_inner(Obj[i]); }
    s += NIL;
    return s; };
function en_associative_array(Obj) {
    var key, Arr = [];
    for (key in Obj) { if (Obj.hasOwnProperty(key)) { Arr.push(tuple(atom(key), Obj[key])); } }
    return en_array(Arr); };

function decode(S) {
    if (S[0] !== BERT) { throw ("Not a valid BERT."); }
    var Obj = de_inner(S.substring(1));
    if (Obj.rest !== "") { throw ("Invalid BERT."); }
    return Obj.value; };
function de_inner(S) {
    var Type = S[0];
    S = S.substring(1);
    switch (Type) {
        case SATOM: de_atom(S, 1);
        case ATOM: return de_atom(S, 2);
        case BINARY: return de_bin(S);
        case SINT: return de_integer(S, 1);
        case INT: return de_integer(S, 4);
        case FLOAT: return de_float(S);
        case SBIG: return de_big(S,1);
        case LBIG: return de_big(S,4);
        case STR: return de_string(S);
        case LIST: return de_list(S);
        case TUPLE: return de_tuple(S, 1);
        case NIL: return de_nil(S);
        default: throw ("BERT: " + S.charCodeAt(0)); } };
function de_atom(S, Count) {
    var Size, Value;
    Size = ltoi(S, Count);
    S = S.substring(Count);
    Value = S.substring(0, Size);
    if (Value === "true") { Value = true; }
    else if (Value === "false") { Value = false; }
    return { value: atom(Value), rest:  S.substring(Size) }; };
function de_bin(S) {
    var Size = ltoi(S, 4);
    S = S.substring(4);
    return { value: bin(S.substring(0, Size)), rest: S.substring(Size) }; };
function de_big(S, Count) {
    var Size, Value;
    Size = ltoi(S, Count);
    S = S.substring(Count);
    Value = ltobi(S, Size);
    return { value : Value, rest: S.substring(Size + 1) }; };
function de_integer(S, Count) {
    var Value = ltoi(S, Count);
    S = S.substring(Count);
    return { value: Value, rest: S }; };
function de_float(S) {
    var Size = 31;
    return { value: parseFloat(S.substring(0, Size)), rest: S.substring(Size) }; };
function de_string(S) {
    var Size = ltoi(S, 2);
    S = S.substring(2);
    return { value: S.substring(0, Size), rest: S.substring(Size) }; };
function de_list(S) {
    var Size, i, El, LastChar, Arr = [];
    Size = ltoi(S, 4);
    S = S.substring(4);
    for (i = 0; i < Size; i++) { El = de_inner(S); Arr.push(El.value); S = El.rest; }
    LastChar = S[0];
    if (LastChar !== NIL) { throw ("BERT: Wrong NIL."); }
    S = S.substring(1);
    return { value: Arr, rest: S }; };
function de_tuple(S, Count) {
    var Size, i, El, Arr = [];
    Size = ltoi(S, Count);
    S = S.substring(Count);
    for (i = 0; i < Size; i++) { El = de_inner(S); Arr.push(El.value); S = El.rest; }
    return { value: tuple(Arr), rest: S }; };
function de_nil(S) { return { value: [], rest: S }; };

var $bert = {};

$bert.on = function onbert(evt, callback) // BERT formatter
{
    //    console.log("Bert On");

    // Check for FileReader.readAsArrayBuffer()
    if(!Blob.prototype.isPrototypeOf(evt.data)) { return { status: "error", desc: "not Bert" }; }
    var reader = new FileReader();
    reader.addEventListener("loadend", function() {
        try {
            var erlang = dec(reader.result);
            if (typeof callback  == 'function') callback(erlang);
        } catch (e) { return { status: "error", desc: e }; }
    });
    reader.readAsArrayBuffer(evt.data);

    return { status: "ok" };
};
<commit_msg>bert fix<commit_after>// Micro BERT encoder/decoder
// Copyright (c) Maxim Sokhatsky (@5HT)

function atom(o) { return { type: "Atom", value: o, toString: function() { return this.value; } }; };
function bin(o) { return { type: "Binary", value: o, toString: function() { return "<<\'"+this.value+"'>>"; } }; };
function tuple() {
    return { type: "Tuple", value: arguments, toString: function() { var s = "";
        for (var i=0;i<this.value.length;i++) { if (s!=="") s+=","; s+=this.value[i]; }
        return "{" + s + "}"; } }; };
function dec(S) { return decode(ltoa(new Uint8Array(S))); };
function enc(s) {
    var ori = encode(s), buf = new Uint8Array(new ArrayBuffer(ori.length));
    for (var i=0; i < buf.length; i++) { buf[i] = ori.charCodeAt(i); }
    return new Blob([buf.buffer]); };

BERT = itoa(131);
SATOM = itoa(115);
ATOM = itoa(100);
BINARY = itoa(109);
SINT = itoa(97);
INT = itoa(98);
FLOAT = itoa(99);
STR = itoa(107);
LIST = itoa(108);
SBIG = itoa(110);
LBIG = itoa(111);
TUPLE = itoa(104);
LTUPLE = itoa(105);
NIL = itoa(106);
ZERO = itoa(0);

function itoa(x) { return String.fromCharCode(x); }
function ltoa(a) { for (var i = 0,s=""; i < a.length; i++) s += itoa(a[i]); return s; };
function itol(Int, Length) {
    var isNegative, OriginalInt, i, Rem, s = "";
    isNegative = (Int < 0);
    if (isNegative) { Int = Int * (0 - 1); }
    OriginalInt = Int;
    for (i = 0; i < Length; i++) {
        Rem = Int % 256;
        if (isNegative) { Rem = 255 - Rem; }
        s = String.fromCharCode(Rem) + s;
        Int = Math.floor(Int / 256);
    }
    if (Int > 0) { throw ("BERT: Range: " + OriginalInt); }
    return s; };
function ltoi(S, Length) {
    var isNegative, i, n, Num = 0;
    isNegative = (S.charCodeAt(0) > 128);
    for (i = 0; i < Length; i++) {
        n = S.charCodeAt(i);
        if (isNegative) { n = 255 - n; }
        if (Num === 0) { Num = n; }
        else { Num = Num * 256 + n; }
    }
    if (isNegative) { Num = Num * (0 - 1); }
    return Num; };
function ltobi(S, Count) {
    var isNegative, i, n, Num = 0;
    isNegative = (S.charCodeAt(0) === 1);
    S = S.substring(1);
    for (i = Count - 1; i >= 0; i--) {
        n = S.charCodeAt(i);
        if (Num === 0) { Num = n; } else { Num = Num * 256 + n; } }
    if (isNegative) { return Num * -1; }
    return Num; };

function encode(o) { return BERT + en_inner(o); };
function en_inner(Obj) { if(Obj === undefined) return NIL; var func = 'en_' + typeof(Obj); return eval(func)(Obj); };
function en_string(Obj) { return STR + itol(Obj.length, 2) + Obj; };
function en_boolean(Obj) { if (Obj) return en_inner(atom("true")); else return en_inner(atom("false")); };
function en_number(Obj) { var isi = (Obj % 1 === 0); if (!isi) { return en_float(Obj); }
    if (isi && Obj >= 0 && Obj < 256) { return SINT + itol(Obj, 1); }
    return INT + itol(Obj, 4); };
function en_float(Obj) { var s = Obj.toExponential(); while (s.length < 31) { s += ZERO; } return FLOAT + s; };
function en_object(Obj) {
    if (Obj.type === "Atom") return en_atom(Obj);
    if (Obj.type === "Binary") return en_bin(Obj);
    if (Obj.type === "Tuple") return en_tuple(Obj);
    if (Obj.constructor.toString().indexOf("Array") !== -1) return en_array(Obj);
    return en_associative_array(Obj); };
function en_atom(Obj) { return ATOM + itol(Obj.value.length, 2) + Obj.value; };
function en_bin(Obj) { return BINARY + itol(Obj.value.length, 4) + Obj.value; };
function en_tuple(Obj) {
    var i, s = "";
    if (Obj.value.length < 256) { s += TUPLE + itol(Obj.value.length, 1); }
    else { s += LTUPLE + itol(Obj.value.length, 4); }
    for (i = 0; i < Obj.value.length; i++) { s += en_inner(Obj.value[i]); }
    return s; };
function en_array(Obj) {
    var i, s = LIST + itol(Obj.length, 4);
    for (i = 0; i < Obj.length; i++) { s += en_inner(Obj[i]); }
    s += NIL;
    return s; };
function en_associative_array(Obj) {
    var key, Arr = [];
    for (key in Obj) { if (Obj.hasOwnProperty(key)) { Arr.push(tuple(atom(key), Obj[key])); } }
    return en_array(Arr); };

function decode(S) {
    if (S[0] !== BERT) { throw ("Not a valid BERT."); }
    var Obj = de_inner(S.substring(1));
    if (Obj.rest !== "") { throw ("Invalid BERT."); }
    return Obj.value; };
function de_inner(S) {
    var Type = S[0];
    S = S.substring(1);
    switch (Type) {
        case SATOM: de_atom(S, 1);
        case ATOM: return de_atom(S, 2);
        case BINARY: return de_bin(S);
        case SINT: return de_integer(S, 1);
        case INT: return de_integer(S, 4);
        case FLOAT: return de_float(S);
        case SBIG: return de_big(S,1);
        case LBIG: return de_big(S,4);
        case STR: return de_string(S);
        case LIST: return de_list(S);
        case TUPLE: return de_tuple(S, 1);
        case NIL: return de_nil(S);
        default: throw ("BERT: " + S.charCodeAt(0)); } };
function de_atom(S, Count) {
    var Size, Value;
    Size = ltoi(S, Count);
    S = S.substring(Count);
    Value = S.substring(0, Size);
    if (Value === "true") { Value = true; }
    else if (Value === "false") { Value = false; }
    return { value: atom(Value), rest:  S.substring(Size) }; };
function de_bin(S) {
    var Size = ltoi(S, 4);
    S = S.substring(4);
    return { value: bin(S.substring(0, Size)), rest: S.substring(Size) }; };
function de_big(S, Count) {
    var Size, Value;
    Size = ltoi(S, Count);
    S = S.substring(Count);
    Value = ltobi(S, Size);
    return { value : Value, rest: S.substring(Size + 1) }; };
function de_integer(S, Count) {
    var Value = ltoi(S, Count);
    S = S.substring(Count);
    return { value: Value, rest: S }; };
function de_float(S) {
    var Size = 31;
    return { value: parseFloat(S.substring(0, Size)), rest: S.substring(Size) }; };
function de_string(S) {
    var Size = ltoi(S, 2);
    S = S.substring(2);
    return { value: S.substring(0, Size), rest: S.substring(Size) }; };
function de_list(S) {
    var Size, i, El, LastChar, Arr = [];
    Size = ltoi(S, 4);
    S = S.substring(4);
    for (i = 0; i < Size; i++) { El = de_inner(S); Arr.push(El.value); S = El.rest; }
    LastChar = S[0];
    if (LastChar !== NIL) { throw ("BERT: Wrong NIL."); }
    S = S.substring(1);
    return { value: Arr, rest: S }; };
function de_tuple(S, Count) {
    var Size, i, El, Arr = [];
    Size = ltoi(S, Count);
    S = S.substring(Count);
    for (i = 0; i < Size; i++) { El = de_inner(S); Arr.push(El.value); S = El.rest; }
    return { value: tuple(Arr), rest: S }; };
function de_nil(S) { return { value: [], rest: S }; };

var $bert = {};

$bert.on = function onbert(evt, callback) // BERT formatter
{
    //    console.log("Bert On");

    // Check for FileReader.readAsArrayBuffer()
    if(!Blob.prototype.isPrototypeOf(evt.data)) { return { status: "error", desc: "not Bert" }; }
    var reader = new FileReader();
    reader.addEventListener("loadend", function() {
        try {
            var erlang = dec(reader.result);
            if (typeof callback  == 'function') callback(erlang);
        } catch (e) { return { status: "error", desc: e }; }
    });

    if (evt.data.length >= HEAD_SIZE) reader.readAsArrayBuffer(evt.data);

    return { status: "ok" };
};
<|endoftext|>