<commit_before>/**
 * Created by ksinger on 19.02.2016.
 */

import  won from '../won-es6';
import { actionTypes, actionCreators, getConnectionRelatedData, messageTypeToEventType } from './actions';
import { getEventsFromMessage,setCommStateFromResponseForLocalNeedMessage } from '../won-message-utils';

import Immutable from 'immutable';

import {
    checkHttpStatus,
    contains,
} from '../utils';

import {
    buildCreateMessage,
    buildOpenMessage,
    buildCloseMessage,
    buildRateMessage,
    buildConnectMessage,
    isSuccessMessage,
    fetchDataForOwnedNeeds
} from '../won-message-utils';

export function successfulCloseNeed(event) {
    return (dispatch, getState) => {
        console.log("got response for DEACTIVATE: " + event.hasMessageType);
        //TODO maybe refactor these response message handling
        if (getState().getIn(['messages', 'waitingForAnswer', event.isRemoteResponseTo])) {
            console.log("messages waitingForAnswer", event);
            //dispatch(actionCreators.connections__denied(event));
        }
    }
}
export function failedCloseNeed(event) {
    return (dispatch, getState) => {
        const needUri = event.hasReceiverNeed;
        /*
        * TODO not sure if it's necessary to invalidate
        * the cache here as the previous action will just have
        * been an optimistic update of the state. Invalidation
        * should happen in the action that causes the interaction
        * with the server.
        */
        won.invalidateCacheForNeed(needUri) // mark need and it's connection container dirty
            .then(() =>
                won.getConnectionUrisOfNeed(needUri)
            ).then(connectionUris =>
                Promise.all(
                    connectionUris.map(cnctUri =>
                        won.invalidateCacheForNewMessage(cnctUri, needUri) // mark connections dirty
                    )
                )
            ).then(() =>
                // as the need and it's connections have been marked dirty
                // they will be reloaded on this action.
                fetchDataForOwnedNeeds([needUri])
                //fetchAllAccessibleAndRelevantData([needUri])
            ).then(allThatData =>
                dispatch({
                    type: actionTypes.messages.closeNeed.failed,
                    payload: allThatData
                })
            );
    }
}

        /*
         hasReceiverNeed: "https://192.168.124.53:8443/won/resource/need/1741189480636743700"
         hasSenderNeed: "https://192.168.124.53:8443/won/resource/need/1741189480636743700"
         has....Connection
         event.uri

         won.WONMSG.hasReceiverNeed = won.WONMSG.baseUri + "hasReceiverNeed";
         won.WONMSG.hasReceiverNeedCompacted = won.WONMSG.prefix + ":hasReceiverNeed";
         won.WONMSG.hasReceiver = won.WONMSG.baseUri + "hasReceiver"; // connection if connection event
         won.WONMSG.hasReceiverCompacted = won.WONMSG.prefix + ":hasReceiver";
         won.WONMSG.hasReceiverNode = won.WONMSG.baseUri + "hasReceiverNode";
         won.WONMSG.hasReceiverNodeCompacted = won.WONMSG.prefix + ":hasReceiverNode";
         won.WONMSG.hasSenderNeed = won.WONMSG.baseUri + "hasSenderNeed";
         won.WONMSG.hasSenderNeedCompacted = won.WONMSG.prefix + ":hasSenderNeed";
         won.WONMSG.hasSender = won.WONMSG.baseUri + "hasSender";
         won.WONMSG.hasSenderCompacted = won.WONMSG.prefix + ":hasSender";
         won.WONMSG.hasSenderNode = won.WONMSG.baseUri + "hasSenderNode";
         won.WONMSG.hasSenderNodeCompacted = won.WONMSG.prefix + ":hasSenderNode";
         */

export function successfulCloseConnection(event) {
    return (dispatch, getState) => {
        const state = getState();
        console.log("got response for CLOSE: " + event.hasMessageType);
        let eventUri = null;
        let receiverUri = null;
        let isRemoteResponse = false;
        //TODO maybe refactor these response message handling
        if (state.getIn(['messages', 'waitingForAnswer', event.isResponseTo])) {
            console.log("messages waitingForAnswer", event);
            eventUri = event.isResponseTo;
            dispatch({
                type: actionTypes.messages.close.success,
                payload: event
            });
        } else if (state.getIn(['messages', 'waitingForAnswer', event.isRemoteResponseTo])) {
            console.log("messages waitingForAnswer", event);
            eventUri = event.isRemoteResponseTo;
            dispatch({
                type: actionTypes.messages.close.success,
                payload: event
            });
        } else {
            //when a connection is closed by the node (e.g. when you close/deactivate a need all its corresponding connections will be closed)
            dispatch({
                type: actionTypes.messages.close.success,
                payload: event
            })
        }
    }
}

export function successfulConnect(event) {
    return (dispatch, getState) => {
        const state = getState();
        console.log("got response for CONNECT: " + event.hasMessageType);
        let eventUri = null;
        let receiverUri = null;
        let isRemoteResponse = false;
        //TODO maybe refactor these response message handling
        if (state.getIn(['messages', 'waitingForAnswer', event.isRemoteResponseTo])) {
            console.log("messages waitingForAnswer", event);
            eventUri = event.isRemoteResponseTo;
            dispatch({
                type: actionTypes.messages.connect.success,
                payload: event
            });
        }
    }
}

export function successfulOpen(event){
    return (dispatch, getState) => {
        const state = getState();
        console.log("got response for OPEN: " + event.hasMessageType);
        if (state.getIn(['messages', 'waitingForAnswer', event.isRemoteResponseTo])) {
            console.log("messages was in waitingForAnswer", event);

            dispatch(actionCreators.connections__accepted(event));
            dispatch(actionCreators.router__stateGo("post", {
                postUri: event.hasReceiverNeed,
                connectionType: won.WON.Connected,
                connectionUri: event.hasReceiver,
            }));
        }
    }
}

export function successfulCreate(event) {
    return (dispatch) => {
        //const state = getState();
        console.log("got response for CREATE: " + event.hasMessageType);
        //TODO: if negative, use alternative need URI and send again
        //fetch need data and store in local RDF store
        //get URI of newly created need from message

        //load the data into the local rdf store and publish NeedCreatedEvent when done
        var needURI = event.hasReceiverNeed;
        won.ensureLoaded(needURI)
            .then(() => {
                var eventData = won.clone(event);
                eventData.eventType = won.EVENT.NEED_CREATED;
                setCommStateFromResponseForLocalNeedMessage(eventData);
                eventData.needURI = needURI;
                won.getNeedWithConnectionUris(needURI).then((need) => {
                    console.log("Dispatching action " + won.EVENT.NEED_CREATED);
                    dispatch(actionCreators.drafts__publishSuccessful({
                        publishEventUri: event.isResponseTo,
                        needUri: event.hasSenderNeed,
                        eventData: eventData
                    }));
                    dispatch(actionCreators.needs__received(need));
                });
            });

        // dispatch routing change
        //TODO back-button doesn't work for returning to the draft
        //TODO instead of going to the feed, this should go back to where the user was before starting the creation process.
        dispatch(actionCreators.router__stateGo('feed'));

        //TODO add to own needs
        //  linkeddataservice.crawl(event.hasSenderNeed) //agents shouldn't directyl communicate with each other, should they?
    }
}

export function connectionMessageReceived(events) {
    return dispatch => {
        const eventOnRemote = events['msg:FromOwner'];
        const eventOnOwn = events['msg:FromExternal'];
        eventOnRemote.eventType = messageTypeToEventType[eventOnRemote.hasMessageType].eventType;
        //TODO data.hasReceiver, the connectionUri is undefined in the response message
        won.invalidateCacheForNewMessage(eventOnOwn.hasReceiver)
        .then(() => getConnectionData(eventOnRemote, eventOnOwn))
        .then(data =>
            dispatch({
                type: actionTypes.messages.connectionMessageReceived,
                payload: data
            })
        )
    }
}

export function openMessageReceived(events) {
    return dispatch => {
        const eventOnRemote = events['msg:FromOwner'];
        const eventOnOwn = events['msg:FromExternal'];
        eventOnRemote.eventType = messageTypeToEventType[eventOnRemote.hasMessageType].eventType;
        won.invalidateCacheForNewMessage(eventOnOwn.hasReceiver || eventOnRemote.hasReceiver)
        .then(() =>
                getConnectionData(eventOnRemote, eventOnOwn))
        .then(data => {
                dispatch({
                    type: actionTypes.messages.openMessageReceived,
                    payload: data
                })
            }
        )
    }
}

export function connectMessageReceived(events) {
    //return reloadConnectionAndDispatch(events, actionTypes.messages.connectMessageReceived);
    return dispatch => {
        const eventOnRemote = events['msg:FromOwner'];
        const eventOnOwn = events['msg:FromExternal'];
        eventOnRemote.eventType = messageTypeToEventType[eventOnRemote.hasMessageType].eventType;
        won.invalidateCacheForNewConnection(eventOnOwn.hasReceiver, eventOnRemote.hasReceiverNeed)
        .then(() => getConnectionData(eventOnRemote, eventOnOwn))
        .then(data =>
            dispatch({
                type: actionTypes.messages.connectMessageReceived,
                payload: data
            })
        )
    }

}

function getConnectionData(eventOnRemote, eventOnOwn) {
    return won
        .getConnectionWithOwnAndRemoteNeed(eventOnRemote.hasReceiverNeed, eventOnRemote.hasSenderNeed)
        .then(connectionData =>
            getConnectionRelatedData(
                eventOnRemote.hasReceiverNeed,
                eventOnRemote.hasSenderNeed,
                connectionData.uri
            )
            .then(data => {
                // if data.events doesn't contain the arguments-events, add them
                const eventUris = data.events.map(e => e.uri);
                if( ! contains(eventUris, eventOnOwn.uri)) {
                    data.events.push(eventOnOwn);
                    /*TODO resolve hasCorrespondingRemoteMesage.
                     * Problem: runs into same race condition again
                     * that motivated these if-clause.
                     */
                }
                if( ! contains(eventUris, eventOnRemote.uri)) {
                    data.events.push(eventOnRemote);
                }

                data.receivedEvent = eventOnOwn.uri;
                data.updatedConnection = connectionData.uri;
                return data

            })
        )
}

