<commit_before>'use strict';

var peerhandler = angular.module('peerhandler', ['ngCordova']);

peerhandler.factory('peerFactory', function($rootScope, $ionicPopup, $ionicHistory, $state, $timeout, $cordovaLocalNotification) {
    // PeerJS object representing the user
    var me                = null;

    // Stream object;
    var localStream       = null;

    // Stream sources
    var remoteVideoSource = null;
    var localVideoSource  = null;

    // currentCallStream   = local
    // currentAnswerStream = remote
    var currentCallStream   = null;
    var currentAnswerStream = null;

    // Private api
    var getLocalStream = function(successCallback) {
        if (localStream && successCallback) {
            successCallback(localStream)
        } else {
            navigator.webkitGetUserMedia(
                {
                    audio: true,
                    video: true
                },
                function (stream) {
                    localStream = stream;
                    setLocalStreamSrc(stream);
                    if (successCallback) {
                        successCallback(stream);
                    }
                },
                function(err){
                    console.log(err);
                }
            )
        }
    };

    var setRemoteStreamSrc = function (stream) {
        remoteVideoSource = window.URL.createObjectURL(stream);
    };

    var setLocalStreamSrc = function (stream) {
        localVideoSource = window.URL.createObjectURL(stream);
    };

    var endCallAndGoBack = function() {
        endCurrentCall();
        if ($state.current.name === "call") {
            $ionicHistory.goBack();
        }
    }

    var callAlertModal = function(reasonMessage) {
        var callEndedAlert = $ionicPopup.alert({
            title: 'Call over!',
            template: reasonMessage
        });
        callEndedAlert.then(function() {
            endCallAndGoBack();
        });

    };

    var answer = function(call) {
        currentAnswerStream = call;
        call.on('stream', setRemoteStreamSrc);
        call.on('close', function() {
            endCallAndGoBack();
        });

        call.on('error', function(error) {
            callAlertModal('Error: ' + error.toString());
        });

        call.answer(localStream);
    };

    var endCurrentCall = function() {
        if (currentCallStream) {
            currentCallStream.close();
            currentCallStream = null;
        }

        if (currentAnswerStream) {
            currentAnswerStream.close();
            currentAnswerStream = null;
        }
    };

     var disconnectFromPeerJS = function() {
        if (me) {
            me.disconnect();
            me.destroy();
            me = null;
            endCurrentCall();
        } else {
            console.log("Warning! Attempted to disconnect without connecting")
        }
    };

    // Public PeerJS api
    return {
        getMe: function() {
            if (!me) {return null;}
            return me;
        },
        getRemoteStreamSrc: function() {
            if (!remoteVideoSource) {console.log('No video remote source!')}
            return remoteVideoSource;
        },
        getLocalStreamSrc: function() {
            if (!localVideoSource) {console.log('No video remote source!')}
            return localVideoSource;
        },
        isConnected: function() {
            if (!me) {return false}
            else {return !me.disconnected}
        },
        connectToPeerJS: function(id)   {
            var disconnectRef = this.disconnectFromPeerJS;

            return new Promise(function(resolve, reject) {

                me = new Peer(id, $rootScope.config.peerjs);

                if (!me) {
                    alert('Error creating peer!');
                    reject();W
                }
                me.on('open', function(id) {
                    resolve();
                    console.log('Connection opened: ' + id);
                });

                me.on('call', function(mediaConnection) {
                    console.log('Call initiated by ' + mediaConnection.peer );

                    $cordovaLocalNotification.schedule({
                        id: 1,
                        title: 'SAR Call from ' + mediaConnection.peer,
                        text: 'SAR Call from ' + mediaConnection.peer
                    }).then(function (result) {
                        console.log(result);
                    });

                    var confirmPopup = $ionicPopup.confirm({
                        title: 'Call from ' + mediaConnection.peer,
                        template: 'Incoming call. Answer?'
                    });

                    confirmPopup.then(function(res) {
                        if(res) {
                            answer(mediaConnection);
                            $timeout(function() {
                                $state.go('call', {user: { displayName: mediaConnection.peer }});
                            }, 500)
                        } else {
                            $cordovaLocalNotification.cancel(1).then(function (result) {
                                console.log(result);
                            });
                            mediaConnection.close();
                            return false;
                        }
                    });
                });

                me.on('connection', function(dataConnection) {
                    console.log('dataconnection formed!');
                    console.log(dataConnection);
                });

                me.on('close', function() {
                    console.log('closed Peerjs connection');
                });

                me.socket._socket.onopen = function() {
                    getLocalStream();
                };

                me.on('error', function(error) {
                    var errorMsg = error.toString();
                    var errorAlert = $ionicPopup.alert({
                        title: 'Something went wrong!',
                        template: errorMsg
                    });
                    errorAlert.then(function(res) {
                        if (_.contains(errorMsg, 'Lost connection to server') || _.contains(errorMsg, 'is taken')) {
                            disconnectRef();
                            $state.go('login');
                        }
                    });
                });
            })
        },
        callPeer: function (userToCall) {
            if (!me) {
                console.log("Warning! no peerjs connection")
            }

            getLocalStream(function (stream) {
                currentCallStream = me.call(userToCall.number, stream, { metadata: me.id});
                currentCallStream.on('error', function (err) {
                    endCurrentCall();

                });

                currentCallStream.on('stream', function(stream) {
                    console.log('going to stream from call')
                    setRemoteStreamSrc(stream);

                    $timeout(function() {
                         $state.go('call', {user: userToCall});
                     }, 500)

                });

                currentCallStream.on('close', function() {
                    endCallAndGoBack();
                });
            });

        },
        endCurrentCall: function() {
            endCurrentCall();
        },
        disconnectFromPeerJS: function() {
            disconnectFromPeerJS();
        }
    }
});
<commit_msg>Use back camera whenever possible<commit_after>'use strict';

var peerhandler = angular.module('peerhandler', ['ngCordova']);

peerhandler.factory('peerFactory', function($rootScope, $ionicPopup, $ionicHistory, $state, $timeout, $cordovaLocalNotification) {
    // PeerJS object representing the user
    var me                = null;

    // Stream object;
    var localStream       = null;

    // Stream sources
    var remoteVideoSource = null;
    var localVideoSource  = null;

    // currentCallStream   = local
    // currentAnswerStream = remote
    var currentCallStream   = null;
    var currentAnswerStream = null;

    // Get a suitable camera for video (first choice is backwards-facing camera)
    var getDeviceCameraID= function() {
        return new Promise(function(resolve, reject) {
            var first = false;
            var back  = false;

            MediaStreamTrack.getSources(function(src) {
                for (var i in src) {
                    if (src[i].kind === 'video') {

                        if (!first) {
                            first = src[i].id;
                        }

                        if (src[i].facing === 'environment') {
                            back = src[i].id;
                        }

                    }
                }
                resolve(back || first);
            });
        })
    };

    // Private api
    var getLocalStream = function(successCallback) {
        if (localStream && successCallback) {
            successCallback(localStream)
        } else {
            getDeviceCameraID().then(function (id) {
                var videoSettings = {};

                if (id) {
                    videoSettings.optional = [{sourceId: id}];
                }

                navigator.webkitGetUserMedia(
                    {
                        audio: true,
                        video: videoSettings
                    },
                    function (stream) {
                        localStream = stream;
                        setLocalStreamSrc(stream);
                        if (successCallback) {
                            successCallback(stream);
                        }
                    },
                    function(err){
                        console.log(err);
                    }
                )
            })
        }
    };

    var setRemoteStreamSrc = function (stream) {
        remoteVideoSource = window.URL.createObjectURL(stream);
    };

    var setLocalStreamSrc = function (stream) {
        localVideoSource = window.URL.createObjectURL(stream);
    };

    var endCallAndGoBack = function() {
        endCurrentCall();
        if ($state.current.name === "call") {
            $ionicHistory.goBack();
        }
    };

    var callAlertModal = function(reasonMessage) {
        var callEndedAlert = $ionicPopup.alert({
            title: 'Call over!',
            template: reasonMessage
        });
        callEndedAlert.then(function() {
            endCallAndGoBack();
        });

    };

    var answer = function(call) {
        currentAnswerStream = call;
        call.on('stream', setRemoteStreamSrc);
        call.on('close', function() {
            endCallAndGoBack();
        });

        call.on('error', function(error) {
            callAlertModal('Error: ' + error.toString());
        });

        call.answer(localStream);
    };

    var endCurrentCall = function() {
        if (currentCallStream) {
            currentCallStream.close();
            currentCallStream = null;
        }

        if (currentAnswerStream) {
            currentAnswerStream.close();
            currentAnswerStream = null;
        }
    };

     var disconnectFromPeerJS = function() {
        if (me) {
            me.disconnect();
            me.destroy();
            me = null;
            endCurrentCall();
        } else {
            console.log("Warning! Attempted to disconnect without connecting")
        }
    };

    // Public PeerJS api
    return {
        getMe: function() {
            if (!me) {return null;}
            return me;
        },
        getRemoteStreamSrc: function() {
            if (!remoteVideoSource) {console.log('No video remote source!')}
            return remoteVideoSource;
        },
        getLocalStreamSrc: function() {
            if (!localVideoSource) {console.log('No video remote source!')}
            return localVideoSource;
        },
        isConnected: function() {
            if (!me) {return false}
            else {return !me.disconnected}
        },
        connectToPeerJS: function(id)   {
            var disconnectRef = this.disconnectFromPeerJS;

            return new Promise(function(resolve, reject) {

                me = new Peer(id, $rootScope.config.peerjs);

                if (!me) {
                    alert('Error creating peer!');
                    reject();W
                }
                me.on('open', function(id) {
                    resolve();
                    console.log('Connection opened: ' + id);
                });

                me.on('call', function(mediaConnection) {
                    console.log('Call initiated by ' + mediaConnection.peer );

                    /*$cordovaLocalNotification.schedule({
                        id: 1,
                        title: 'SAR Call from ' + mediaConnection.peer,
                        text: 'SAR Call from ' + mediaConnection.peer
                    }).then(function (result) {
                        console.log(result);
                    });*/

                    var confirmPopup = $ionicPopup.confirm({
                        title: 'Call from ' + mediaConnection.peer,
                        template: 'Incoming call. Answer?'
                    });

                    confirmPopup.then(function(res) {
                        if(res) {
                            answer(mediaConnection);
                            $timeout(function() {
                                $state.go('call', {user: { displayName: mediaConnection.peer }});
                            }, 500)
                        } else {
                            $cordovaLocalNotification.cancel(1).then(function (result) {
                                console.log(result);
                            });
                            mediaConnection.close();
                            return false;
                        }
                    });
                });

                me.on('connection', function(dataConnection) {
                    console.log('dataconnection formed!');
                    console.log(dataConnection);
                });

                me.on('close', function() {
                    console.log('closed Peerjs connection');
                });

                me.socket._socket.onopen = function() {
                    getLocalStream();
                };

                me.on('error', function(error) {
                    var errorMsg = error.toString();
                    var errorAlert = $ionicPopup.alert({
                        title: 'Something went wrong!',
                        template: errorMsg
                    });
                    errorAlert.then(function(res) {
                        if (_.contains(errorMsg, 'Lost connection to server') || _.contains(errorMsg, 'is taken')) {
                            disconnectRef();
                            $state.go('login');
                        }
                    });
                });
            })
        },
        callPeer: function (userToCall) {
            if (!me) {
                console.log("Warning! no peerjs connection")
            }

            getLocalStream(function (stream) {
                currentCallStream = me.call(userToCall.number, stream, { metadata: me.id});
                currentCallStream.on('error', function (err) {
                    endCurrentCall();

                });

                currentCallStream.on('stream', function(stream) {
                    console.log('going to stream from call')
                    setRemoteStreamSrc(stream);

                    $timeout(function() {
                         $state.go('call', {user: userToCall});
                     }, 500)

                });

                currentCallStream.on('close', function() {
                    endCallAndGoBack();
                });
            });

        },
        endCurrentCall: function() {
            endCurrentCall();
        },
        disconnectFromPeerJS: function() {
            disconnectFromPeerJS();
        }
    }
});
<|endoftext|>


