<commit_before>var Q = require('q');
var _ = require('lodash');

var utils = require('../utils');
var BaseError = utils.BaseError;

var events = require('../events');
var Event = events.Event;
var Eventable = events.Eventable;

var translate = require('../translate');
var LazyText = translate.LazyText;

var StateError = BaseError.extend(function(self, state, message) {
    /**class:StateError(state, message)
    Occurs when interacting or manipulating a state causes an error.

    :param State state: the state that caused the error.
    :param string message: the error message.
    */
    self.name = 'StateError';
    self.state = state;
    self.message = message;
});

var StateInvalidError = StateError.extend(function(self, state, response, opts) {
    /**class:StateInvalidError(state, response[, opts])
    Occurs when a state receives invalid input. Raised either by a failed
    validation check or by explicitly calling :meth:`State.invalidate`.

    :param State state: the state that caused the error.
    :type response: string or LazyText
    :param response: the response to send back to the user.
    :param string opts.reason: the reason for the error.
    :param string opts.input: the user input that caused the error, if relevant
    */
    StateError.call(self, state);
    self.name = 'StateInvalidError';
    self.state = state;
    self.response = response;

    opts = _.defaults(opts || {}, {
        input: null,
        reason: null
    });
    self.input = opts.input;
    self.reason = opts.reason;

    self.translate = function(i18n) {
        /**:StateInvalidError.translate(i18n)
        Translate the error response.

        :param Translator i18n:
            the translation function to be used for translating the text.
        */
        self.response = i18n(self.response);
    };

    function message() {
        var parts = [
            self.reason || '',
            "(state: " + self.state.name + ")",
            "(response: " + self.response + ")"];

        if (utils.exists(self.input)) {
            parts.push("(input: " + self.input + ")");
        }

        return parts.join(' ').trim();
    }
    Object.defineProperty(self, 'message', {get: message});
});

var StateEvent = Event.extend(function(self, name, state) {
    /**class:StateEvent(name, state, data)
    An event relating to a state.

    :param string name: the event type's name.
    :param State state: the state associated to the event.
    */
    Event.call(self, name);
    self.state = state;
});

var StateInvalidEvent = StateEvent.extend(function(self, state, error) {
    /**class:StateEvent(name, state, error)
    Emitted when a state becomes invalid.

    :param State state: the state associated to the event.
    :param StateInvalidError error: the validation error that occured.
    */
   StateEvent.call(self, 'state:invalid', state);
   self.error = error;
});

var StateInputEvent = StateEvent.extend(function(self, state, content) {
    /**class:StateInputEvent(content)
    Emitted when the user has given input to the state.

    :param State state: the state that the input was given to.
    :param string content: text from the user.

    The event type is ``state:input``.
    */
   StateEvent.call(self, 'state:input', state);
   self.content = content;
});

var StateEnterEvent = StateEvent.extend(function(self, state) {
    /**class:StateEnterEvent()
    Emitted when the state is entered by a user.

    :param State state: the state being entered.

    The event type is ``state:enter``.
    */
   StateEvent.call(self, 'state:enter', state);
});

var StateExitEvent = StateEvent.extend(function(self, state) {
    /**class:StateEnterEvent()
    Emitted when the state is exited by the user.

    :param State state: the state being exited.

    The event type is ``state:exit``.
    */
   StateEvent.call(self, 'state:exit', state);
});

var StateShowEvent = StateEvent.extend(function(self, state, content) {
    /**class:StateShowEvent()
    Emitted when a state's is shown to a user, immediately after
    :meth:`State.display` has completed.

    :param State state: the state being shown.
    :param string content: the content being shown.

    The event type is ``state:show``.
    */
   StateEvent.call(self, 'state:show', state);
   self.content = content;
});

var State = Eventable.extend(function(self, name, opts) {
    /**class:State(name, opts)
    Base class for states in the interaction machine. States can be thought of
    as a single screen in a set of interactions with the user.

    :param string name:
        name used to identify and refer to the state
    :param object opts.metadata:
        data about the state relevant to the interaction machine's current
        user. Optional.
    :param boolean opts.send_reply:
        whether or not a reply should be sent to the user's message. Default is
        `true`. May also be a function, which may return its result via a
        promise.
    :param boolean opts.continue_session:
        whether or not this is the last state in a session. Default is ``true``.
        May also be a function, which may return its result via a promise.
    :param function opts.check:
        a function ``func(input)`` for validating a user's response, where
        ``input`` is the user's input. If a string or :class:`LazyText` is
        returned, the text will be taken as the error response to send back
        to the user. If a :class:`StateInvalidError` is returned, its
        ``response`` property will be taken as the error response to send back
        to the user. Any other value returned will be taken as a non-error. The
        result may be returned via a promise. See :meth:`State.validate`.
    :param object opts.events:
        Optional event name-listener mappings to bind. For example:

        .. code-block:: javascript

            {
                'state:invalid': function(e) {
                    console.log(e);
                }
            }
     */
    Eventable.call(self);

    opts = _.defaults(opts || {}, {
        send_reply: true,
        continue_session: true,
        check: utils.functor(),
        events: {}
    });

    self.im = null;
    self.error = null;
    self.name = name;
    self.send_reply = utils.functor(opts.send_reply);
    self.continue_session = utils.functor(opts.continue_session);
    self.check = opts.check;
    self.events = opts.events;

    // internal reference to the creator opts used to create the state
    self._creator_opts = null;

    self.setup = function(im, metadata) {
        /**:State.setup(im)
        Called before any other methods on the state are called to allow the
        state to set itself up.

        :param InteractionMachine im: interaction machine using the state.
        */
        self.im = im;
        self.metadata = metadata || {};
        self.on(self.events);

        return Q(self.init()).then(function() {
            return self.emit.setup();
        });
    };

    self.init = function() {
        /**:State.init()
        Invoked just after setup has completed, and just before 'setup' event
        is fired to provide subclasses with a setup hook. May return a promise.
        */
    };

    self.input = function(content) {
        /**:State.input()
        Accepts input, invokes :meth:`State.translate.before_input`, then
        emits a :class:`StateInputEvent`` to allow input to be processed.
        */
        var p = Q(self.translators.before_input(self.im.user.i18n));
        return p.then(function() {
            return self.emit.input(content);
        });
    };

    self.save_response = function(response) {
        /**:State.save_response(response)
        Called by sub-classes to store accepted user responses on the user
        object.

        :param string response: value to store as an answer.
        */
        self.im.user.set_answer(self.name, response);
    };

    self.set_next_state = function(next) {
        /**:State:set_next_state(name)

        Set the state that the user will visit after this state using the given
        state name.

        :param string opts.name:
            The name of the next state
        */
        /**:State:set_next_state(opts)

        Set the state that the user will visit after this state using an
        options object.

        :param string opts.name:
            The name of the next state
        :param object opts.metadata:
            Metadata to pass to the next state. Optional.
        :param object opts.creator_opts:
            Options to pass to the state creator when creating the state.
            Optional.
        */
        /**:State:set_next_state(fn[, arg1[, arg2[, ...]]])

        Use a function to set the state that the user will visit this state.

        :param function fn:
            a function that returns name of the next state or an options object
            with data about the next state. The value of ``this`` inside ``f``
            will be the calling state instance. May also return its result via
            a promise.
        :param arguments arg1, arg2, ...:
            arguments to pass to ``fn``
        */
        var args = Array.prototype.slice.call(arguments, 1);
        var p = Q(utils.maybe_call(next, self, args));
        return p.then(function(next) {
            if (typeof next != 'undefined' && next !== null) {
                return self.im.user.state.reset(next);
            }
        });
    };

    self.validate = function(input) {
        /**meth:State.validate(input)
        Validates the given ``input`` using the state's validation checker
        (``self.check``), setting the state's ``self.error`` object and
        emitting a :class:`StateInvalidError` if validation fails.

        :type input: string or LazyText
        :param input:
            The input to be validated. May also be also be of a type supported
            by the relevant state type (for example, :class:`Choice` as an
            input for :class:`ChoiceState`).
        */
        return Q(self.check(input)).then(function(result) {
            var error;

            if (typeof result === "undefined" || result === null) {
                return;
            }

            if (result instanceof StateInvalidError) {
                error = result;
            }
            else if (typeof result == 'string' ||
                     result instanceof LazyText) {
                error = new StateInvalidError(self, result, {
                    reason: 'Bad user input',
                    input: input
                });
            }
            else {
                throw new StateError(self, [
                    ".check() may only return null or undefined (to indicate",
                    " success), or string, LazyText or StateInvalidError",
                    " objects (to indicate errors)",
                ].join(""));
            }

            return self.invalidate(error);
        });
    };

    self.invalidate = function(error) {
        /**:State.invalidate(response)
        Invalidates the user's state, sending the given response to the user.
        Sets the state's ``self.error`` object to an appropriate error and
        emits a  :class:`StateInvalidEvent`.

        :type response: string or :class:`LazyText`
        :param response:
            the response to send back to the user
        */
        /**:State.invalidate(error)
        Invalidates the user's state using an error. Sets the state's
        ``self.error`` object to an appropriate error and emits a
        :class:StateInvalidEvent``.

        :param StateInvalidError error:
            the error to invalidate the user's state with.
        */
        if (typeof error == 'string' || error instanceof LazyText) {
            error = new StateInvalidError(self, error);
        }

        self.error = error;
        return self.emit(new StateInvalidEvent(self, error));
    };

    self.show = function() {
        /**:State.show()

        Translates the state using :meth:`State.translators.before_display`,
        then displays its text.
        */
        var p = Q(self.translators.before_display(self.im.user.i18n));

        return p
            .then(function() {
                return self.display();
            })
            .then(function(content) {
                return self
                    .emit(new StateShowEvent(self, content))
                    .thenResolve(content);
            });
    };

    self.display = function() {
        /**:State.display()
        The content to be displayed to the user. May return a promise.
        */
        return "State: [" + self.name + "]";
    };

    self.translate = function(i18n) {
        /**:State.translate(i18n)

        Translate's a state's text using the given translator.
        May return a promise.

        :param Translator i18n:
            the translation function to be used for translating the text.
        */
    };

    self.translators = {};

    self.translators.before_input = function(i18n) {
        /**:State.translators.before_input(i18n)

        Translate's a state's text using the given translator. Invoked before
        user input is processed. By default, just delegates to
        :meth:`State.translate`. May return a promise.

        :param Translator i18n:
            the translation function to be used for translating the text.
        */
        return self.translate(i18n);
    };

    self.translators.before_display = function(i18n) {
        /**:State.translators.before_display(i18n)
        Translate's a state's text using the given translator. Invoked before
        text is displayed to the user. By default, just delegates to
        :meth:`State.translate`. May return a promise.

        :param Translator i18n:
            the translation function to be used for translating the text.
        */
        return self.translate(i18n);
    };

    self.emit.input = function(content) {
        /**:State.emit.input(im)
        Shortcut for emitting an input event for the state (since this is done
        quite often). See :class:`StateInputEvent`.
        */
        return self.emit(new StateInputEvent(self, content));
    };

    self.serialize = function() {
        return {
            name: self.name,
            metadata: self.metadata
        };
    };

    self._record_creator_opts = function(opts) {
        // Internal method used in AppStates.create(). Only sets its creator opts
        // the first time it is called. Ensures we can recreate the state with the
        // same creator opts when the state is recreated next sandbox run. Needed
        // for cases where AppStates.create() is called inside a state creator. A
        // workaround needed to maintain backwards compatability (a better approach
        // would be to have AppStates.create() return both the new state and the
        // creator opts used to create it.
        if (self._creator_opts !== null) { return; }
        self._creator_opts = opts;
    }

    self.toJSON = self.serialize;
});