export function hintMessageReceived(event) {
    return dispatch=> {
        event.eventType = messageTypeToEventType[event.hasMessageType].eventType;
        won.invalidateCacheForNewConnection(event.hasReceiver, event.hasReceiverNeed)
            .then(() => {
                let needUri = event.hasReceiverNeed;
                let match = {}

                event.unreadUri = event.hasReceiver;//TODO should be deprecated (as it's set in the event-reducer)
                event.matchScore = event.framedMessage[won.WON.hasMatchScoreCompacted];
                event.matchCounterpartURI = won.getSafeJsonLdValue(event.framedMessage[won.WON.hasMatchCounterpart]);

                console.log('going to crawl connection related data');//deletme

                getConnectionRelatedData(needUri, event.hasMatchCounterpart, event.hasReceiver)
                .then(data => {
                        data.receivedEvent = event.uri;
                        data.updatedConnection = event.hasReceiver;
                        dispatch({
                            type: actionTypes.messages.hintMessageReceived,
                            payload: data
                        });
                    }
                );

                // /add some properties to the eventData so as to make them easily accessible to consumers
                //of the hint event
                // below is commented as it seems to cause to hint event data loaded/displayed
                //if (eventData.matchCounterpartURI != null) {
                //    //load the data of the need the hint is about, if required
                //    //linkedDataService.ensureLoaded(eventData.uri);
                //    linkedDataService.ensureLoaded(eventData.matchCounterpartURI);
                //}

                console.log("handling hint message")
            });
    }
}

<commit_msg>Adding missing event and hasCorrespondingRemoteEvent.<commit_after>/**
 * Created by ksinger on 19.02.2016.
 */

import  won from '../won-es6';
import { actionTypes, actionCreators, getConnectionRelatedData, messageTypeToEventType } from './actions';
import { getEventsFromMessage,setCommStateFromResponseForLocalNeedMessage } from '../won-message-utils';

import Immutable from 'immutable';

import {
    checkHttpStatus,
    clone,
} from '../utils';

import {
    buildCreateMessage,
    buildOpenMessage,
    buildCloseMessage,
    buildRateMessage,
    buildConnectMessage,
    isSuccessMessage,
    fetchDataForOwnedNeeds
} from '../won-message-utils';

export function successfulCloseNeed(event) {
    return (dispatch, getState) => {
        console.log("got response for DEACTIVATE: " + event.hasMessageType);
        //TODO maybe refactor these response message handling
        if (getState().getIn(['messages', 'waitingForAnswer', event.isRemoteResponseTo])) {
            console.log("messages waitingForAnswer", event);
            //dispatch(actionCreators.connections__denied(event));
        }
    }
}
export function failedCloseNeed(event) {
    return (dispatch, getState) => {
        const needUri = event.hasReceiverNeed;
        /*
        * TODO not sure if it's necessary to invalidate
        * the cache here as the previous action will just have
        * been an optimistic update of the state. Invalidation
        * should happen in the action that causes the interaction
        * with the server.
        */
        won.invalidateCacheForNeed(needUri) // mark need and it's connection container dirty
            .then(() =>
                won.getConnectionUrisOfNeed(needUri)
            ).then(connectionUris =>
                Promise.all(
                    connectionUris.map(cnctUri =>
                        won.invalidateCacheForNewMessage(cnctUri, needUri) // mark connections dirty
                    )
                )
            ).then(() =>
                // as the need and it's connections have been marked dirty
                // they will be reloaded on this action.
                fetchDataForOwnedNeeds([needUri])
                //fetchAllAccessibleAndRelevantData([needUri])
            ).then(allThatData =>
                dispatch({
                    type: actionTypes.messages.closeNeed.failed,
                    payload: allThatData
                })
            );
    }
}

        /*
         hasReceiverNeed: "https://192.168.124.53:8443/won/resource/need/1741189480636743700"
         hasSenderNeed: "https://192.168.124.53:8443/won/resource/need/1741189480636743700"
         has....Connection
         event.uri

         won.WONMSG.hasReceiverNeed = won.WONMSG.baseUri + "hasReceiverNeed";
         won.WONMSG.hasReceiverNeedCompacted = won.WONMSG.prefix + ":hasReceiverNeed";
         won.WONMSG.hasReceiver = won.WONMSG.baseUri + "hasReceiver"; // connection if connection event
         won.WONMSG.hasReceiverCompacted = won.WONMSG.prefix + ":hasReceiver";
         won.WONMSG.hasReceiverNode = won.WONMSG.baseUri + "hasReceiverNode";
         won.WONMSG.hasReceiverNodeCompacted = won.WONMSG.prefix + ":hasReceiverNode";
         won.WONMSG.hasSenderNeed = won.WONMSG.baseUri + "hasSenderNeed";
         won.WONMSG.hasSenderNeedCompacted = won.WONMSG.prefix + ":hasSenderNeed";
         won.WONMSG.hasSender = won.WONMSG.baseUri + "hasSender";
         won.WONMSG.hasSenderCompacted = won.WONMSG.prefix + ":hasSender";
         won.WONMSG.hasSenderNode = won.WONMSG.baseUri + "hasSenderNode";
         won.WONMSG.hasSenderNodeCompacted = won.WONMSG.prefix + ":hasSenderNode";
         */

export function successfulCloseConnection(event) {
    return (dispatch, getState) => {
        const state = getState();
        console.log("got response for CLOSE: " + event.hasMessageType);
        let eventUri = null;
        let receiverUri = null;
        let isRemoteResponse = false;
        //TODO maybe refactor these response message handling
        if (state.getIn(['messages', 'waitingForAnswer', event.isResponseTo])) {
            console.log("messages waitingForAnswer", event);
            eventUri = event.isResponseTo;
            dispatch({
                type: actionTypes.messages.close.success,
                payload: event
            });
        } else if (state.getIn(['messages', 'waitingForAnswer', event.isRemoteResponseTo])) {
            console.log("messages waitingForAnswer", event);
            eventUri = event.isRemoteResponseTo;
            dispatch({
                type: actionTypes.messages.close.success,
                payload: event
            });
        } else {
            //when a connection is closed by the node (e.g. when you close/deactivate a need all its corresponding connections will be closed)
            dispatch({
                type: actionTypes.messages.close.success,
                payload: event
            })
        }
    }
}

export function successfulConnect(event) {
    return (dispatch, getState) => {
        const state = getState();
        console.log("got response for CONNECT: " + event.hasMessageType);
        let eventUri = null;
        let receiverUri = null;
        let isRemoteResponse = false;
        //TODO maybe refactor these response message handling
        if (state.getIn(['messages', 'waitingForAnswer', event.isRemoteResponseTo])) {
            console.log("messages waitingForAnswer", event);
            eventUri = event.isRemoteResponseTo;
            dispatch({
                type: actionTypes.messages.connect.success,
                payload: event
            });
        }
    }
}

export function successfulOpen(event){
    return (dispatch, getState) => {
        const state = getState();
        console.log("got response for OPEN: " + event.hasMessageType);
        if (state.getIn(['messages', 'waitingForAnswer', event.isRemoteResponseTo])) {
            console.log("messages was in waitingForAnswer", event);

            dispatch(actionCreators.connections__accepted(event));
            dispatch(actionCreators.router__stateGo("post", {
                postUri: event.hasReceiverNeed,
                connectionType: won.WON.Connected,
                connectionUri: event.hasReceiver,
            }));
        }
    }
}

export function successfulCreate(event) {
    return (dispatch) => {
        //const state = getState();
        console.log("got response for CREATE: " + event.hasMessageType);
        //TODO: if negative, use alternative need URI and send again
        //fetch need data and store in local RDF store
        //get URI of newly created need from message

        //load the data into the local rdf store and publish NeedCreatedEvent when done
        var needURI = event.hasReceiverNeed;
        won.ensureLoaded(needURI)
            .then(() => {
                var eventData = won.clone(event);
                eventData.eventType = won.EVENT.NEED_CREATED;
                setCommStateFromResponseForLocalNeedMessage(eventData);
                eventData.needURI = needURI;
                won.getNeedWithConnectionUris(needURI).then((need) => {
                    console.log("Dispatching action " + won.EVENT.NEED_CREATED);
                    dispatch(actionCreators.drafts__publishSuccessful({
                        publishEventUri: event.isResponseTo,
                        needUri: event.hasSenderNeed,
                        eventData: eventData
                    }));
                    dispatch(actionCreators.needs__received(need));
                });
            });

        // dispatch routing change
        //TODO back-button doesn't work for returning to the draft
        //TODO instead of going to the feed, this should go back to where the user was before starting the creation process.
        dispatch(actionCreators.router__stateGo('feed'));

        //TODO add to own needs
        //  linkeddataservice.crawl(event.hasSenderNeed) //agents shouldn't directyl communicate with each other, should they?
    }
}

export function connectionMessageReceived(events) {
    return dispatch => {
        const eventOnRemote = events['msg:FromOwner'];
        const eventOnOwn = events['msg:FromExternal'];
        eventOnRemote.eventType = messageTypeToEventType[eventOnRemote.hasMessageType].eventType;
        //TODO data.hasReceiver, the connectionUri is undefined in the response message
        won.invalidateCacheForNewMessage(eventOnOwn.hasReceiver)
        .then(() => getConnectionData(eventOnRemote, eventOnOwn))
        .then(data =>
            dispatch({
                type: actionTypes.messages.connectionMessageReceived,
                payload: data
            })
        )
    }
}

export function openMessageReceived(events) {
    return dispatch => {
        const eventOnRemote = events['msg:FromOwner'];
        const eventOnOwn = events['msg:FromExternal'];
        eventOnRemote.eventType = messageTypeToEventType[eventOnRemote.hasMessageType].eventType;
        won.invalidateCacheForNewMessage(eventOnOwn.hasReceiver || eventOnRemote.hasReceiver)
        .then(() =>
                getConnectionData(eventOnRemote, eventOnOwn))
        .then(data => {
                dispatch({
                    type: actionTypes.messages.openMessageReceived,
                    payload: data
                })
            }
        )
    }
}

export function connectMessageReceived(events) {
    //return reloadConnectionAndDispatch(events, actionTypes.messages.connectMessageReceived);
    return dispatch => {
        const eventOnRemote = events['msg:FromOwner'];
        const eventOnOwn = events['msg:FromExternal'];
        eventOnRemote.eventType = messageTypeToEventType[eventOnRemote.hasMessageType].eventType;
        won.invalidateCacheForNewConnection(eventOnOwn.hasReceiver, eventOnRemote.hasReceiverNeed)
        .then(() => getConnectionData(eventOnRemote, eventOnOwn))
        .then(data =>
            dispatch({
                type: actionTypes.messages.connectMessageReceived,
                payload: data
            })
        )
    }

}