<commit_before>import {button, div, h1, header, section, span} from '@cycle/dom';
import xs from 'xstream';
import _ from 'lodash';
import classNames from 'classnames';

import GameClock from './game-clock';
import gameScore, { hasGameFinished } from './game-score';
import {getGameAnimationIndexes} from './utils';

export default function main(animations) {
  return ({DOM, HTTP}) => {
    const url = getApiUrl();
    return {
      DOM: view(model(intent(DOM, HTTP), animations)),
      HTTP: xs.of({ url })
    };
  };
}

function getApiUrl() {
  const host = process.env.SCORE_API_HOST || 'https://nhl-score-api.herokuapp.com';
  return `${host}/api/scores/latest`;
}

function intent(DOM, HTTP) {
  const apiResponseWithErrors$ = HTTP
    .select()
    .map(response$ => response$.replaceError(error => xs.of({ error })))
    .flatten()
    .map(response => {
      if (response.error) {
        return response;
      } else {
        const responseJson = JSON.parse(response.text);
        return responseJson.games.length > 0
          ? { success: responseJson }
          : { error: { message: 'No latest scores available.', expected: true } };
      }
    });
  const successApiResponse$ = apiResponseWithErrors$
    .filter(scores => scores.success)
    .map(scores => scores.success);

  const playClicks$ = DOM.select('.button--play').events('click')
    .mapTo(true);
  const pauseClicks$ = DOM.select('.button--pause').events('click')
    .mapTo(false);
  const isPlaying$ = xs.merge(playClicks$, pauseClicks$);
  const playbackHasStarted$ = playClicks$.take(1);

  return {
    successApiResponse$,
    isPlaying$,
    playbackHasStarted$,
    status$: apiResponseWithErrors$
      .filter(scores => scores.error)
      .map(scores => scores.error.expected ?
        scores.error.message :
        `Failed to fetch latest scores: ${scores.error.message}.`)
  };
}

function model(actions, animations) {
  const initialState = { date: {}, games: [] };
  const successResponse$ = actions.successApiResponse$.startWith(initialState);

  const scores$ = successResponse$.map(({ date, games }) => ({
    date,
    games: games.map((game, index) =>
      _.extend({}, game, {
        goalCounts: {
          away$: createGoalCountSubject('away', index, animations),
          home$: createGoalCountSubject('home', index, animations)
        }
      })
    )
  }));

  const gameClock = GameClock({
    scores$: actions.successApiResponse$.map(({ games }) => games),
    isPlaying$: actions.isPlaying$,
    props$: xs.of({ interval: 20 })
  });

  actions.playbackHasStarted$.addListener({ next: () => animations.setInfoPanelsPlaybackHeight() });
  actions.successApiResponse$
    .filter(({ games }) => games.some(game => hasGameFinished(game.status.state)))
    .addListener({ next: () => gameClock.clock$.addListener({ complete: () => animations.setInfoPanelsFinalHeight() })});

  return xs.combine(
    scores$,
    actions.isPlaying$.startWith(false),
    actions.status$.startWith('Fetching latest scores...'),
    gameClock.DOM.startWith(span('.clock')),
    gameClock.clock$.startWith(null)
  ).map(([scores, isPlaying, status, clockVtree, clock]) =>
    ({ scores, isPlaying, status, clockVtree, clock, gameCount: scores.games.length })
  );
}

function createGoalCountSubject(classModifier, gameIndex, animations) {
  const subject$ = new xs.create();
  subject$.fold((acc, curr) => ({ last: curr, changed: acc.last !== curr }), { last: 0 })
    .filter(({ changed }) => changed)
    .addListener({ next: () => animations.highlightGoal(classModifier, gameIndex) });
  return subject$;
}

function view(state$) {
  return state$.map(({scores, isPlaying, status, clockVtree, clock, gameCount}) =>
    div([
      header('.header', renderHeader({ clockVtree, clock, gameCount, isPlaying, date: scores.date })),
      section('.score-panel', renderScores({ games: scores.games, status, clock }))
    ])
  );
}

function renderHeader(state) {
  const hasNotStarted = !state.clock;
  const isFinished = !!(state.clock && state.clock.end && !state.clock.period);
  const buttonType = state.isPlaying ? 'pause' : 'play';
  const buttonClass = classNames({
    '.button': true,
    [`.button--${buttonType}`]: state.gameCount > 0,
    [`.expand--${state.gameCount}`]: state.gameCount > 0 && hasNotStarted,
    '.button--hidden': isFinished
  }).replace(/\s/g, '');
  const showDate = hasNotStarted && !!state.date;

  return div('.header__container', [
    h1('.header__title', [
      span('.all-caps', 'NHL'),
      ' Recap'
    ]),
    button(buttonClass),
    showDate ? renderDate(state.date) : state.clockVtree
  ]);
}

function renderScores(state) {
  const gameAnimationIndexes = getGameAnimationIndexes(state.games.length);
  return state.games.length > 0 ?
    div('.score-list', state.games.map((game, index) => gameScore(state.clock, game, gameAnimationIndexes[index]))) :
    div('.status.fade-in', [state.status || 'No scores available.']);
}

function renderDate(date) {
  return span('.date.fade-in-slow', date.pretty);
}
<commit_msg>Remove spaces around curly brackets in imports<commit_after>import {button, div, h1, header, section, span} from '@cycle/dom';
import xs from 'xstream';
import _ from 'lodash';
import classNames from 'classnames';

import GameClock from './game-clock';
import gameScore, {hasGameFinished} from './game-score';
import {getGameAnimationIndexes} from './utils';

export default function main(animations) {
  return ({DOM, HTTP}) => {
    const url = getApiUrl();
    return {
      DOM: view(model(intent(DOM, HTTP), animations)),
      HTTP: xs.of({ url })
    };
  };
}

function getApiUrl() {
  const host = process.env.SCORE_API_HOST || 'https://nhl-score-api.herokuapp.com';
  return `${host}/api/scores/latest`;
}

function intent(DOM, HTTP) {
  const apiResponseWithErrors$ = HTTP
    .select()
    .map(response$ => response$.replaceError(error => xs.of({ error })))
    .flatten()
    .map(response => {
      if (response.error) {
        return response;
      } else {
        const responseJson = JSON.parse(response.text);
        return responseJson.games.length > 0
          ? { success: responseJson }
          : { error: { message: 'No latest scores available.', expected: true } };
      }
    });
  const successApiResponse$ = apiResponseWithErrors$
    .filter(scores => scores.success)
    .map(scores => scores.success);

  const playClicks$ = DOM.select('.button--play').events('click')
    .mapTo(true);
  const pauseClicks$ = DOM.select('.button--pause').events('click')
    .mapTo(false);
  const isPlaying$ = xs.merge(playClicks$, pauseClicks$);
  const playbackHasStarted$ = playClicks$.take(1);

  return {
    successApiResponse$,
    isPlaying$,
    playbackHasStarted$,
    status$: apiResponseWithErrors$
      .filter(scores => scores.error)
      .map(scores => scores.error.expected ?
        scores.error.message :
        `Failed to fetch latest scores: ${scores.error.message}.`)
  };
}

function model(actions, animations) {
  const initialState = { date: {}, games: [] };
  const successResponse$ = actions.successApiResponse$.startWith(initialState);

  const scores$ = successResponse$.map(({ date, games }) => ({
    date,
    games: games.map((game, index) =>
      _.extend({}, game, {
        goalCounts: {
          away$: createGoalCountSubject('away', index, animations),
          home$: createGoalCountSubject('home', index, animations)
        }
      })
    )
  }));

  const gameClock = GameClock({
    scores$: actions.successApiResponse$.map(({ games }) => games),
    isPlaying$: actions.isPlaying$,
    props$: xs.of({ interval: 20 })
  });

  actions.playbackHasStarted$.addListener({ next: () => animations.setInfoPanelsPlaybackHeight() });
  actions.successApiResponse$
    .filter(({ games }) => games.some(game => hasGameFinished(game.status.state)))
    .addListener({ next: () => gameClock.clock$.addListener({ complete: () => animations.setInfoPanelsFinalHeight() })});

  return xs.combine(
    scores$,
    actions.isPlaying$.startWith(false),
    actions.status$.startWith('Fetching latest scores...'),
    gameClock.DOM.startWith(span('.clock')),
    gameClock.clock$.startWith(null)
  ).map(([scores, isPlaying, status, clockVtree, clock]) =>
    ({ scores, isPlaying, status, clockVtree, clock, gameCount: scores.games.length })
  );
}

function createGoalCountSubject(classModifier, gameIndex, animations) {
  const subject$ = new xs.create();
  subject$.fold((acc, curr) => ({ last: curr, changed: acc.last !== curr }), { last: 0 })
    .filter(({ changed }) => changed)
    .addListener({ next: () => animations.highlightGoal(classModifier, gameIndex) });
  return subject$;
}

function view(state$) {
  return state$.map(({scores, isPlaying, status, clockVtree, clock, gameCount}) =>
    div([
      header('.header', renderHeader({ clockVtree, clock, gameCount, isPlaying, date: scores.date })),
      section('.score-panel', renderScores({ games: scores.games, status, clock }))
    ])
  );
}

function renderHeader(state) {
  const hasNotStarted = !state.clock;
  const isFinished = !!(state.clock && state.clock.end && !state.clock.period);
  const buttonType = state.isPlaying ? 'pause' : 'play';
  const buttonClass = classNames({
    '.button': true,
    [`.button--${buttonType}`]: state.gameCount > 0,
    [`.expand--${state.gameCount}`]: state.gameCount > 0 && hasNotStarted,
    '.button--hidden': isFinished
  }).replace(/\s/g, '');
  const showDate = hasNotStarted && !!state.date;

  return div('.header__container', [
    h1('.header__title', [
      span('.all-caps', 'NHL'),
      ' Recap'
    ]),
    button(buttonClass),
    showDate ? renderDate(state.date) : state.clockVtree
  ]);
}

function renderScores(state) {
  const gameAnimationIndexes = getGameAnimationIndexes(state.games.length);
  return state.games.length > 0 ?
    div('.score-list', state.games.map((game, index) => gameScore(state.clock, game, gameAnimationIndexes[index]))) :
    div('.status.fade-in', [state.status || 'No scores available.']);
}

function renderDate(date) {
  return span('.date.fade-in-slow', date.pretty);
}
<|endoftext|>


<commit_before>/**
 * @license Copyright 2013 Google Inc. All rights reserved.
 * Use of this source code is governed by the Apache license that can be
 * found in the LICENSE file.
 */

/**
 * @constructor
 * @struct
 * @param {HTMLCanvasElement} sourceCanvas
 * @param {HTMLCanvasElement} displayCanvas
 * @param {HTMLCanvasElement} brushCanvas
 * @param {HTMLElement} canvasPlaceholder
 * @param {HTMLElement} canvasViewport
 */