this.State = State;

this.StateError = StateError;
this.StateInvalidError = StateInvalidError;

this.StateEvent = StateEvent;
this.StateInputEvent = StateInputEvent;
this.StateEnterEvent = StateEnterEvent;
this.StateExitEvent = StateExitEvent;
this.StateInvalidEvent = StateInvalidEvent;
<commit_msg>Add StateResumeEvent (along with slightly better documenting of state events)<commit_after>var Q = require('q');
var _ = require('lodash');

var utils = require('../utils');
var BaseError = utils.BaseError;

var events = require('../events');
var Event = events.Event;
var Eventable = events.Eventable;

var translate = require('../translate');
var LazyText = translate.LazyText;

var StateError = BaseError.extend(function(self, state, message) {
    /**class:StateError(state, message)
    Occurs when interacting or manipulating a state causes an error.

    :param State state: the state that caused the error.
    :param string message: the error message.
    */
    self.name = 'StateError';
    self.state = state;
    self.message = message;
});

var StateInvalidError = StateError.extend(function(self, state, response, opts) {
    /**class:StateInvalidError(state, response[, opts])
    Occurs when a state receives invalid input. Raised either by a failed
    validation check or by explicitly calling :meth:`State.invalidate`.

    :param State state: the state that caused the error.
    :type response: string or LazyText
    :param response: the response to send back to the user.
    :param string opts.reason: the reason for the error.
    :param string opts.input: the user input that caused the error, if relevant
    */
    StateError.call(self, state);
    self.name = 'StateInvalidError';
    self.state = state;
    self.response = response;

    opts = _.defaults(opts || {}, {
        input: null,
        reason: null
    });
    self.input = opts.input;
    self.reason = opts.reason;

    self.translate = function(i18n) {
        /**:StateInvalidError.translate(i18n)
        Translate the error response.

        :param Translator i18n:
            the translation function to be used for translating the text.
        */
        self.response = i18n(self.response);
    };

    function message() {
        var parts = [
            self.reason || '',
            "(state: " + self.state.name + ")",
            "(response: " + self.response + ")"];

        if (utils.exists(self.input)) {
            parts.push("(input: " + self.input + ")");
        }

        return parts.join(' ').trim();
    }
    Object.defineProperty(self, 'message', {get: message});
});

var StateEvent = Event.extend(function(self, name, state) {
    /**class:StateEvent(name, state, data)
    An event relating to a state.

    :param string name: the event type's name.
    :param State state: the state associated to the event.
    */
    Event.call(self, name);
    self.state = state;
});

var StateInvalidEvent = StateEvent.extend(function(self, state, error) {
    /**class:StateEvent(name, state, error)
    Emitted when a state becomes invalid.

    :param State state: the state associated to the event.
    :param StateInvalidError error: the validation error that occured.
    */
   StateEvent.call(self, 'state:invalid', state);
   self.error = error;
});

var StateInputEvent = StateEvent.extend(function(self, state, content) {
    /**class:StateInputEvent(content)
    Emitted when the user has given input to the state.

    :param State state: the state that the input was given to.
    :param string content: text from the user.

    The event type is ``state:input``.
    */
   StateEvent.call(self, 'state:input', state);
   self.content = content;
});

var StateEnterEvent = StateEvent.extend(function(self, state) {
    /**class:StateEnterEvent()
    Emitted when the state is entered by a user.

    This happens when the state is
    switched to from another state, or when the state is created if this is the
    start of a new session).

    :param State state: the state being entered.

    The event type is ``state:enter``.
    */
   StateEvent.call(self, 'state:enter', state);
});

var StateResumeEvent = StateEvent.extend(function(self, state) {
    /**class:StateResumeEvent()
    Emitted when the state is resumed.

    When the user enters input, the new sandbox run is started, causing the
    state to be re-created (or resumed) to process the user's input. This means
    that when this event is emitted, the state has already been entered (see
    :class:`StateEnterEvent`) and its content has been shown to the user (in a
    previous sandbox run).

    :param State state: the state being resumed.

    The event type is ``state:resume``.
    */
   StateEvent.call(self, 'state:resume', state);
});

var StateExitEvent = StateEvent.extend(function(self, state) {
    /**class:StateEnterEvent()
    Emitted when the state is exited by the user. This happens immediately
    before the interaction machine switches to a different state (see
    :class:`StateEnterEvent`).

    :param State state: the state being exited.

    The event type is ``state:exit``.
    */
   StateEvent.call(self, 'state:exit', state);
});

var StateShowEvent = StateEvent.extend(function(self, state, content) {
    /**class:StateShowEvent()
    Emitted when a state's is shown to a user, immediately after
    :meth:`State.display` has completed.

    :param State state: the state being shown.
    :param string content: the content being shown.

    The event type is ``state:show``.
    */
   StateEvent.call(self, 'state:show', state);
   self.content = content;
});

var State = Eventable.extend(function(self, name, opts) {
    /**class:State(name, opts)
    Base class for states in the interaction machine. States can be thought of
    as a single screen in a set of interactions with the user.

    :param string name:
        name used to identify and refer to the state
    :param object opts.metadata:
        data about the state relevant to the interaction machine's current
        user. Optional.
    :param boolean opts.send_reply:
        whether or not a reply should be sent to the user's message. Default is
        `true`. May also be a function, which may return its result via a
        promise.
    :param boolean opts.continue_session:
        whether or not this is the last state in a session. Default is ``true``.
        May also be a function, which may return its result via a promise.
    :param function opts.check:
        a function ``func(input)`` for validating a user's response, where
        ``input`` is the user's input. If a string or :class:`LazyText` is
        returned, the text will be taken as the error response to send back
        to the user. If a :class:`StateInvalidError` is returned, its
        ``response`` property will be taken as the error response to send back
        to the user. Any other value returned will be taken as a non-error. The
        result may be returned via a promise. See :meth:`State.validate`.
    :param object opts.events:
        Optional event name-listener mappings to bind. For example:

        .. code-block:: javascript

            {
                'state:invalid': function(e) {
                    console.log(e);
                }
            }
     */
    Eventable.call(self);

    opts = _.defaults(opts || {}, {
        send_reply: true,
        continue_session: true,
        check: utils.functor(),
        events: {}
    });

    self.im = null;
    self.error = null;
    self.name = name;
    self.send_reply = utils.functor(opts.send_reply);
    self.continue_session = utils.functor(opts.continue_session);
    self.check = opts.check;
    self.events = opts.events;

    // internal reference to the creator opts used to create the state
    self._creator_opts = null;

    self.setup = function(im, metadata) {
        /**:State.setup(im)
        Called before any other methods on the state are called to allow the
        state to set itself up.

        :param InteractionMachine im: interaction machine using the state.
        */
        self.im = im;
        self.metadata = metadata || {};
        self.on(self.events);

        return Q(self.init()).then(function() {
            return self.emit.setup();
        });
    };

    self.init = function() {
        /**:State.init()
        Invoked just after setup has completed, and just before 'setup' event
        is fired to provide subclasses with a setup hook. May return a promise.
        */
    };

    self.input = function(content) {
        /**:State.input()
        Accepts input, invokes :meth:`State.translate.before_input`, then
        emits a :class:`StateInputEvent`` to allow input to be processed.
        */
        var p = Q(self.translators.before_input(self.im.user.i18n));
        return p.then(function() {
            return self.emit.input(content);
        });
    };

    self.save_response = function(response) {
        /**:State.save_response(response)
        Called by sub-classes to store accepted user responses on the user
        object.

        :param string response: value to store as an answer.
        */
        self.im.user.set_answer(self.name, response);
    };

    self.set_next_state = function(next) {
        /**:State:set_next_state(name)

        Set the state that the user will visit after this state using the given
        state name.

        :param string opts.name:
            The name of the next state
        */
        /**:State:set_next_state(opts)

        Set the state that the user will visit after this state using an
        options object.

        :param string opts.name:
            The name of the next state
        :param object opts.metadata:
            Metadata to pass to the next state. Optional.
        :param object opts.creator_opts:
            Options to pass to the state creator when creating the state.
            Optional.
        */
        /**:State:set_next_state(fn[, arg1[, arg2[, ...]]])

        Use a function to set the state that the user will visit this state.

        :param function fn:
            a function that returns name of the next state or an options object
            with data about the next state. The value of ``this`` inside ``f``
            will be the calling state instance. May also return its result via
            a promise.
        :param arguments arg1, arg2, ...:
            arguments to pass to ``fn``
        */
        var args = Array.prototype.slice.call(arguments, 1);
        var p = Q(utils.maybe_call(next, self, args));
        return p.then(function(next) {
            if (typeof next != 'undefined' && next !== null) {
                return self.im.user.state.reset(next);
            }
        });
    };

    self.validate = function(input) {
        /**meth:State.validate(input)
        Validates the given ``input`` using the state's validation checker
        (``self.check``), setting the state's ``self.error`` object and
        emitting a :class:`StateInvalidError` if validation fails.

        :type input: string or LazyText
        :param input:
            The input to be validated. May also be also be of a type supported
            by the relevant state type (for example, :class:`Choice` as an
            input for :class:`ChoiceState`).
        */
        return Q(self.check(input)).then(function(result) {
            var error;

            if (typeof result === "undefined" || result === null) {
                return;
            }

            if (result instanceof StateInvalidError) {
                error = result;
            }
            else if (typeof result == 'string' ||
                     result instanceof LazyText) {
                error = new StateInvalidError(self, result, {
                    reason: 'Bad user input',
                    input: input
                });
            }
            else {
                throw new StateError(self, [
                    ".check() may only return null or undefined (to indicate",
                    " success), or string, LazyText or StateInvalidError",
                    " objects (to indicate errors)",
                ].join(""));
            }

            return self.invalidate(error);
        });
    };

    self.invalidate = function(error) {
        /**:State.invalidate(response)
        Invalidates the user's state, sending the given response to the user.
        Sets the state's ``self.error`` object to an appropriate error and
        emits a  :class:`StateInvalidEvent`.

        :type response: string or :class:`LazyText`
        :param response:
            the response to send back to the user
        */
        /**:State.invalidate(error)
        Invalidates the user's state using an error. Sets the state's
        ``self.error`` object to an appropriate error and emits a
        :class:StateInvalidEvent``.

        :param StateInvalidError error:
            the error to invalidate the user's state with.
        */
        if (typeof error == 'string' || error instanceof LazyText) {
            error = new StateInvalidError(self, error);
        }

        self.error = error;
        return self.emit(new StateInvalidEvent(self, error));
    };

    self.show = function() {
        /**:State.show()

        Translates the state using :meth:`State.translators.before_display`,
        then displays its text.
        */
        var p = Q(self.translators.before_display(self.im.user.i18n));

        return p
            .then(function() {
                return self.display();
            })
            .then(function(content) {
                return self
                    .emit(new StateShowEvent(self, content))
                    .thenResolve(content);
            });
    };

    self.display = function() {
        /**:State.display()
        The content to be displayed to the user. May return a promise.
        */
        return "State: [" + self.name + "]";
    };

    self.translate = function(i18n) {
        /**:State.translate(i18n)

        Translate's a state's text using the given translator.
        May return a promise.

        :param Translator i18n:
            the translation function to be used for translating the text.
        */
    };

    self.translators = {};

    self.translators.before_input = function(i18n) {
        /**:State.translators.before_input(i18n)

        Translate's a state's text using the given translator. Invoked before
        user input is processed. By default, just delegates to
        :meth:`State.translate`. May return a promise.

        :param Translator i18n:
            the translation function to be used for translating the text.
        */
        return self.translate(i18n);
    };

    self.translators.before_display = function(i18n) {
        /**:State.translators.before_display(i18n)
        Translate's a state's text using the given translator. Invoked before
        text is displayed to the user. By default, just delegates to
        :meth:`State.translate`. May return a promise.

        :param Translator i18n:
            the translation function to be used for translating the text.
        */
        return self.translate(i18n);
    };

    self.emit.input = function(content) {
        /**:State.emit.input(im)
        Shortcut for emitting an input event for the state (since this is done
        quite often). See :class:`StateInputEvent`.
        */
        return self.emit(new StateInputEvent(self, content));
    };

    self.serialize = function() {
        return {
            name: self.name,
            metadata: self.metadata
        };
    };

    self._record_creator_opts = function(opts) {
        // Internal method used in AppStates.create(). Only sets its creator opts
        // the first time it is called. Ensures we can recreate the state with the
        // same creator opts when the state is recreated next sandbox run. Needed
        // for cases where AppStates.create() is called inside a state creator. A
        // workaround needed to maintain backwards compatability (a better approach
        // would be to have AppStates.create() return both the new state and the
        // creator opts used to create it.
        if (self._creator_opts !== null) { return; }
        self._creator_opts = opts;
    }

    self.toJSON = self.serialize;
});