/**
 * @deprecated due to the reason given in the TODO.
 * TODO this function indirectly fetches the entire
 * connection again! It should be enough to just
 * use the two events we get in most cases and make
 * the reducers correspondingly smarter.
 * @param eventOnRemote
 * @param eventOnOwn
 * @return {*}
 */
function getConnectionData(eventOnRemote, eventOnOwn) {
    return won
        .getConnectionWithOwnAndRemoteNeed(eventOnRemote.hasReceiverNeed, eventOnRemote.hasSenderNeed)
        .then(connectionData =>
            getConnectionRelatedData(
                eventOnRemote.hasReceiverNeed,
                eventOnRemote.hasSenderNeed,
                connectionData.uri
            )
            .then(data => {
                if(data.events.filter(e => e.uri === eventOnOwn.uri).length === 0) {
                    //
                    /*
                     * if data.events doesn't contain the arguments-events,
                     * add them. they might not be contained in the events-list
                     * due to a race condition, i.e. if data hasn't been
                     * stored on the node when the query resolves.
                     */
                    const eventOnOwn_ = clone(eventOnOwn);
                    eventOnOwn_.hasCorrespondingRemoteMessage = clone(eventOnRemote);
                    data.events.push( eventOnOwn_ );
                }

                data.receivedEvent = eventOnOwn.uri;
                data.updatedConnection = connectionData.uri;

                return data

            })
        )
}

export function hintMessageReceived(event) {
    return dispatch=> {
        event.eventType = messageTypeToEventType[event.hasMessageType].eventType;
        won.invalidateCacheForNewConnection(event.hasReceiver, event.hasReceiverNeed)
            .then(() => {
                let needUri = event.hasReceiverNeed;
                let match = {}

                event.unreadUri = event.hasReceiver;//TODO should be deprecated (as it's set in the event-reducer)
                event.matchScore = event.framedMessage[won.WON.hasMatchScoreCompacted];
                event.matchCounterpartURI = won.getSafeJsonLdValue(event.framedMessage[won.WON.hasMatchCounterpart]);

                console.log('going to crawl connection related data');//deletme

                getConnectionRelatedData(needUri, event.hasMatchCounterpart, event.hasReceiver)
                .then(data => {
                        data.receivedEvent = event.uri;
                        data.updatedConnection = event.hasReceiver;
                        dispatch({
                            type: actionTypes.messages.hintMessageReceived,
                            payload: data
                        });
                    }
                );

                // /add some properties to the eventData so as to make them easily accessible to consumers
                //of the hint event
                // below is commented as it seems to cause to hint event data loaded/displayed
                //if (eventData.matchCounterpartURI != null) {
                //    //load the data of the need the hint is about, if required
                //    //linkedDataService.ensureLoaded(eventData.uri);
                //    linkedDataService.ensureLoaded(eventData.matchCounterpartURI);
                //}

                console.log("handling hint message")
            });
    }
}

<|endoftext|>


<commit_before>var restify = require('restify');
var util = require('./util');
var socketio = require('socket.io');
var async = require('async');
var process = require('process');
var machine = require('./machine');
var detection_daemon = require('./detection_daemon');
var config = require('./config');
var PLATFORM = process.platform;
var log = require('./log').logger('engine');
var db = require('./db');
var macros = require('./macros');
var dashboard = require('./dashboard');
var network = require('./network');
var glob = require('glob');
var argv = require('minimist')(process.argv);
var fs = require('fs');
var sessions = require("client-sessions");
var authentication = require('./authentication');
var crypto = require('crypto');

var Engine = function() {
    this.version = null;
    this.time_synced = false;
};

function EngineConfigFirstTime(callback) {
    switch(PLATFORM) {
        case 'linux':
            callback();
            break;
        case 'darwin':
            config.engine.set('server_port', 9876);
            glob.glob('/dev/cu.usbmodem*', function(err, files) {
                if(files.length >= 2) {
                    var ports = {
                        'control_port_osx' : files[0],
                        'data_port_osx' : files[1]
                    }
                    config.engine.update(ports, function() {
                        callback();
                    });
                } else {
                    callback();
                }
            });
        break;

        default:
            callback();
        break;
    }
};

Engine.prototype.setTime = function(obj) {
    if(!this.time_synced) {
        this.time_synced = true;
        var d = new Date(obj.utc);
        log.debug("Setting the time to " + d.toUTCString());
        var t = d.getUTCFullYear() + '-' + d.getUTCMonth() + '-' + d.getUTCDay() + ' ' + d.getUTCHours() + ':' + d.getUTCMinutes() + ':' + d.getUTCSeconds()
    cmd = 'timedatectl set-time ' + t + '; timedatectl';
        util.doshell(cmd, function(stdout) {
            console.log(stdout);
        });
    }
}

Engine.prototype.stop = function(callback) {
    this.machine.disconnect();
    this.machine.setState(this.machine, 'stopped');

    //this.server.close();
    //this.server.io.server.close();
    callback(null);
};

Engine.prototype.getVersion = function(callback) {
    util.doshell('git rev-parse --verify HEAD', function(data) {
        this.version = {};
        this.version.hash = (data || "").trim();
        this.version.number = "";
        this.version.debug = ('debug' in argv);
        fs.readFile('version.json', 'utf8', function(err, data) {
            if(err) {
                this.version.type = 'dev';
                return callback(null, this.version);
            }
            try {
                data = JSON.parse(data);
                if(data.number) {
                    this.version.number = data.number;
                    this.version.type = 'release';
                }
            } catch(e) {
                this.version.type = 'dev';
                this.version.number
            } finally {
                callback(null, this.version);
            }
        }.bind(this))
    });
}

Engine.prototype.start = function(callback) {

    async.series([

        // Configure the engine data directories
       function setup_application(callback) {
            log.info('Checking engine data directory tree...');
            config.createDataDirectories(callback);
        },

        // Load the engine configuration from disk
        function load_engine_config(callback) {
            log.info("Loading engine configuration...");
            config.configureEngine(callback);
        },

        function check_engine_config(callback) {
            if(!config.engine.userConfigLoaded) {
                EngineConfigFirstTime(callback);
            } else {
                callback();
            }
        },

        function get_fabmo_version(callback) {
            log.info("Getting engine version...");
            this.getVersion(function(err, data) {
                if(err) {
                    log.error(err);
                    this.version = "";
                    return callback();
                }
                this.version = data;
                callback();
            }.bind(this));
        }.bind(this),

        function clear_approot(callback) {
            if('debug' in argv) {
                log.info("Running in debug mode - clearing the approot.");
                config.clearAppRoot(function(err, stdout) {
                    if(err) { log.error(err); }
                    else {
                        log.debug(stdout);
                    }
                    callback();
                });
            } else {
                var last_time_version = config.engine.get('version').trim();
                var this_time_version = this.version.hash.trim();
                log.debug("Previous engine version: " + last_time_version);
                log.debug(" Current engine version: " + this_time_version);

                if(last_time_version != this_time_version) {
                    log.info("Engine version has changed - clearing the approot.");
                    config.clearAppRoot(function(err, stdout) {
                        if(err) { log.error(err); }
                        else {
                            log.debug(stdout);
                        }
                        callback();
                    });
                } else {
                    log.info("Engine version is unchanged since last run.");
                    callback();
                }
            }
            config.engine.set('version', this_time_version);
        }.bind(this),

        function create_data_directories(callback) {
            config.createDataDirectories(callback);
        }.bind(this),

        // "Apply" the engine configuration, that is, take the configuration values loaded and actually
        // set up the application based on them.
        function apply_engine_config(callback) {
            log.info("Applying engine configuration...");
            config.engine.apply(callback);
        },

        // Configure the DB
        function setup_database(callback) {
            log.info("Configuring database...");
            db.configureDB(callback);
        },

        // Cleanup the DB
        function clean_database(callback) {
            log.info("Cleaning up database...");
            db.cleanup(callback);
        },

        // Connect to G2 and initialize machine runtimes
        function connect(callback) {
            log.info("Connecting to G2...");
            machine.connect(function(err, machine) {
                if(err) {
                    log.error("!!!!!!!!!!!!!!!!!!!!!!!!");
                    log.error("Could not connect to G2.");
                    log.error("(" + err + ")");
                    log.error("!!!!!!!!!!!!!!!!!!!!!!!!");
                }
                callback(null);
            });
        }.bind(this),

        function launch_detection_daemon(callback){
            log.info("Launching detection daemon...");
            detection_daemon();
            callback(null);
        }.bind(this),
        function load_machine_config(callback) {
            this.machine = machine.machine;
            log.info('Loading the machine configuration...')
            config.configureMachine(this.machine, function(err, result) {
                if(err) {
                    log.warn(err);
                }
                callback(null);
            });
        }.bind(this),

/*        function set_units(callback) {
            this.machine.driver.setUnits(config.machine.get('units'), callback);
        }.bind(this),
*/
        // Configure G2 by loading all its json settings and static configuration parameters
        function load_driver_config(callback) {
            if(this.machine.isConnected()) {
                log.info("Configuring G2...");
                config.configureDriver(machine.machine.driver, function(err, data) {
                    if(err) {
                        log.error("There were problems loading the G2 configuration.");
                    }
                    callback(null);
                });
            } else {
                log.warn("Skipping G2 configuration due to no connection.");
                config.configureDriver(null, function(err, data) {
                    callback(null);
                })
                callback(null);
            }
        }.bind(this),

        function get_g2_version(callback) {
            if(this.machine.isConnected()) {
                log.info("Getting G2 firmware version...");
                this.machine.driver.get('fb', function(err, value) {
                    if(err) {
                        log.error('Could not get the G2 firmware build. (' + err + ')');
                    } else {
                        log.info('G2 Firmware Build: ' + value);
                    }
                    callback(null);
                });
            } else {
                log.warn("Skipping G2 firmware version check due to no connection.")
                callback(null);
            }
        }.bind(this),

        function load_opensbp_commands(callback) {
            log.info("Loading OpenSBP Commands...");
            this.machine.sbp_runtime.loadCommands(callback);
        }.bind(this),

        function load_opensbp_config(callback) {
            log.info("Configuring OpenSBP runtime...");
            config.configureOpenSBP(callback);
        },

        function apply_machine_config(callback) {
            log.info("Applying machine configuration...");
            config.machine.apply(callback);
        }.bind(this),

        function configure_dashboard(callback) {
            log.info("Configuring dashboard...");
            dashboard.configure(callback);
        },

        function load_apps(callback) {
            log.info("Loading dashboard apps...");
            dashboard.loadApps(function(err, result) {
                callback(null, result);
            });
        },

        function load_macros(callback) {
            log.info("Loading macros...")
            macros.load(callback);
        },

        function load_instance_config(callback) {
            log.info("Loading instance info...");
            config.configureInstance(this.machine.driver, callback);
        }.bind(this),

        function apply_instance_config(callback) {
            log.info("Applying instance configuration...");
            config.instance.apply(callback);
        },

        // Kick off the server if all of the above went OK.
        function start_server(callback) {
            log.info("Setting up the webserver...");
            var server = restify.createServer({name:"FabMo Engine"});
            this.server = server;

            // Allow JSON over Cross-origin resource sharing
            log.info("Configuring cross-origin requests...");
            server.use(
                function crossOrigin(req,res,next){
                    res.header("Access-Control-Allow-Origin", "*");
                    res.header("Access-Control-Allow-Headers", "X-Requested-With");
                    return next();
                }
            );

            if('debug' in argv && argv.debug === 'slow') {
                // Introduce deliberate latency for testing
                log.warn("Configuring deliberate latency for testing...")
                server.use(
                    function latency(req, res, next) {
                        setTimeout(next,500*Math.random());
                    }
                );
            }

            if('debug' in argv) {
                server.use(
                    function debug(req, res, next) {
                        log.debug(req.method + ' ' + req.url);
                        next();
                    });
            }

            server.use(restify.queryParser());

            server.on('uncaughtException', function(req, res, route, err) {
                log.uncaught(err);
                answer = {
                    status:"error",
                    message:err.message
                };
                res.json(answer)
            });

            // Configure local directory for uploading files
            log.info("Cofiguring upload directory...");
            server.use(restify.bodyParser({'uploadDir':config.engine.get('upload_dir') || '/tmp'}));
            server.pre(restify.pre.sanitizePath());

            //configuring authentication
            log.info("Cofiguring authentication...");
            if('debug' in argv) {
                server.cookieSecret = "fabmodebugsecret";
            } else {
                server.cookieSecret = crypto.randomBytes(256).toString('hex');
            }

            server.use(sessions({
                // cookie name dictates the key name added to the request object
                cookieName: 'session',
                // should be a large unguessable string
                secret: server.cookieSecret, // REQUIRE HTTPS SUPPORT !!!
                // how long the session will stay valid in ms
                duration: 1 * 24 * 60 * 60 * 1000, // 1 day
                cookie: {
                  //: '/api', // cookie will only be sent to requests under '/api'
                  //maxAge: 60000, // duration of the cookie in milliseconds, defaults to duration above
                  ephemeral: true, // when true, cookie expires when the browser closes
                  httpOnly: false, // when true, cookie is not accessible from javascript
                  secure: false // when true, cookie will only be sent over SSL. use key 'secureProxy' instead if you handle SSL not in your node process
                }
            }));

            server.use(authentication.passport.initialize());
            server.use(authentication.passport.session());

            authentication.configure();
            log.info("Enabling gzip for transport...");
            server.use(restify.gzipResponse());
            // Import the routes module and apply the routes to the server
            log.info("Loading routes...");
            server.io = socketio.listen(server.server);
            var routes = require('./routes')(server);

            // Kick off the server listening for connections
            server.listen(config.engine.get('server_port'), "0.0.0.0", function() {
                log.info(server.name+ ' listening at '+ server.url);
                callback(null, server);
            });

        }.bind(this),
        ],

        function(err, results) {
            if(err) {
                log.error(err);
                typeof callback === 'function' && callback(err);
            } else {
                typeof callback === 'function' && callback(null, this);
            }
        }.bind(this)
    );
};