function ZoomManager(sourceCanvas,
                     displayCanvas,
                     brushCanvas,
                     canvasPlaceholder,
                     canvasViewport) {
}

(function() {

  /**
   * Encapsulates data related to the zoom manager.
   * @constructor
   * @struct
   * @param {HTMLCanvasElement} sourceCanvas
   * @param {HTMLCanvasElement} displayCanvas
   * @param {HTMLCanvasElement} brushCanvas
   * @param {HTMLElement} canvasPlaceholder
   * @param {HTMLElement} canvasViewport
   */
  function ZoomManager(sourceCanvas,
                       displayCanvas,
                       brushCanvas,
                       canvasPlaceholder,
                       canvasViewport) {
    /**
     * @type {number}
     */
    this.zoomFactor = 1;

    /**
     * @type {HTMLCanvasElement}
     */
    this.sourceCanvas = sourceCanvas;

    /**
     * @type {HTMLCanvasElement}
     */
    this.displayCanvas = displayCanvas;

    /**
     * @type {HTMLCanvasElement}
     */
    this.brushCanvas = brushCanvas;

    /**
     * @type {HTMLElement}
     * A placeholder for the displayCanvas which sizes to the logical pixel size
     * of the zoomed image.
     */
    this.canvasPlaceholder = canvasPlaceholder;

    /**
     * optionProviders is initialized after zoomManager has been initialized.
     * initialization done in this.setOptionProvders().
     * @type {OptionProviders}
     */
    this.optionProviders = null;

    /**
     * @type {HTMLElement}
     * The element which controls the scrolling and visible region of the
     * displayed image.
     */
    this.canvasViewport = canvasViewport;

    var outer = this;
    canvasViewport.addEventListener(
        'scroll',
        function() { outer.drawDisplayCanvas(); },
        true);

    this.drawDisplayCanvas();
  };

  /**
   * Constant for default opacity value.
   * @const {number}
   */
  ZoomManager.DEFAULT_OPACITY = 1;

  /**
   * Constant for eraser tool provided in penciltool.js
   * @const {number}
   */
  ZoomManager.ERASER = 1;

  /**
   * Sets zoom factor, repositioning the viewport to respect |anchorX| and
   * |anchorY|.
   * @param {number} zoomFactor
   * @param {number} anchorX
   * @param {number} anchorY
   */
  ZoomManager.prototype.setZoomFactor = function(
      zoomFactor, anchorX, anchorY) {
    this.repositionViewportForZoom(
        anchorX, anchorY, this.zoomFactor, zoomFactor);
    this.zoomFactor = zoomFactor;
  };

  /**
   * Sets the top and left of the canvasViewport to keep |anchorX| and |anchorY|
   * in the same place relative to the visible area. Clamps to the canvas bounds
   * if repositioning would show an area outside the image.
   *
   * The anchor is specified in display coordinates.
   * @param {number} anchorX
   * @param {number} anchorY
   * @param {number} oldZoom
   * @param {number} newZoom
   */
  ZoomManager.prototype.repositionViewportForZoom = function(
      anchorX, anchorY, oldZoom, newZoom) {
    var anchorDistanceX = anchorX - this.canvasViewport.scrollLeft;
    var anchorDistanceY = anchorY - this.canvasViewport.scrollTop;

    var rescaledAnchorX = anchorX * newZoom / oldZoom;
    var rescaledAnchorY = anchorY * newZoom / oldZoom;

    var scrollLeftMax = Math.max(0,
        this.sourceCanvas.width * newZoom - this.canvasViewport.clientWidth);
    var scrollTopMax = Math.max(0,
        this.sourceCanvas.height * newZoom - this.canvasViewport.clientHeight);

    // Resize the canvas placeholder first so that there's sufficient scrollbar
    // on zoom-in.
    this.resizeCanvasPlaceholder(newZoom);
    this.canvasViewport.scrollLeft =
        bitmapper.util.constrain(rescaledAnchorX - anchorDistanceX,
                                 0,
                                 scrollLeftMax);
    this.canvasViewport.scrollTop =
        bitmapper.util.constrain(rescaledAnchorY - anchorDistanceY,
                                 0,
                                 scrollTopMax);
  };

  /**
   * Resizes the canvasPlaceholder to fill up the space of the sourceCanvas
   * zoomed |zoom| times.
   * @param {number} zoom
   */
  ZoomManager.prototype.resizeCanvasPlaceholder = function(zoom) {
    // Size the placeholder to the desired image size.
    this.canvasPlaceholder.style.height =
        (this.sourceCanvas.height * zoom) + 'px';
    this.canvasPlaceholder.style.width =
        (this.sourceCanvas.width * zoom) + 'px';
  };

  /**
   * Scales and redraws the display canvas.
   */
  ZoomManager.prototype.drawDisplayCanvas = function() {
    var zoom = this.zoomFactor;
    this.resizeCanvasPlaceholder(zoom);

    // Size and move the displayCanvas to the area visible in the viewport.
    var left = this.canvasViewport.scrollLeft;
    var top = this.canvasViewport.scrollTop;
    this.displayCanvas.style.left = left + 'px';
    this.displayCanvas.style.top = top + 'px';
    this.displayCanvas.height = this.canvasViewport.clientHeight;
    this.displayCanvas.width = this.canvasViewport.clientWidth;

    var displayContext = Canvas2DContext(this.displayCanvas);
    // Displays crisp pixels when scaled.
    displayContext.imageSmoothingEnabled = false;
    displayContext.clearRect(
        0, 0, this.displayCanvas.width, this.displayCanvas.height);

    // Draw the visible region of the sourceCanvas to the display canvas.
    displayContext.drawImage(
        this.sourceCanvas,
        left / zoom, top / zoom,
        this.displayCanvas.width / zoom,
        this.displayCanvas.height / zoom,
        0,
        0,
        this.displayCanvas.width,
        this.displayCanvas.height);

    // brushCanvas represents pixels drawn by the current drawing operation
    // and is yet to be drawn to the displayCanvas.
    // draw brushCanvas onto displayCanvas with opacity value from current
    // slider input value.
    var opacityValue = this.getOpacity();
    var tempAlpha = displayContext.globalAlpha;
    displayContext.globalAlpha = opacityValue;

    // Set globalCompositeOperation based on whether eraser tool is selected.
    displayContext.globalCompositeOperation = this.eraserToolSelected() ==
        ZoomManager.ERASER ? 'destination-out' : 'source-over';

    // Draw the visible region of the brushCanvas to the display canvas.
    displayContext.drawImage(
        this.brushCanvas,
        left / zoom, top / zoom,
        this.displayCanvas.width / zoom,
        this.displayCanvas.height / zoom,
        0,
        0,
        this.displayCanvas.width,
        this.displayCanvas.height);
    // Restore initial alpha value.
    displayContext.globalAlpha = tempAlpha;
    // Restore default value for globalCompositeOperation.
    displayContext.globalCompositeOperation = 'source-over';
  };

  /**
   * Initialise optionProviders property for zoomManager.
   * optionProviders is created after zoomManager has been created.
   * @param {OptionProviders} optionProviders
   */
  ZoomManager.prototype.setOptionProviders = function(optionProviders) {
    this.optionProviders = optionProviders;
  };

  /**
   * Returns whether the eraserTool is currently selected.
   * @return {boolean}
   */
  ZoomManager.prototype.eraserToolSelected = function() {
    if (bitmapper.selectedTool == null ||
        bitmapper.selectedTool.constructor.name != 'PencilTool')
      return false;
    else
      return bitmapper.selectedTool['type'] == ZoomManager.ERASER;
  };

  /**
   * Returns opacity value, from colorSelector in optionProviders
   * @return {number}
   */
  ZoomManager.prototype.getOpacity = function() {
    if (this.optionProviders == null)
      // Use default opacity value.
      return ZoomManager.DEFAULT_OPACITY;

    return this.optionProviders.colorPalette.getOpacity();
  };

  /**
   * Returns current zoom factor.
   * @return {number}
   */
  ZoomManager.prototype.getZoomFactor = function() {
    return this.zoomFactor;
  };

  /**
   * Calculates the co-ordinate relative to source canvas.
   * @param {number} displayCoordinate
   * @return {number}
   */
  ZoomManager.prototype.getSourceCoordinate = function(displayCoordinate) {
    return displayCoordinate / this.zoomFactor;
  };

  bitmapper.ZoomManager = ZoomManager;
})();
<commit_msg>Only change displayCanvas height if canvasViewport is defined.<commit_after>/**
 * @license Copyright 2013 Google Inc. All rights reserved.
 * Use of this source code is governed by the Apache license that can be
 * found in the LICENSE file.
 */

/**
 * @constructor
 * @struct
 * @param {HTMLCanvasElement} sourceCanvas
 * @param {HTMLCanvasElement} displayCanvas
 * @param {HTMLCanvasElement} brushCanvas
 * @param {HTMLElement} canvasPlaceholder
 * @param {HTMLElement} canvasViewport
 */
function ZoomManager(sourceCanvas,
                     displayCanvas,
                     brushCanvas,
                     canvasPlaceholder,
                     canvasViewport) {
}