this.State = State;

this.StateError = StateError;
this.StateInvalidError = StateInvalidError;

this.StateEvent = StateEvent;
this.StateInputEvent = StateInputEvent;
this.StateEnterEvent = StateEnterEvent;
this.StateExitEvent = StateExitEvent;
this.StateInvalidEvent = StateInvalidEvent;
<|endoftext|>


<commit_before>function StatsWriter() {
  this.result = {
    element: 0,
    text: 0,
    attribute: 0,
    elements: {},
    maxDepth: 0,
    leaf: 0,
    inlineStyle: 0,
    classAttr: 0,
    idAttr: 0
  };
  this.currentDepth = 1;
  this.depths = 0;
  this.leafDepths = 0;
  this.pendingLeafDepth = 0;

}

StatsWriter.prototype.beginOpenElement = function(name) {
  if (name == 'base')
    return;
  this.result.element += 1;
  if (!(name in this.result.elements)) {
    this.result.elements[name] = 0;
  }
  this.result.elements[name] += 1;
  this.currentDepth += 1;
  if (this.currentDepth > this.result.maxDepth) {
    this.result.maxDepth = this.currentDepth;
  }
  this.depths += this.currentDepth;
  this.pendingLeafDepth = this.currentDepth;
}

StatsWriter.prototype.attribute = function(name, value) {
  this.result.attribute += 1;
  if (name == 'style') {
    this.result.inlineStyle += 1;
    console.log(value);
  }
  else if (name == 'class')
    this.result.classAttr += 1;
  else
    this.result.idAttr += 1;
}

StatsWriter.prototype.endOpenElement = function() {
}

StatsWriter.prototype.closeElement = function(name) {
  this.currentDepth -= 1;
  if (this.pendingLeafDepth > 0) {
    this.leafDepths += this.pendingLeafDepth;
    this.result.leaf += 1;
    this.pendingLeafDepth = 0;
  }
}

StatsWriter.prototype.text = function(text) {
  this.result.text += 1;
}

StatsWriter.prototype.comment = function(comment) {
}

StatsWriter.prototype.getHTML = function() {
  var result = "elements: " + this.result.element + "\n";
  result += "leaf elements: " + this.result.leaf + "\n";
  for (var element in this.result.elements) {
    result += '\t' + element + ': ' + this.result.elements[element] + '\n';
  }
  result += "text segments: " + this.result.text + "\nattributes: " + this.result.attribute + "\n";
  result += "  inline styles: " + this.result.inlineStyle + "\n";
  result += "  class attributes: " + this.result.classAttr + "\n";
  result += "  id attributes: " + this.result.idAttr + "\n";
  result += "max depth: " + this.result.maxDepth + "\n";
  result += "average depth: " + (this.depths / this.result.element) + "\n";
  result += "average leaf depth: " + (this.leafDepths / this.result.leaf) + "\n";

  return result;
}

module.exports = StatsWriter;
<commit_msg>Dont print out the contents of style attributes<commit_after>function StatsWriter() {
  this.result = {
    element: 0,
    text: 0,
    attribute: 0,
    elements: {},
    maxDepth: 0,
    leaf: 0,
    inlineStyle: 0,
    classAttr: 0,
    idAttr: 0
  };
  this.currentDepth = 1;
  this.depths = 0;
  this.leafDepths = 0;
  this.pendingLeafDepth = 0;

}

StatsWriter.prototype.beginOpenElement = function(name) {
  if (name == 'base')
    return;
  this.result.element += 1;
  if (!(name in this.result.elements)) {
    this.result.elements[name] = 0;
  }
  this.result.elements[name] += 1;
  this.currentDepth += 1;
  if (this.currentDepth > this.result.maxDepth) {
    this.result.maxDepth = this.currentDepth;
  }
  this.depths += this.currentDepth;
  this.pendingLeafDepth = this.currentDepth;
}

StatsWriter.prototype.attribute = function(name, value) {
  this.result.attribute += 1;
  if (name == 'style') {
    this.result.inlineStyle += 1;
  }
  else if (name == 'class')
    this.result.classAttr += 1;
  else
    this.result.idAttr += 1;
}

StatsWriter.prototype.endOpenElement = function() {
}

StatsWriter.prototype.closeElement = function(name) {
  this.currentDepth -= 1;
  if (this.pendingLeafDepth > 0) {
    this.leafDepths += this.pendingLeafDepth;
    this.result.leaf += 1;
    this.pendingLeafDepth = 0;
  }
}

StatsWriter.prototype.text = function(text) {
  this.result.text += 1;
}

StatsWriter.prototype.comment = function(comment) {
}

StatsWriter.prototype.getHTML = function() {
  var result = "elements: " + this.result.element + "\n";
  result += "leaf elements: " + this.result.leaf + "\n";
  for (var element in this.result.elements) {
    result += '\t' + element + ': ' + this.result.elements[element] + '\n';
  }
  result += "text segments: " + this.result.text + "\nattributes: " + this.result.attribute + "\n";
  result += "  inline styles: " + this.result.inlineStyle + "\n";
  result += "  class attributes: " + this.result.classAttr + "\n";
  result += "  id attributes: " + this.result.idAttr + "\n";
  result += "max depth: " + this.result.maxDepth + "\n";
  result += "average depth: " + (this.depths / this.result.element) + "\n";
  result += "average leaf depth: " + (this.leafDepths / this.result.leaf) + "\n";

  return result;
}

module.exports = StatsWriter;
<|endoftext|>


<commit_before>'use strict';

var assert = require('assert');
var Contract = require('../contract');
var merge = require('merge');

/**
 * Represents a storage item, including contracts, challenges, the shard itself
 * along with metadata describing download count, payments, etc
 * @constructor
 * @license AGPL-3.0
 * @param {Object} data
 * @param {String|null} data.hash - Shard hash to use as storage key
 * @param {Stream|null} data.shard - Raw binary blob of shard
 * @param {Object} data.contracts - Dictionary of nodeID:{@link Contract}
 * @param {Object} data.trees - Dictionary of nodeID:merkleLeaves
 * @param {Object} data.challenges - Dictionary of nodeID:privateAuditData
 * @param {Object} data.meta - Dictionary of arbitrary nodeID:metadata
 */
function StorageItem(data) {
  if (!(this instanceof StorageItem)) {
    return new StorageItem(data);
  }

  data = merge({}, data);

  this.hash = null;
  this.shard = null;
  this.contracts = {};
  this.trees = data.trees || {};
  this.challenges = data.challenges || {};
  this.meta = data.meta || {};
  this.modified = data.modified || Date.now();

  this._init(data);
}

/**
 * Adds the trees and challenges to the item keyed by nodeID
 * @param {Contact} contact - The contact associated with the trees
 * @param {Audit|AuditStream} audit - The audit or challenge generator
 */
StorageItem.prototype.addAuditRecords = function(contact, audit) {
  this.trees[contact.nodeID] = audit.getPublicRecord();
  this.challenges[contact.nodeID] = audit.getPrivateRecord();

  return this;
};

/**
 * Adds the contract data keyed by nodeID
 * @param {Contact} contact - The contact associated with the trees
 * @param {Contract} contract - The storage contract instance
 */
StorageItem.prototype.addContract = function(contact, contract) {
  this.contracts[contact.nodeID] = contract;

  return this;
};

/**
 * Adds the meta data keyed by nodeID
 * @param {Contact} contact - The contact associated with the trees
 * @param {Object} meta - Arbitrary metadata about the shard
 */
StorageItem.prototype.addMetaData = function(contact, meta) {
  this.meta[contact.nodeID] = meta;

  return this;
};

/**
 * Updates the timestamp for the item
 */
StorageItem.prototype.updateTimestamp = function() {
  this.modified = Date.now();

  return this;
};

/**
 * Returns a plain object representation of the item
 */
StorageItem.prototype.toObject = function() {
  var contracts = {};

  for (var c in this.contracts) {
    contracts[c] = this.contracts[c].toObject();
  }

  return {
    hash: this.hash,
    shard: null,
    contracts: contracts,
    challenges: this.challenges,
    trees: this.trees,
    meta: this.meta,
    modified: this.modified
  };
};

/**
 * Initializes the item values with the given data
 * @private
 * @param {Object} data
 * @returns {StorageItem}
 */