module.exports = new Engine();
<commit_msg>Make secret key regeneration less frequent - more reliable login<commit_after>var restify = require('restify');
var util = require('./util');
var socketio = require('socket.io');
var async = require('async');
var process = require('process');
var machine = require('./machine');
var detection_daemon = require('./detection_daemon');
var config = require('./config');
var PLATFORM = process.platform;
var log = require('./log').logger('engine');
var db = require('./db');
var macros = require('./macros');
var dashboard = require('./dashboard');
var network = require('./network');
var glob = require('glob');
var argv = require('minimist')(process.argv);
var fs = require('fs');
var sessions = require("client-sessions");
var authentication = require('./authentication');
var crypto = require('crypto');

var Engine = function() {
    this.version = null;
    this.time_synced = false;
};

function EngineConfigFirstTime(callback) {
    switch(PLATFORM) {
        case 'linux':
            callback();
            break;
        case 'darwin':
            config.engine.set('server_port', 9876);
            glob.glob('/dev/cu.usbmodem*', function(err, files) {
                if(files.length >= 2) {
                    var ports = {
                        'control_port_osx' : files[0],
                        'data_port_osx' : files[1]
                    }
                    config.engine.update(ports, function() {
                        callback();
                    });
                } else {
                    callback();
                }
            });
        break;

        default:
            callback();
        break;
    }
};

Engine.prototype.setTime = function(obj) {
    if(!this.time_synced) {
        this.time_synced = true;
        var d = new Date(obj.utc);
        log.debug("Setting the time to " + d.toUTCString());
        var t = d.getUTCFullYear() + '-' + d.getUTCMonth() + '-' + d.getUTCDay() + ' ' + d.getUTCHours() + ':' + d.getUTCMinutes() + ':' + d.getUTCSeconds()
    cmd = 'timedatectl set-time ' + t + '; timedatectl';
        util.doshell(cmd, function(stdout) {
            console.log(stdout);
        });
    }
}

Engine.prototype.stop = function(callback) {
    this.machine.disconnect();
    this.machine.setState(this.machine, 'stopped');

    //this.server.close();
    //this.server.io.server.close();
    callback(null);
};

Engine.prototype.getVersion = function(callback) {
    util.doshell('git rev-parse --verify HEAD', function(data) {
        this.version = {};
        this.version.hash = (data || "").trim();
        this.version.number = "";
        this.version.debug = ('debug' in argv);
        fs.readFile('version.json', 'utf8', function(err, data) {
            if(err) {
                this.version.type = 'dev';
                return callback(null, this.version);
            }
            try {
                data = JSON.parse(data);
                if(data.number) {
                    this.version.number = data.number;
                    this.version.type = 'release';
                }
            } catch(e) {
                this.version.type = 'dev';
                this.version.number
            } finally {
                callback(null, this.version);
            }
        }.bind(this))
    });
}