(function() {

  /**
   * Encapsulates data related to the zoom manager.
   * @constructor
   * @struct
   * @param {HTMLCanvasElement} sourceCanvas
   * @param {HTMLCanvasElement} displayCanvas
   * @param {HTMLCanvasElement} brushCanvas
   * @param {HTMLElement} canvasPlaceholder
   * @param {HTMLElement} canvasViewport
   */
  function ZoomManager(sourceCanvas,
                       displayCanvas,
                       brushCanvas,
                       canvasPlaceholder,
                       canvasViewport) {
    /**
     * @type {number}
     */
    this.zoomFactor = 1;

    /**
     * @type {HTMLCanvasElement}
     */
    this.sourceCanvas = sourceCanvas;

    /**
     * @type {HTMLCanvasElement}
     */
    this.displayCanvas = displayCanvas;

    /**
     * @type {HTMLCanvasElement}
     */
    this.brushCanvas = brushCanvas;

    /**
     * @type {HTMLElement}
     * A placeholder for the displayCanvas which sizes to the logical pixel size
     * of the zoomed image.
     */
    this.canvasPlaceholder = canvasPlaceholder;

    /**
     * optionProviders is initialized after zoomManager has been initialized.
     * initialization done in this.setOptionProvders().
     * @type {OptionProviders}
     */
    this.optionProviders = null;

    /**
     * @type {HTMLElement}
     * The element which controls the scrolling and visible region of the
     * displayed image.
     */
    this.canvasViewport = canvasViewport;

    var outer = this;
    canvasViewport.addEventListener(
        'scroll',
        function() { outer.drawDisplayCanvas(); },
        true);

    this.drawDisplayCanvas();
  };

  /**
   * Constant for default opacity value.
   * @const {number}
   */
  ZoomManager.DEFAULT_OPACITY = 1;

  /**
   * Constant for eraser tool provided in penciltool.js
   * @const {number}
   */
  ZoomManager.ERASER = 1;

  /**
   * Sets zoom factor, repositioning the viewport to respect |anchorX| and
   * |anchorY|.
   * @param {number} zoomFactor
   * @param {number} anchorX
   * @param {number} anchorY
   */
  ZoomManager.prototype.setZoomFactor = function(
      zoomFactor, anchorX, anchorY) {
    this.repositionViewportForZoom(
        anchorX, anchorY, this.zoomFactor, zoomFactor);
    this.zoomFactor = zoomFactor;
  };

  /**
   * Sets the top and left of the canvasViewport to keep |anchorX| and |anchorY|
   * in the same place relative to the visible area. Clamps to the canvas bounds
   * if repositioning would show an area outside the image.
   *
   * The anchor is specified in display coordinates.
   * @param {number} anchorX
   * @param {number} anchorY
   * @param {number} oldZoom
   * @param {number} newZoom
   */
  ZoomManager.prototype.repositionViewportForZoom = function(
      anchorX, anchorY, oldZoom, newZoom) {
    var anchorDistanceX = anchorX - this.canvasViewport.scrollLeft;
    var anchorDistanceY = anchorY - this.canvasViewport.scrollTop;

    var rescaledAnchorX = anchorX * newZoom / oldZoom;
    var rescaledAnchorY = anchorY * newZoom / oldZoom;

    var scrollLeftMax = Math.max(0,
        this.sourceCanvas.width * newZoom - this.canvasViewport.clientWidth);
    var scrollTopMax = Math.max(0,
        this.sourceCanvas.height * newZoom - this.canvasViewport.clientHeight);

    // Resize the canvas placeholder first so that there's sufficient scrollbar
    // on zoom-in.
    this.resizeCanvasPlaceholder(newZoom);
    this.canvasViewport.scrollLeft =
        bitmapper.util.constrain(rescaledAnchorX - anchorDistanceX,
                                 0,
                                 scrollLeftMax);
    this.canvasViewport.scrollTop =
        bitmapper.util.constrain(rescaledAnchorY - anchorDistanceY,
                                 0,
                                 scrollTopMax);
  };

  /**
   * Resizes the canvasPlaceholder to fill up the space of the sourceCanvas
   * zoomed |zoom| times.
   * @param {number} zoom
   */
  ZoomManager.prototype.resizeCanvasPlaceholder = function(zoom) {
    // Size the placeholder to the desired image size.
    this.canvasPlaceholder.style.height =
        (this.sourceCanvas.height * zoom) + 'px';
    this.canvasPlaceholder.style.width =
        (this.sourceCanvas.width * zoom) + 'px';
  };

  /**
   * Scales and redraws the display canvas.
   */
  ZoomManager.prototype.drawDisplayCanvas = function() {
    var zoom = this.zoomFactor;
    this.resizeCanvasPlaceholder(zoom);

    // Size and move the displayCanvas to the area visible in the viewport.
    var left = this.canvasViewport.scrollLeft;
    var top = this.canvasViewport.scrollTop;
    this.displayCanvas.style.left = left + 'px';
    this.displayCanvas.style.top = top + 'px';

    // clientHeight/Width is not defined in tests.
    if (this.canvasViewport.clientHeight)
      this.displayCanvas.height = this.canvasViewport.clientHeight;
    if (this.canvasViewport.clientWidth)
      this.displayCanvas.width = this.canvasViewport.clientWidth;

    var displayContext = Canvas2DContext(this.displayCanvas);
    // Displays crisp pixels when scaled.
    displayContext.imageSmoothingEnabled = false;
    displayContext.clearRect(
        0, 0, this.displayCanvas.width, this.displayCanvas.height);

    // Draw the visible region of the sourceCanvas to the display canvas.
    displayContext.drawImage(
        this.sourceCanvas,
        left / zoom, top / zoom,
        this.displayCanvas.width / zoom,
        this.displayCanvas.height / zoom,
        0,
        0,
        this.displayCanvas.width,
        this.displayCanvas.height);

    // brushCanvas represents pixels drawn by the current drawing operation
    // and is yet to be drawn to the displayCanvas.
    // draw brushCanvas onto displayCanvas with opacity value from current
    // slider input value.
    var opacityValue = this.getOpacity();
    var tempAlpha = displayContext.globalAlpha;
    displayContext.globalAlpha = opacityValue;

    // Set globalCompositeOperation based on whether eraser tool is selected.
    displayContext.globalCompositeOperation = this.eraserToolSelected() ==
        ZoomManager.ERASER ? 'destination-out' : 'source-over';

    // Draw the visible region of the brushCanvas to the display canvas.
    displayContext.drawImage(
        this.brushCanvas,
        left / zoom, top / zoom,
        this.displayCanvas.width / zoom,
        this.displayCanvas.height / zoom,
        0,
        0,
        this.displayCanvas.width,
        this.displayCanvas.height);
    // Restore initial alpha value.
    displayContext.globalAlpha = tempAlpha;
    // Restore default value for globalCompositeOperation.
    displayContext.globalCompositeOperation = 'source-over';
  };

  /**
   * Initialise optionProviders property for zoomManager.
   * optionProviders is created after zoomManager has been created.
   * @param {OptionProviders} optionProviders
   */
  ZoomManager.prototype.setOptionProviders = function(optionProviders) {
    this.optionProviders = optionProviders;
  };

  /**
   * Returns whether the eraserTool is currently selected.
   * @return {boolean}
   */
  ZoomManager.prototype.eraserToolSelected = function() {
    if (bitmapper.selectedTool == null ||
        bitmapper.selectedTool.constructor.name != 'PencilTool')
      return false;
    else
      return bitmapper.selectedTool['type'] == ZoomManager.ERASER;
  };

  /**
   * Returns opacity value, from colorSelector in optionProviders
   * @return {number}
   */
  ZoomManager.prototype.getOpacity = function() {
    if (this.optionProviders == null)
      // Use default opacity value.
      return ZoomManager.DEFAULT_OPACITY;

    return this.optionProviders.colorPalette.getOpacity();
  };

  /**
   * Returns current zoom factor.
   * @return {number}
   */
  ZoomManager.prototype.getZoomFactor = function() {
    return this.zoomFactor;
  };

  /**
   * Calculates the co-ordinate relative to source canvas.
   * @param {number} displayCoordinate
   * @return {number}
   */
  ZoomManager.prototype.getSourceCoordinate = function(displayCoordinate) {
    return displayCoordinate / this.zoomFactor;
  };

  bitmapper.ZoomManager = ZoomManager;
})();
<|endoftext|>


<commit_before>'use strict';

require('classtool');

function spec() {
  var async           = require('async');
  var BitcoreAddress  = require('bitcore/Address').class();
  var BitcoreUtil     = require('bitcore/util/util');
  var TransactionDb   = require('../../lib/TransactionDb').class();

  function Address(addrStr) {
    this.balanceSat        = 0;
    this.totalReceivedSat  = 0;
    this.totalSentSat      = 0;

    this.unconfirmedBalanceSat  = 0;

    this.txApperances           = 0;
    this.unconfirmedTxApperances= 0;

    // TODO store only txids? +index? +all?
    this.transactions   = [];

    var a = new BitcoreAddress(addrStr);
    a.validate();
    this.addrStr        = addrStr;

    Object.defineProperty(this, 'totalSent', {
      get: function() {
        return parseFloat(this.totalSentSat) / parseFloat(BitcoreUtil.COIN);
      },
      set:  function(i) {
        this.totalSentSat =  i * BitcoreUtil.COIN;
      },
      enumerable: 1,
    });

    Object.defineProperty(this, 'balance', {
      get: function() {
        return parseFloat(this.balanceSat) / parseFloat(BitcoreUtil.COIN);
      },
      set:  function(i) {
        this.balance =   i * BitcoreUtil.COIN;
      },
      enumerable: 1,
    });

    Object.defineProperty(this, 'totalReceived', {
      get: function() {
        return parseFloat(this.totalReceivedSat) / parseFloat(BitcoreUtil.COIN);
      },
      set:  function(i) {
        this.totalReceived =  i * BitcoreUtil.COIN;
      },
      enumerable: 1,
    });

    Object.defineProperty(this, 'unconfirmedBalance', {
      get: function() {
        return parseFloat(this.unconfirmedBalanceSat) / parseFloat(BitcoreUtil.COIN);
      },
      set:  function(i) {
        this.unconfirmedBalanceSat =  i * BitcoreUtil.COIN;
      },
      enumerable: 1,
    });

  }

  Address.prototype.update = function(next) {
    var self = this;
    if (!self.addrStr) return next();

    var txs  = [];
    var db   = new TransactionDb();
    async.series([
      function (cb) {
        db.fromAddr(self.addrStr, function(err,txOut){
          if (err) return cb(err);
          txOut.forEach(function(txItem){

            var v = txItem.value_sat;

            txs.push({txid: txItem.txid, ts: txItem.ts});

            if (txItem.spentTxId) {
              txs.push({txid: txItem.spentTxId, ts: txItem.spentTs});
            }

            if (txItem.isConfirmed) {
              self.txApperances += 1;
              self.totalReceivedSat += v;
              if (! txItem.spentTxId ) {
                //unspent
                self.balanceSat   += v;
              }
              else if(!txItem.spentIsConfirmed) {
                // unspent
                self.balanceSat   += v;
                self.unconfirmedBalanceSat -= v;
                self.unconfirmedTxApperances += 1;
              }
              else {
                // spent
                self.totalSentSat += v;
                self.txApperances += 1;
              }
            }
            else {
              self.unconfirmedBalanceSat += v;
              self.unconfirmedTxApperances += 1;
            }
          });
          return cb();
        });
      },
    ], function (err) {

      // sort input and outputs togheter
      txs.sort(
        function compare(a,b) {
          if (a.ts < b.ts) return 1;
          if (a.ts > b.ts) return -1;
          return 0;
        });

      self.transactions = txs.map(function(i) { return i.txid; } );
      return next(err);
    });
  };

  return Address;
}
module.defineClass(spec);

<commit_msg>fix spents in first part of key matching errors<commit_after>'use strict';

require('classtool');