StorageItem.prototype._init = function(data) {
  assert(typeof data === 'object', 'Invalid item data supplied');

  this.shard = data.shard || null;
  this.hash = data.hash || null;

  for (var nodeID in data.contracts) {
    this.contracts[nodeID] = new Contract(data.contracts[nodeID]);
  }

  return this;
};

module.exports = StorageItem;
<commit_msg>logging<commit_after>'use strict';

var assert = require('assert');
var Contract = require('../contract');
var merge = require('merge');

/**
 * Represents a storage item, including contracts, challenges, the shard itself
 * along with metadata describing download count, payments, etc
 * @constructor
 * @license AGPL-3.0
 * @param {Object} data
 * @param {String|null} data.hash - Shard hash to use as storage key
 * @param {Stream|null} data.shard - Raw binary blob of shard
 * @param {Object} data.contracts - Dictionary of nodeID:{@link Contract}
 * @param {Object} data.trees - Dictionary of nodeID:merkleLeaves
 * @param {Object} data.challenges - Dictionary of nodeID:privateAuditData
 * @param {Object} data.meta - Dictionary of arbitrary nodeID:metadata
 */
function StorageItem(data) {
  if (!(this instanceof StorageItem)) {
    return new StorageItem(data);
  }

  data = merge({}, data);

  this.hash = null;
  this.shard = null;
  this.contracts = {};
  this.trees = data.trees || {};
  this.challenges = data.challenges || {};
  this.meta = data.meta || {};
  this.modified = data.modified || Date.now();

  this._init(data);
}

/**
 * Adds the trees and challenges to the item keyed by nodeID
 * @param {Contact} contact - The contact associated with the trees
 * @param {Audit|AuditStream} audit - The audit or challenge generator
 */
StorageItem.prototype.addAuditRecords = function(contact, audit) {
  this.trees[contact.nodeID] = audit.getPublicRecord();
  this.challenges[contact.nodeID] = audit.getPrivateRecord();

  return this;
};

/**
 * Adds the contract data keyed by nodeID
 * @param {Contact} contact - The contact associated with the trees
 * @param {Contract} contract - The storage contract instance
 */
StorageItem.prototype.addContract = function(contact, contract) {
  this.contracts[contact.nodeID] = contract;

  return this;
};

/**
 * Adds the meta data keyed by nodeID
 * @param {Contact} contact - The contact associated with the trees
 * @param {Object} meta - Arbitrary metadata about the shard
 */
StorageItem.prototype.addMetaData = function(contact, meta) {
  this.meta[contact.nodeID] = meta;

  return this;
};

/**
 * Updates the timestamp for the item
 */
StorageItem.prototype.updateTimestamp = function() {
  this.modified = Date.now();

  return this;
};

/**
 * Returns a plain object representation of the item
 */
StorageItem.prototype.toObject = function() {
  var contracts = {};

  for (var c in this.contracts) {
    console.log(this.contracts)
    console.log(this.contracts[c].toObject())
    contracts[c] = this.contracts[c].toObject();
  }

  return {
    hash: this.hash,
    shard: null,
    contracts: contracts,
    challenges: this.challenges,
    trees: this.trees,
    meta: this.meta,
    modified: this.modified
  };
};

/**
 * Initializes the item values with the given data
 * @private
 * @param {Object} data
 * @returns {StorageItem}
 */
StorageItem.prototype._init = function(data) {
  assert(typeof data === 'object', 'Invalid item data supplied');

  this.shard = data.shard || null;
  this.hash = data.hash || null;

  for (var nodeID in data.contracts) {
    this.contracts[nodeID] = new Contract(data.contracts[nodeID]);
  }

  return this;
};

module.exports = StorageItem;
<|endoftext|>


<commit_before>var utils = require("../utils"),
    EdgeQuery = require("../edge_query")

exports.mixin = function(Model) {
  var fn = Model.prototype

  Model.key = function() {
    return Model.type + "s"
  }

  fn.key = function() {
    return Model.type + ":" + this.id
  }

  Model.destroy = function(id, cb) {
    Model.client.del(Model.type + ":" + id, function(err, data) {
      Model.client.ZREM(Model.type +"s", id, function(err, data) {
        cb()
      })
    })
  }

  fn._persist = function(next) {
    var self = this, o = self.stringy_object()

    Model.client.HMSET(this.key(), o, function(err, data) {
      if(err) return next(false)
      if(data != "OK") throw "no error and data != 1"
      next()
    })
  }

  fn._get_id = function(next) {

    var self = this
    if(this.id == null && Model.properties.id.auto_inc) {
      Model.client.INCR(Model.key() + ":id", function(err, data) {
        self.id = data
        next()
      })
    }
    else next()
  }

  fn._update_keys = function (next) {
    var self = this
    Model.client.ZADD(Model.key(), self.rank(), self.id, function(err, data) {
      if(err) console.log("failed ZADD", err)
      next()
    })
  }

  fn._saveStack = [
    fn.validate,
    fn._get_id,
    fn._persist,
    fn._update_keys,
    fn._finalize_save
  ]

  fn.stringy_object = function() {
    var o = {}
    for(var name in Model.properties)
      o[name] = Model.stringify_property(name, this[name])
    return o
  }

  Model.stringify_property = function(name, data) {
    var type = Model.properties[name].type
    if(data == null)      return ""
    if(type == "string")  return data
    if(type == "number")  return data.toString()
    if(type == "json")    return JSON.stringify(data)
    if(type == "date")    return (data/1).toString()
  }

  Model.unstringify_property = function(name, data) {
    var type = Model.properties[name].type
    if(data === "")       return null
    if(type == "string")  return data
    if(type == "number")  return parseFloat(data)
    if(type == "json")    return JSON.parse(data)
    if(type == "date")    return new Date(data/1)
  }

  Model.new_from_strings = function(o) {
    var ret = {}
    for(var name in Model.properties) {
      if(!(name in o)) continue
      ret[name] = this.unstringify_property(name, o[name])
    }
    return ret
  }

  fn.rank = function() {
    return this.id
  }

  Model.count = function(cb) {
    Model.client.ZCARD(Model.key(), function(err, data) {
      cb(data)
    })
  }

  Model.find = function(id, cb) {
    Model.client.HGETALL(Model.type + ":" +id, function(err, data) {
      if(err || !data || data.id == null) return cb(null, err)
      var o = Model.new_from_strings(data)
      cb(Model.load(o))
    })
  }

  Model.all = function(a, b) {
    var query, cb

    if(arguments.length == 1)  cb = a
    else if(arguments.length == 2)  cb = b, query = a
    else throw "bad args for MMModel.all"

    var func = function(err, data) {

      if(err) return cb(false)
      if(!data) return cb(0)
      for(var i=0; i<data.length; i++)
        data[i] = Model.properties.id.type == "string" ? data[i].toString() : parseInt(data[i])

      Model.load_ids(data, function() {
        cb.apply(this, arguments)
        // Q.complete()
      })
    }

    var args = query ? query.split(" ") : []
    args.unshift(Model.key())
    args.push(func)

    // Q.add(function() {
      Model.client.sort.apply(Model.client, args)
    // })
  }

  Model.load_ids = function(ids, cb) {

    var ex = Model.client.multi();
    for(var i=0; i< ids.length;i++)  {
      var key = Model.type + ":" +ids[i]
      ex = ex.hgetall(key)
    }

    ex.exec(function (err, replies) {
      var models = []
      replies.forEach(function (data, index) {
        models[index] = Model.load(Model.new_from_strings(data))
      });
      cb(models)
    });
  }

  Model.exists = function(id, cb) {
    Model.client.exists(Model.type + ":" + id, function(err, data) {
      cb(err || data == 1)
    })
  }

  Model.find_edges = Model.prototype.find_edges = function(type) {
     return new EdgeQuery(this.key() +":"+type, this)
  }

  Model.add_edge = Model.prototype.add_edge = function(type, id, score, cb) {
    if(arguments.length == 3 && typeof score == "function") {
        cb = score; score = id
    }
    Model.client.ZADD(this.key() +":"+type, score, id, function(err, data) {
      cb ? cb(!err) : null
    })
  }

  Model.remove_edge = Model.prototype.remove_edge = function(type, id, cb) {
    Model.client.ZREM(this.key() +":"+type, id, function(err, data) {
      cb ? cb(!err) : null
    })
  }
}

<commit_msg>tweak<commit_after>var utils = require("../utils"),
    EdgeQuery = require("../edge_query")

exports.mixin = function(Model) {
  var fn = Model.prototype

  Model.key = function() {
    return Model.type + "s"
  }

  fn.key = function() {
    return Model.type + ":" + this.id
  }

  Model.destroy = function(id, cb) {
    Model.client.del(Model.type + ":" + id, function(err, data) {
      Model.client.ZREM(Model.type +"s", id, function(err, data) {
        cb()
      })
    })
  }

  fn._persist = function(next) {
    var self = this, o = self.stringy_object()

    Model.client.HMSET(this.key(), o, function(err, data) {
      if(err) return next(false)
      if(data != "OK") throw "no error and data != 1"
      next()
    })
  }

  fn._get_id = function(next) {

    var self = this
    if(this.id == null && Model.properties.id.auto_inc) {
      Model.client.INCR(Model.key() + ":id", function(err, data) {
        self.id = data
        next()
      })
    }
    else next()
  }

  fn._update_keys = function (next) {
    var self = this
    Model.client.ZADD(Model.key(), self.rank(), self.id, function(err, data) {
      if(err) console.log("failed ZADD", err)
      next()
    })
  }

  fn._saveStack = [
    fn.validate,
    fn._get_id,
    fn._persist,
    fn._update_keys,
    fn._finalize_save
  ]

  fn.stringy_object = function() {
    var o = {}
    for(var name in Model.properties)
      o[name] = Model.stringify_property(name, this[name])
    return o
  }

  Model.stringify_property = function(name, data) {
    var type = Model.properties[name].type
    if(data == null)      return ""
    if(type == "string")  return data
    if(type == "number")  return data.toString()
    if(type == "json")    return JSON.stringify(data)
    if(type == "date")    return (data/1).toString()
  }

  Model.unstringify_property = function(name, data) {
    var type = Model.properties[name].type
    if(data === "")       return null
    if(type == "string")  return data
    if(type == "number")  return parseFloat(data)
    if(type == "json")    return JSON.parse(data)
    if(type == "date")    return new Date(data/1)
  }

  Model.new_from_strings = function(o) {
    var ret = {}
    for(var name in Model.properties) {
      if(!(name in o)) continue
      ret[name] = this.unstringify_property(name, o[name])
    }
    return ret
  }

  fn.rank = function() {
    return this.id
  }

  Model.count = function(cb) {
    Model.client.ZCARD(Model.key(), function(err, data) {
      cb(data)
    })
  }

  Model.find = function(id, cb) {
    Model.client.HGETALL(Model.type + ":" +id, function(err, data) {
      if(err || !data || data.id == null) return cb(null, err)
      var o = Model.new_from_strings(data)
      cb(Model.load(o))
    })
  }

  Model.all = function(a, b) {
    var query, cb

    if(arguments.length == 1)  cb = a
    else if(arguments.length == 2)  cb = b, query = a
    else throw "bad args for MMModel.all"

    var func = function(err, data) {

      if(err) return cb(false)
      if(!data) return cb(0)
      for(var i=0; i<data.length; i++)
        data[i] = Model.properties.id.type == "string" ? data[i].toString() : parseInt(data[i])

      Model.load_ids(data, function() {
        cb.apply(this, arguments)
        // Q.complete()
      })
    }

    var args = query ? query.split(" ") : []
    args.unshift(Model.key())
    args.push(func)

    // Q.add(function() {
      Model.client.sort.apply(Model.client, args)
    // })
  }

  Model.load_ids = function(ids, cb) {

    var ex = Model.client.multi();
    for(var i=0; i< ids.length;i++)  {
      var key = Model.type + ":" +ids[i]
      ex = ex.hgetall(key)
    }

    ex.exec(function (err, replies) {
      var models = []
      replies.forEach(function (data, index) {
        models[index] = Model.load(Model.new_from_strings(data))
      });
      cb(models)
    });
  }

  Model.exists = function(id, cb) {
    Model.client.exists(Model.type + ":" + id, function(err, data) {
      cb(err || data == 1)
    })
  }

  Model.find_edges = Model.prototype.find_edges = function(type) {
     return new EdgeQuery(this.key() +":"+type, this)
  }

  Model.add_edge = Model.prototype.add_edge = function(type, id, score, cb) {
    if(arguments.length == 3 && typeof score == "function") {
        cb = score; score = id
    }
    var fscore = parseFloat(score) || score/1
    if(isNaN(fscore)) {
      throw "ERROR: score: " + score + " is NaN"
    }

    Model.client.ZADD(this.key() +":"+type, fscore, id, function(err, data) {
      console.log(err, data, "X")
      cb && cb(!err)
    })
  }

  Model.remove_edge = Model.prototype.remove_edge = function(type, id, cb) {
    Model.client.ZREM(this.key() +":"+type, id, function(err, data) {
      cb && cb(!err)
    })
  }
}