Engine.prototype.start = function(callback) {

    async.series([

        // Configure the engine data directories
       function setup_application(callback) {
            log.info('Checking engine data directory tree...');
            config.createDataDirectories(callback);
        },

        // Load the engine configuration from disk
        function load_engine_config(callback) {
            log.info("Loading engine configuration...");
            config.configureEngine(callback);
        },

        function check_engine_config(callback) {
            if(!config.engine.userConfigLoaded) {
                EngineConfigFirstTime(callback);
            } else {
                callback();
            }
        },

        function get_fabmo_version(callback) {
            log.info("Getting engine version...");
            this.getVersion(function(err, data) {
                if(err) {
                    log.error(err);
                    this.version = "";
                    return callback();
                }
                this.version = data;
                callback();
            }.bind(this));
        }.bind(this),

        function clear_approot(callback) {
            if('debug' in argv) {
                log.info("Running in debug mode - clearing the approot.");
                config.clearAppRoot(function(err, stdout) {
                    if(err) { log.error(err); }
                    else {
                        log.debug(stdout);
                    }
                    callback();
                });
            } else {
                var last_time_version = config.engine.get('version').trim();
                var this_time_version = this.version.hash.trim();
                log.debug("Previous engine version: " + last_time_version);
                log.debug(" Current engine version: " + this_time_version);

                if(last_time_version != this_time_version) {
                    log.info("Engine version has changed - clearing the approot.");
                    config.clearAppRoot(function(err, stdout) {
                        if(err) { log.error(err); }
                        else {
                            log.debug(stdout);
                        }
                        callback();
                    });
                } else {
                    log.info("Engine version is unchanged since last run.");
                    callback();
                }
            }
            config.engine.set('version', this_time_version);
        }.bind(this),

        function create_data_directories(callback) {
            config.createDataDirectories(callback);
        }.bind(this),

        // "Apply" the engine configuration, that is, take the configuration values loaded and actually
        // set up the application based on them.
        function apply_engine_config(callback) {
            log.info("Applying engine configuration...");
            config.engine.apply(callback);
        },

        // Configure the DB
        function setup_database(callback) {
            log.info("Configuring database...");
            db.configureDB(callback);
        },

        // Cleanup the DB
        function clean_database(callback) {
            log.info("Cleaning up database...");
            db.cleanup(callback);
        },

        // Connect to G2 and initialize machine runtimes
        function connect(callback) {
            log.info("Connecting to G2...");
            machine.connect(function(err, machine) {
                if(err) {
                    log.error("!!!!!!!!!!!!!!!!!!!!!!!!");
                    log.error("Could not connect to G2.");
                    log.error("(" + err + ")");
                    log.error("!!!!!!!!!!!!!!!!!!!!!!!!");
                }
                callback(null);
            });
        }.bind(this),

        function launch_detection_daemon(callback){
            log.info("Launching detection daemon...");
            detection_daemon();
            callback(null);
        }.bind(this),

        function load_machine_config(callback) {
            this.machine = machine.machine;
            log.info('Loading the machine configuration...')
            config.configureMachine(this.machine, function(err, result) {
                if(err) {
                    log.warn(err);
                }
                callback(null);
            });
        }.bind(this),

/*        function set_units(callback) {
            this.machine.driver.setUnits(config.machine.get('units'), callback);
        }.bind(this),
*/
        // Configure G2 by loading all its json settings and static configuration parameters
        function load_driver_config(callback) {
            if(this.machine.isConnected()) {
                log.info("Configuring G2...");
                config.configureDriver(machine.machine.driver, function(err, data) {
                    if(err) {
                        log.error("There were problems loading the G2 configuration.");
                    }
                    callback(null);
                });
            } else {
                log.warn("Skipping G2 configuration due to no connection.");
                config.configureDriver(null, function(err, data) {
                    callback(null);
                })
                callback(null);
            }
        }.bind(this),

        function get_g2_version(callback) {
            if(this.machine.isConnected()) {
                log.info("Getting G2 firmware version...");
                this.machine.driver.get('fb', function(err, value) {
                    if(err) {
                        log.error('Could not get the G2 firmware build. (' + err + ')');
                    } else {
                        log.info('G2 Firmware Build: ' + value);
                    }
                    callback(null);
                });
            } else {
                log.warn("Skipping G2 firmware version check due to no connection.")
                callback(null);
            }
        }.bind(this),

        function load_opensbp_commands(callback) {
            log.info("Loading OpenSBP Commands...");
            this.machine.sbp_runtime.loadCommands(callback);
        }.bind(this),

        function load_opensbp_config(callback) {
            log.info("Configuring OpenSBP runtime...");
            config.configureOpenSBP(callback);
        },

        function apply_machine_config(callback) {
            log.info("Applying machine configuration...");
            config.machine.apply(callback);
        }.bind(this),

        function configure_dashboard(callback) {
            log.info("Configuring dashboard...");
            dashboard.configure(callback);
        },

        function load_apps(callback) {
            log.info("Loading dashboard apps...");
            dashboard.loadApps(function(err, result) {
                callback(null, result);
            });
        },

        function load_macros(callback) {
            log.info("Loading macros...")
            macros.load(callback);
        },

        function load_instance_config(callback) {
            log.info("Loading instance info...");
            config.configureInstance(this.machine.driver, callback);
        }.bind(this),

        function apply_instance_config(callback) {
            log.info("Applying instance configuration...");
            config.instance.apply(callback);
        },

        function generate_auth_key(callback) {
          log.info("Configuring secret key...")
          var secret_file = config.getDataDir() + '/config/auth_secret'
          fs.readFile(secret_file, 'utf8', function(err, data) {

            // If there's already a secret key from disk, use it
            if(!err && data && (data.length == 512)) {
              log.info("Secret key already exists, using that.")
              this.auth_secret = data;
              return callback();
            }

            // If not, generate, save and use a new one
            log.info("Generating a new secret key.")
            this.auth_secret = crypto.randomBytes(256).toString('hex');
            fs.writeFile(secret_file, this.auth_secret, function(err, data) {
              callback();
            }.bind(this));

          }.bind(this))
        }.bind(this),

        // Kick off the server if all of the above went OK.
        function start_server(callback) {
            log.info("Setting up the webserver...");
            var server = restify.createServer({name:"FabMo Engine"});
            this.server = server;

            // Allow JSON over Cross-origin resource sharing
            log.info("Configuring cross-origin requests...");
            server.use(
                function crossOrigin(req,res,next){
                    res.header("Access-Control-Allow-Origin", "*");
                    res.header("Access-Control-Allow-Headers", "X-Requested-With");
                    return next();
                }
            );

            if('debug' in argv && argv.debug === 'slow') {
                // Introduce deliberate latency for testing
                log.warn("Configuring deliberate latency for testing...")
                server.use(
                    function latency(req, res, next) {
                        setTimeout(next,500*Math.random());
                    }
                );
            }

            if('debug' in argv) {
                server.use(
                    function debug(req, res, next) {
                        log.debug(req.method + ' ' + req.url);
                        next();
                    });
            }

            server.use(restify.queryParser());

            server.on('uncaughtException', function(req, res, route, err) {
                log.uncaught(err);
                answer = {
                    status:"error",
                    message:err.message
                };
                res.json(answer)
            });

            // Configure local directory for uploading files
            log.info("Cofiguring upload directory...");
            server.use(restify.bodyParser({'uploadDir':config.engine.get('upload_dir') || '/tmp'}));
            server.pre(restify.pre.sanitizePath());

            log.info("Cofiguring authentication...");
            log.info("Secret Key: " + this.auth_secret.slice(0,5) + '...' + this.auth_secret.slice(-5));
            server.cookieSecret = this.auth_secret;
            server.use(sessions({
                // cookie name dictates the key name added to the request object
                cookieName: 'session',
                // should be a large unguessable string
                secret: server.cookieSecret, // REQUIRE HTTPS SUPPORT !!!
                // how long the session will stay valid in ms
                duration: 1 * 24 * 60 * 60 * 1000, // 1 day
                cookie: {
                  //: '/api', // cookie will only be sent to requests under '/api'
                  //maxAge: 60000, // duration of the cookie in milliseconds, defaults to duration above
                  ephemeral: false, // when true, cookie expires when the browser closes
                  httpOnly: false, // when true, cookie is not accessible from javascript
                  secure: false // when true, cookie will only be sent over SSL. use key 'secureProxy' instead if you handle SSL not in your node process
                }
            }));

            server.use(authentication.passport.initialize());
            server.use(authentication.passport.session());

            authentication.configure();
            log.info("Enabling gzip for transport...");
            server.use(restify.gzipResponse());
            // Import the routes module and apply the routes to the server
            log.info("Loading routes...");
            server.io = socketio.listen(server.server);
            var routes = require('./routes')(server);

            // Kick off the server listening for connections
            server.listen(config.engine.get('server_port'), "0.0.0.0", function() {
                log.info(server.name+ ' listening at '+ server.url);
                callback(null, server);
            });

        }.bind(this),
        ],

        function(err, results) {
            if(err) {
                log.error(err);
                typeof callback === 'function' && callback(err);
            } else {
                typeof callback === 'function' && callback(null, this);
            }
        }.bind(this)
    );
};

module.exports = new Engine();
<|endoftext|>


<commit_before>// Create and initialize the singleton engine object. This runs inside
// the bench.html page in the headless browser on the server.

window.engine = (function() {

    var Q = MathJax.Hub.queue;
    var in_formats = ["latex", "mml"];
    var types = {};
    var current_type;
    var buffer = [];

    // Initialize the engine. This is pushed onto the MathJax queue to make sure
    // it is called after MathJax initialization is done.
    var _init = function() {
        Q.Push(function () {
            in_formats.forEach(function(format) {
                var id = "math-" + format;
                var div = document.getElementById(id);
                var jax = MathJax.Hub.getAllJax(id)[0];
                types[format] = {
                    div: div,
                    jax: jax,
                    script_elem: document.getElementById(jax.inputID),
                    error_message: ''
                }
            });

            // Capture all error signals
            MathJax.Hub.signal.Interest(function(message) {
                var m = message[0];
                if (m.match("^TeX Jax") || m.match("^MathML Jax")) {
                    current_type.error_message = message[0] + ": " + message[1];
                }
            });

            _process_buffered();
        });
    };

    // If someone calls process() before init is complete, that call will be stored into
    // a buffer. After the init is complete, all buffered stuff will get resolved.
    var _process_buffered = function() {
        for (var i = 0; i < buffer.length; ++i) {
            process(buffer[i][0], buffer[i][1]);
        }
        buffer = [];
    };

    // Serialize an (svg) element
    var _serialize = function(svg) {
        var tmpDiv = document.createElement('div');
        tmpDiv.appendChild(svg);
        return tmpDiv.innerHTML;
    };

    // MathJax keeps parts of SVG symbols in one hidden svg at
    // the begining of the DOM, this function should take two
    // SVGs and return one stand-alone svg.
    var _merge_svgs = function(svg) {
        var origDefs = document.getElementById('MathJax_SVG_Hidden')
            .nextSibling.childNodes[0];
        var defs = origDefs.cloneNode(false);

        // append shallow defs and change xmlns.
        svg.insertBefore(defs, svg.childNodes[0]);
        svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");

        // clone and copy all used paths into local defs.
        // xlink:href in uses FIX
        var uses = svg.getElementsByTagName("use");
        for (var k = 0; k < uses.length; ++k) {
            var id = uses[k].getAttribute("href");
            defs.appendChild(
                document.getElementById(id.substr(1)).cloneNode(true)
            );
            if (uses[k]["href"] === undefined) {
                uses[k].setAttribute("xlink:href", id);
            }
        }

        svg.style.position = "static";
        return _serialize(svg);
    };

    // The process() function is called from main.js's listenLoop, when it gets
    // a request that requires MathJax.
    // When process() is finished, the callback cb will be invoked with an
    // array [<q string>, <svg out>]. That, in turn, causes the page's onCallback
    // function to be called (in main.js), which sends the response to the client.
    // If there is an error during the rendering, then the second
    // element, instead of a string, will be a nested array with
    // one string element giving the error message.
    var process = function(query, cb) {
        // For debugging, the console doesn't work from here, but you can return dummy
        // data, as follows.  It will show up in the browser instead of the real results.
        //cb([query.num, query.q, ["debug message"]]);
        //return;

        var t = types[query.in_format];
        if (typeof t === 'undefined') {
            cb([query.num, query.q, ["Invalid in_format: '" + query.in_format + "'"]]);
            return;
        }

        // If we get called too early, buffer the request
        if (t === null || t.jax === null) {
            buffer.push( [query, cb] );
            return;
        }

        var q = query.q,
            width = query.width,
            div = t.div,
            jax = t.jax,
            script_elem = t.script_elem;

        // We'll push three functions onto the queue: initialization, mathjax processing,
        // and then evaluating the results. They're all pushed at the same time, making sure
        // they get executed sequentially, with no intervening functions.

        Q.Push(

            // Initialize, get ready to process the equation
            function() {
                if (width === null) {
                    // Let's just use a default width of 1000 (arbitrary)
                    div.setAttribute('style', 'width: 1000px');
                }
                else {
                    div.setAttribute('style', 'width: ' + width + 'px');
                }
                current_type = t;
                current_type.error_message = '';
                script_elem.removeChild(script_elem.firstChild);
                script_elem.appendChild(document.createTextNode(q));
            },

            // (Re)process the equation
            ["Reprocess", jax],

            // Evaluate the results
            function() {
                var ret = null;

                if (current_type.error_message) {
                    ret = [current_type.error_message];
                }
                else {
                    var svg_elem = div.getElementsByTagName("svg")[0];
                    if (!svg_elem) {
                        ret = ['MathJax error'];
                    }
                    else {
                        ret = _merge_svgs(svg_elem.cloneNode(true));
                    }
                }

                cb([query.num, query.q, ret]);
            }
        );
    };

    _init();

    // This sets the global window.engine object value; can be used for debugging
    // in a browser.
    return {
        Q: Q,
        types: types,
        buffer: buffer,
        current_type: current_type,
        _init: _init,
        _process_buffered: _process_buffered,
        _serialize: _serialize,
        _merge_svgs: _merge_svgs,
        process: process
    };
})();
<commit_msg>Make sure equation labels are reset<commit_after>// Create and initialize the singleton engine object. This runs inside
// the bench.html page in the headless browser on the server.