function spec() {
  var async           = require('async');
  var BitcoreAddress  = require('bitcore/Address').class();
  var BitcoreUtil     = require('bitcore/util/util');
  var TransactionDb   = require('../../lib/TransactionDb').class();

  function Address(addrStr) {
    this.balanceSat        = 0;
    this.totalReceivedSat  = 0;
    this.totalSentSat      = 0;

    this.unconfirmedBalanceSat  = 0;

    this.txApperances           = 0;
    this.unconfirmedTxApperances= 0;

    // TODO store only txids? +index? +all?
    this.transactions   = [];

    var a = new BitcoreAddress(addrStr);
    a.validate();
    this.addrStr        = addrStr;

    Object.defineProperty(this, 'totalSent', {
      get: function() {
        return parseFloat(this.totalSentSat) / parseFloat(BitcoreUtil.COIN);
      },
      set:  function(i) {
        this.totalSentSat =  i * BitcoreUtil.COIN;
      },
      enumerable: 1,
    });

    Object.defineProperty(this, 'balance', {
      get: function() {
        return parseFloat(this.balanceSat) / parseFloat(BitcoreUtil.COIN);
      },
      set:  function(i) {
        this.balance =   i * BitcoreUtil.COIN;
      },
      enumerable: 1,
    });

    Object.defineProperty(this, 'totalReceived', {
      get: function() {
        return parseFloat(this.totalReceivedSat) / parseFloat(BitcoreUtil.COIN);
      },
      set:  function(i) {
        this.totalReceived =  i * BitcoreUtil.COIN;
      },
      enumerable: 1,
    });

    Object.defineProperty(this, 'unconfirmedBalance', {
      get: function() {
        return parseFloat(this.unconfirmedBalanceSat) / parseFloat(BitcoreUtil.COIN);
      },
      set:  function(i) {
        this.unconfirmedBalanceSat =  i * BitcoreUtil.COIN;
      },
      enumerable: 1,
    });

  }

  Address.prototype.update = function(next) {
    var self = this;
    if (!self.addrStr) return next();

    var txs  = [];
    var db   = new TransactionDb();
    async.series([
      function (cb) {
        var seen={};
        db.fromAddr(self.addrStr, function(err,txOut){
          if (err) return cb(err);
          txOut.forEach(function(txItem){
            var add=0, addSpend=0;
            var v = txItem.value_sat;

            if ( !seen[txItem.txid] ) {
              txs.push({txid: txItem.txid, ts: txItem.ts});
              seen[txItem.txid]=1;
              add=1;
            }

            if (txItem.spentTxId && !seen[txItem.spentTxId]  ) {
              txs.push({txid: txItem.spentTxId, ts: txItem.spentTs});
              seen[txItem.spentTxId]=1;
              addSpend=1;
            }

            if (txItem.isConfirmed) {
              self.txApperances += add;
              self.totalReceivedSat += v;
              if (! txItem.spentTxId ) {
                //unspent
                self.balanceSat   += v;
              }
              else if(!txItem.spentIsConfirmed) {
                // unspent
                self.balanceSat   += v;
                self.unconfirmedBalanceSat -= v;
                self.unconfirmedTxApperances += addSpend;
              }
              else {
                // spent
                self.totalSentSat += v;
                self.txApperances += addSpend;
              }
            }
            else {
              self.unconfirmedBalanceSat += v;
              self.unconfirmedTxApperances += add;
            }
          });
          return cb();
        });
      },
    ], function (err) {

      // sort input and outputs togheter
      txs.sort(
        function compare(a,b) {
          if (a.ts < b.ts) return 1;
          if (a.ts > b.ts) return -1;
          return 0;
        });

      self.transactions = txs.map(function(i) { return i.txid; } );
      return next(err);
    });
  };

  return Address;
}
module.defineClass(spec);

<|endoftext|>


<commit_before>'use strict';

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const CommentSchema = new Schema({
  publicId: {
    type: String,
    required: true,
    unique: true,
  },
  gameId: {
    type: Schema.Types.ObjectId,
    ref: 'Game',
    required: true,
  },
  authorId: {
    type: Schema.Type.ObjectId,
    ref: 'User',
    required: true,
  },
  authorName: {
    type: String,
    required: true,
  },
  content: {
    type: String,
    requireD: true,
  },
  createdAt: {
    type: Date,
    required: true,
  },
  updatedAt: {
    type: Date,
    required: true,
  },
},
{
  collection: 'comments',
});

CommentSchema.index({ publicId: 1 });

module.exports = mongoose.model('Comment', CommentSchema);
<commit_msg>Correct typo.<commit_after>'use strict';

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const CommentSchema = new Schema({
  publicId: {
    type: String,
    required: true,
    unique: true,
  },
  gameId: {
    type: Schema.Types.ObjectId,
    ref: 'Game',
    required: true,
  },
  authorId: {
    type: Schema.Types.ObjectId,
    ref: 'User',
    required: true,
  },
  authorName: {
    type: String,
    required: true,
  },
  content: {
    type: String,
    requireD: true,
  },
  createdAt: {
    type: Date,
    required: true,
  },
  updatedAt: {
    type: Date,
    required: true,
  },
},
{
  collection: 'comments',
});

CommentSchema.index({ publicId: 1 });

module.exports = mongoose.model('Comment', CommentSchema);
<|endoftext|>


<commit_before>var data = [];
var current = null;
var last = null;
var svg;
var rxStatus = false;
var rx = null;
var counter = null;

var group = null;
var youAreHere = null;
var grid = null;
var gridH = null;
var gridV = null;

var bounds = {
	top: -Number.MAX_VALUE,
	left: Number.MAX_VALUE,
	bottom: Number.MAX_VALUE,
	right: -Number.MAX_VALUE,

	width: function() {
		return this.right - this.left;
	},

	height: function() {
		return this.top - this.bottom;
	},

	centerX: function() {
		return (this.right + this.left) / 2;
	},

	centerY: function() {
		return (this.top + this.bottom) / 2;
	}
}

var updateBounds = function(position, bounds) {
	if (position.x > bounds.right) {
		bounds.right = position.x;
	}
	if (position.x < bounds.left) {
		bounds.left = position.x;
	}
	if (position.y > bounds.top) {
		bounds.top = position.y;
	}
	if (position.y < bounds.bottom) {
		bounds.bottom = position.y;
	}
}

var connectionState = {
	notConnected: "not connected",
	connecting: "connecting",
	connected: "connected"
};

var state = connectionState.notConnected;

var connect = function() {
	state = connectionState.connecting;
	document.getElementById('status').innerHTML = 'connecting';

	url = "ws://localhost:60600";

	if ("WebSocket" in window) {
		ws = new WebSocket(url);
	} else if ("MozWebSocket" in window) {
		ws = new MozWebSocket(url);
	} else {
		alert('This Browser does not support WebSockets');
		autoConnect = false;
		return;
	}

	ws.onopen = function(e)
	{
		state = connectionState.connected;
		document.getElementById('status').innerHTML = 'cconnected';
	};

	ws.onmessage = function(message) {
		var sample = JSON.parse(message.data);
		data.push(sample);

		updateBounds(sample.position, bounds);

		last = current;
		current = JSON.parse(message.data);

		update();
		makeGrid(bounds);

		rxStatus = !rxStatus;
		if (rxStatus) {
			rx.attr('class', 'off');
		} else {
			rx.attr('class', 'on');
		}
		counter.hit();
		counter.print();
	};

	ws.onerror = function(event) {
		// console.log(event);
		state = connectionState.notConnected;
		document.getElementById('status').innerHTML = 'disconnected';
	};
}

var update = function() {
	if (last) {
		group
			.append('line')
				.attr('x1', last.position.x)
				.attr('x2', current.position.x)
				.attr('y1', last.position.y)
				.attr('y2', current.position.y)
				.attr('vector-effect', 'inherit');

		youAreHere.attr('cx', current.position.x)
			.attr('cy', current.position.y);
	}

	updateGroup(bounds);
	// svg.selectAll('line').data(data)
	// 	.enter()
	// 		.append('line')
	// 			.attr('x1', function(d) {return d.position.x * 200})
	// 			.attr('x2', function(d, i) {return data[i-1] && data[i-1].position.x* 200})
	// 			.attr('y1', function(d) {return d.position.y* 200})
	// 			.attr('y2', function(d, i) {return data[i-1] && data[i-1].position.y* 200});

}

var updateGroup = function(bounds) {
	var scale = Math.min(svg.width / bounds.width(), svg.height / bounds.height());

	group.transition()
		.attr('transform', 'translate(' + (-bounds.centerX() * scale + svg.width / 2) + ' '+
			(-bounds.centerY() * scale + svg.height / 2) + ') scale(' + scale + ')');
}

var makeGrid = function(bounds) {

	var size = 4;
	var logStep = Math.pow(size, Math.floor(Math.log(Math.min(bounds.width(), bounds.height())) / Math.log(size)));
	// console.log(logStep);

	var count = Math.ceil(bounds.width() / logStep);
	// console.log(count);

	var anchorV = Math.floor((bounds.left - (bounds.width())) / logStep) * logStep;

	var dataV = [];
	for (var i = 0; i < count * 4; i++) {
		dataV.push(anchorV + logStep * i);
	}

	var foo = gridV.selectAll('line').data(dataV);
	foo
		.attr('x1', function(d) {return d;})
		.attr('x2', function(d) {return d;})
		.attr('y1', bounds.bottom - bounds.height())
		.attr('y2', bounds.top + bounds.height())
		.attr('vector-effect', 'inherit')

	foo.enter()
		.append('line')
			.attr('x1', function(d) {return d;})
			.attr('x2', function(d) {return d;})
			.attr('y1', bounds.bottom - bounds.height())
			.attr('y2', bounds.top + bounds.height())
			.attr('vector-effect', 'inherit');
	foo.exit()
		.remove();

	var anchorH = Math.floor((bounds.bottom - (bounds.height())) / logStep) * logStep;

	var dataH = [];
	for (var i = 0; i < count * 4; i++) {
		dataH.push(anchorH + logStep * i);
	}

	foo = gridH.selectAll('line').data(dataH);
	foo
		.attr('x1', bounds.left - bounds.width())
		.attr('x2', bounds.right + bounds.width())
		.attr('y1', function(d) {return d;})
		.attr('y2', function(d) {return d;})
		.attr('vector-effect', 'inherit')

	foo.enter()
		.append('line')
	.attr('x1', bounds.left - bounds.width())
		.attr('x2', bounds.right + bounds.width())
		.attr('y1', function(d) {return d;})
		.attr('y2', function(d) {return d;})
		.attr('vector-effect', 'inherit')
	foo.exit()
		.remove();
}

window.onload = function() {

	var Counter = function() {
		var count = 0;
		var start = performance.now();

		var totalSpan = document.getElementById('total');
		var rateSpan = document.getElementById('rate');

		this.print = function() {
			totalSpan.innerHTML = count;
			rateSpan.innerHTML = (count / ((performance.now() - start) / 1000)).toFixed(3);
		}

		this.hit = function() {
			count++;
		}
	}

	counter = new Counter();

	rx = d3.select('#rx');
	rx.attr('class', 'off');

	svg = d3.select('#map');
	svg.width = svg[0][0].clientWidth;
	svg.height = svg[0][0].clientHeight;

	group = svg.append('g');
	group.attr('vector-effect', 'non-scaling-stroke');

	youAreHere = group.append('circle')
		.attr('cx', '0')
		.attr('cy', '0')
		.attr('r', '4');

	grid = group.append('g').attr('id', 'grid');
	gridV = grid.append('g').attr('vector-effect', 'non-scaling-stroke');;
	gridH = grid.append('g').attr('vector-effect', 'non-scaling-stroke');;

	grid.attr('vector-effect', 'non-scaling-stroke');

	window.setInterval(function() {
		if (state === connectionState.notConnected) {
			connect();
		}
	}, 1000);
}
<commit_msg>started on dragging, gave up...<commit_after>var data = [];
var data2 = [];
var data4 = [];
var data8 = [];
var data16 = [];
var dataCount = 0;
currentData = data;
var maxElements = 1000;

var current = null;
var last = null;
var svg;
var rxStatus = false;
var rx = null;
var counter = null;

var group = null;
var youAreHere = null;
var grid = null;
var gridH = null;
var gridV = null;

var bounds = {
	top: -Number.MAX_VALUE,
	left: Number.MAX_VALUE,
	bottom: Number.MAX_VALUE,
	right: -Number.MAX_VALUE,

	width: function() {
		return this.right - this.left;
	},

	height: function() {
		return this.top - this.bottom;
	},

	centerX: function() {
		return (this.right + this.left) / 2;
	},

	centerY: function() {
		return (this.top + this.bottom) / 2;
	}
}

var updateBounds = function(position, bounds) {
	if (position.x > bounds.right) {
		bounds.right = position.x;
	}
	if (position.x < bounds.left) {
		bounds.left = position.x;
	}
	if (position.y > bounds.top) {
		bounds.top = position.y;
	}
	if (position.y < bounds.bottom) {
		bounds.bottom = position.y;
	}
}

var connectionState = {
	notConnected: "not connected",
	connecting: "connecting",
	connected: "connected"
};

var state = connectionState.notConnected;