<|endoftext|>


<commit_before>var chokidar = require("chokidar");
var ReadableStream = require("stream").Readable;
var isWindowsCI = require("is-appveyor");

module.exports = function(graphStream){
	var watcher, addresses, allNodes;
	var stream = new ReadableStream({ objectMode: true });
	stream._read = function(){};

	function updateWatch(data){
		allNodes = invert(data.graph);
		addresses = Object.keys(allNodes);

		watcher.add(addresses);
	}

	function changed(event, address){
		var node = allNodes[address] || "";
		stream.push(node || "");
	}

	var watchOptions = { ignoreInitial: true };
	// Use this to prevent false positives on AppVeyor
	if(isWindowsCI) {
		watchOptions.awaitWriteFinish = true;
	}

	watcher = chokidar.watch(null, watchOptions);
	watcher.on("all", changed);

	graphStream.on("data", updateWatch);

	return stream;
};

// Given an array of bundles, inverts them into a table of addresses to nodes
function invert(graph){
	var table = {};
	Object.keys(graph).forEach(function(moduleName){
		var node = graph[moduleName];
		if(node.load.address){
			table[node.load.address.replace("file:", "")] = node;
		}

	});
	return table;
}
<commit_msg>Ignore node_modules in live-reload on Windows CI<commit_after>var chokidar = require("chokidar");
var ReadableStream = require("stream").Readable;
var isWindowsCI = require("is-appveyor");

var ignored = /node_modules/;

module.exports = function(graphStream){
	var watcher, addresses, allNodes;
	var stream = new ReadableStream({ objectMode: true });
	stream._read = function(){};

	function updateWatch(data){
		allNodes = invert(data.graph);
		addresses = Object.keys(allNodes);

		watcher.add(addresses);
	}

	function changed(event, address){
		if(isWindowsCI && address && ignored.test(address)) {
			return;
		}

		var node = allNodes[address] || "";
		stream.push(node || "");
	}

	watcher = chokidar.watch(null, { ignoreInitial: true });
	watcher.on("all", changed);

	graphStream.on("data", updateWatch);

	return stream;
};

// Given an array of bundles, inverts them into a table of addresses to nodes
function invert(graph){
	var table = {};
	Object.keys(graph).forEach(function(moduleName){
		var node = graph[moduleName];
		if(node.load.address){
			table[node.load.address.replace("file:", "")] = node;
		}

	});
	return table;
}
<|endoftext|>


<commit_before>// Requires
//var request = require('request');
var http = require('http');
var encode = require('../encode');

function errorHandler(data) {
    console.log('converter stderr: ' + data);
}

function streamGet(req, res) {
    var format = encode.formats.ogv;
    var url = req.player.streamUrl();

    var ffmpeg = encode.encoderStream(format, function(code) {
        console.log('Finished converting with code =', code);
        res.end();
    });

    var vreq = http.request(url, function(vres) {
        // Write header
        res.writeHead(200, {'Cotent-Type': format.mime});

        ffmpeg.stdout.pipe(res);

        vres.pipe(ffmpeg.stdin);
    });

    vreq.on('error', function(err) {
        return res.send(err.toString());
    });

    res.on('end', function() {
        ffmpeg.kill();
    });

    vreq.end();
}

// Just return length and other info
// No need to convert here
// since there is no content obviously
function streamHead(req, res) {
    var url = req.player.streamUrl();
    request.head(url).pipe(res);
}

// Exports
exports.streamGet = streamGet;
exports.streamHead = streamHead;<commit_msg>ffmpeg kill fix<commit_after>// Requires
//var request = require('request');
var http = require('http');
var encode = require('../encode');

function errorHandler(data) {
    console.log('converter stderr: ' + data);
}

function streamGet(req, res) {
    var format = encode.formats.ogv;
    var url = req.player.streamUrl();

    var dead = false;
    var ffmpeg = encode.encoderStream(format, function(code) {
        dead = true;
        console.log('Finished converting with code =', code);
        res.end();
    });

    var vreq = http.request(url, function(vres) {
        // Write header
        res.writeHead(200, {'Cotent-Type': format.mime});

        ffmpeg.stdout.pipe(res);

        vres.pipe(ffmpeg.stdin);
    });

    vreq.on('error', function(err) {
        return res.send(err.toString());
    });

    res.on('end', function() {
        if(dead) return;
        ffmpeg.kill();
    });

    vreq.end();
}

// Just return length and other info
// No need to convert here
// since there is no content obviously
function streamHead(req, res) {
    var url = req.player.streamUrl();
    request.head(url).pipe(res);
}

// Exports
exports.streamGet = streamGet;
exports.streamHead = streamHead;<|endoftext|>


<commit_before>var http = require('http'),
    url = require('url'),
    fs = require('fs'),
    events = require('events'),
    mess = require('mess'),
    path = require('path'),
    spawn = require('child_process').spawn,
    MapPool = require('./mappool'),
    netlib = require('./netlib'),
    localized = {},
    locked = {};

/**
 * Map constructor
 *
 * @param {String} mapfile the location of the mapfile.
 * @param {String} mapfile_dir directory to which the mapfile should be cached.
 * @param {Boolean} base64 whether the mapfile is base64 encoded.
 */
var Map = function(mapfile, mapfile_dir, base64) {
    this.mapfile_dir = mapfile_dir;
    this.mapfile = (new Buffer(mapfile, (base64) ? 'base64' : 'utf-8'))
        .toString('utf-8');
};

Map.__defineGetter__('mapfile_64', function() {
    return this.mapfile.toString('base64');
});

/**
 * Return whether the current mapfile is downloaded and completed
 * TODO: make multi-process safe
 *
 * @return {Boolean} whether the mapfile is downloaded and ready.
 */
Map.prototype.localized = function() {
    if (localized[this.mapfile]) {
        return true;
    }
    return false;
};

/**
 * Return whether the current mapfile is "locked", ie. currently
 * being downloaded concurrently.
 * TODO: make multi-process safe
 *
 * @return {Boolean} whether the mapfile is locked.
 */
Map.prototype.locked = function() {
    if (locked[this.mapfile]) {
        return locked[this.mapfile];
    }
    return false;
};

Map.prototype.mapfilePos = function(mapfile) {
    return this.mapfile_dir + '/' + netlib.safe64(mapfile) + '.xml';
}

/**
 * Localize a mapfile - download core and related files
 *
 * @param {Function} callback to call once completed.
 */
Map.prototype.localize = function(callback) {
    var that = this;
    if (this.localized()) {
        callback();
    }
    else if (this.locked()) {
        console.log('waiting on lock ' + this.mapfile);
        this.locked().on('unlock', callback);
    }
    else {
        locked[this.mapfile] = new events.EventEmitter();
        console.log('downloading ' + this.mapfile);
        this.localizeSelf(this.mapfile, function(err, filename, data) {
            new(mess.Renderer)({
                data_dir: that.mapfile_dir,
                optimization: false,
                filename: this.mapfile
            }).render(data, function(err, output) {
                if (err) {
                    callback(err);
                } else {
                    fs.writeFile(that.mapfilePos(that.mapfile), output, function() {
                        localized[that.mapfile] = true;
                        locked[that.mapfile].emit('unlock');
                        locked[that.mapfile] = false;
                        callback();
                    });
                }
            });
        });
    }
};

/**
 * Download just a mapfile to the local host
 *
 * @param {String} mapfile the XML map file to download.
 * @param {Function} callback function to run once downloaded.
 */
Map.prototype.localizeSelf = function(mapfile, callback) {
    // this downloads the mapfile into a buffer, but doesn't save it until
    // externals are fixed up - unclear of whether this will scale for
    // very large mapfiles
    netlib.get(mapfile, null, callback);
};

/**
 * Download all files referenced by a mapfile
 *
 * @param {String} data string of XML data in a mapfile.
 * @param {Function} callback function to run once completed.
 */
Map.prototype.render = function(bbox, callback) {
    // requires a localized, loaded map
    this.localize(function(err) {
        this.map.render(bbox, callback);
    });
};

/**
 * Get a mapnik map from the pool
 *
 * @return {Object} mapnik map.
 */
Map.prototype.mapnik_map = function() {
    return MapPool.get(this.mapfilePos(this.mapfile));
};

module.exports = Map;
<commit_msg>Unlock when appropriate<commit_after>var http = require('http'),
    url = require('url'),
    fs = require('fs'),
    events = require('events'),
    mess = require('mess'),
    path = require('path'),
    spawn = require('child_process').spawn,
    MapPool = require('./mappool'),
    netlib = require('./netlib'),
    localized = {},
    locked = {};