window.engine = (function() {

    var Q = MathJax.Hub.queue;
    var in_formats = ["latex", "mml"];
    var types = {};
    var current_type;
    var buffer = [];

    // Initialize the engine. This is pushed onto the MathJax queue to make sure
    // it is called after MathJax initialization is done.
    var _init = function() {
        Q.Push(function () {
            in_formats.forEach(function(format) {
                var id = "math-" + format;
                var div = document.getElementById(id);
                var jax = MathJax.Hub.getAllJax(id)[0];
                types[format] = {
                    div: div,
                    jax: jax,
                    script_elem: document.getElementById(jax.inputID),
                    error_message: ''
                }
            });

            // Capture all error signals
            MathJax.Hub.signal.Interest(function(message) {
                var m = message[0];
                if (m.match("^TeX Jax") || m.match("^MathML Jax")) {
                    current_type.error_message = message[0] + ": " + message[1];
                }
            });

            _process_buffered();
        });
    };

    // If someone calls process() before init is complete, that call will be stored into
    // a buffer. After the init is complete, all buffered stuff will get resolved.
    var _process_buffered = function() {
        for (var i = 0; i < buffer.length; ++i) {
            process(buffer[i][0], buffer[i][1]);
        }
        buffer = [];
    };

    // Serialize an (svg) element
    var _serialize = function(svg) {
        var tmpDiv = document.createElement('div');
        tmpDiv.appendChild(svg);
        return tmpDiv.innerHTML;
    };

    // MathJax keeps parts of SVG symbols in one hidden svg at
    // the begining of the DOM, this function should take two
    // SVGs and return one stand-alone svg.
    var _merge_svgs = function(svg) {
        var origDefs = document.getElementById('MathJax_SVG_Hidden')
            .nextSibling.childNodes[0];
        var defs = origDefs.cloneNode(false);

        // append shallow defs and change xmlns.
        svg.insertBefore(defs, svg.childNodes[0]);
        svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");

        // clone and copy all used paths into local defs.
        // xlink:href in uses FIX
        var uses = svg.getElementsByTagName("use");
        for (var k = 0; k < uses.length; ++k) {
            var id = uses[k].getAttribute("href");
            defs.appendChild(
                document.getElementById(id.substr(1)).cloneNode(true)
            );
            if (uses[k]["href"] === undefined) {
                uses[k].setAttribute("xlink:href", id);
            }
        }

        svg.style.position = "static";
        return _serialize(svg);
    };

    // The process() function is called from main.js's listenLoop, when it gets
    // a request that requires MathJax.
    // When process() is finished, the callback cb will be invoked with an
    // array [<q string>, <svg out>]. That, in turn, causes the page's onCallback
    // function to be called (in main.js), which sends the response to the client.
    // If there is an error during the rendering, then the second
    // element, instead of a string, will be a nested array with
    // one string element giving the error message.
    var process = function(query, cb) {
        // For debugging, the console doesn't work from here, but you can return dummy
        // data, as follows.  It will show up in the browser instead of the real results.
        //cb([query.num, query.q, ["debug message"]]);
        //return;

        var t = types[query.in_format];
        if (typeof t === 'undefined') {
            cb([query.num, query.q, ["Invalid in_format: '" + query.in_format + "'"]]);
            return;
        }

        // If we get called too early, buffer the request
        if (t === null || t.jax === null) {
            buffer.push( [query, cb] );
            return;
        }

        var q = query.q,
            width = query.width,
            div = t.div,
            jax = t.jax,
            script_elem = t.script_elem;

        // We'll push three functions onto the queue: initialization, mathjax processing,
        // and then evaluating the results. They're all pushed at the same time, making sure
        // they get executed sequentially, with no intervening functions.

        Q.Push(

            // Initialize, get ready to process the equation
            function() {
                if (width === null) {
                    // Let's just use a default width of 1000 (arbitrary)
                    div.setAttribute('style', 'width: 1000px');
                }
                else {
                    div.setAttribute('style', 'width: ' + width + 'px');
                }
                current_type = t;
                current_type.error_message = '';
                script_elem.removeChild(script_elem.firstChild);
                script_elem.appendChild(document.createTextNode(q));
            },

            // (Re)process the equation. Make sure equation labels are reset, see
            // https://groups.google.com/forum/#!topic/mathjax-users/cpwy5eCH1ZQ/discussion
            ["resetEquationNumbers", MathJax.InputJax.TeX],
            ["PreProcess", MathJax.Hub],
            ["Reprocess", jax],

            // Evaluate the results
            function() {
                var ret = null;

                if (current_type.error_message) {
                    ret = [current_type.error_message];
                }
                else {
                    var svg_elem = div.getElementsByTagName("svg")[0];
                    if (!svg_elem) {
                        ret = ['MathJax error'];
                    }
                    else {
                        ret = _merge_svgs(svg_elem.cloneNode(true));
                    }
                }

                cb([query.num, query.q, ret]);
            }
        );
    };

    _init();

    // This sets the global window.engine object value; can be used for debugging
    // in a browser.
    return {
        Q: Q,
        types: types,
        buffer: buffer,
        current_type: current_type,
        _init: _init,
        _process_buffered: _process_buffered,
        _serialize: _serialize,
        _merge_svgs: _merge_svgs,
        process: process
    };
})();
<|endoftext|>


<commit_before>'use strict';

let mmError = function(code, msg, toString) {
  return function(data, message) {
    let err = new Error();
    err.code = code;
    err.message = msg;

    if (message) {
      err.message += '. ' + message;
    }

    if (data) {
      err.data = data;
    }

    if (toString) {
      err.toString = toString;
    }

    return err;
  }
}

//generic error
const Call                  = mmError(4000, 'Error'); //general error to return from your methods

//auth errors
const Unauthorized          = mmError(4100, 'Unauthorized');
const ProviderNotFound      = mmError(4104, 'Auth provider not found');
const Forbidden             = mmError(4110, 'Forbidden');
const NotValidToken         = mmError(4120, 'Token not valid or expired');

//validation errors
const RequestValidation     = mmError(4200, 'Request validation failed');
const ResponseValidation    = mmError(4210, 'Response validation failed');
const RequestTooLarge       = mmError(4220, 'Request entity too large');
const RequestDecode         = mmError(4230, 'Request decode error');
const ResponseEncode        = mmError(4240, 'Response encode error');
const UnsupportedMedia      = mmError(4250, 'Unsupported media');

//call errors
const MethodNotFound        = mmError(4400, 'Method not found');

//response errors
const Duplicate             = mmError(4500, 'Duplicate entity');
const NotFound              = mmError(4540, 'Not found');

//server errors
const ServerError           = mmError(5000, 'Server error');

module.exports = {
  Error: mmError,
  Call,
  Unauthorized,
  ProviderNotFound,
  Forbidden,
  NotValidToken,
  RequestValidation,
  ResponseValidation,
  RequestTooLarge,
  RequestDecode,
  ResponseEncode,
  UnsupportedMedia,
  MethodNotFound,

  Duplicate,
  NotFound,

  ServerError
};

<commit_msg>+ network error code<commit_after>'use strict';

let mmError = function(code, msg, toString) {
  return function(data, message) {
    let err = new Error();
    err.code = code;
    err.message = msg;

    if (message) {
      err.message += '. ' + message;
    }

    if (data) {
      err.data = data;
    }

    if (toString) {
      err.toString = toString;
    }

    return err;
  }
}

//generic error
const Call                  = mmError(4000, 'Error'); //general error to return from your methods

//auth errors
const Unauthorized          = mmError(4100, 'Unauthorized');
const ProviderNotFound      = mmError(4104, 'Auth provider not found');
const Forbidden             = mmError(4110, 'Forbidden');
const NotValidToken         = mmError(4120, 'Token not valid or expired');

//validation errors
const RequestValidation     = mmError(4200, 'Request validation failed');
const ResponseValidation    = mmError(4210, 'Response validation failed');
const RequestTooLarge       = mmError(4220, 'Request entity too large');
const RequestDecode         = mmError(4230, 'Request decode error');
const ResponseEncode        = mmError(4240, 'Response encode error');
const UnsupportedMedia      = mmError(4250, 'Unsupported media');

//call errors
const MethodNotFound        = mmError(4400, 'Method not found');

//response errors
const Duplicate             = mmError(4500, 'Duplicate entity');
const NotFound              = mmError(4540, 'Not found');

//server errors
const ServerError           = mmError(5000, 'Server error');
const NetworkError          = mmError(5100, 'Network error');

module.exports = {
  Error: mmError,
  Call,
  Unauthorized,
  ProviderNotFound,
  Forbidden,
  NotValidToken,
  RequestValidation,
  ResponseValidation,
  RequestTooLarge,
  RequestDecode,
  ResponseEncode,
  UnsupportedMedia,
  MethodNotFound,

  Duplicate,
  NotFound,

  ServerError,
  NetworkError
};

<|endoftext|>


<commit_before>window.onload = function(){
  theGame.run();
}