var connect = function() {
	state = connectionState.connecting;
	document.getElementById('status').innerHTML = 'connecting';

	url = "ws://localhost:60600";

	if ("WebSocket" in window) {
		ws = new WebSocket(url);
	} else if ("MozWebSocket" in window) {
		ws = new MozWebSocket(url);
	} else {
		alert('This Browser does not support WebSockets');
		autoConnect = false;
		return;
	}

	ws.onopen = function(e)
	{
		state = connectionState.connected;
		document.getElementById('status').innerHTML = 'cconnected';
	};

	ws.onmessage = function(message) {
		var sample = JSON.parse(message.data);

		dataCount++;
		data.push(sample);
		if (dataCount % 2 === 0) {
			data2.push(sample);
		}
		if (dataCount % 4 === 0) {
			data2.push(sample);
		}
		if (dataCount % 8 === 0) {
			data2.push(sample);
		}
		if (dataCount % 16 === 0) {
			data2.push(sample);
		}

		// if (currentData.length > maxElements) {
		// 	currentData = data2;
		// }

		updateBounds(sample.position, bounds);

		last = current;
		current = JSON.parse(message.data);

		update();
		makeGrid(bounds);

		rxStatus = !rxStatus;
		if (rxStatus) {
			rx.attr('class', 'off');
		} else {
			rx.attr('class', 'on');
		}
		counter.hit();
		counter.print();
	};

	ws.onerror = function(event) {
		// console.log(event);
		state = connectionState.notConnected;
		document.getElementById('status').innerHTML = 'disconnected';
	};
}

var update = function() {
	// if (data.length > 1) {
	// 	group.selectAll('line').data(data)
	// 		.enter()
	// 			.append('line')
	// 				.attr('x1', function(d) {return d.position.x })
	// 				.attr('x2', function(d, i) {
	// 					return data[i-1].position.x})
	// 				.attr('y1', function(d) {return d.position.y})
	// 				.attr('y2', function(d, i) {return data[i-1].position.y})
	// 				.attr('vector-effect', 'inherit');
	// 	}

	if (last) {
		group
			.append('line')
				.attr('x1', last.position.x)
				.attr('x2', current.position.x)
				.attr('y1', last.position.y)
				.attr('y2', current.position.y)
				.attr('vector-effect', 'inherit');

		youAreHere.attr('cx', current.position.x)
			.attr('cy', current.position.y);
	}

	updateGroup(bounds);

}

var updateGroup = function(bounds) {
	var scale = Math.min(svg.width / bounds.width(), svg.height / bounds.height());

	group.transition()
		.attr('transform', 'translate(' + (-bounds.centerX() * scale + svg.width / 2) + ' '+
			(-bounds.centerY() * scale + svg.height / 2) + ') scale(' + scale + ')');
}

var makeGrid = function(bounds) {

	var size = 4;
	var logStep = Math.pow(size, Math.floor(Math.log(Math.min(bounds.width(), bounds.height())) / Math.log(size)));
	// console.log(logStep);

	var count = Math.ceil(bounds.width() / logStep);
	// console.log(count);

	var anchorV = Math.floor((bounds.left - (bounds.width())) / logStep) * logStep;

	var dataV = [];
	for (var i = 0; i < count * 4; i++) {
		dataV.push(anchorV + logStep * i);
	}

	var foo = gridV.selectAll('line').data(dataV);
	foo
		.attr('x1', function(d) {return d;})
		.attr('x2', function(d) {return d;})
		.attr('y1', bounds.bottom - bounds.height())
		.attr('y2', bounds.top + bounds.height())
		.attr('vector-effect', 'inherit')

	foo.enter()
		.append('line')
			.attr('x1', function(d) {return d;})
			.attr('x2', function(d) {return d;})
			.attr('y1', bounds.bottom - bounds.height())
			.attr('y2', bounds.top + bounds.height())
			.attr('vector-effect', 'inherit');
	foo.exit()
		.remove();

	var anchorH = Math.floor((bounds.bottom - (bounds.height())) / logStep) * logStep;

	var dataH = [];
	for (var i = 0; i < count * 4; i++) {
		dataH.push(anchorH + logStep * i);
	}

	foo = gridH.selectAll('line').data(dataH);
	foo
		.attr('x1', bounds.left - bounds.width())
		.attr('x2', bounds.right + bounds.width())
		.attr('y1', function(d) {return d;})
		.attr('y2', function(d) {return d;})
		.attr('vector-effect', 'inherit')

	foo.enter()
		.append('line')
	.attr('x1', bounds.left - bounds.width())
		.attr('x2', bounds.right + bounds.width())
		.attr('y1', function(d) {return d;})
		.attr('y2', function(d) {return d;})
		.attr('vector-effect', 'inherit')
	foo.exit()
		.remove();
}

window.onload = function() {

	var Counter = function() {
		var count = 0;
		var start = performance.now();

		var totalSpan = document.getElementById('total');
		var rateSpan = document.getElementById('rate');

		this.print = function() {
			totalSpan.innerHTML = count;
			rateSpan.innerHTML = (count / ((performance.now() - start) / 1000)).toFixed(3);
		}

		this.hit = function() {
			count++;
		}
	}

	counter = new Counter();

	rx = d3.select('#rx');
	rx.attr('class', 'off');

	svg = d3.select('#map');
	svg.width = svg[0][0].clientWidth;
	svg.height = svg[0][0].clientHeight;

	var domSVG = document.getElementById('map');

	var mouseDown = false;
	document.onmousemove = function(event) {
		if (mouseDown) {
			// console.log('dragging')
			// console.log(event.clientX);
		}
	}

	domSVG.onmousedown = function() {
		mouseDown = true;
	}

	document.onmouseup = function(event) {
		mouseDown = false;
	}

	group = svg.append('g');
	group.attr('vector-effect', 'non-scaling-stroke');

	youAreHere = group.append('circle')
		.attr('cx', '0')
		.attr('cy', '0')
		.attr('r', '4');

	grid = group.append('g').attr('id', 'grid');
	gridV = grid.append('g').attr('vector-effect', 'non-scaling-stroke');;
	gridH = grid.append('g').attr('vector-effect', 'non-scaling-stroke');;

	grid.attr('vector-effect', 'non-scaling-stroke');

	window.setInterval(function() {
		if (state === connectionState.notConnected) {
			connect();
		}
	}, 1000);
}
<|endoftext|>


<commit_before>/** @jsx React.DOM */

'use strict';

var React = require('react');
var debug = require('debug');
debug.enable('*');
window.React = React;
var appContext = require('../shared/context');
var dehydratedState = window.Morpheus; // Sent from the server
window.React = React; // For chrome dev tool support
appContext.rehydrate(dehydratedState, function(err, context) {
  if (err) {
    throw err;
  }
  var mountNode = document.body;
  React.render(appContext.getAppComponent()({
    context: context.getComponentContext()
  }), mountNode, function() {});
});
<commit_msg>remove typo<commit_after>/** @jsx React.DOM */

'use strict';

var React = require('react');
var debug = require('debug');
debug.enable('*');
var appContext = require('../shared/context');
var dehydratedState = window.Morpheus; // Sent from the server
window.React = React; // For chrome dev tool support
appContext.rehydrate(dehydratedState, function(err, context) {
  if (err) {
    throw err;
  }
  var mountNode = document.body;
  React.render(appContext.getAppComponent()({
    context: context.getComponentContext()
  }), mountNode, function() {});
});
<|endoftext|>


<commit_before>/* global ace */
/* global join */

'use strict';

require('../css/edward.css');

const {createPatch} = require('daffy');
const exec = require('execon');
const Emitify = require('emitify');
const load = require('load.js');
const loadremote = require('./loadremote');
const wraptile = require('wraptile');
const smalltalk = require('smalltalk');
const jssha = require('jssha').default;
const restafary = require('restafary/client');
const tryToCatch = require('try-to-catch');

window.load = window.load || load;
window.exec = window.exec || exec;

const Story = require('./story');
const _clipboard = require('./_clipboard');
const _setEmmet = require('./_set-emmet');
const _initSocket = require('./_init-socket');
const setKeyMap = require('./set-key-map');
const showMessage = require('./show-message');
const save = require('./save');

function empty() {}

module.exports = Edward;

function Edward(el, options, callback) {
    if (!(this instanceof Edward))
        return new Edward(el, options, callback);

    this._Ace;
    this._Emmet;
    this._value;

    this._Config = {
        options: {},
    };

    this._Options;
    this._filename = '';
    this._Modelist;
    this._ElementMsg;
    this._Ext;
    this._title = 'Edward';
    this._DIR = '/modules/';
    this._story = Story();
    this._Emitter;
    this._isKey = true;

    if (!callback)
        callback = options;

    if (typeof el === 'string')
        el = document.querySelector(el);

    this._maxSize = options._maxSize || 512_000;
    this._PREFIX = options.prefix || '/edward';
    this._prefixSocket = options.prefixSocket || '/edward';
    this._SOCKET_PATH = options.socketPath || '';

    this._Element = el || document.body;

    const onDrop = this._onDrop.bind(this);
    const onDragOver = this._onDragOver.bind(this);

    this._Element.addEventListener('drop', onDrop);
    this._Element.addEventListener('dragover', onDragOver);

    this._init(() => {
        callback(this);
    });

    this._patch = function(path, patch) {
        this._patchHttp(path, patch);
    };

    this._write = function(path, result) {
        this._writeHttp(path, result);
    };
}

Edward.prototype.showMessage = showMessage;

Edward.prototype.isKey = function() {
    return this._isKey;
};

Edward.prototype.disableKey = function() {
    this._isKey = false;
};

Edward.prototype.enableKey = function() {
    this._isKey = true;
    return this;
};

Edward.prototype._init = function(fn) {
    const edward = this;
    const loadFiles = this._loadFiles.bind(this);
    const initSocket = _initSocket.bind(this);

    exec.series([
        loadFiles,
        async (callback) => {
            await loadremote('socket', {
                name : 'io',
                prefix: this._SOCKET_PATH,
            });

            initSocket();
            callback();
        },
        async () => {
            this._Emitter = Emitify();
            this._Ace = ace.edit(this._Element);
            this._Modelist = ace.require('ace/ext/modelist');

            this._Emitter.on('auth', (username, password) => {
                this._socket.emit('auth', username, password);
            });

            ace.require('ace/ext/language_tools');

            this._addCommands();
            this._Ace.$blockScrolling = Infinity;

            const config = await load.json(this._PREFIX + '/edit.json');

            const {
                options = {},
            } = config;

            const preventOverwrite = () => {
                for (const name of Object.keys(this._Config.options)) {
                    options[name] = this._Config.options[name];
                }
            };

            fn();
            preventOverwrite();

            this._Config = config;

            edward.setOptions(options);
        },
    ]);
};

Edward.prototype._addCommands = function() {
    const edward = this;
    const addKey = this._addKey.bind(this);

    const call = (fn) => fn.call(this);
    const wrapCall = wraptile(call);

    const callIfKey = wraptile((fn) => {
        edward.isKey() && call(fn);
    });

    const commands = [{
        name    : 'goToLine',
        bindKey : {win: 'Ctrl-G', mac: 'Command-G'},
        exec    : wrapCall(edward.goToLine),
    }, {
        name    : 'save',
        bindKey : {win: 'Ctrl-S', mac: 'Command-S'},
        exec    : callIfKey(edward.save),
    }, {
        name    : 'saveMC',
        bindKey : {win: 'F2', mac: 'F2'},
        exec    : callIfKey(edward.save),
    }, {
        name    : 'evaluate',
        bindKey : {win: 'Ctrl-E', mac: 'Command-E'},
        exec    : callIfKey(edward.evaluate),
    }];

    commands.forEach(addKey);
};