/**
 * Map constructor
 *
 * @param {String} mapfile the location of the mapfile.
 * @param {String} mapfile_dir directory to which the mapfile should be cached.
 * @param {Boolean} base64 whether the mapfile is base64 encoded.
 */
var Map = function(mapfile, mapfile_dir, base64) {
    this.mapfile_dir = mapfile_dir;
    this.mapfile = (new Buffer(mapfile, (base64) ? 'base64' : 'utf-8'))
        .toString('utf-8');
};

Map.__defineGetter__('mapfile_64', function() {
    return this.mapfile.toString('base64');
});

/**
 * Return whether the current mapfile is downloaded and completed
 * TODO: make multi-process safe
 *
 * @return {Boolean} whether the mapfile is downloaded and ready.
 */
Map.prototype.localized = function() {
    if (localized[this.mapfile]) {
        return true;
    }
    return false;
};

/**
 * Return whether the current mapfile is "locked", ie. currently
 * being downloaded concurrently.
 * TODO: make multi-process safe
 *
 * @return {Boolean} whether the mapfile is locked.
 */
Map.prototype.locked = function() {
    if (locked[this.mapfile]) {
        return locked[this.mapfile];
    }
    return false;
};

Map.prototype.mapfilePos = function(mapfile) {
    return this.mapfile_dir + '/' + netlib.safe64(mapfile) + '.xml';
}

/**
 * Localize a mapfile - download core and related files
 *
 * @param {Function} callback to call once completed.
 */
Map.prototype.localize = function(callback) {
    var that = this;
    if (this.localized()) {
        callback();
    }
    else if (this.locked()) {
        console.log('waiting on lock ' + this.mapfile);
        this.locked().on('unlock', callback);
    }
    else {
        locked[this.mapfile] = new events.EventEmitter();
        console.log('downloading ' + this.mapfile);
        this.localizeSelf(this.mapfile, function(err, filename, data) {
            new(mess.Renderer)({
                data_dir: that.mapfile_dir,
                optimization: false,
                filename: this.mapfile
            }).render(data, function(err, output) {
                if (err) {
                    locked[that.mapfile].emit('unlock');
                    locked[that.mapfile] = false;
                    callback(err);
                } else {
                    fs.writeFile(that.mapfilePos(that.mapfile), output, function() {
                        localized[that.mapfile] = true;
                        locked[that.mapfile].emit('unlock');
                        locked[that.mapfile] = false;
                        callback();
                    });
                }
            });
        });
    }
};

/**
 * Download just a mapfile to the local host
 *
 * @param {String} mapfile the XML map file to download.
 * @param {Function} callback function to run once downloaded.
 */
Map.prototype.localizeSelf = function(mapfile, callback) {
    // this downloads the mapfile into a buffer, but doesn't save it until
    // externals are fixed up - unclear of whether this will scale for
    // very large mapfiles
    netlib.get(mapfile, null, callback);
};

/**
 * Download all files referenced by a mapfile
 *
 * @param {String} data string of XML data in a mapfile.
 * @param {Function} callback function to run once completed.
 */
Map.prototype.render = function(bbox, callback) {
    // requires a localized, loaded map
    this.localize(function(err) {
        this.map.render(bbox, callback);
    });
};

/**
 * Get a mapnik map from the pool
 *
 * @return {Object} mapnik map.
 */
Map.prototype.mapnik_map = function() {
    return MapPool.get(this.mapfilePos(this.mapfile));
};

module.exports = Map;
<|endoftext|>


<commit_before>(function () {"use strict";})();
/**
 * Created by apizzimenti on 5/19/16.
 */

/**
 * @author Anthony Pizzimenti
 *
 * @desc Binds the left, right, up, and down keys to the running game instance.
 *
 * @param game {object} Phaser game instance.
 * @param context {object} the context game is bound to.
 */

function keymap (game, context) {

    context.cursors = game.input.keyboard.createCursorKeys();

    context.game.input.keyboard.addKeyCapture([
        Phaser.Keyboard.LEFT,
        Phaser.Keyboard.RIGHT,
        Phaser.Keyboard.UP,
        Phaser.Keyboard.DOWN
    ]);

    return {
        esc: game.input.keyboard.addKey(Phaser.Keyboard.ESC),
        space: game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR)
    }
};

/**
 * @author Anthony Pizzimenti
 *
 * @desc Changes the direction of the given sprite based on keypresses.
 *
 * @param Player {Player} Phaser Player sprite.
 * @param context {object} the context game is bound to.
 */

function keymapTrack (Player, context) {

    /**
     * Default speed in px/s for specified sprite.
     * @type {number}
     */

    var speed = 50,
        sprite = Player.sprite;

    sprite.body.velocity.z = 0;
    // sprite.isoZ = -3 + (3 * Math.sin(sprite.isoX + sprite.isoY));

    if (context.cursors.up.isDown) {
        sprite.body.velocity.y = -speed;
        sprite.direction = 1;
    } else if (context.cursors.down.isDown) {
        sprite.body.velocity.y = speed;
        sprite.direction = 3;
    } else {
        sprite.body.velocity.y = 0;
    }

    if (context.cursors.left.isDown) {
        sprite.body.velocity.x = -speed;
        sprite.direction = 2;
    } else if (context.cursors.right.isDown) {
        sprite.body.velocity.x = speed;
        sprite.direction = 0;
    } else {
        sprite.body.velocity.x = 0;
    }
}
<commit_msg>organize System functions<commit_after>(function () {"use strict";})();
/**
 * Created by apizzimenti on 5/19/16.
 * This is automatically included in the isometric-features package to provide its users some ease-of-use.
 */

/**
 * @author Anthony Pizzimenti
 *
 * @desc Setup object.
 *
 * @param game {object} Current Phaser game instance.
 * @param scope {object} Angular scope.
 *
 * @property game {object} Phaser game instance.
 * @property scope {object} Angular scope.
 *
 * @class Setup
 * @constructor
 * @this Setup
 */

function System (game, scope) {
    this.game = game;
    this.scope = scope;
}

/**
 * @author Anthony Pizzimenti
 *
 * @desc Binds the left, right, up, and down keys to the running game instance.
 *
 * @param game {object} Phaser game instance.
 * @param context {object} the context game is bound to.
 *
 * @returns {{esc: object, space: object}}
 *
 * @this System
 */

System.prototype.keymap = function (game, context) {

    context.cursors = game.input.keyboard.createCursorKeys();

    context.game.input.keyboard.addKeyCapture([
        Phaser.Keyboard.LEFT,
        Phaser.Keyboard.RIGHT,
        Phaser.Keyboard.UP,
        Phaser.Keyboard.DOWN
    ]);

    return {
        esc: game.input.keyboard.addKey(Phaser.Keyboard.ESC),
        space: game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR)
    }
};

/**
 * @author Anthony Pizzimenti
 *
 * @desc Changes the direction of the given sprite based on keypresses.
 *
 * @param Player {Player} Phaser Player sprite.
 * @param context {object} the context game is bound to.
 *
 * @this System
 */

System.prototype.keymapTrack = function (Player, context) {

    /**
     * Default speed in px/s for specified sprite.
     * @type {number}
     */

    var speed = 50,
        sprite = Player.sprite;

    sprite.body.velocity.z = 0;
    // sprite.isoZ = -3 + (3 * Math.sin(sprite.isoX + sprite.isoY));

    if (context.cursors.up.isDown) {
        sprite.body.velocity.y = -speed;
        sprite.direction = 1;
    } else if (context.cursors.down.isDown) {
        sprite.body.velocity.y = speed;
        sprite.direction = 3;
    } else {
        sprite.body.velocity.y = 0;
    }

    if (context.cursors.left.isDown) {
        sprite.body.velocity.x = -speed;
        sprite.direction = 2;
    } else if (context.cursors.right.isDown) {
        sprite.body.velocity.x = speed;
        sprite.direction = 0;
    } else {
        sprite.body.velocity.x = 0;
    }
};

/**
 * @author Anthony Pizzimenti
 *
 * @desc Called in the default state's preload() function property.
 *
 * @property tiles {Phaser.Group} Phaser group dedicated to tiles.
 * @property characters {Phaser.Group} Phaser group dedicated to moving sprites.
 *
 * @this System
 */

System.prototype.preload = function () {

    // add isometric plugin
    this.game.plugins.add(new Phaser.Plugin.Isometric(this.game));

    // new group for tiles
    this.tiles = this.game.add.group();
    this.tiles.enableBody = true;
    this.tiles.physicsBodyType = Phaser.Plugin.Isometric.ISOARCADE;

    // new group for moving sprites
    this.characters = this.game.add.group();
    this.characters.enableBody = true;
    this.characters.physicsBodyType = Phaser.Plugin.Isometric.ISOARCADE;

    // start the physics system and set the anchor for the game
    this.game.physics.startSystem(Phaser.Plugin.Isometric.ISOARCADE);
    this.game.iso.anchor.setTo(...Globals.anchor);

    // set the world bounds; these aren't the bounds of the physics system, but are larger than the game window
    // so the camera follows the Player
    this.game.world.setBounds(0, 0, 2400, 2400);

    // set up game properties
    this.game.time.advancedTiming = true;
    this.game.debug.renderShadow = false;
    this.game.stage.disableVisibilityChange = true;

    // shoot some messages into the console when the onLoad event is fired.
    this.scope.$on("load", (e) => {

        var host = window.location.origin + "/",
            docs = "https://apizzimenti.github.io/isometric-features-docs/";

        console.log("%c isometric-features documentation " + `%c ${docs}`,
            "background: #0095dd; color: #FFF",
            "color: #5D5D5D");
    });
};

/**
 * @author Anthony Pizzimenti
 *
 * @desc Called in the default state's update() function.
 *
 * @param context {object} Context to which the space, escape, and arrow keys are attached.
 * @param characters {Phaser.Group} Group dedicated to moving sprites.
 * @param [player=null] {Player} Main character.
 * @param [creatures=null] {Animal[]} Array of animal sprites.
 * @param [mouse=null] {Mouse} Game's Mouse object.
 * @param [props=null] {object} Properties for mouse animation.
 *
 * @see Mouse#selected
 *
 * @this System
 */

System.prototype.update = function (context, characters, player, creatures, mouse, props) {

    // collision physics
    // this.game.physics.isoArcade.collide(characters);
    this.game.iso.simpleSort(characters);

    // keep track of the Player's direction, keymapping, and restrict the vision radius
    if (Globals.paramNotExist(player)) {
        direction(player);
        this.keymapTrack(player, context);
        player.visionRadius();
    }

    // keep track of each Animal's direction
    if (Globals.paramNotExist(creatures)) {
        creatures.forEach((creature) => {
            direction(creature);
            creature.isVisible(player);
        });
    }

    // mouse tracking; if the mouse switch is ON, make sure the tiles are glowing
    if (Globals.paramNotExist(mouse)) {
        mouse.update();

        if (mouse.switch) {
            Globals.paramNotExist(props) ? mouse.selected(props) : mouse.selected();
        }
    }
};
<|endoftext|>