var theGame = {
  EMPTY: 0,
  ROCK: -1,
  board: null,
  theme: 'metro',
  themes: {
    'metro': {
      'rock': 'metro',
      'regularPieces': ['m1', 'm2', 'm3', 'm3bis', 'm4', 'm5', 'm6', 'm7', 'm7bis', 'm8', 'm9', 'm10', 'm11', 'm12', 'm13', 'm14']
    }
  },
  currentPieces: [],
  maxAvailablePieces: 0,
  nbPieces: 0,
  level: 10,

  init: function init() {
    theGame.board = Raphael(document.body, 320, 480);

    var border = theGame.board.rect(0.5, 0.5, 319, 479),
        header = theGame.board.path('M 0 0 l 0 235 l 200 -200 l 120 0 l 0 -35 z'),
        title = theGame.board.print(0, 40, "esviji", theGame.board.getFont('ChewyRegular'), 60),
        score = theGame.board.print(220, 20, "score: 0", theGame.board.getFont('ChewyRegular'), 20);

    header.attr({
      'fill': '#9999cc',
      'stroke': '#666699',
      'stroke-width': 1
    });

    theGame.maxAvailablePieces = theGame.themes[theGame.theme].regularPieces.length;

    theGame.nextLevel();
  },

  nextLevel: function nextLevel() {
    theGame.level++;
    theGame.nbPieces = Math.min(theGame.maxAvailablePieces, Math.floor(5 + (theGame.level / 5)));
    theGame.currentPieces = theGame.initPieces();
    theGame.drawPieces(theGame.currentPieces);
  },

  initPieces: function initPieces() {
    var pieces = [];

    for(x = 1; x <= 6; x++) {
      pieces[x] = [];
      for (y = 1; y <= 6; y++) {
        pieces[x][y] = 1 + Math.floor(Math.random() * theGame.nbPieces);
      }
    }

    // add rocks in the middle after level 10
    if (theGame.level > 10) {
      for(z = 1; z <= Math.floor(theGame.level / 5); z++) {
        rock_x = 1 + Math.floor(Math.random() * 6);
        rock_y = 1 + Math.floor(Math.random() * 6);
        pieces[rock_x][rock_y] = theGame.ROCK;
      }
    }
    return pieces;
  },

  drawPieces: function drawPieces(pieces) {
    for(x = 1; x <= 6; x++) {
      for (y = 1; y <= 6; y++) {
        if (pieces[x][y] != theGame.EMPTY) {
          if (pieces[x][y] == theGame.ROCK) {
            piece_fichier = 'themes/' + theGame.theme + '/' + theGame.themes[theGame.theme].rock + '.svg';
          } else {
            piece_fichier = 'themes/' + theGame.theme + '/' + theGame.themes[theGame.theme].regularPieces[pieces[x][y] - 1] + '.svg';
          }
          piece_x = x * 35 - 30;
          piece_y = 480 - 35 * y;
          drawnPiece = theGame.board.image(piece_fichier, piece_x, -30, 30, 30);
          drawnPiece.animate({'y': piece_y}, 2000, 'bounce');
        }
      }
    }
  },

  getValidPieces: function getValidPieces() {
  },

  run: function run() {
    theGame.init();
  }
}<commit_msg>adding rocks after 10 levels<commit_after>window.onload = function(){
  theGame.run();
}

var theGame = {
  EMPTY: 0,
  ROCK: -1,
  board: null,
  theme: 'metro',
  themes: {
    'metro': {
      'rock': 'metro',
      'regularPieces': ['m1', 'm2', 'm3', 'm3bis', 'm4', 'm5', 'm6', 'm7', 'm7bis', 'm8', 'm9', 'm10', 'm11', 'm12', 'm13', 'm14']
    }
  },
  currentPieces: [],
  maxAvailablePieces: 0,
  nbPieces: 0,
  level: 0,

  init: function init() {
    theGame.board = Raphael(document.body, 320, 480);

    var border = theGame.board.rect(0.5, 0.5, 319, 479),
        header = theGame.board.path('M 0 0 l 0 235 l 200 -200 l 120 0 l 0 -35 z'),
        title = theGame.board.print(0, 40, "esviji", theGame.board.getFont('ChewyRegular'), 60),
        score = theGame.board.print(220, 20, "score: 0", theGame.board.getFont('ChewyRegular'), 20);

    header.attr({
      'fill': '#9999cc',
      'stroke': '#666699',
      'stroke-width': 1
    });

    theGame.maxAvailablePieces = theGame.themes[theGame.theme].regularPieces.length;

    theGame.nextLevel();
  },

  nextLevel: function nextLevel() {
    theGame.level++;
    theGame.nbPieces = Math.min(theGame.maxAvailablePieces, Math.floor(5 + (theGame.level / 5)));
    theGame.currentPieces = theGame.initPieces();
    theGame.drawPieces(theGame.currentPieces);
  },

  initPieces: function initPieces() {
    var pieces = [];

    for(x = 1; x <= 6; x++) {
      pieces[x] = [];
      for (y = 1; y <= 6; y++) {
        pieces[x][y] = 1 + Math.floor(Math.random() * theGame.nbPieces);
      }
    }

    // add rocks in the middle after level 10
    if (theGame.level > 10) {
      nbRocks = Math.floor((theGame.level - 5) / 5);
      positionedRocks = 0;
      while (positionedRocks < nbRocks) {
        rock_x = 1 + Math.floor(Math.random() * 6);
        rock_y = 1 + Math.floor(Math.random() * 6);
        if (pieces[rock_x][rock_y] != theGame.ROCK) {
          pieces[rock_x][rock_y] = theGame.ROCK;
          positionedRocks++;
        }
      }
    }
    return pieces;
  },

  drawPieces: function drawPieces(pieces) {
    for(x = 1; x <= 6; x++) {
      for (y = 1; y <= 6; y++) {
        if (pieces[x][y] != theGame.EMPTY) {
          if (pieces[x][y] == theGame.ROCK) {
            piece_fichier = 'themes/' + theGame.theme + '/' + theGame.themes[theGame.theme].rock + '.svg';
          } else {
            piece_fichier = 'themes/' + theGame.theme + '/' + theGame.themes[theGame.theme].regularPieces[pieces[x][y] - 1] + '.svg';
          }
          piece_x = x * 35 - 30;
          piece_y = 480 - 35 * y;
          drawnPiece = theGame.board.image(piece_fichier, piece_x, -30, 30, 30);
          drawnPiece.animate({'y': piece_y}, 2000, 'bounce');
        }
      }
    }
  },

  getValidPieces: function getValidPieces() {
  },

  run: function run() {
    theGame.init();
  }
}<|endoftext|>


<commit_before>define(["/jquery.js"], function() {

    function addHandlerOnEvent(handler, miid, eventName) {

        var self = this;

        // if the handler is a module function name as string
        if (typeof handler === "string" && typeof self[handler] === "function") {
            self.on(eventName, miid, function(data) {
                self[handler].call(self, data);
            });
            return;
        }

        // else it must be an array of objects
        if (handler.length) {
            self.on(eventName, miid, function(data) {
                for (var i in handler) {
                    var step = handler[i];

                    // this step is a handler function
                    if (step.handler) {
                        if (typeof self[step.handler] === "function") {
                            self[step.handler].call(self, data);
                        }
                        continue;
                    }

                    // this step is an event emit
                    if (step.emit) {
                        self.emit(step.emit, data);
                        continue;
                    }
                }
            });

            return;
        }
    }

    return function(config) {

        var self = this;

        // process only the listen property in the configurations
        for (var miid in config.listen) {

            var miidEvents = config.listen[miid];

            for (var eventName in miidEvents) {
                addHandlerOnEvent.call(self, miidEvents[eventName], miid, eventName);
            }
        }
    }
});
<commit_msg>The events module does not need jQuery for now<commit_after>define(function() {

    function addHandlerOnEvent(handler, miid, eventName) {

        var self = this;

        // if the handler is a module function name as string
        if (typeof handler === "string" && typeof self[handler] === "function") {
            self.on(eventName, miid, function(data) {
                self[handler].call(self, data);
            });
            return;
        }

        // else it must be an array of objects
        if (handler.length) {
            self.on(eventName, miid, function(data) {
                for (var i in handler) {
                    var step = handler[i];

                    // this step is a handler function
                    if (step.handler) {
                        if (typeof self[step.handler] === "function") {
                            self[step.handler].call(self, data);
                        }
                        continue;
                    }

                    // this step is an event emit
                    if (step.emit) {
                        self.emit(step.emit, data);
                        continue;
                    }
                }
            });

            return;
        }
    }

    return function(config) {

        var self = this;

        // process only the listen property in the configurations
        for (var miid in config.listen) {

            var miidEvents = config.listen[miid];

            for (var eventName in miidEvents) {
                addHandlerOnEvent.call(self, miidEvents[eventName], miid, eventName);
            }
        }
    }
});
<|endoftext|>


<commit_before>var _ = require('lodash')
  , raf = require('raf/polyfill')
  , Ribcage
  , extendView;