Edward.prototype.evaluate = function() {
    const edward = this;
    const focus = edward.focus.bind(this);
    const isJS = /\.js$/.test(this._filename);

    if (!isJS)
        return smalltalk.alert(this._title, 'Evaluation supported for JavaScript only')
            .then(focus);

    const value = edward.getValue();
    const msg = exec.try(Function(value));

    msg && smalltalk.alert(this._title, msg)
        .then(focus);
};

Edward.prototype._addKey = function(options) {
    this._Ace.commands.addCommand(options);
};

Edward.prototype.addKeyMap = function(keyMap) {
    if (typeof keyMap !== 'object')
        throw Error('map should be object!');

    const map = Object.keys(keyMap).map((name, i) => {
        const key = {
            name: String(Math.random()) + i,
            bindKey : {
                win : name,
                mac : name.replace('Ctrl', 'Command'),
            },
            exec: keyMap[name],
        };

        return key;
    });

    map.forEach(this._addKey());

    return this;
};

Edward.prototype.goToLine = function() {
    const msg = 'Enter line number:';
    const cursor = this.getCursor();
    const number = cursor.row + 1;

    const goToLine = (line) => {
        this._Ace.gotoLine(line);
    };

    const focus = () => {
        this._Ace.focus();
    };

    smalltalk.prompt(this._title, msg, number)
        .then(goToLine)
        .catch(empty)
        .then(focus);

    return this;
};

Edward.prototype.moveCursorTo = function(row, column) {
    this._Ace.moveCursorTo(row, column);
    return this;
};

Edward.prototype.refresh = function() {
    this._Ace.resize();
    return this;
};

Edward.prototype.focus = function() {
    this._Ace.focus();
    return this;
};

Edward.prototype.remove = function(direction) {
    this._Ace.remove(direction);
    return this;
};

Edward.prototype.getCursor = function() {
    return this._Ace.selection.getCursor();
};

Edward.prototype.getValue = function() {
    return this._Ace.getValue();
};

Edward.prototype.on = function(event, fn) {
    this._Emitter.on(event, fn);
    return this;
};

Edward.prototype.once = function(event, fn) {
    this._Emitter.once(event, fn);
    return this;
};

Edward.prototype.emit = function(...args) {
    this._Emitter.emit(...args);
    return this;
};

Edward.prototype.isChanged = function() {
    const value = this.getValue();
    const isEqual = value === this._value;

    return !isEqual;
};

Edward.prototype.setValue = function(value) {
    const session = this._getSession();

    session.setScrollTop(0);

    this._Ace.setValue(value);
    this._Ace.clearSelection();

    this._Emitter.emit('change');

    return this;
};

Edward.prototype.setValueFirst = function(name, value) {
    const session = this._getSession();
    const {UndoManager} = ace.require('ace/undomanager');

    this._filename = name;
    this._value = value;

    this.setValue(value);

    session.setUndoManager(new UndoManager());

    return this;
};

Edward.prototype.setOption = function(name, value) {
    const preventOverwrite = () => {
        this._Config.options[name] = value;
    };

    preventOverwrite();

    if (name === 'keyMap') {
        this.setKeyMap(value);

        return this;
    }

    this._Ace.setOption(name, value);

    return this;
};

Edward.prototype.setOptions = function(options) {
    for (const name of Object.keys(options)) {
        this.setOption(name, options[name]);
    }

    return this;
};

Edward.prototype.setKeyMap = setKeyMap;
Edward.prototype._setUseOfWorker = function(mode) {
    const session = this._getSession();
    const isStr = typeof mode === 'string';
    const regStr = 'coffee|css|html|json|lua|php|xquery';
    const regExp = new RegExp(regStr);

    let isMatch;

    if (isStr)
        isMatch = regExp.test(mode);

    session.setUseWorker(isMatch);

    return this;
};

Edward.prototype.setMode = function(mode) {
    const {modesByName} = this._Modelist;

    if (!modesByName[mode])
        return this;

    const [ext] = modesByName[mode].extensions.split('|');
    this.setModeForPath('.' + ext);

    return this;
};

Edward.prototype.setModeForPath = function(path) {
    const session = this._getSession();
    const {modesByName} = this._Modelist;
    const name = path.split('/').pop();

    this._addExt(name, (name) => {
        const {mode} = this._Modelist.getModeForPath(name);
        const htmlMode = modesByName.html.mode;

        const isHTML = mode === htmlMode;

        session.setMode(mode, () => {
            this._setUseOfWorker(mode);

            if (isHTML)
                this._setEmmet();
        });
    });

    return this;
};

Edward.prototype.selectAll = function() {
    this._Ace.selectAll();
    return this;
};

Edward.prototype.copyToClipboard = function() {
    const msg = 'Could not copy, use &ltCtrl&gt + &ltС&gt insted!';

    this._clipboard('copy')
        .catch(wraptile(smalltalk.alert, this._title, msg));

    return this;
};

Edward.prototype.cutToClipboard = function() {
    const msg = 'Could not cut, use &ltCtrl&gt + &ltX&gt insted!';
    const remove = this.remove.bind(this);

    this._clipboard('cut')
        .then(wraptile(remove, 'right'))
        .catch(wraptile(smalltalk.alert, this._title, msg));

    return this;
};

Edward.prototype.pasteFromClipboard = function() {
    const msg = 'Could not paste, use &ltCtrl&gt + &ltV&gt insted!';

    this._clipboard('paste')
        .catch(wraptile(smalltalk.alert, this._title, msg));

    return this;
};

Edward.prototype._clipboard = _clipboard;

Edward.prototype._getSession = function() {
    return this._Ace.getSession();
};

Edward.prototype.sha = function() {
    const value = this.getValue();
    const shaObj = new jssha('SHA-1', 'TEXT');
    shaObj.update(value);

    return shaObj.getHash('HEX');
};

Edward.prototype.save = save;

Edward.prototype._loadOptions = async function() {
    const url = this._PREFIX + '/options.json';

    if (this._Options)
        return this._Options;

    const data = await load.json(url);

    this._Options = data;

    return data;
};

Edward.prototype._patchHttp = function(path, patch) {
    const onSave = this._onSave.bind(this);
    restafary.patch(path, patch, onSave);
};

Edward.prototype._writeHttp = function(path, result) {
    const onSave = this._onSave.bind(this);
    restafary.write(path, result, onSave);
};

Edward.prototype._onSave = require('./_on-save');

Edward.prototype._doDiff = async function(path) {
    const value = this.getValue();
    const patch = this._diff(value);
    const equal = await this._story.checkHash(path);

    return equal ? patch : '';
};

Edward.prototype._diff = function(newValue) {
    this._value = this._story.getData(this._filename) || this._value;
    return createPatch(this._value, newValue);
};

Edward.prototype._setEmmet = _setEmmet;

Edward.prototype._addExt = async function(name, fn) {
    if (this._Ext)
        return add(null, this._Ext);

    const [error, data] = await tryToCatch(load.json, this._PREFIX + '/json/ext.json');
    this._Ext = data;

    add(error, this._Ext);

    function add(error, exts) {
        if (error)
            return console.error(Error('Could not load ext.json!'));

        Object.keys(exts).some((ext) => {
            const arr = exts[ext];
            const is = arr.includes(name);

            if (is)
                name += '.' + ext;

            return is;
        });

        fn(name);
    }
};

/**
 * In Mac OS Chrome dropEffect = 'none'
 * so drop do not firing up when try
 * to upload file from download bar
 */
Edward.prototype._onDragOver = function(event) {
    const {dataTransfer} = event;
    const {effectAllowed} = dataTransfer;

    if (/move|linkMove/.test(effectAllowed))
        dataTransfer.dropEffect = 'move';
    else
        dataTransfer.dropEffect = 'copy';

    event.preventDefault();
};

Edward.prototype._onDrop = function(event) {
    const edward = this;
    const onLoad = ({target}) => {
        const {result} = target;

        edward.setValue(result);
    };

    event.preventDefault();

    const files = Array.from(event.dataTransfer.files);

    for (const file of files) {
        const reader = new FileReader();
        reader.addEventListener('load', onLoad);
        reader.readAsBinaryString(file);
    }
};

Edward.prototype._loadFiles = function(callback) {
    const PREFIX = this._PREFIX;
    const DIR = this._DIR;

    exec.series([
        async function(callback) {
            const obj = {
                join        : '/join/join.js',
            };

            const scripts = Object.keys(obj)
                .filter((name) => {
                    return !window[name];
                })
                .map((name) => {
                    return PREFIX + obj[name];
                });

            if (scripts.lengths)
                return callback();

            await load.parallel(scripts);
            callback();
        },

        async function(callback) {
            await loadremote('ace', {prefix: PREFIX});
            callback();
        },

        async function(callback) {
            const ace = DIR + 'ace-builds/src-min/';
            const url = PREFIX + join([
                'language_tools',
                'searchbox',
                'modelist',
            ].map((name) => {
                return 'ext-' + name;
            }).map((name) => {
                return ace + name + '.js';
            }));

            await load.js(url);
            callback();
        },

        function() {
            restafary.prefix(PREFIX + '/api/v1/fs');
            callback();
        },
    ]);
};

<commit_msg>fix(edward) jssha.default -> jssha<commit_after>/* global ace */
/* global join */

'use strict';

require('../css/edward.css');

const {createPatch} = require('daffy');
const exec = require('execon');
const Emitify = require('emitify');
const load = require('load.js');
const loadremote = require('./loadremote');
const wraptile = require('wraptile');
const smalltalk = require('smalltalk');
const jssha = require('jssha');
const restafary = require('restafary/client');
const tryToCatch = require('try-to-catch');

window.load = window.load || load;
window.exec = window.exec || exec;

const Story = require('./story');
const _clipboard = require('./_clipboard');
const _setEmmet = require('./_set-emmet');
const _initSocket = require('./_init-socket');
const setKeyMap = require('./set-key-map');
const showMessage = require('./show-message');
const save = require('./save');

function empty() {}

module.exports = Edward;

function Edward(el, options, callback) {
    if (!(this instanceof Edward))
        return new Edward(el, options, callback);

    this._Ace;
    this._Emmet;
    this._value;

    this._Config = {
        options: {},
    };

    this._Options;
    this._filename = '';
    this._Modelist;
    this._ElementMsg;
    this._Ext;
    this._title = 'Edward';
    this._DIR = '/modules/';
    this._story = Story();
    this._Emitter;
    this._isKey = true;

    if (!callback)
        callback = options;

    if (typeof el === 'string')
        el = document.querySelector(el);

    this._maxSize = options._maxSize || 512_000;
    this._PREFIX = options.prefix || '/edward';
    this._prefixSocket = options.prefixSocket || '/edward';
    this._SOCKET_PATH = options.socketPath || '';

    this._Element = el || document.body;

    const onDrop = this._onDrop.bind(this);
    const onDragOver = this._onDragOver.bind(this);

    this._Element.addEventListener('drop', onDrop);
    this._Element.addEventListener('dragover', onDragOver);

    this._init(() => {
        callback(this);
    });

    this._patch = function(path, patch) {
        this._patchHttp(path, patch);
    };

    this._write = function(path, result) {
        this._writeHttp(path, result);
    };
}