<commit_before>var debug = require('debug')('liftie:coerce');
var entities = require('./entities');

module.exports = coerceStatus;

var map = {
  'o': 'open',
  'y': 'open',
  'yes': 'open',
  'ok': 'open',
  'open': 'open',
  'opened': 'open',
  'n': 'closed',
  'x': 'closed',
  'no': 'closed',
  'closed': 'closed',
  'closedfortheday': 'closed',
  'geschlossen': 'closed',
  'blank': 'closed',
  'h': 'hold',
  'hold': 'hold',
  'windhold': 'hold',
  'maintenancehold': 'hold',
  'safety': 'hold',
  'onhold': 'hold',
  'standby': 'hold',
  'scheduled': 'scheduled',
  'onschedule': 'scheduled',
  'expected': 'scheduled',
  'delay': 'scheduled',
  'delayed': 'scheduled'
};

/**
 * More flexible slice. If from, or to are strings we determine their position in the string
 * and slice appropriately. In all other cases it works like normal slice
 */
function slice(str, from, to) {
  if (typeof from === 'string') {
    from = str.lastIndexOf(from) + from.length;
  }
  if (typeof to === 'string') {
    to = str.indexOf(to);
  }
  return str.slice(from, to);
}

/*
 * slice, trim, lowercase and coerce to standard liftie statuses
 * If no usuable status is found, return 'scheduled'
 */
function coerceStatus (status, start, stop) {
  var s;
  if (arguments.length > 1) {
    status = slice(status, start, stop);
  }
  status = entities(status);
  status = status.replace(/[\s_\-]+/g, '').toLowerCase();
	s = map[status];
  if (!s) {
    debug('Uknown status %s. Treated as <scheduled>', status);
    s = 'scheduled';
  }
  return s;
}<commit_msg>add typical colors to be coerced as status<commit_after>var debug = require('debug')('liftie:coerce');
var entities = require('./entities');

module.exports = coerceStatus;

var map = {
  'o': 'open',
  'y': 'open',
  'yes': 'open',
  'ok': 'open',
  'open': 'open',
  'opened': 'open',
  'green': 'open',
  'n': 'closed',
  'x': 'closed',
  'no': 'closed',
  'closed': 'closed',
  'closedfortheday': 'closed',
  'geschlossen': 'closed',
  'blank': 'closed',
  'red': 'closed',
  'h': 'hold',
  'hold': 'hold',
  'windhold': 'hold',
  'maintenancehold': 'hold',
  'safety': 'hold',
  'onhold': 'hold',
  'standby': 'hold',
  'scheduled': 'scheduled',
  'onschedule': 'scheduled',
  'expected': 'scheduled',
  'delay': 'scheduled',
  'delayed': 'scheduled',
  'blue': 'scheduled'
};

/**
 * More flexible slice. If from, or to are strings we determine their position in the string
 * and slice appropriately. In all other cases it works like normal slice
 */
function slice(str, from, to) {
  if (typeof from === 'string') {
    from = str.lastIndexOf(from) + from.length;
  }
  if (typeof to === 'string') {
    to = str.indexOf(to);
  }
  return str.slice(from, to);
}

/*
 * slice, trim, lowercase and coerce to standard liftie statuses
 * If no usuable status is found, return 'scheduled'
 */
function coerceStatus (status, start, stop) {
  var s;
  if (arguments.length > 1) {
    status = slice(status, start, stop);
  }
  status = entities(status);
  status = status.replace(/[\s_\-]+/g, '').toLowerCase();
	s = map[status];
  if (!s) {
    debug('Uknown status %s. Treated as <scheduled>', status);
    s = 'scheduled';
  }
  return s;
}<|endoftext|>


<commit_before>'use babel';
import SubAtom from 'sub-atom';

import {
  isEnabled as autohideEnabled,
  show as showTreeView,
  hide as hideTreeView,
} from './autohide-tree-view.js';

import {
  getConfig,
} from './config.js';

import {
  getTreeViewEl,
  getContentWidth,
  isChildOf,
} from './utils.js';

var touchEvents;

export function consumeTouchEvents(touchEventsService) {
  touchEvents = touchEventsService;
  if(getConfig('showOn').match('touch')) {
    atom.packages.activatePackage('atom-touch-events').then(() => enable());
  }
}

var enabled = false;
var disposables;

export function enable() {
  if(enabled || !atom.packages.isPackageActive('atom-touch-events')) return;
  enabled = true;

  disposables = new SubAtom();
  disposables.add([
    touchEvents.onDidTouchSwipeLeft(swipeChange, () => swipeEnd(false)),
    touchEvents.onDidTouchSwipeRight(swipeChange, () => swipeEnd(true)),
  ]);
}

export function disable() {
  if(!enabled) return;
  enabled = false;

  disposables.dispose();
  disposables = null;
}

export function isEnabled() {
  return enabled;
}

var currentSwipeEvent = null;

function shouldInitSwipe(event, source) {
  // no swipe if either autohide or touch events is disabled
  if(!autohideEnabled() || !getConfig('showOn').match('touch')) return false;

  var { pageX } = event.touches[0];

  // if swipe target isn't the tree view, check if
  // swipe is in touchArea
  if(!isChildOf(source, getTreeViewEl().parentNode)) {
    // no swipe if not in touch area
    if(getConfig('showOnRightSide', 'tree-view')) {
      if(pageX < window.innerWidth - getConfig('touchAreaSize')) return false;
    } else {
      if(pageX > getConfig('touchAreaSize')) return false;
    }
  }

  currentSwipeEvent = event;
  return true;
}

// triggered while swiping the tree view
function swipeChange({ args: event, source, deltaX }) {
  // check if swipe should show the tree view
  if(!currentSwipeEvent && !shouldInitSwipe(event, source)) return;
  // calculate new tree view width
  if(getConfig('showOnRightSide', 'tree-view')) deltaX *= -1;
  var newWidth = getTreeViewEl().clientWidth + deltaX;
  newWidth = Math.min(getContentWidth(), Math.max(getConfig('minWidth'), newWidth));
  // request the frame
  requestAnimationFrame(function frame() {
    getTreeViewEl().style.width = `${newWidth}px`;
  });
}

// triggered after swipe, completely opens/closes the tree view
// depending on the side of the tree view and swipe direction
function swipeEnd(toRight) {
  if(!currentSwipeEvent) return;
  currentSwipeEvent = null;
  // showOnRightSide XOR toRight
  !getConfig('showOnRightSide', 'tree-view') + !toRight == 1 ? showTreeView() : hideTreeView();
}
<commit_msg>cleanup<commit_after>'use babel';
import SubAtom from 'sub-atom';

import {
  isEnabled as autohideEnabled,
  show as showTreeView,
  hide as hideTreeView,
} from './autohide-tree-view.js';

import {
  getConfig,
} from './config.js';

import {
  getTreeViewEl,
  getContentWidth,
  isChildOf,
} from './utils.js';

var touchEvents;

export function consumeTouchEvents(touchEventsService) {
  touchEvents = touchEventsService;
  if(getConfig('showOn').match('touch')) enable();
}

var enabled = false;
var disposables;

export function enable() {
  if(enabled || !touchEvents) return;
  enabled = true;

  disposables = new SubAtom();
  disposables.add([
    touchEvents.onDidTouchSwipeLeft(swipeChange, () => swipeEnd(false)),
    touchEvents.onDidTouchSwipeRight(swipeChange, () => swipeEnd(true)),
  ]);
}

export function disable() {
  if(!enabled) return;
  enabled = false;

  disposables.dispose();
  disposables = null;
}

export function isEnabled() {
  return enabled;
}

var currentSwipeEvent = null;

function shouldInitSwipe(event, source) {
  // no swipe if either autohide or touch events is disabled
  if(!autohideEnabled() || !getConfig('showOn').match('touch')) return false;

  var { pageX } = event.touches[0];

  // if swipe target isn't the tree view, check if
  // swipe is in touchArea
  if(!isChildOf(source, getTreeViewEl().parentNode)) {
    // no swipe if not in touch area
    if(getConfig('showOnRightSide', 'tree-view')) {
      if(pageX < window.innerWidth - getConfig('touchAreaSize')) return false;
    } else {
      if(pageX > getConfig('touchAreaSize')) return false;
    }
  }

  currentSwipeEvent = event;
  return true;
}

// triggered while swiping the tree view
function swipeChange({ args: event, source, deltaX }) {
  // check if swipe should show the tree view
  if(!currentSwipeEvent && !shouldInitSwipe(event, source)) return;
  // calculate new tree view width
  if(getConfig('showOnRightSide', 'tree-view')) deltaX *= -1;
  var newWidth = getTreeViewEl().clientWidth + deltaX;
  newWidth = Math.min(getContentWidth(), Math.max(getConfig('minWidth'), newWidth));
  // request the frame
  requestAnimationFrame(function frame() {
    getTreeViewEl().style.width = `${newWidth}px`;
  });
}

// triggered after swipe, completely opens/closes the tree view
// depending on the side of the tree view and swipe direction
function swipeEnd(toRight) {
  if(!currentSwipeEvent) return;
  currentSwipeEvent = null;
  // showOnRightSide XOR toRight
  !getConfig('showOnRightSide', 'tree-view') + !toRight == 1 ? showTreeView() : hideTreeView();
}
<|endoftext|>


<commit_before>'use strict';
const qs = require('querystring');

// Get URLs to attached media in MMS.
function createMediaArray(messageObject) {
  return Object.keys(messageObject)
    .filter(function (key) {
      return key.match(/^MediaUrl/);
    })
    .map(function (key) {
      return messageObject[key];
    });
}

module.exports = function(messageObject) {
  messageObject = qs.parse(messageObject);
  console.log('TWILIO PARSE RECEIVED MESSAGE:', messageObject);
  if (!messageObject) return;
  var hasBody = typeof messageObject.Body !== 'undefined'
    && messageObject.Body.length > 0;
  var hasSender = typeof messageObject.From !== 'undefined'
    && messageObject.From.length > 0;
  var hasMedia = parseInt(messageObject.NumMedia || 0);
  if (hasSender && (hasBody || hasMedia)) {
    var o = {
      sender: messageObject.From,
      text: messageObject.Body,
      originalRequest: messageObject,
      type: 'twilio'
    };
    if (hasMedia) {
      o.media = createMediaArray(messageObject);
    }
    return o;
  }
};
<commit_msg>use array of objects so we can provide content type<commit_after>'use strict';
const qs = require('querystring');

// Get URLs to attached media in MMS.
function createMediaArray(messageObject) {
  const media = [];
  for (let i = 0; i < messageObject.NumMedia; i++) {
    media.push({
      contentType: messageObject[`MediaContentType${i}`],
      url: messageObject[`MediaUrl${i}`]
    });
  }
  return media;
}

