<commit_before>/*
 * Copyright (c) Novedia Group 2012.
 *
 *     This file is part of Hubiquitus.
 *
 *     Hubiquitus is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     Hubiquitus is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with Hubiquitus.  If not, see <http://www.gnu.org/licenses/>.
 */
//Make it compatible with node and web browser
if (typeof define !== 'function') {
    var define = require('amdefine')(module);
}

define(
    ['./strophe.pubsub', '../../codes', '../../sessionStorage'],
    function(Strophe, codes, sessionStorageAux){

        //Load correct modules if running on Node
        if(typeof module !== 'undefined' && module.exports){
            $iq     = Strophe.$iq;
            $msg    = Strophe.$msg;
            $pres   = Strophe.$pres;
            $build  = Strophe.$build;
        }

        //Taking care of session Storage. Sometimes the browser doesn't find it
        //And if it's node or we don't have support so we use 'our version'
        if(!sessionStorage){
            if(typeof window !== 'undefined' && window.sessionStorage)
                var sessionStorage = window.sessionStorage;
            else
                var sessionStorage = sessionStorageAux.sessionStorage;
        }

        var Strophe = Strophe.Strophe;
        var errors = codes.errors;
        var statuses = codes.statuses;
        var types = codes.types;

        // Constructor
        var hSessionBosh = function(opts) {
            this.params = opts;
            this.callback = opts.hCallback;
            this.pubsub =  "pubsub." + this.params.publisher.split('@')[1];
            this.retryIntervals = this.params.retryIntervals;
            this.status = statuses.DISCONNECTED;
            this.errorCode = errors.NO_ERROR;
            this.userDisconnect = false; //True when the user wants to disconnect

            //Create a Strophe connection
            this.conn = new Strophe.Connection(this.params.endpoint);
        };

        hSessionBosh.prototype = {

            /**
             * Tries to recover and reattach and old connection or connect to a new one.
             * @param force (boolean, optional). If defined and true. A reattach won't be tried and
             * a new connection will be started.
             */
            connect: function(force) {
                var re = new RegExp(this.params.publisher,"i");
                if( !force &&
                    typeof sessionStorage.getItem('publisher') === 'string'){
                    if(sessionStorage.getItem('publisher').search(re) != -1){
                        // Tell the user we are reattaching.
                        this.onStatus(true, {status: statuses.REATTACHING, errorCode: errors.NO_ERROR});
                        this.conn.attach(
                            sessionStorage.getItem('publisher'),
                            sessionStorage.getItem('sid'),
                            sessionStorage.getItem('rid'),
                            this.onStatus.bind(this, false),
                            null, null, null)
                    }else{
                        //We have an open connection but JIDs do not match, send an error.
                        this.onStatus(true, {status: statuses.CONNECTED, errorCode: errors.ALREADY_CONNECTED});
                    }
                }else{
                    //We don't have enough information to restablish the connection. Establish a new one
                    this.conn.connect(
                        this.params.publisher,
                        this.params.password,
                        this.onStatus.bind(this, false),
                        null, null, null
                    );
                }
            },

            /**
             * Callback for the connection status. Accepts Strophe or proper hStatus as values
             * @param isHStatus - if the status should be interpreted as an hStatus or an Strophe Status
             * @param status - The status to send to the client.
             * @param msg - Specific message sent --sometimes-- by strophe to indicate the error
             */
            onStatus: function(isHStatus, value, msg)
            {
                var errorCode;
                var statusCode;
                var errorMsg;
                var status = value;
                var self = this;

                function _onConnection(){
                    //Send Presence
                    self.conn.send($pres());

                    //Add Handlers
                    self.conn.addHandler(self.handleIQ.bind(self), null, 'iq', 'get',null, null, {matchBare: true});
                    self.conn.addHandler(self.handleMessage.bind(self), Strophe.NS.PUBSUB_EVENT,'message',
                        null, null, self.pubsub, {matchBare: true});
                    self.conn.addHandler(self.handleHResult.bind(self), null, 'message',
                        null, null, self.hnode, {matchBare: true});

                    //Save attrs when changing pages/refreshing (only browser)
                    if(typeof window !== 'undefined')
                        window.onunload = function(){
                            sessionStorage.setItem('publisher', self.conn.jid);
                            sessionStorage.setItem('sid', self.conn.sid);
                            sessionStorage.setItem('rid', self.conn.rid);
                        };

                    self.retryIntervals = self.params.retryIntervals; //Resets the reconnection counter
                }
                function _onError(){
                    sessionStorage.removeItem('publisher');
                    sessionStorage.removeItem('sid');
                    sessionStorage.removeItem('rid');

                    var timeout = self.retryIntervals.length == 1 ? self.retryIntervals[0] : self.retryIntervals.pop();
                    self.connect_retry = setTimeout(self.connect.bind(self, true),timeout*1000);
                }

                if(!isHStatus){
                    switch(value){
                        case Strophe.Status.ATTACHED:
                            statusCode = statuses.REATTACHED;
                        case Strophe.Status.CONNECTED:
                            statusCode = statusCode || statuses.CONNECTED;
                            _onConnection();
                            break;
                        case Strophe.Status.CONNFAIL:
                        case Strophe.Status.ERROR:
                            errorCode = errors.CONN_TIMEOUT;
                            statusCode = statuses.CONNECTING;
                            errorMsg = msg;
                            _onError();
                            break;
                        case Strophe.Status.CONNECTING:
                            //This removes  the second connecting after a connection error
                            statusCode = this.errorCode != errors.NO_ERROR ? undefined : statuses.CONNECTING;
                            break;
                        case Strophe.Status.DISCONNECTING:
                            //Clear the reconnection timeout
                            clearTimeout(this.connect_retry);
                            //If the disconnection was not by the user, treat it as an error.
                            if(this.userDisconnect)
                                statusCode = statuses.DISCONNECTING;
                            else{
                                statusCode = statuses.DISCONNECTED;
                                errorCode = errors.TECH_ERROR;
                            }
                            break;
                        case Strophe.Status.DISCONNECTED:
                            statusCode = statuses.DISCONNECTED;
                            //If the disconnection was not by the user, treat it as an error.
                            if(!this.userDisconnect)
                                _onError();
                            break;
                        case Strophe.Status.AUTHFAIL:
                            statusCode = statuses.DISCONNECTED;
                            errorCode = errors.AUTH_FAILED;
                            break;
                    }
                    status = {status: statusCode, errorCode: errorCode || errors.NO_ERROR, errorMsg: errorMsg};
                }
                if(status.status){
                    this.status = status.status;
                    this.errorCode = status.errorCode || errors.NO_ERROR;
                    this.sendCallback(types.hStatus, status);
                }
            },

            //Handle to retrieve items (needs to return true to persist callback in strophe)
            handleMessage: function(stanza) {
                var _data = stanza.getElementsByTagName('items')[0];

                if(!_data) return true;

                var item = _data.getElementsByTagName('item')[0];
                if(item){
                    var hMessage = item.childNodes[0].childNodes[0].textContent;
                    try{
                        hMessage = JSON.parse(hMessage);
                    }catch(err){return true}

                    if(hMessage)
                        this.callback({
                            type: 'hMessage',
                            data: hMessage
                        });
                }
                return true;
            },

            //Handles an hResult from the server
            handleHResult: function(stanza){
                var body = stanza.getElementsByTagName('hbody');
                body = body ? body[0] : undefined;

                if(body && body.getAttribute('type') == 'hresult'){
                    var hResult = null;
                    try{
                        hResult = JSON.parse(body.textContent);
                    }
                    catch(err){
                        console.log('Error parsing hResult');
                    }
                    if(hResult)
                        this.callback({type: 'hResult', data: hResult});
                }
                return true;
            },

            //In case we receive an iq-get request, send an error cause we don't allow them
            handleIQ : function(stanza){
                var msg = $iq({
                    to: stanza.attributes.from.nodeValue,
                    type: 'error',
                    id: stanza.attributes.id.nodeValue
                })
                    .c(stanza.firstChild.nodeName, {xmlns: stanza.firstChild.attributes.xmlns.nodeValue}).up()
                    .c('error', { type: 'cancel'})
                    .c('service-unavailable', {xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'});

                console.log("Received an IQ from the server. Responding.");
                this.conn.send(msg);
                return true; //If it doesn't return true, Strophe will remove it from Handler's list
            },

            disconnect: function() {
                clearTimeout(this.connect_retry);
                //Remove our attrs
                sessionStorage.removeItem('publisher');
                sessionStorage.removeItem('sid');
                sessionStorage.removeItem('rid');
                //Set user intention of disconnecting
                this.userDisconnect = true;
                //In case there is an error, disconnected or a disconnection
                // Do not tell strophe to disconnect again
                if( this.status == statuses.DISCONNECTED || this.status == statuses.DISCONNECTING ) return;
                this.conn.flush();
                this.conn.disconnect();
            },

            sendhCommand : function(hCommand){
                var msg = $msg({to: hCommand.entity})
                    .c('hbody', {type: 'hcommand'}, JSON.stringify(hCommand));
                this.conn.send(msg);
            },

            sendCallback : function(type, data){
                this.callback({
                    type: type,
                    data: data
                })
            }
        };

        //requireJS way to import
        return{
            hSessionBosh : hSessionBosh
        }
    }
);
<commit_msg>Support for stress option<commit_after>/*
 * Copyright (c) Novedia Group 2012.
 *
 *     This file is part of Hubiquitus.
 *
 *     Hubiquitus is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     Hubiquitus is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with Hubiquitus.  If not, see <http://www.gnu.org/licenses/>.
 */
//Make it compatible with node and web browser
if (typeof define !== 'function') {
    var define = require('amdefine')(module);
}

define(
    ['./strophe.pubsub', '../../codes', '../../sessionStorage'],
    function(Strophe, codes, sessionStorageAux){

        //Load correct modules if running on Node
        if(typeof module !== 'undefined' && module.exports){
            $iq     = Strophe.$iq;
            $msg    = Strophe.$msg;
            $pres   = Strophe.$pres;
            $build  = Strophe.$build;
        }

        //Taking care of session Storage. Sometimes the browser doesn't find it
        //And if it's node or we don't have support so we use 'our version'
        if(!sessionStorage){
            if(typeof window !== 'undefined' && window.sessionStorage)
                var sessionStorage = window.sessionStorage;
            else
                var sessionStorage = sessionStorageAux.sessionStorage;
        }

        var Strophe = Strophe.Strophe;
        var errors = codes.errors;
        var statuses = codes.statuses;
        var types = codes.types;

        // Constructor
        var hSessionBosh = function(opts) {
            this.params = opts;
            this.callback = opts.hCallback;
            this.pubsub =  "pubsub." + this.params.publisher.split('@')[1];
            this.retryIntervals = this.params.retryIntervals;
            this.status = statuses.DISCONNECTED;
            this.errorCode = errors.NO_ERROR;
            this.userDisconnect = false; //True when the user wants to disconnect

            //Create a Strophe connection
            this.conn = new Strophe.Connection(this.params.endpoint);
        };

        hSessionBosh.prototype = {

            /**
             * Tries to recover and reattach and old connection or connect to a new one.
             * @param force (boolean, optional). If defined and true. A reattach won't be tried and
             * a new connection will be started.
             */
            connect: function(force) {
                force = force || this.params.stress;

                var re = new RegExp(this.params.publisher,"i");
                if( !force &&
                    typeof sessionStorage.getItem('publisher') === 'string'){
                    if(sessionStorage.getItem('publisher').search(re) != -1){
                        // Tell the user we are reattaching.
                        this.onStatus(true, {status: statuses.REATTACHING, errorCode: errors.NO_ERROR});
                        this.conn.attach(
                            sessionStorage.getItem('publisher'),
                            sessionStorage.getItem('sid'),
                            sessionStorage.getItem('rid'),
                            this.onStatus.bind(this, false),
                            null, null, null)
                    }else{
                        //We have an open connection but JIDs do not match, send an error.
                        this.onStatus(true, {status: statuses.CONNECTED, errorCode: errors.ALREADY_CONNECTED});
                    }
                }else{
                    //We don't have enough information to restablish the connection. Establish a new one
                    this.conn.connect(
                        this.params.publisher,
                        this.params.password,
                        this.onStatus.bind(this, false),
                        null, null, null
                    );
                }
            },

            /**
             * Callback for the connection status. Accepts Strophe or proper hStatus as values
             * @param isHStatus - if the status should be interpreted as an hStatus or an Strophe Status
             * @param status - The status to send to the client.
             * @param msg - Specific message sent --sometimes-- by strophe to indicate the error
             */
            onStatus: function(isHStatus, value, msg)
            {
                var errorCode;
                var statusCode;
                var errorMsg;
                var status = value;
                var self = this;

                function _onConnection(){
                    //Send Presence
                    self.conn.send($pres());

                    //Add Handlers
                    self.conn.addHandler(self.handleIQ.bind(self), null, 'iq', 'get',null, null, {matchBare: true});
                    self.conn.addHandler(self.handleMessage.bind(self), Strophe.NS.PUBSUB_EVENT,'message',
                        null, null, self.pubsub, {matchBare: true});
                    self.conn.addHandler(self.handleHResult.bind(self), null, 'message',
                        null, null, self.hnode, {matchBare: true});

                    //Save attrs when changing pages/refreshing (only browser)
                    if(typeof window !== 'undefined')
                        window.onunload = function(){
                            sessionStorage.setItem('publisher', self.conn.jid);
                            sessionStorage.setItem('sid', self.conn.sid);
                            sessionStorage.setItem('rid', self.conn.rid);
                        };

                    self.retryIntervals = self.params.retryIntervals; //Resets the reconnection counter
                }
                function _onError(){
                    sessionStorage.removeItem('publisher');
                    sessionStorage.removeItem('sid');
                    sessionStorage.removeItem('rid');

                    var timeout = self.retryIntervals.length == 1 ? self.retryIntervals[0] : self.retryIntervals.pop();
                    self.connect_retry = setTimeout(self.connect.bind(self, true),timeout*1000);
                }

                if(!isHStatus){
                    switch(value){
                        case Strophe.Status.ATTACHED:
                            statusCode = statuses.REATTACHED;
                        case Strophe.Status.CONNECTED:
                            statusCode = statusCode || statuses.CONNECTED;
                            _onConnection();
                            break;
                        case Strophe.Status.CONNFAIL:
                        case Strophe.Status.ERROR:
                            errorCode = errors.CONN_TIMEOUT;
                            statusCode = statuses.CONNECTING;
                            errorMsg = msg;
                            _onError();
                            break;
                        case Strophe.Status.CONNECTING:
                            //This removes  the second connecting after a connection error
                            statusCode = this.errorCode != errors.NO_ERROR ? undefined : statuses.CONNECTING;
                            break;
                        case Strophe.Status.DISCONNECTING:
                            //Clear the reconnection timeout
                            clearTimeout(this.connect_retry);
                            //If the disconnection was not by the user, treat it as an error.
                            if(this.userDisconnect)
                                statusCode = statuses.DISCONNECTING;
                            else{
                                statusCode = statuses.DISCONNECTED;
                                errorCode = errors.TECH_ERROR;
                            }
                            break;
                        case Strophe.Status.DISCONNECTED:
                            statusCode = statuses.DISCONNECTED;
                            //If the disconnection was not by the user, treat it as an error.
                            if(!this.userDisconnect)
                                _onError();
                            break;
                        case Strophe.Status.AUTHFAIL:
                            statusCode = statuses.DISCONNECTED;
                            errorCode = errors.AUTH_FAILED;
                            break;
                    }
                    status = {status: statusCode, errorCode: errorCode || errors.NO_ERROR, errorMsg: errorMsg};
                }
                if(status.status){
                    this.status = status.status;
                    this.errorCode = status.errorCode || errors.NO_ERROR;
                    this.sendCallback(types.hStatus, status);
                }
            },

            //Handle to retrieve items (needs to return true to persist callback in strophe)
            handleMessage: function(stanza) {
                var _data = stanza.getElementsByTagName('items')[0];

                if(!_data) return true;

                var item = _data.getElementsByTagName('item')[0];
                if(item){
                    var hMessage = item.childNodes[0].childNodes[0].textContent;
                    try{
                        hMessage = JSON.parse(hMessage);
                    }catch(err){return true}

                    if(hMessage)
                        this.callback({
                            type: 'hMessage',
                            data: hMessage
                        });
                }
                return true;
            },

            //Handles an hResult from the server
            handleHResult: function(stanza){
                var body = stanza.getElementsByTagName('hbody');
                body = body ? body[0] : undefined;

                if(body && body.getAttribute('type') == 'hresult'){
                    var hResult = null;
                    try{
                        hResult = JSON.parse(body.textContent);
                    }
                    catch(err){
                        console.log('Error parsing hResult');
                    }
                    if(hResult)
                        this.callback({type: 'hResult', data: hResult});
                }
                return true;
            },

            //In case we receive an iq-get request, send an error cause we don't allow them
            handleIQ : function(stanza){
                var msg = $iq({
                    to: stanza.attributes.from.nodeValue,
                    type: 'error',
                    id: stanza.attributes.id.nodeValue
                })
                    .c(stanza.firstChild.nodeName, {xmlns: stanza.firstChild.attributes.xmlns.nodeValue}).up()
                    .c('error', { type: 'cancel'})
                    .c('service-unavailable', {xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'});

                console.log("Received an IQ from the server. Responding.");
                this.conn.send(msg);
                return true; //If it doesn't return true, Strophe will remove it from Handler's list
            },

            disconnect: function() {
                clearTimeout(this.connect_retry);
                //Remove our attrs
                sessionStorage.removeItem('publisher');
                sessionStorage.removeItem('sid');
                sessionStorage.removeItem('rid');
                //Set user intention of disconnecting
                this.userDisconnect = true;
                //In case there is an error, disconnected or a disconnection
                // Do not tell strophe to disconnect again
                if( this.status == statuses.DISCONNECTED || this.status == statuses.DISCONNECTING ) return;
                this.conn.flush();
                this.conn.disconnect();
            },

            sendhCommand : function(hCommand){
                var msg = $msg({to: hCommand.entity})
                    .c('hbody', {type: 'hcommand'}, JSON.stringify(hCommand));
                this.conn.send(msg);
            },

            sendCallback : function(type, data){
                this.callback({
                    type: type,
                    data: data
                })
            }
        };

        //requireJS way to import
        return{
            hSessionBosh : hSessionBosh
        }
    }
);
<|endoftext|>


<commit_before>/*
 * Copyright (c) Novedia Group 2012.
 *
 *     This file is part of Hubiquitus.
 *
 *     Hubiquitus is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     Hubiquitus is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with Hubiquitus.  If not, see <http://www.gnu.org/licenses/>.
 */
//Make it compatible with node and web browser
if (typeof define !== 'function') {
    var define = require('amdefine')(module);
}

define(
    ['./strophe.pubsub', '../../codes', '../../sessionStorage'],
    function(Strophe, codes, sessionStorageAux){

        //Load correct modules if running on Node
        if(typeof module !== 'undefined' && module.exports){
            $iq     = Strophe.$iq;
            $msg    = Strophe.$msg;
            $pres   = Strophe.$pres;
            $build  = Strophe.$build;
        }

        //Taking care of session Storage. Sometimes the browser doesn't find it
        //And if it's node or we don't have support so we use 'our version'
        if(!sessionStorage){
            if(typeof window !== 'undefined' && window.sessionStorage)
                var sessionStorage = window.sessionStorage;
            else
                var sessionStorage = sessionStorageAux.sessionStorage;
        }

        var Strophe = Strophe.Strophe;
        var errors = codes.errors;
        var statuses = codes.statuses;
        var types = codes.types;

        // Constructor
        var hSessionBosh = function(opts) {
            this.params = opts;
            this.callback = opts.hCallback;
            this.pubsub =  "pubsub." + this.params.publisher.split('@')[1];
            this.retryIntervals = this.params.retryIntervals;
            this.status = statuses.DISCONNECTED;
            this.errorCode = undefined;
            this.userDisconnect = false; //True when the user wants to disconnect

            //Create a Strophe connection
            this.conn = new Strophe.Connection(this.params.endpoint);
        };

        hSessionBosh.prototype = {

            /**
             * Tries to recover and reattach and old connection or connect to a new one.
             * @param force (boolean, optional). If defined and true. A reattach won't be tried and
             * a new connection will be started.
             */
            connect: function(force) {
                var re = new RegExp(this.params.publisher,"i");
                if( !force &&
                    typeof sessionStorage.getItem('publisher') === 'string'){
                    if(sessionStorage.getItem('publisher').search(re) != -1){
                        this.onStatus(true, {status: statuses.REATTACHING}); // Tell the user we are reattaching.
                        this.conn.attach(
                            sessionStorage.getItem('publisher'),
                            sessionStorage.getItem('sid'),
                            sessionStorage.getItem('rid'),
                            this.onStatus.bind(this, false),
                            null, null, null)
                    }else{
                        //We have an open connection but JIDs do not match, send an error.
                        this.onStatus(true, {status: statuses.ERROR, errorCode: errors.ALREADY_CONNECTED});
                    }
                }else{
                    //We don't have enough information to restablish the connection. Establish a new one
                    this.conn.connect(
                        this.params.publisher,
                        this.params.password,
                        this.onStatus.bind(this, false),
                        null, null, null
                    );
                }
            },

            /**
             * Callback for the connection status. Accepts Strophe or proper hStatus as values
             * @param isHStatus - if the status should be interpreted as an hStatus or an Strophe Status
             * @param status - The status to send to the client.
             * @param msg - Specific message sent --sometimes-- by strophe to indicate the error
             */
            onStatus: function(isHStatus, value, msg)
            {
                var errorCode;
                var statusCode;
                var errorMsg;
                var status = value;
                var self = this;

                function _onConnection(){
                    //Send Presence
                    self.conn.send($pres());

                    //Add Handlers
                    self.conn.addHandler(self.handleIQ.bind(self), null, 'iq', 'get',null, null, {matchBare: true});
                    self.conn.addHandler(self.handleMessage.bind(self), Strophe.NS.PUBSUB_EVENT,'message',
                        null, null, self.pubsub, {matchBare: true});
                    self.conn.addHandler(self.handleHResult.bind(self), null, 'message',
                        null, null, self.hnode, {matchBare: true});

                    //Save attrs when changing pages/refreshing (only browser)
                    if(typeof window !== 'undefined')
                        window.onunload = function(){
                            sessionStorage.setItem('publisher', self.conn.jid);
                            sessionStorage.setItem('sid', self.conn.sid);
                            sessionStorage.setItem('rid', self.conn.rid);
                        };

                    self.retryIntervals = self.params.retryIntervals; //Resets the reconnection counter
                }
                function _onError(){
                    sessionStorage.removeItem('publisher');
                    sessionStorage.removeItem('sid');
                    sessionStorage.removeItem('rid');

                    var timeout = self.retryIntervals.length == 1 ? self.retryIntervals[0] : self.retryIntervals.pop();
                    self.connect_retry = setTimeout(self.connect.bind(self, true),timeout*1000);
                }

                if(!isHStatus){
                    switch(value){
                        case Strophe.Status.ATTACHED:
                            statusCode = statuses.REATTACHED;
                        case Strophe.Status.CONNECTED:
                            statusCode = statusCode || statuses.CONNECTED;
                            _onConnection();
                            break;
                        case Strophe.Status.CONNFAIL:
                        case Strophe.Status.ERROR:
                            errorCode = errors.TECH_ERROR;
                            statusCode = statuses.ERROR;
                            errorMsg = msg;
                            _onError();
                            break;
                        case Strophe.Status.CONNECTING:
                            statusCode = statuses.CONNECTING;
                            break;
                        case Strophe.Status.DISCONNECTING:
                            //If the disconnection was not by the user, treat it as an error.
                            if(this.userDisconnect)
                                statusCode = statuses.DISCONNECTING;
                            else{
                                statusCode = statuses.ERROR;
                                errorCode = errors.TECH_ERROR;
                            }
                            break;
                        case Strophe.Status.DISCONNECTED:
                            statusCode = statuses.DISCONNECTED;
                            //If the disconnection was not by the user, treat it as an error.
                            if(!this.userDisconnect)
                                _onError();
                            break;
                        case Strophe.Status.AUTHFAIL:
                            statusCode = statuses.ERROR;
                            errorCode = errors.AUTH_FAILED;
                            break;
                    }
                    status = {status: statusCode, errorCode: errorCode, errorMsg: errorMsg};
                }
                if(status.status){
                    this.status = status.status;
                    this.errorCode = status.errorCode;
                    this.sendCallback(types.hStatus, status);
                }
            },

            //Handle to retrieve items
            handleMessage: function(stanza) {
                var parsedItems = [];
                var _data = stanza.getElementsByTagName('items')[0];
                var _channel = _data.getAttribute('node');

                var items = _data.getElementsByTagName('item');
                for(var i = 0; i < items.length; i++)
                    parsedItems.push(items[i].childNodes[0].childNodes[0].nodeValue);

                //Calling client callback with the extracted message
                for(var i in parsedItems)
                    this.callback({type: 'message', data :
                    {channel: _channel, message: parsedItems[i]}})
                return true;
            },

            //Handles an hResult from the server
            handleHResult: function(stanza){
                var body = stanza.getElementsByTagName('body');
                body = body ? body[0] : undefined;

                if(body && body.getAttribute('type') == 'hresult'){
                    try{
                        var hResult = JSON.parse(body.textContent);
                        this.callback({type: 'hResult', data: hResult});
                    }
                    catch(err){
                        console.log('Error parsing hResult');
                    }
                }
                return true;
            },

            //In case we receive an iq-get request, send an error cause we don't allow them
            handleIQ : function(stanza){
                var msg = $iq({
                    to: stanza.attributes.from.nodeValue,
                    type: 'error',
                    id: stanza.attributes.id.nodeValue
                })
                    .c(stanza.firstChild.nodeName, {xmlns: stanza.firstChild.attributes.xmlns.nodeValue}).up()
                    .c('error', { type: 'cancel'})
                    .c('service-unavailable', {xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'});

                console.log("Received an IQ from the server. Responding.");
                this.conn.send(msg);
                return true; //If it doesn't return true, Strophe will remove it from Handler's list
            },

            disconnect: function() {
                clearTimeout(this.connect_retry);
                //Remove our attrs
                sessionStorage.removeItem('publisher');
                sessionStorage.removeItem('sid');
                sessionStorage.removeItem('rid');
                //In case there is an error, disconnected or a disconnection
                // Do not tell strophe to disconnect again
                if( (this.status == statuses.ERROR && this.errorCode == errors.AUTH_FAILED) ||
                    this.status == statuses.DISCONNECTING ||
                    this.status == statuses.DISCONNECTED) return;
                //Set user intention of disconnecting
                this.userDisconnect = true;
                this.conn.flush();
                this.conn.disconnect();
            },

            subscribe: function(channel){
                /*
                 Done in two phases:
                 1. Ask for subscriptions and check if there are others to the same node
                 2. If there aren't, request a new one
                 */
                function _success(stanza){
                    this.callback({type: 'result', data:
                    {type: 'subscribe', channel: channel, msgid: msgId}});
                }

                function _failure(stanza, errorCode){
                    this.callback({type: 'error', data:
                    {type: 'subscribe', code: errorCode, channel: channel, msgid: msgId}});
                }

                function _callbackSubscriptions(stanza){
                    var subs = stanza.getElementsByTagName('subscriptions')[0].childNodes;
                    var i = 0;
                    while(i < subs.length && subs[i].getAttribute('node') != channel)
                        i++;

                    if(!subs[i] || subs[i].getAttribute('node') != channel){
                        //Not Subscribed
                        this.conn.pubsub.subscribe(
                            channel,
                            undefined,
                            undefined,
                            _success.bind(this),
                            _failure.bind(this));
                    } else{
                        //Already subscribed
                        _failure.call(this,stanza, errors.ALREADY_SUBSCRIBED);
                    }
                }

                var msgId = this.conn.pubsub.getSubscriptions(_callbackSubscriptions.bind(this));
                return msgId;
            },

            unsubscribe : function(channel){
                function _success(stanza){
                    this.callback({type: 'result', data:
                    {type: 'unsubscribe', channel: channel, msgid: stanza.getAttribute('id')}});
                }

                function _failure(stanza){
                    this.callback({type: 'error', data:
                    {type: 'unsubscribe', channel: channel, msgid: stanza.getAttribute('id')}});
                }

                return this.conn.pubsub.unsubscribe(
                    channel,
                    undefined,
                    undefined,
                    _success.bind(this),
                    _failure.bind(this));
            },

            publish : function(channel,item){
                function _callback(stanza){
                    if(stanza.getAttribute('type') == 'error')
                        this.callback({type: 'error', data:
                        {type: 'publish', channel: channel, msgid: stanza.getAttribute('id')}});
                    else
                        this.callback({type: 'result', data:
                        {type: 'publish', channel: channel, msgid: stanza.getAttribute('id')}});
                }

                return this.conn.pubsub.publish(
                    channel,
                    [{  data: $build("entry", {}).t(item).tree(),
                        attrs: {}
                    }],
                    _callback.bind(this));
            },

            getMessages : function(channel){
                function _failure(stanza){
                    this.callback({type: 'error', data:
                    {type: 'get_messages', channel: channel, msgid: stanza.getAttribute('id')}});
                }
                return this.conn.pubsub.items(channel,
                    this.handleMessage.bind(this), _failure.bind(this));
            },

            sendhCommand : function(hCommand){
                var msg = $msg({to: hCommand.entity})
                    .c('body', {type: 'hcommand'}, JSON.stringify(hCommand));
                this.conn.send(msg);
            },

            sendCallback : function(type, data){
                this.callback({
                    type: type,
                    data: data
                })
            }
        };

        //requireJS way to import
        return{
            hSessionBosh : hSessionBosh
        }
    }
);
<commit_msg>Fixed error parsing messages<commit_after>/*
 * Copyright (c) Novedia Group 2012.
 *
 *     This file is part of Hubiquitus.
 *
 *     Hubiquitus is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     Hubiquitus is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with Hubiquitus.  If not, see <http://www.gnu.org/licenses/>.
 */
//Make it compatible with node and web browser
if (typeof define !== 'function') {
    var define = require('amdefine')(module);
}

define(
    ['./strophe.pubsub', '../../codes', '../../sessionStorage'],
    function(Strophe, codes, sessionStorageAux){

        //Load correct modules if running on Node
        if(typeof module !== 'undefined' && module.exports){
            $iq     = Strophe.$iq;
            $msg    = Strophe.$msg;
            $pres   = Strophe.$pres;
            $build  = Strophe.$build;
        }

        //Taking care of session Storage. Sometimes the browser doesn't find it
        //And if it's node or we don't have support so we use 'our version'
        if(!sessionStorage){
            if(typeof window !== 'undefined' && window.sessionStorage)
                var sessionStorage = window.sessionStorage;
            else
                var sessionStorage = sessionStorageAux.sessionStorage;
        }

        var Strophe = Strophe.Strophe;
        var errors = codes.errors;
        var statuses = codes.statuses;
        var types = codes.types;

        // Constructor
        var hSessionBosh = function(opts) {
            this.params = opts;
            this.callback = opts.hCallback;
            this.pubsub =  "pubsub." + this.params.publisher.split('@')[1];
            this.retryIntervals = this.params.retryIntervals;
            this.status = statuses.DISCONNECTED;
            this.errorCode = undefined;
            this.userDisconnect = false; //True when the user wants to disconnect

            //Create a Strophe connection
            this.conn = new Strophe.Connection(this.params.endpoint);
        };

        hSessionBosh.prototype = {

            /**
             * Tries to recover and reattach and old connection or connect to a new one.
             * @param force (boolean, optional). If defined and true. A reattach won't be tried and
             * a new connection will be started.
             */
            connect: function(force) {
                var re = new RegExp(this.params.publisher,"i");
                if( !force &&
                    typeof sessionStorage.getItem('publisher') === 'string'){
                    if(sessionStorage.getItem('publisher').search(re) != -1){
                        this.onStatus(true, {status: statuses.REATTACHING}); // Tell the user we are reattaching.
                        this.conn.attach(
                            sessionStorage.getItem('publisher'),
                            sessionStorage.getItem('sid'),
                            sessionStorage.getItem('rid'),
                            this.onStatus.bind(this, false),
                            null, null, null)
                    }else{
                        //We have an open connection but JIDs do not match, send an error.
                        this.onStatus(true, {status: statuses.ERROR, errorCode: errors.ALREADY_CONNECTED});
                    }
                }else{
                    //We don't have enough information to restablish the connection. Establish a new one
                    this.conn.connect(
                        this.params.publisher,
                        this.params.password,
                        this.onStatus.bind(this, false),
                        null, null, null
                    );
                }
            },

            /**
             * Callback for the connection status. Accepts Strophe or proper hStatus as values
             * @param isHStatus - if the status should be interpreted as an hStatus or an Strophe Status
             * @param status - The status to send to the client.
             * @param msg - Specific message sent --sometimes-- by strophe to indicate the error
             */
            onStatus: function(isHStatus, value, msg)
            {
                var errorCode;
                var statusCode;
                var errorMsg;
                var status = value;
                var self = this;

                function _onConnection(){
                    //Send Presence
                    self.conn.send($pres());

                    //Add Handlers
                    self.conn.addHandler(self.handleIQ.bind(self), null, 'iq', 'get',null, null, {matchBare: true});
                    self.conn.addHandler(self.handleMessage.bind(self), Strophe.NS.PUBSUB_EVENT,'message',
                        null, null, self.pubsub, {matchBare: true});
                    self.conn.addHandler(self.handleHResult.bind(self), null, 'message',
                        null, null, self.hnode, {matchBare: true});

                    //Save attrs when changing pages/refreshing (only browser)
                    if(typeof window !== 'undefined')
                        window.onunload = function(){
                            sessionStorage.setItem('publisher', self.conn.jid);
                            sessionStorage.setItem('sid', self.conn.sid);
                            sessionStorage.setItem('rid', self.conn.rid);
                        };

                    self.retryIntervals = self.params.retryIntervals; //Resets the reconnection counter
                }
                function _onError(){
                    sessionStorage.removeItem('publisher');
                    sessionStorage.removeItem('sid');
                    sessionStorage.removeItem('rid');

                    var timeout = self.retryIntervals.length == 1 ? self.retryIntervals[0] : self.retryIntervals.pop();
                    self.connect_retry = setTimeout(self.connect.bind(self, true),timeout*1000);
                }

                if(!isHStatus){
                    switch(value){
                        case Strophe.Status.ATTACHED:
                            statusCode = statuses.REATTACHED;
                        case Strophe.Status.CONNECTED:
                            statusCode = statusCode || statuses.CONNECTED;
                            _onConnection();
                            break;
                        case Strophe.Status.CONNFAIL:
                        case Strophe.Status.ERROR:
                            errorCode = errors.TECH_ERROR;
                            statusCode = statuses.ERROR;
                            errorMsg = msg;
                            _onError();
                            break;
                        case Strophe.Status.CONNECTING:
                            statusCode = statuses.CONNECTING;
                            break;
                        case Strophe.Status.DISCONNECTING:
                            //If the disconnection was not by the user, treat it as an error.
                            if(this.userDisconnect)
                                statusCode = statuses.DISCONNECTING;
                            else{
                                statusCode = statuses.ERROR;
                                errorCode = errors.TECH_ERROR;
                            }
                            break;
                        case Strophe.Status.DISCONNECTED:
                            statusCode = statuses.DISCONNECTED;
                            //If the disconnection was not by the user, treat it as an error.
                            if(!this.userDisconnect)
                                _onError();
                            break;
                        case Strophe.Status.AUTHFAIL:
                            statusCode = statuses.ERROR;
                            errorCode = errors.AUTH_FAILED;
                            break;
                    }
                    status = {status: statusCode, errorCode: errorCode, errorMsg: errorMsg};
                }
                if(status.status){
                    this.status = status.status;
                    this.errorCode = status.errorCode;
                    this.sendCallback(types.hStatus, status);
                }
            },

            //Handle to retrieve items
            handleMessage: function(stanza) {
                var parsedItems = [];
                var _data = stanza.getElementsByTagName('items')[0];
                var _channel = _data.getAttribute('node');

                var items = _data.getElementsByTagName('item');
                for(var i = 0; i < items.length; i++)
                    parsedItems.push(items[i].childNodes[0].childNodes[0].textContent);

                //Calling client callback with the extracted message
                for(var i = 0; i < parsedItems.length; i++)
                    this.callback({type: 'message', data :
                    {channel: _channel, message: parsedItems[i]}})
                return true;
            },

            //Handles an hResult from the server
            handleHResult: function(stanza){
                var body = stanza.getElementsByTagName('body');
                body = body ? body[0] : undefined;

                if(body && body.getAttribute('type') == 'hresult'){
                    try{
                        var hResult = JSON.parse(body.textContent);
                        this.callback({type: 'hResult', data: hResult});
                    }
                    catch(err){
                        console.log('Error parsing hResult');
                    }
                }
                return true;
            },

            //In case we receive an iq-get request, send an error cause we don't allow them
            handleIQ : function(stanza){
                var msg = $iq({
                    to: stanza.attributes.from.nodeValue,
                    type: 'error',
                    id: stanza.attributes.id.nodeValue
                })
                    .c(stanza.firstChild.nodeName, {xmlns: stanza.firstChild.attributes.xmlns.nodeValue}).up()
                    .c('error', { type: 'cancel'})
                    .c('service-unavailable', {xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'});

                console.log("Received an IQ from the server. Responding.");
                this.conn.send(msg);
                return true; //If it doesn't return true, Strophe will remove it from Handler's list
            },

            disconnect: function() {
                clearTimeout(this.connect_retry);
                //Remove our attrs
                sessionStorage.removeItem('publisher');
                sessionStorage.removeItem('sid');
                sessionStorage.removeItem('rid');
                //In case there is an error, disconnected or a disconnection
                // Do not tell strophe to disconnect again
                if( (this.status == statuses.ERROR && this.errorCode == errors.AUTH_FAILED) ||
                    this.status == statuses.DISCONNECTING ||
                    this.status == statuses.DISCONNECTED) return;
                //Set user intention of disconnecting
                this.userDisconnect = true;
                this.conn.flush();
                this.conn.disconnect();
            },

            subscribe: function(channel){
                /*
                 Done in two phases:
                 1. Ask for subscriptions and check if there are others to the same node
                 2. If there aren't, request a new one
                 */
                function _success(stanza){
                    this.callback({type: 'result', data:
                    {type: 'subscribe', channel: channel, msgid: msgId}});
                }

                function _failure(stanza, errorCode){
                    this.callback({type: 'error', data:
                    {type: 'subscribe', code: errorCode, channel: channel, msgid: msgId}});
                }

                function _callbackSubscriptions(stanza){
                    var subs = stanza.getElementsByTagName('subscriptions')[0].childNodes;
                    var i = 0;
                    while(i < subs.length && subs[i].getAttribute('node') != channel)
                        i++;

                    if(!subs[i] || subs[i].getAttribute('node') != channel){
                        //Not Subscribed
                        this.conn.pubsub.subscribe(
                            channel,
                            undefined,
                            undefined,
                            _success.bind(this),
                            _failure.bind(this));
                    } else{
                        //Already subscribed
                        _failure.call(this,stanza, errors.ALREADY_SUBSCRIBED);
                    }
                }

                var msgId = this.conn.pubsub.getSubscriptions(_callbackSubscriptions.bind(this));
                return msgId;
            },

            unsubscribe : function(channel){
                function _success(stanza){
                    this.callback({type: 'result', data:
                    {type: 'unsubscribe', channel: channel, msgid: stanza.getAttribute('id')}});
                }

                function _failure(stanza){
                    this.callback({type: 'error', data:
                    {type: 'unsubscribe', channel: channel, msgid: stanza.getAttribute('id')}});
                }

                return this.conn.pubsub.unsubscribe(
                    channel,
                    undefined,
                    undefined,
                    _success.bind(this),
                    _failure.bind(this));
            },

            publish : function(channel,item){
                function _callback(stanza){
                    if(stanza.getAttribute('type') == 'error')
                        this.callback({type: 'error', data:
                        {type: 'publish', channel: channel, msgid: stanza.getAttribute('id')}});
                    else
                        this.callback({type: 'result', data:
                        {type: 'publish', channel: channel, msgid: stanza.getAttribute('id')}});
                }

                return this.conn.pubsub.publish(
                    channel,
                    [{  data: $build("entry", {}).t(item).tree(),
                        attrs: {}
                    }],
                    _callback.bind(this));
            },

            getMessages : function(channel){
                function _failure(stanza){
                    this.callback({type: 'error', data:
                    {type: 'get_messages', channel: channel, msgid: stanza.getAttribute('id')}});
                }
                return this.conn.pubsub.items(channel,
                    this.handleMessage.bind(this), _failure.bind(this));
            },

            sendhCommand : function(hCommand){
                var msg = $msg({to: hCommand.entity})
                    .c('body', {type: 'hcommand'}, JSON.stringify(hCommand));
                this.conn.send(msg);
            },

            sendCallback : function(type, data){
                this.callback({
                    type: type,
                    data: data
                })
            }
        };

        //requireJS way to import
        return{
            hSessionBosh : hSessionBosh
        }
    }
);
<|endoftext|>


<commit_before>(function(){
	"use strict";

	var Config = function(j){

		var defaults = {
			firstDayOfWeek: 1, // Monday
			periods: {
				day: [1, 2, 3],
				week: [1, 2],
				month: [1, 2],
				year: [1],
				custom: ['']
			},
			container: 'body'
		}

		var js = {data:j, defaults:defaults};
		Initclass.call(this, js);

		this.init();
	}

	/**
	 * Builds the dom and initializes functionality
	 * @return {undefined} no return value
	 */
	Config.prototype.init = function() {
		this.container = document.querySelector(this.container);

		this.innerContainer = document.createElement('form');
		this.innerContainer.className = 'form-horizontal'

		this.container.appendChild(this.innerContainer);

		if(this.container !== null){
			this.buildSContainer();
		}else{
			console.log('The container is not found, please, check again');
		}
	};

	/**
	 * Gets the chosen date by user, and/or the custom range
	 * @return {object} chosen date
	 */
	Config.prototype.getData = function() {
		var r = {};
		r.selector = this.selector.options[this.selector.selectedIndex].value;

		if(typeof this.date1 === 'object'){
			r.range = {};
			r.range.date1 = this.date1.value;
			r.range.date2 = this.date2.value;
		}

		return r;
	};

	Config.prototype.buildSContainer = function() {
		var scontainer = document.createElement('div')
		scontainer.className = 'form-group';

		var label = document.createElement('label');
		label.className = 'col-sm-4 control-label';
		label.setAttribute('for', 'date_selector');
		label.setAttribute('data-ltag', 'dateselector-label');
		var t = App.current.getMainText('dateselector-label');
		label.appendChild(document.createTextNode(t));
		scontainer.appendChild(label);

		var selector = this.buildSelector();
		var div = document.createElement('div');
		div.className = 'col-sm-8';
		div.appendChild(selector);
		scontainer.appendChild(div);

		this.innerContainer.appendChild(scontainer);
	};

	/**
	 * Builds the main date selector
	 * @return {object} returns DOM object (the selector)
	 */
	Config.prototype.buildSelector = function() {
		var selector = document.createElement('select');
		selector.className = 'form-control date-selector';
		selector.id = 'date_selector';

		var periods = Object.keys(this.periods);

		for(var i = 0, len = periods.length; i < len; i++){
			var key = periods[i];
			var period = this.periods[key];

			for(var j = 0, len2 = period.length; j < len2; j++){
				var p = period[j];
				var tag = (typeof p === 'number') ? key+'-'+p : key;

				var option = document.createElement('option');
				option.setAttribute('data-tag', tag);
				option.value = tag;

				var span = document.createElement('span');
				span.setAttribute('data-ltag', tag);
				var t = App.current.getMainText(tag);
				span.appendChild(document.createTextNode(t));
				option.appendChild(span);

				selector.appendChild(option);
			}
		}

		selector._t = this;
		selector.onchange = function(e){
			var s = e.target;
			var chosen = s.options[s.selectedIndex];
			var tag = chosen.getAttribute('data-tag');

			if(tag === 'custom'){
				s._t.buildDateRange();
			}else{
				s._t.deleteDateRange();
			}
		}

		this.selector = selector;
		return selector;
	};

	/**
	 * Builds the "date 1" and "date 2" inputs to make a custom date range
	 * @return {undefined} no return value
	 */
	Config.prototype.buildDateRange = function() {
		this.deleteDateRange();

		var date = new Date().toISOString();
		date = date.match(/([0-9]+){4}[-]([0-9]+){2}[-]([0-9]+){2}/gi)[0];

		//Date 1
		//-------------------------
		this.buildDateContainer(1);
		this.date1.value = date;

		//Date 2
		//-------------------------
		this.buildDateContainer(2);
		this.date2.value = date;

	};

	Config.prototype.buildDateContainer = function(no) {
		var container = document.createElement('div');
		container.className = 'form-group date-container';

		var label = document.createElement('label');
		label.className = 'col-sm-4 control-label';
		label.setAttribute('for', 'range-date-'+no);
		label.setAttribute('data-ltag', 'range-date-'+no);
		var t = App.current.getMainText('range-date-'+no);
		label.appendChild(document.createTextNode(t));

		container.appendChild(label);

		var d = document.createElement('div');
		d.className = 'col-sm-8';
		var date = this.buildDate('range-date-'+no);
		d.appendChild(date);
		this['date'+no] = date.getElementsByClassName('form-control')[0];
		container.appendChild(d);

		this.innerContainer.appendChild(container);
	};

	/**
	 * Builds the date input individually.
	 * @return {object} returns DOM object
	 */
	Config.prototype.buildDate = function(id) {
		var container = document.createElement('div');
		container.className = 'input-group';

		var span = document.createElement('span');
		span.className = 'input-group-addon';
		container.appendChild(span);

		var i = document.createElement('i');
		i.className = 'glyphicon glyphicon-calendar';
		span.appendChild(i);

		var input = document.createElement('input');
		input.type = 'date';
		input.className = 'form-control';
		input.id = (typeof id === 'string') ? id : '';

		container.appendChild(input);

		return container;
	};

	/**
	 * Deletes both "date 1" and "date 2" inputs
	 * @return {undefined} no return value
	 */
	Config.prototype.deleteDateRange = function() {
		var prev = this.innerContainer.querySelectorAll('.date-container');
		if(prev.length > 0){
			delete this.date1;
			delete this.date2;

			for(var i = 0, len = prev.length; i < len; i++){
				prev[i].parentNode.removeChild(prev[i]);
			}
		}
	};

	window.DateConfig = function(j){
		return new Config(j);
	}
})();<commit_msg>Added some validations in data range<commit_after>(function(){
	"use strict";

	var Config = function(j){

		var defaults = {
			firstDayOfWeek: 1, // Monday
			periods: {
				day: [1, 2, 3],
				week: [1, 2],
				month: [1, 2],
				year: [1],
				custom: ['']
			},
			container: 'body',
			choosed: function(obj, data){}
		}

		var js = {data:j, defaults:defaults};
		Initclass.call(this, js);

		this.init();
	}

	/**
	 * Builds the dom and initializes functionality
	 * @return {undefined} no return value
	 */
	Config.prototype.init = function() {
		this.container = document.querySelector(this.container);

		this.innerContainer = document.createElement('form');
		this.innerContainer.className = 'form-horizontal'

		this.container.appendChild(this.innerContainer);

		if(this.container !== null){
			this.buildSContainer();
		}else{
			console.log('The container is not found, please, check again');
		}
	};

	/**
	 * Gets the chosen date by user, and/or the custom range
	 * @return {object} chosen date
	 */
	Config.prototype.getData = function() {
		var r = {};
		r.type = this.selector.options[this.selector.selectedIndex].value;

		if(typeof this.date1 === 'object'){
			r.range = {};
			r.range.date1 = this.date1.value;
			r.range.date2 = this.date2.value;
		}

		return r;
	};

	Config.prototype.buildSContainer = function() {
		var scontainer = document.createElement('div')
		scontainer.className = 'form-group';

		var label = document.createElement('label');
		label.className = 'col-sm-4 control-label';
		label.setAttribute('for', 'date_selector');
		label.setAttribute('data-ltag', 'dateselector-label');
		var t = App.current.getMainText('dateselector-label');
		label.appendChild(document.createTextNode(t));
		scontainer.appendChild(label);

		var selector = this.buildSelector();
		var div = document.createElement('div');
		div.className = 'col-sm-8';
		div.appendChild(selector);
		scontainer.appendChild(div);

		this.innerContainer.appendChild(scontainer);
	};

	/**
	 * Builds the main date selector
	 * @return {object} returns DOM object (the selector)
	 */
	Config.prototype.buildSelector = function() {
		var selector = document.createElement('select');
		selector.className = 'form-control date-selector';
		selector.id = 'date_selector';

		var periods = Object.keys(this.periods);

		for(var i = 0, len = periods.length; i < len; i++){
			var key = periods[i];
			var period = this.periods[key];

			for(var j = 0, len2 = period.length; j < len2; j++){
				var p = period[j];
				var tag = (typeof p === 'number') ? key+'-'+p : key;

				var option = document.createElement('option');
				option.setAttribute('data-tag', tag);
				option.value = tag;

				var span = document.createElement('span');
				span.setAttribute('data-ltag', tag);
				var t = App.current.getMainText(tag);
				span.appendChild(document.createTextNode(t));
				option.appendChild(span);

				selector.appendChild(option);
			}
		}

		selector._t = this;
		selector.onchange = function(e){
			var s = e.target;
			var chosen = s.options[s.selectedIndex];
			var tag = chosen.getAttribute('data-tag');

			if(tag === 'custom'){
				s._t.buildDateRange();
			}else{
				s._t.deleteDateRange();
			}

			if(s._t.validateRange()){
				s._t.choosed(this, s._t.getData());
			}
		}

		this.selector = selector;
		return selector;
	};

	/**
	 * Builds the "date 1" and "date 2" inputs to make a custom date range
	 * @return {undefined} no return value
	 */
	Config.prototype.buildDateRange = function() {
		this.deleteDateRange();

		var date = new Date().toISOString();
		date = date.match(/([0-9]+){4}[-]([0-9]+){2}[-]([0-9]+){2}/gi)[0];

		//Date 1
		//-------------------------
		this.buildDateContainer(1);
		this.date1.value = date;

		//Date 2
		//-------------------------
		this.buildDateContainer(2);
		this.date2.value = date;

		var dates = [this.date1, this.date2];

		for(var i = 0, len = dates.length; i < len; i++){
			var d = dates[i];

			d._t = this;
			d.onblur = function(){
				if(this._t.validateRange()){
					this._t.choosed(this._t, this._t.getData());
				}
			}
		}

	};

	Config.prototype.validateRange = function() {
		var valid = false;
		if(typeof this.date1 !== 'object'){
			return true;
		}

		var date1 = this.dateStringToObj(this.date1.value);
		var date2 = this.dateStringToObj(this.date2.value);

		if(date1 <= date2){
			return true;
		}

		return valid;
	};

	Config.prototype.dateStringToObj = function(date) {
		if(typeof date === 'object'){
			date = date.match(/(\d+)/gi);
			date = new Date(date[0], date[1], date[2]);
		}

		return date;
	};

	Config.prototype.buildDateContainer = function(no) {
		var container = document.createElement('div');
		container.className = 'form-group date-container';

		var label = document.createElement('label');
		label.className = 'col-sm-4 control-label';
		label.setAttribute('for', 'range-date-'+no);
		label.setAttribute('data-ltag', 'range-date-'+no);
		var t = App.current.getMainText('range-date-'+no);
		label.appendChild(document.createTextNode(t));

		container.appendChild(label);

		var d = document.createElement('div');
		d.className = 'col-sm-8';
		var date = this.buildDate('range-date-'+no);
		d.appendChild(date);
		this['date'+no] = date.getElementsByClassName('form-control')[0];
		container.appendChild(d);

		this.innerContainer.appendChild(container);
	};

	/**
	 * Builds the date input individually.
	 * @return {object} returns DOM object
	 */
	Config.prototype.buildDate = function(id) {
		var container = document.createElement('div');
		container.className = 'input-group';

		var span = document.createElement('span');
		span.className = 'input-group-addon';
		container.appendChild(span);

		var i = document.createElement('i');
		i.className = 'glyphicon glyphicon-calendar';
		span.appendChild(i);

		var input = document.createElement('input');
		input.type = 'date';
		input.className = 'form-control';
		input.id = (typeof id === 'string') ? id : '';

		container.appendChild(input);

		return container;
	};

	/**
	 * Deletes both "date 1" and "date 2" inputs
	 * @return {undefined} no return value
	 */
	Config.prototype.deleteDateRange = function() {
		var prev = this.innerContainer.querySelectorAll('.date-container');
		if(prev.length > 0){
			delete this.date1;
			delete this.date2;

			for(var i = 0, len = prev.length; i < len; i++){
				prev[i].parentNode.removeChild(prev[i]);
			}
		}
	};

	window.DateConfig = function(j){
		return new Config(j);
	}
})();<|endoftext|>


<commit_before>module.exports = (function(){
    'use strict';

    var mongoose = require('mongoose'),
        q = require('q'),
        error = require('../../utils/error'),
        async = require('async'),
        crud = require('./mixins/crud'),
        _ = require('underscore'),
        collectionName = 'users',
        filterFactory = require('./search/filterFactory'),
        schema = require('./schemas/user');

    function handleUser(err, doc, deferred){
        if(err) {
            deferred.reject(error(err));
        }
        else if (doc !== null) {
            deferred.resolve(doc);
        }
        else {
            deferred.reject(error(404));
        }
    }

    var user = Object.create(crud, {
        model: {value: mongoose.model(collectionName, schema)},
        privateFields: {value: ['salt', 'pass_hash','identities']}
    });

    user.getByEmail = function(email) {
        var deferred = q.defer();

        this.model.findOne({email: email}, this.buildIncludes()).lean().exec(function(err, doc){
            handleUser(err, doc, deferred);
        });

        return deferred.promise;
    };

    user.basicAuthentication = function(username, password){
        var deferred = q.defer();

        this.model.findOne({'identities.basic.username': username}, function(err, doc){
            var valid = (doc !== null) ? doc.basicAuthentication(password) : false;

            if(!valid){
                handleUser(null, null, deferred);
            }
            else {
                handleUser(err, doc, deferred);
            }
        });

        return deferred.promise;
    };

    user.googleAuthentication = function(googleId) {
        var deferred = q.defer();

        this.model.findOne({'identities.google.id': googleId}, function(err, doc){
            handleUser(err, doc, deferred);
        });

        return deferred.promise;
    };

    user.savePermissions = function(userId, nodeid, role){
        var deferred = q.defer();

        this.model.findOne({_id: userId}, function(err, doc){
            if(err){
                deferred.reject(error(err));
            }
            else {
                var permissions = (!doc.permissions) ? [] : _.filter(doc.permissions, function(permission) {
                    return permission.nodeid.toString() != nodeid;
                });

                permissions[permissions.length] = {nodeid: nodeid, role: role};

                doc.permissions = permissions;
                doc.save(function(err){
                    if(err){
                        deferred.reject(error(err));
                    } else {
                        deferred.resolve('Success');
                    }
                });
            }
        });

        return deferred.promise;
    };

    user.deletePermission = function(userId, nodeId){
        var deferred = q.defer();

        this.model.findOne({_id: userId}, function(err, doc){
            if(err){
                deferred.reject(error(err));
            }
            else {
                doc.permissions = (!doc.permissions) ? [] : _.filter(doc.permissions, function(permission) {
                    return permission.nodeid.toString() != nodeId;
                });

                doc.save(function(err){
                    if(err){
                        deferred.reject(error(err));
                    } else {
                        deferred.resolve('Success');
                    }
                });
            }
        });

        return deferred.promise;
    };

    user.query = function(filters, options){
        var qry = {},
            deferred = q.defer(),
            self = this;

        filterFactory.createQuery(filters, qry);

        async.parallel(
            [
                function(cb){
                    self.model.find(qry, self.buildIncludes(options)).lean().exec(function(err, data){
                        if(err){
                            cb(err);
                        } else {
                            cb(null, data);
                        }
                    });
                },
                function(cb){
                    self.model.count(qry).lean().exec(function (err, data) {
                        if (err) {
                            cb(err);
                        }
                        else {
                            cb(null, data);
                        }
                    });
                }
            ],function(err, results){
                var result;

                if(err){
                    deferred.reject(err);
                }
                else {

                    result = {
                        total: _.isUndefined(results[1]) ? 0 : results[1],
                        limit: options.limit,
                        skip: options.skip,
                        results: results[0]
                    };

                    deferred.resolve(result);
                }
            }
        );

        return deferred.promise;
    };

    user.linkIdentity = function(userId, key, options) {
        var deferred = q.defer();

        this.model.findOne({_id: userId}, function(err, doc){
            if(err){
                deferred.reject(error(err));
            }
            else {
                doc.identities[key] = options;

                // http://mongoosejs.com/docs/guide.html#toObject Calling to obj will remove a toObj method from the obj.
                doc.linkedIdentities = _.keys(doc.identities);

                doc.save(function(err){
                    if(err){
                        deferred.reject(error(err));
                    } else {
                        deferred.resolve('Success');
                    }
                });
            }
        });

        return deferred.promise;
    };

    user.unLinkIdentity = function(userId, key) {
        var deferred = q.defer();

        this.model.findOne({_id: userId}, function(err, doc){
            if(err){
                deferred.reject(error(err));
            }
            else {

                if(_.has(doc.identities, key)) {
                    delete doc.identities[key];
                }

                // http://mongoosejs.com/docs/guide.html#toObject Calling to obj will remove a toObj method from the obj.
                doc.linkedIdentities = _.keys(doc.identities);

                doc.save(function(err){
                    if(err){
                        deferred.reject(error(err));
                    } else {
                        deferred.resolve('Success');
                    }
                });
            }
        });

        return deferred.promise;
    };

    return user;
})();<commit_msg>Moved update method into this module since it is different than other content.<commit_after>module.exports = (function(){
    'use strict';

    var mongoose = require('mongoose'),
        q = require('q'),
        error = require('../../utils/error'),
        async = require('async'),
        crud = require('./mixins/crud'),
        _ = require('lodash'),
        collectionName = 'users',
        filterFactory = require('./search/filterFactory'),
        schema = require('./schemas/user');

    function handleUser(err, doc, deferred){
        if(err) {
            deferred.reject(error(err));
        }
        else if (doc !== null) {
            deferred.resolve(doc);
        }
        else {
            deferred.reject(error(404));
        }
    }

    var user = Object.create(crud, {
        model: {value: mongoose.model(collectionName, schema)},
        privateFields: {value: ['salt', 'pass_hash','identities']}
    });

    user.getByEmail = function(email) {
        var deferred = q.defer();

        this.model.findOne({email: email}, this.buildIncludes()).lean().exec(function(err, doc){
            handleUser(err, doc, deferred);
        });

        return deferred.promise;
    };

    user.basicAuthentication = function(username, password){
        var deferred = q.defer();

        this.model.findOne({'identities.basic.username': username}, function(err, doc){
            var valid = (doc !== null) ? doc.basicAuthentication(password) : false;

            if(!valid){
                handleUser(null, null, deferred);
            }
            else {
                handleUser(err, doc, deferred);
            }
        });

        return deferred.promise;
    };

    user.googleAuthentication = function(googleId) {
        var deferred = q.defer();

        this.model.findOne({'identities.google.id': googleId}, function(err, doc){
            handleUser(err, doc, deferred);
        });

        return deferred.promise;
    };

    user.savePermissions = function(userId, nodeid, role){
        var deferred = q.defer();

        this.model.findOne({_id: userId}, function(err, doc){
            if(err){
                deferred.reject(error(err));
            }
            else {
                var permissions = (!doc.permissions) ? [] : _.filter(doc.permissions, function(permission) {
                    return permission.nodeid.toString() != nodeid;
                });

                permissions[permissions.length] = {nodeid: nodeid, role: role};

                doc.permissions = permissions;
                doc.save(function(err){
                    if(err){
                        deferred.reject(error(err));
                    } else {
                        deferred.resolve('Success');
                    }
                });
            }
        });

        return deferred.promise;
    };

    user.deletePermission = function(userId, nodeId){
        var deferred = q.defer();

        this.model.findOne({_id: userId}, function(err, doc){
            if(err){
                deferred.reject(error(err));
            }
            else {
                doc.permissions = (!doc.permissions) ? [] : _.filter(doc.permissions, function(permission) {
                    return permission.nodeid.toString() != nodeId;
                });

                doc.save(function(err){
                    if(err){
                        deferred.reject(error(err));
                    } else {
                        deferred.resolve('Success');
                    }
                });
            }
        });

        return deferred.promise;
    };

    user.update = function(obj){
        var primaryKey = _.extend({}, obj)._id,
            self = this,
            deferred = q.defer();

        delete obj._id;

        this.model.findById(primaryKey, function(err, doc) {
            var mergedObj = null;

            if(err) {
                deferred.reject(self.handleError(err));
                return;
            }

            if(doc) {
                console.log(obj);
                mergedObj = _.merge({}, doc.toJSON(), obj);

                _.each(_.keys(obj), function(key){
                    try{
                        doc[key] = mergedObj[key];
                    }
                    catch(ex){
                        console.log(ex);
                    }
                });

                doc.save(function(err){
                    if(err){
                        deferred.reject(self.handleError(err));
                    }
                    else {
                        self.getById(primaryKey)
                            .then(function(data){
                                deferred.resolve(data);
                            })
                            .fail(function(err) {
                                deferred.reject(error(err));
                            });
                    }
                });
            }
            else {
                deferred.reject(error(404));
            }
        });

        return deferred.promise;
    };

    user.query = function(filters, options){
        var qry = {},
            deferred = q.defer(),
            self = this;

        filterFactory.createQuery(filters, qry);

        async.parallel(
            [
                function(cb){
                    self.model.find(qry, self.buildIncludes(options)).lean().exec(function(err, data){
                        if(err){
                            cb(err);
                        } else {
                            cb(null, data);
                        }
                    });
                },
                function(cb){
                    self.model.count(qry).lean().exec(function (err, data) {
                        if (err) {
                            cb(err);
                        }
                        else {
                            cb(null, data);
                        }
                    });
                }
            ],function(err, results){
                var result;

                if(err){
                    deferred.reject(err);
                }
                else {

                    result = {
                        total: _.isUndefined(results[1]) ? 0 : results[1],
                        limit: options.limit,
                        skip: options.skip,
                        results: results[0]
                    };

                    deferred.resolve(result);
                }
            }
        );

        return deferred.promise;
    };

    user.linkIdentity = function(userId, key, options) {
        var deferred = q.defer();

        this.model.findOne({_id: userId}, function(err, doc){
            if(err){
                deferred.reject(error(err));
            }
            else {
                doc.identities[key] = options;

                // http://mongoosejs.com/docs/guide.html#toObject Calling to obj will remove a toObj method from the obj.
                doc.linkedIdentities = _.keys(doc.identities);

                doc.save(function(err){
                    if(err){
                        deferred.reject(error(err));
                    } else {
                        deferred.resolve('Success');
                    }
                });
            }
        });

        return deferred.promise;
    };

    user.unLinkIdentity = function(userId, key) {
        var deferred = q.defer();

        this.model.findOne({_id: userId}, function(err, doc){
            if(err){
                deferred.reject(error(err));
            }
            else {

                if(_.has(doc.identities, key)) {
                    delete doc.identities[key];
                }

                // http://mongoosejs.com/docs/guide.html#toObject Calling to obj will remove a toObj method from the obj.
                doc.linkedIdentities = _.keys(doc.identities);

                doc.save(function(err){
                    if(err){
                        deferred.reject(error(err));
                    } else {
                        deferred.resolve('Success');
                    }
                });
            }
        });

        return deferred.promise;
    };

    return user;
})();<|endoftext|>


<commit_before>class API {
    constructor() {
        this.socket = null
        this.connected = false
        this.reconnection = null
        this.buffer = []
        this.handlers = {}
    }

    connect() {
        // Check that the user's browser supports WebSockets.
        if (!window['WebSocket']) {
            // TODO: warn user that their browser does not support websockets.
            console.log('WARNING: this browser does not support websockets');
        } else {
            this.socket =  new WebSocket('ws://'+window.location.host+'/ws')

            var self = this;

            this.socket.onopen = function(event) {
                // console.log('API::onopen:', event)
                self.connected = true;
                for (var i = 0; i < self.buffer.length; i++) {
                    var message = self.buffer[i];
                    self.send(message.topic, message.payload);
                }
            }

            this.socket.onclose = function(event) {
                // console.log('API::onclose:', event)
                self.connected = false;

                // Attempt to reconnect.
                setTimeout(function() {
                    self.connect();
                }, 5000);
            }

            this.socket.onmessage = function(event) {
                // console.log('API::onmessage:', event)
                var message = JSON.parse(event.data);
                var handled = false;

                for (var h in self.handlers) {
                    var handler = self.handlers[h];
                    if (handler.topic === message.topic) {
                        handler.handler(message.payload);
                        handled = true;
                        break;
                    }
                }

                if (handled === false) {
                    console.log('API::onmessage: received unhandled message:', message);
                    // console.log('API::onmessage: handlers:', self.handlers);
                }
            }
        }
    }

    send(topic, payload) {
        var message = { topic: topic };
        if (payload) {
            message.payload = payload;
        }

        // Either send if the WebSocket has connected or buffer.
        if (this.connected) {
            // console.log('API::send: sending:', message);
            var data = JSON.stringify(message);
            this.socket.send(data);
        } else {
            // console.log('API::send: buffering: ', message);
            this.buffer.push(message);
        }
    }

    addHandler(topic, handler) {
        this.handlers[topic] = {
            topic: topic,
            handler: handler
        };
    }
}

export default new API()
<commit_msg>add connect/disconnect handlers to api.js<commit_after>class API {
    constructor() {
        this.socket = null
        this.connected = false
        this.reconnection = null
        this.buffer = []
        this.handlers = {}
        this.onConnectHandler = null
        this.onDisconnectHandler = null
    }

    connect() {
        // Check that the user's browser supports WebSockets.
        if (!window['WebSocket']) {
            // TODO: warn user that their browser does not support websockets.
            console.log('WARNING: this browser does not support websockets');
        } else {
            this.socket =  new WebSocket('ws://'+window.location.host+'/ws')

            var self = this;

            this.socket.onopen = function(event) {
                // console.log('API::onopen:', event)
                self.connected = true;

                if (self.onConnectHandler != null) {
                    self.onConnectHandler();
                }

                for (var i = 0; i < self.buffer.length; i++) {
                    var message = self.buffer[i];
                    self.send(message.topic, message.payload);
                }
            }

            this.socket.onclose = function(event) {
                // console.log('API::onclose:', event)
                if (self.connected) {
                    self.connected = false;

                    if (self.onDisconnectHandler != null) {
                        self.onDisconnectHandler();
                    }
                }

                // Attempt to reconnect.
                setTimeout(function() {
                    self.connect();
                }, 5000);
            }

            this.socket.onmessage = function(event) {
                // console.log('API::onmessage:', event)
                var message = JSON.parse(event.data);
                var handled = false;

                for (var h in self.handlers) {
                    var handler = self.handlers[h];
                    if (handler.topic === message.topic) {
                        handler.handler(message.payload);
                        handled = true;
                        break;
                    }
                }

                if (handled === false) {
                    console.log('API::onmessage: received unhandled message:', message);
                    // console.log('API::onmessage: handlers:', self.handlers);
                }
            }
        }
    }

    send(topic, payload) {
        var message = { topic: topic };
        if (payload) {
            message.payload = payload;
        }

        // Either send if the WebSocket has connected or buffer.
        if (this.connected) {
            // console.log('API::send: sending:', message);
            var data = JSON.stringify(message);
            this.socket.send(data);
        } else {
            // console.log('API::send: buffering: ', message);
            this.buffer.push(message);
        }
    }

    onConnect(handler) {
        this.onConnectHandler = handler;
    }

    onDisconnect(handler) {
        this.onDisconnectHandler = handler;
    }

    addHandler(topic, handler) {
        this.handlers[topic] = {
            topic: topic,
            handler: handler
        };
    }
}

export default new API()
<|endoftext|>