Edward.prototype.showMessage = showMessage;

Edward.prototype.isKey = function() {
    return this._isKey;
};

Edward.prototype.disableKey = function() {
    this._isKey = false;
};

Edward.prototype.enableKey = function() {
    this._isKey = true;
    return this;
};

Edward.prototype._init = function(fn) {
    const edward = this;
    const loadFiles = this._loadFiles.bind(this);
    const initSocket = _initSocket.bind(this);

    exec.series([
        loadFiles,
        async (callback) => {
            await loadremote('socket', {
                name : 'io',
                prefix: this._SOCKET_PATH,
            });

            initSocket();
            callback();
        },
        async () => {
            this._Emitter = Emitify();
            this._Ace = ace.edit(this._Element);
            this._Modelist = ace.require('ace/ext/modelist');

            this._Emitter.on('auth', (username, password) => {
                this._socket.emit('auth', username, password);
            });

            ace.require('ace/ext/language_tools');

            this._addCommands();
            this._Ace.$blockScrolling = Infinity;

            const config = await load.json(this._PREFIX + '/edit.json');

            const {
                options = {},
            } = config;

            const preventOverwrite = () => {
                for (const name of Object.keys(this._Config.options)) {
                    options[name] = this._Config.options[name];
                }
            };

            fn();
            preventOverwrite();

            this._Config = config;

            edward.setOptions(options);
        },
    ]);
};

Edward.prototype._addCommands = function() {
    const edward = this;
    const addKey = this._addKey.bind(this);

    const call = (fn) => fn.call(this);
    const wrapCall = wraptile(call);

    const callIfKey = wraptile((fn) => {
        edward.isKey() && call(fn);
    });

    const commands = [{
        name    : 'goToLine',
        bindKey : {win: 'Ctrl-G', mac: 'Command-G'},
        exec    : wrapCall(edward.goToLine),
    }, {
        name    : 'save',
        bindKey : {win: 'Ctrl-S', mac: 'Command-S'},
        exec    : callIfKey(edward.save),
    }, {
        name    : 'saveMC',
        bindKey : {win: 'F2', mac: 'F2'},
        exec    : callIfKey(edward.save),
    }, {
        name    : 'evaluate',
        bindKey : {win: 'Ctrl-E', mac: 'Command-E'},
        exec    : callIfKey(edward.evaluate),
    }];

    commands.forEach(addKey);
};

Edward.prototype.evaluate = function() {
    const edward = this;
    const focus = edward.focus.bind(this);
    const isJS = /\.js$/.test(this._filename);

    if (!isJS)
        return smalltalk.alert(this._title, 'Evaluation supported for JavaScript only')
            .then(focus);

    const value = edward.getValue();
    const msg = exec.try(Function(value));

    msg && smalltalk.alert(this._title, msg)
        .then(focus);
};

Edward.prototype._addKey = function(options) {
    this._Ace.commands.addCommand(options);
};

Edward.prototype.addKeyMap = function(keyMap) {
    if (typeof keyMap !== 'object')
        throw Error('map should be object!');

    const map = Object.keys(keyMap).map((name, i) => {
        const key = {
            name: String(Math.random()) + i,
            bindKey : {
                win : name,
                mac : name.replace('Ctrl', 'Command'),
            },
            exec: keyMap[name],
        };

        return key;
    });

    map.forEach(this._addKey());

    return this;
};

Edward.prototype.goToLine = function() {
    const msg = 'Enter line number:';
    const cursor = this.getCursor();
    const number = cursor.row + 1;

    const goToLine = (line) => {
        this._Ace.gotoLine(line);
    };

    const focus = () => {
        this._Ace.focus();
    };

    smalltalk.prompt(this._title, msg, number)
        .then(goToLine)
        .catch(empty)
        .then(focus);

    return this;
};

Edward.prototype.moveCursorTo = function(row, column) {
    this._Ace.moveCursorTo(row, column);
    return this;
};

Edward.prototype.refresh = function() {
    this._Ace.resize();
    return this;
};

Edward.prototype.focus = function() {
    this._Ace.focus();
    return this;
};

Edward.prototype.remove = function(direction) {
    this._Ace.remove(direction);
    return this;
};

Edward.prototype.getCursor = function() {
    return this._Ace.selection.getCursor();
};

Edward.prototype.getValue = function() {
    return this._Ace.getValue();
};

Edward.prototype.on = function(event, fn) {
    this._Emitter.on(event, fn);
    return this;
};

Edward.prototype.once = function(event, fn) {
    this._Emitter.once(event, fn);
    return this;
};

Edward.prototype.emit = function(...args) {
    this._Emitter.emit(...args);
    return this;
};

Edward.prototype.isChanged = function() {
    const value = this.getValue();
    const isEqual = value === this._value;

    return !isEqual;
};

Edward.prototype.setValue = function(value) {
    const session = this._getSession();

    session.setScrollTop(0);

    this._Ace.setValue(value);
    this._Ace.clearSelection();

    this._Emitter.emit('change');

    return this;
};

Edward.prototype.setValueFirst = function(name, value) {
    const session = this._getSession();
    const {UndoManager} = ace.require('ace/undomanager');

    this._filename = name;
    this._value = value;

    this.setValue(value);

    session.setUndoManager(new UndoManager());

    return this;
};

Edward.prototype.setOption = function(name, value) {
    const preventOverwrite = () => {
        this._Config.options[name] = value;
    };

    preventOverwrite();

    if (name === 'keyMap') {
        this.setKeyMap(value);

        return this;
    }

    this._Ace.setOption(name, value);

    return this;
};

Edward.prototype.setOptions = function(options) {
    for (const name of Object.keys(options)) {
        this.setOption(name, options[name]);
    }

    return this;
};

Edward.prototype.setKeyMap = setKeyMap;
Edward.prototype._setUseOfWorker = function(mode) {
    const session = this._getSession();
    const isStr = typeof mode === 'string';
    const regStr = 'coffee|css|html|json|lua|php|xquery';
    const regExp = new RegExp(regStr);

    let isMatch;

    if (isStr)
        isMatch = regExp.test(mode);

    session.setUseWorker(isMatch);

    return this;
};

Edward.prototype.setMode = function(mode) {
    const {modesByName} = this._Modelist;

    if (!modesByName[mode])
        return this;

    const [ext] = modesByName[mode].extensions.split('|');
    this.setModeForPath('.' + ext);

    return this;
};

Edward.prototype.setModeForPath = function(path) {
    const session = this._getSession();
    const {modesByName} = this._Modelist;
    const name = path.split('/').pop();

    this._addExt(name, (name) => {
        const {mode} = this._Modelist.getModeForPath(name);
        const htmlMode = modesByName.html.mode;

        const isHTML = mode === htmlMode;

        session.setMode(mode, () => {
            this._setUseOfWorker(mode);

            if (isHTML)
                this._setEmmet();
        });
    });

    return this;
};

Edward.prototype.selectAll = function() {
    this._Ace.selectAll();
    return this;
};

Edward.prototype.copyToClipboard = function() {
    const msg = 'Could not copy, use &ltCtrl&gt + &ltС&gt insted!';

    this._clipboard('copy')
        .catch(wraptile(smalltalk.alert, this._title, msg));

    return this;
};

Edward.prototype.cutToClipboard = function() {
    const msg = 'Could not cut, use &ltCtrl&gt + &ltX&gt insted!';
    const remove = this.remove.bind(this);

    this._clipboard('cut')
        .then(wraptile(remove, 'right'))
        .catch(wraptile(smalltalk.alert, this._title, msg));

    return this;
};

Edward.prototype.pasteFromClipboard = function() {
    const msg = 'Could not paste, use &ltCtrl&gt + &ltV&gt insted!';

    this._clipboard('paste')
        .catch(wraptile(smalltalk.alert, this._title, msg));

    return this;
};

Edward.prototype._clipboard = _clipboard;

Edward.prototype._getSession = function() {
    return this._Ace.getSession();
};

Edward.prototype.sha = function() {
    const value = this.getValue();
    const shaObj = new jssha('SHA-1', 'TEXT');
    shaObj.update(value);

    return shaObj.getHash('HEX');
};

Edward.prototype.save = save;

Edward.prototype._loadOptions = async function() {
    const url = this._PREFIX + '/options.json';

    if (this._Options)
        return this._Options;

    const data = await load.json(url);

    this._Options = data;

    return data;
};

Edward.prototype._patchHttp = function(path, patch) {
    const onSave = this._onSave.bind(this);
    restafary.patch(path, patch, onSave);
};

Edward.prototype._writeHttp = function(path, result) {
    const onSave = this._onSave.bind(this);
    restafary.write(path, result, onSave);
};

Edward.prototype._onSave = require('./_on-save');

Edward.prototype._doDiff = async function(path) {
    const value = this.getValue();
    const patch = this._diff(value);
    const equal = await this._story.checkHash(path);

    return equal ? patch : '';
};

Edward.prototype._diff = function(newValue) {
    this._value = this._story.getData(this._filename) || this._value;
    return createPatch(this._value, newValue);
};

Edward.prototype._setEmmet = _setEmmet;

Edward.prototype._addExt = async function(name, fn) {
    if (this._Ext)
        return add(null, this._Ext);

    const [error, data] = await tryToCatch(load.json, this._PREFIX + '/json/ext.json');
    this._Ext = data;

    add(error, this._Ext);

    function add(error, exts) {
        if (error)
            return console.error(Error('Could not load ext.json!'));

        Object.keys(exts).some((ext) => {
            const arr = exts[ext];
            const is = arr.includes(name);

            if (is)
                name += '.' + ext;

            return is;
        });

        fn(name);
    }
};

/**
 * In Mac OS Chrome dropEffect = 'none'
 * so drop do not firing up when try
 * to upload file from download bar
 */
Edward.prototype._onDragOver = function(event) {
    const {dataTransfer} = event;
    const {effectAllowed} = dataTransfer;

    if (/move|linkMove/.test(effectAllowed))
        dataTransfer.dropEffect = 'move';
    else
        dataTransfer.dropEffect = 'copy';

    event.preventDefault();
};

Edward.prototype._onDrop = function(event) {
    const edward = this;
    const onLoad = ({target}) => {
        const {result} = target;

        edward.setValue(result);
    };

    event.preventDefault();

    const files = Array.from(event.dataTransfer.files);

    for (const file of files) {
        const reader = new FileReader();
        reader.addEventListener('load', onLoad);
        reader.readAsBinaryString(file);
    }
};

Edward.prototype._loadFiles = function(callback) {
    const PREFIX = this._PREFIX;
    const DIR = this._DIR;

    exec.series([
        async function(callback) {
            const obj = {
                join        : '/join/join.js',
            };

            const scripts = Object.keys(obj)
                .filter((name) => {
                    return !window[name];
                })
                .map((name) => {
                    return PREFIX + obj[name];
                });

            if (scripts.lengths)
                return callback();

            await load.parallel(scripts);
            callback();
        },

        async function(callback) {
            await loadremote('ace', {prefix: PREFIX});
            callback();
        },

        async function(callback) {
            const ace = DIR + 'ace-builds/src-min/';
            const url = PREFIX + join([
                'language_tools',
                'searchbox',
                'modelist',
            ].map((name) => {
                return 'ext-' + name;
            }).map((name) => {
                return ace + name + '.js';
            }));

            await load.js(url);
            callback();
        },

        function() {
            restafary.prefix(PREFIX + '/api/v1/fs');
            callback();
        },
    ]);
};

<|endoftext|>