module.exports = function(messageObject) {
  messageObject = qs.parse(messageObject);
  if (!messageObject) return;
  const hasBody = typeof messageObject.Body !== 'undefined'
    && messageObject.Body.length > 0;
  const hasSender = typeof messageObject.From !== 'undefined'
    && messageObject.From.length > 0;
  const hasMedia = parseInt(messageObject.NumMedia || 0);
  if (hasSender && (hasBody || hasMedia)) {
    const o = {
      sender: messageObject.From,
      text: messageObject.Body,
      originalRequest: messageObject,
      type: 'twilio'
    };
    if (hasMedia) {
      o.media = createMediaArray(messageObject);
    }
    return o;
  }
};
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const jsesc = require('jsesc')
const utils = require('../utils')

class common extends utils {
  static objToString (obj) {
    try {
      return `${obj}`
    } catch (e) {
      return `[${e}]`
    }
  }

  static getAllProperties (obj) {
    let list = []
    while (obj) {
      list = list.concat(Object.getOwnPropertyNames(obj))
      obj = Object.getPrototypeOf(obj)
    }
    return list
  }

  static getKeysFromHash (obj) {
    let list = []
    for (let p in obj) {
      list.push(p)
    }
    return list
  }

  static quote (s) {
    if (typeof s === 'string') {
      return `'${jsesc(s)}'`
    } else {
      return jsesc(s)
    }
  }

  static b64encode (str) {
    // Unicode safe b64 encoding
    // https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_Unicode_Problem
    if (process.browser) {
      return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g,
        function toSolidBytes (match, p1) {
          // noinspection JSCheckFunctionSignatures
          return String.fromCharCode('0x' + p1)
        })
      )
    } else {
      return Buffer.from(str).toString('base64')
    }
  }

  static b64decode (str) {
    // Unicode safe b64 decoding
    // https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_Unicode_Problem
    if (process.browser) {
      return decodeURIComponent(atob(str).split('').map(function (c) {
        return `%${('00' + c.charCodeAt(0).toString(16)).slice(-2)}`
      }).join(''))
    } else {
      return Buffer.from(str, 'base64').toString('ascii')
    }
  }

  static uniqueList (list) {
    let tmp = {}
    let r = []
    for (let i = 0; i < list.length; i++) {
      tmp[list[i]] = list[i]
    }
    for (let i in tmp) {
      r.push(tmp[i])
    }
    return r
  }

  static mergeHash (obj1, obj2) {
    for (let p in obj2) {
      try {
        if (obj2[p].constructor === Object) {
          obj1[p] = utils.common.mergeHash(obj1[p], obj2[p])
        } else {
          obj1[p] = obj2[p]
        }
      } catch (e) {
        obj1[p] = obj2[p]
      }
    }
    return obj1
  }

  static mockup (obj) {
    return obj.split('\n').map((ln) => ln.trim()).join('')
  }
}

module.exports = common
<commit_msg>Only escape minimal characters and allow HTML escapes<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const jsesc = require('jsesc')
const utils = require('../utils')

class common extends utils {
  static objToString (obj) {
    try {
      return `${obj}`
    } catch (e) {
      return `[${e}]`
    }
  }

  static getAllProperties (obj) {
    let list = []
    while (obj) {
      list = list.concat(Object.getOwnPropertyNames(obj))
      obj = Object.getPrototypeOf(obj)
    }
    return list
  }

  static getKeysFromHash (obj) {
    let list = []
    for (let p in obj) {
      list.push(p)
    }
    return list
  }

  /**
   * Escape and quote a string
   * @param s {string} - String to be quoted
   * @param html {boolean} - Identifies whether the string must be HTML safe
   * @returns {*}
   */
  static quote (s, html = false) {
    const options = {
      minimal: true,
      isScriptContext: html
    }

    if (typeof s === 'string') {
      return `'${jsesc(s, options)}'`
    } else {
      return jsesc(s, options)
    }
  }

  static b64encode (str) {
    // Unicode safe b64 encoding
    // https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_Unicode_Problem
    if (process.browser) {
      return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g,
        function toSolidBytes (match, p1) {
          // noinspection JSCheckFunctionSignatures
          return String.fromCharCode('0x' + p1)
        })
      )
    } else {
      return Buffer.from(str).toString('base64')
    }
  }

  static b64decode (str) {
    // Unicode safe b64 decoding
    // https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_Unicode_Problem
    if (process.browser) {
      return decodeURIComponent(atob(str).split('').map(function (c) {
        return `%${('00' + c.charCodeAt(0).toString(16)).slice(-2)}`
      }).join(''))
    } else {
      return Buffer.from(str, 'base64').toString('ascii')
    }
  }

  static uniqueList (list) {
    let tmp = {}
    let r = []
    for (let i = 0; i < list.length; i++) {
      tmp[list[i]] = list[i]
    }
    for (let i in tmp) {
      r.push(tmp[i])
    }
    return r
  }

  static mergeHash (obj1, obj2) {
    for (let p in obj2) {
      try {
        if (obj2[p].constructor === Object) {
          obj1[p] = utils.common.mergeHash(obj1[p], obj2[p])
        } else {
          obj1[p] = obj2[p]
        }
      } catch (e) {
        obj1[p] = obj2[p]
      }
    }
    return obj1
  }

  static mockup (obj) {
    return obj.split('\n').map((ln) => ln.trim()).join('')
  }
}

module.exports = common
<|endoftext|>


<commit_before>/**
 * @author Gilles Coomans <gilles.coomans@gmail.com>
 *
 */

if (typeof define !== 'function') {
    var define = require('amdefine')(module);
}
define(["require", "../utils", "./catch-parenthesis"], function(require, utils, parenthesis){

	utils.catchParenthesis = parenthesis;
	/**
	 * Fake hash algorithm for dummies manipulation
	 * @param {[type]} string [description]
	 */
	utils.Hash = function(string) {
		var hash = 0;
		if (string.length == 0)
			return hash;
		for (i = 0; i < string.length; i++) {
			char = string.charCodeAt(i);
			hash = ((hash << 5) - hash) + char;
			hash = hash & hash; // Convert to 32bit integer
		}
		return hash;
	};

	/**
	 * Generates a GUID string.
	 * @returns {String} The generated GUID.
	 * @example af8a8416-6e18-a307-bd9c-f2c947bbb3aa
	 * @author Slavik Meltser (slavik@meltser.info).
	 * @link http://slavik.meltser.info/?p=142
	 */
	utils.guid = function() {
	    function _p8(s) {
	        var p = (Math.random().toString(16)+"000000000").substr(2,8);
	        return s ? "-" + p.substr(0,4) + "-" + p.substr(4,4) : p ;
	    }
	    return _p8() + _p8(true) + _p8(true) + _p8();
	};

	utils.stripFirstSlash = function(text) {
		if (text.substring(0, 1) == "/")
			return text.substring(1);
		return text;
	};

	function trim_words(theString, numWords, maxChar) {
		expString = theString.split(/\s+/, numWords);
		if (expString.length == 1) {
			maxChar = maxChar || 10;
			if (expString[0].length > maxChar)
				return theString.substring(0, maxChar);
			return expString[0];
		}
		theNewString = expString.join(" ");
		if (theNewString.length < theString.length && theNewString[theNewString.length - 1] != ".")
			theNewString += "...";
		return theNewString;
	}

	utils.trimWords = function(string, numWords, maxChar) {
		return trim_words(string.replace(/<[^>]*>/gi, ""), numWords, maxChar);
	};

	//_________________________________  change string cases

	var camelCase = /\s(.)/g;
	var titleCase = /([^\s])([^\s]*)/g;

	utils.camelCase = function(input) {
	    return input.toLowerCase().replace(camelCase, function(match, group1) {
	        return group1.toUpperCase();
	    });
	};
	utils.titleCase = function(input) {
		return input.toLowerCase().replace(titleCase, function(match, group1, group2) {
			return group1.toUpperCase() + group2;
		});
	};

	return utils;
});<commit_msg>clean and update string<commit_after>/**
 * @author Gilles Coomans <gilles.coomans@gmail.com>
 *
 */

if (typeof define !== 'function') {
	var define = require('amdefine')(module);
}
define(["require", "../utils", "./catch-parenthesis"], function(require, utils, parenthesis) {

	utils.catchParenthesis = parenthesis;
	/**
	 * Fake hash algorithm for dummies manipulation
	 * @param {[type]} string [description]
	 */
	utils.Hash = function(string) {
		var hash = 0;
		if (string.length == 0)
			return hash;
		for (i = 0; i < string.length; i++) {
			char = string.charCodeAt(i);
			hash = ((hash << 5) - hash) + char;
			hash = hash & hash; // Convert to 32bit integer
		}
		return hash;
	};

	/**
	 * Generates a GUID string.
	 * @returns {String} The generated GUID.
	 * @example af8a8416-6e18-a307-bd9c-f2c947bbb3aa
	 * @author Slavik Meltser (slavik@meltser.info).
	 * @link http://slavik.meltser.info/?p=142
	 */
	utils.guid = function() {
		function _p8(s) {
			var p = (Math.random().toString(16) + "000000000").substr(2, 8);
			return s ? "-" + p.substr(0, 4) + "-" + p.substr(4, 4) : p;
		}
		return _p8() + _p8(true) + _p8(true) + _p8();
	};

	utils.stripFirstSlash = function(text) {
		if (text.substring(0, 1) == "/")
			return text.substring(1);
		return text;
	};

	function trim_words(theString, numWords, maxChar) {
		expString = theString.split(/\s+/, numWords);
		if (expString.length == 1) {
			maxChar = maxChar || 10;
			if (expString[0].length > maxChar)
				return theString.substring(0, maxChar);
			return expString[0];
		}
		theNewString = expString.join(" ");
		if (theNewString.length < theString.length && theNewString[theNewString.length - 1] != ".")
			theNewString += "...";
		return theNewString;
	}

	utils.trimWords = function(string, numWords, maxChar) {
		return trim_words(string.replace(/<[^>]*>/gi, ""), numWords, maxChar);
	};

	//_________________________________  change string cases

	var camelCase = /\s(.)/g;
	var titleCase = /([^\s])([^\s]*)/g;

	utils.camelCase = function(input) {
		return input.toLowerCase().replace(camelCase, function(match, group1) {
			return group1.toUpperCase();
		});
	};
	utils.titleCase = function(input) {
		return input.toLowerCase().replace(titleCase, function(match, group1, group2) {
			return group1.toUpperCase() + group2;
		});
	};

	var stringify = function(key, value) {
		if (value && value._deep_query_node_)
			return nodes.print(value);
		if (typeof value === 'function')
			return 'Function';
		if (value instanceof Date)
			return value.toString();
		if (value instanceof RegExp)
			return value.toString();
		if (typeof jQuery !== 'undefined' && value instanceof jQuery)
			return "jquery:" + value.selector;
		return value;
	};
	utils.stringify = function(value, nospace) {
		return JSON.stringify(value, stringify, nospace ? null : ' ');
	};

	return utils;
});<|endoftext|>