Ribcage = {
  initialize: function (opts){

    opts = opts || {};

    // if we need to load data before rendering, do it
    if (typeof this.loadData == 'function') {
      this._dataLoaded = false
    }
    // otherwise, assume we have the data already
    else {
      this._dataLoaded = true
    }

    this._nukeOnRender = opts._nukeOnRender
    delete opts._nukeOnRender

    // backbone 1.1+ doesn't auto-set this.options
    this.options = opts

    if(this.beforeInit) {
      this.beforeInit(opts);
    }

    this.template = this.template || opts.template;

    if (!this.template) {
      this.template = function () { return ''; };
    }

    if (this.afterInit) {
      this.afterInit(opts);
    }

    this.render();
  }

, context: function () {
    return _.extend({}, this.options, this.model);
  }

, render: function () {
    var self = this
      , model = this.model;

    if(typeof this.bindEvents === 'function') {
      //Make sure we don't double up on event listeners
      //You no longer have to manually stop listening on re-render -cstumph
      _.each(this._listeners, function (emitter){
        this.stopListening(emitter)
      }, this)

      this.bindEvents()
    }

    if (!this._dataLoaded && typeof this.loadData == 'function') {
      return this.loadData(function () {
        self._dataLoaded = true
        self.render()
      })
    }

    if (this.beforeRender) {
      this.beforeRender();
    }

    // Maintainers of legacy code might want to use this
    // but certainly not YOU, young grasshopper!
    if (this._nukeOnRender) {
      this.closeSubviews();
      this.$el.empty();
    }

    if (this.beforeTemplating) {
      model = this.beforeTemplating();
    }

    if (this.context) {
      model = this.context();
    }

    this.$el.html(this.template(model));

    // As soon as the view is bound to DOM, we need to delegate events and re-render all
    // subviews to keep events intact. -cstumph
    this.delegateEvents()
    this.eachSubview( function eachSubviewInRender (view){
      view.render()
    })

    // I'm not happy deferring here, but backbone's
    // event system is based on the DOM, so my hands
    // are tied. - Daniel
    _.defer(function () {
      self.trigger('afterRender')
    });

    if (this.afterRender) {
      this.afterRender();
    }

    return this;
  }

, proxy: function (name, view){
    this.listenTo(view, name, function proxiedEventCallback(){
      var args = Array.prototype.slice.call(arguments, 0);
      args.splice(0, 0, name);
      this.trigger.apply(this, args);
    })
  }

, eachSubview: function (iterator){
    _.each(this.subviews, iterator);
  }

, _attachSubView: function (view){
    this.subviews = this.subviews || {};
    this.subviewByModelId = this.subviewByModelId || {};

    this.subviews[view.cid] = view;
    if (view.model) {
      if (!this.subviewByModelId[view.model.id]) this.subviewByModelId[view.model.id] = [];
      this.subviewByModelId[view.model.id].push(view);
    }

    return view;
  }

, appendSubview: function (view, el){
    el || (el = this.$el);

    this._attachSubView(view);

    if (view.options.render) view.render()
    el.append(view.el);

    _.defer(function (){
      view.trigger('afterAppend', view);
    });

    if (view.afterAppend) {
      view.afterAppend();
    }
  }

, prependSubview: function (view, el){
    el || (el = this.$el);

    this._attachSubView(view);

    if (view.options.renderOnAppend) view.render()
    el.prepend(view.el);

    _.defer(function () {
      view.trigger('afterPrepend', view);
    });

    if (view.afterPrepend) {
      view.afterPrepend();
    }
  }

, appendSubviews: function (views, el, callback){
    el || (el = this.$el);

    var fragment = document.createDocumentFragment();

    _.each(views, function(view){
      this._attachSubView(view);

      if (view.options.renderOnAppend) view.render()
      fragment.appendChild(view.el);

      _.defer(function () {
        view.trigger('afterAppend', view);
      });

      if (view.afterAppend) {
        view.afterAppend();
      }
    }, this);

    raf.call(window, function(){
      el[0].appendChild(fragment);
      if (_.isFunction(callback)) callback(views);
    });
  }

, batchAppendSubviews: function (views, el, batchCount, batchCallback, callback){
    var self = this
      , batches = _(views)
                  .groupBy( function (view, index){
                    return Math.floor(index / batchCount);
                  })
                  .toArray()
                  .valueOf();

    function appendNextBatch () {
      if(!batches.length && typeof callback == 'function') {
        return callback();
      }

      var viewBatch = batches.shift();

      self.appendSubviews(viewBatch, el, function (){
        if (typeof batchCallback == 'function') {
          batchCallback(viewBatch);
        }

        appendNextBatch();
      });
    }

    appendNextBatch();
  }

, close: function(options) {
    if (this.beforeClose) {
      this.beforeClose();
    }

    this.closeSubviews(options);
    if (!options.keepDom) this.remove();
  }

, closeSubviews: function (options){
    // by default, we won't destroy the DOM elements of subviews
    // b/c we can assume that removing the parent will remove the subview DOM
    _.defaults(options, {
      keepDom: true
    })

    this.eachSubview( function (subview){
      subview.close(options);
    });

    this.subviews = {};
  }

, detachSubview: function (view){
    var msg

    if (this.subviews) {

      if (view && !this.subviews[view.cid]) {
        msg = 'View not found in ' + this.className + '\'s subviews: ' + view.className

        if(view.model)
          msg += '\n\n'+ view.model.toJSON()

        throw new Error(msg)
      }

      delete this.subviews[view.cid];
    }

    view.$el.detach();
  }

, _removeSubviewByModel: function _removeSubviewByModel(model, method){
    var id = model.id
    if (this.subviewByModelId){
      if (!id || !this.subviewByModelId[id]){
        throw new Error('No views with ' + id + ' model id found in ' + this.className + ' subviews');
      }

      _.each(this.subviewByModelId[id], function(view){
        method(view);
      }, this);

      delete this.subviewByModelId[id];
    }

  }

, closeSubviewsByModel: function closeSubviewsByModel(model){
    this._removeSubviewByModel(model, this.close)
  }

, detachSubviewByModel: function detachSubviewByModel(model){
    this._removeSubviewByModel(model, this.detachSubview)
  }

};//end Ribcage{}

extendView = function (view){
  return view.extend(Ribcage);
};

module.exports = extendView;
<commit_msg>remove all events on closing views<commit_after>var _ = require('lodash')
  , raf = require('raf/polyfill')
  , Ribcage
  , extendView;

Ribcage = {
  initialize: function (opts){

    opts = opts || {};

    // if we need to load data before rendering, do it
    if (typeof this.loadData == 'function') {
      this._dataLoaded = false
    }
    // otherwise, assume we have the data already
    else {
      this._dataLoaded = true
    }

    this._nukeOnRender = opts._nukeOnRender
    delete opts._nukeOnRender

    // backbone 1.1+ doesn't auto-set this.options
    this.options = opts

    if(this.beforeInit) {
      this.beforeInit(opts);
    }

    this.template = this.template || opts.template;

    if (!this.template) {
      this.template = function () { return ''; };
    }

    if (this.afterInit) {
      this.afterInit(opts);
    }

    this.render();
  }

, context: function () {
    return _.extend({}, this.options, this.model);
  }

, render: function () {
    var self = this
      , model = this.model;

    if(typeof this.bindEvents === 'function') {
      //Make sure we don't double up on event listeners
      //You no longer have to manually stop listening on re-render -cstumph
      _.each(this._listeners, function (emitter){
        this.stopListening(emitter)
      }, this)

      this.bindEvents()
    }

    if (!this._dataLoaded && typeof this.loadData == 'function') {
      return this.loadData(function () {
        self._dataLoaded = true
        self.render()
      })
    }

    if (this.beforeRender) {
      this.beforeRender();
    }

    // Maintainers of legacy code might want to use this
    // but certainly not YOU, young grasshopper!
    if (this._nukeOnRender) {
      this.closeSubviews();
      this.$el.empty();
    }

    if (this.beforeTemplating) {
      model = this.beforeTemplating();
    }

    if (this.context) {
      model = this.context();
    }

    this.$el.html(this.template(model));

    // As soon as the view is bound to DOM, we need to delegate events and re-render all
    // subviews to keep events intact. -cstumph
    this.delegateEvents()
    this.eachSubview( function eachSubviewInRender (view){
      view.render()
    })

    // I'm not happy deferring here, but backbone's
    // event system is based on the DOM, so my hands
    // are tied. - Daniel
    _.defer(function () {
      self.trigger('afterRender')
    });

    if (this.afterRender) {
      this.afterRender();
    }

    return this;
  }

, proxy: function (name, view){
    this.listenTo(view, name, function proxiedEventCallback(){
      var args = Array.prototype.slice.call(arguments, 0);
      args.splice(0, 0, name);
      this.trigger.apply(this, args);
    })
  }

, eachSubview: function (iterator){
    _.each(this.subviews, iterator);
  }

, _attachSubView: function (view){
    this.subviews = this.subviews || {};
    this.subviewByModelId = this.subviewByModelId || {};

    this.subviews[view.cid] = view;
    if (view.model) {
      if (!this.subviewByModelId[view.model.id]) this.subviewByModelId[view.model.id] = [];
      this.subviewByModelId[view.model.id].push(view);
    }

    return view;
  }

, appendSubview: function (view, el){
    el || (el = this.$el);

    this._attachSubView(view);

    if (view.options.render) view.render()
    el.append(view.el);

    _.defer(function (){
      view.trigger('afterAppend', view);
    });

    if (view.afterAppend) {
      view.afterAppend();
    }
  }

, prependSubview: function (view, el){
    el || (el = this.$el);

    this._attachSubView(view);

    if (view.options.renderOnAppend) view.render()
    el.prepend(view.el);

    _.defer(function () {
      view.trigger('afterPrepend', view);
    });

    if (view.afterPrepend) {
      view.afterPrepend();
    }
  }

, appendSubviews: function (views, el, callback){
    el || (el = this.$el);

    var fragment = document.createDocumentFragment();

    _.each(views, function(view){
      this._attachSubView(view);

      if (view.options.renderOnAppend) view.render()
      fragment.appendChild(view.el);

      _.defer(function () {
        view.trigger('afterAppend', view);
      });

      if (view.afterAppend) {
        view.afterAppend();
      }
    }, this);

    raf.call(window, function(){
      el[0].appendChild(fragment);
      if (_.isFunction(callback)) callback(views);
    });
  }

, batchAppendSubviews: function (views, el, batchCount, batchCallback, callback){
    var self = this
      , batches = _(views)
                  .groupBy( function (view, index){
                    return Math.floor(index / batchCount);
                  })
                  .toArray()
                  .valueOf();

    function appendNextBatch () {
      if(!batches.length && typeof callback == 'function') {
        return callback();
      }

      var viewBatch = batches.shift();

      self.appendSubviews(viewBatch, el, function (){
        if (typeof batchCallback == 'function') {
          batchCallback(viewBatch);
        }

        appendNextBatch();
      });
    }

    appendNextBatch();
  }

, close: function(options) {
    if (this.beforeClose) {
      this.beforeClose();
    }

    this.closeSubviews(options);
    this.undelegateEvents();
    this.stopListening();
    if (!options.keepDom) this.remove();
  }

, closeSubviews: function (options){
    // by default, we won't destroy the DOM elements of subviews
    // b/c we can assume that removing the parent will remove the subview DOM
    _.defaults(options, {
      keepDom: true
    })

    this.eachSubview( function (subview){
      subview.close(options);
    });

    this.subviews = {};
  }

, detachSubview: function (view){
    var msg

    if (this.subviews) {

      if (view && !this.subviews[view.cid]) {
        msg = 'View not found in ' + this.className + '\'s subviews: ' + view.className

        if(view.model)
          msg += '\n\n'+ view.model.toJSON()

        throw new Error(msg)
      }

      delete this.subviews[view.cid];
    }

    view.$el.detach();
  }

, _removeSubviewByModel: function _removeSubviewByModel(model, method){
    var id = model.id
    if (this.subviewByModelId){
      if (!id || !this.subviewByModelId[id]){
        throw new Error('No views with ' + id + ' model id found in ' + this.className + ' subviews');
      }

      _.each(this.subviewByModelId[id], function(view){
        method(view);
      }, this);

      delete this.subviewByModelId[id];
    }

  }

, closeSubviewsByModel: function closeSubviewsByModel(model){
    this._removeSubviewByModel(model, this.close)
  }

, detachSubviewByModel: function detachSubviewByModel(model){
    this._removeSubviewByModel(model, this.detachSubview)
  }

};//end Ribcage{}

extendView = function (view){
  return view.extend(Ribcage);
};

module.exports = extendView;
<|endoftext|>