<commit_before>
var net = require('net');
var util = require('util');
var events = require('events');
var async = require('async');

var mdns = require('mdns');
var isme = require('isme');
var gmid = require('gmid');

function smallerHex(hexA, hexB) {
  for (var i = 0, l = hexA.length; i < l; i++) {
    if (parseInt(hexA[i], 16) > parseInt(hexB[i], 16)) {
      return false;
    }
  }

  return true;
}

function Service(serviceName, connectionHandler) {
  if (!(this instanceof Service)) {
    return new Service(serviceName, connectionHandler);
  }

  // Collection of online sockets
  this.connections = [];

  // Add connection handler if given
  if (connectionHandler) this.on('connection', connectionHandler);

  // UUID: we need a string to send and a number to compare
  this._uuidString = gmid();

  // Collection of services
  this._services = [];
  this._serviceBuffer = [];

  // Keeps a key and port
  this._key = mdns.tcp(serviceName);
  this._address = {
    addresses: null,
    port: null
  };

  // Keeps main objects, unfortunately the announce object
  // can't be created before the service server is online
  this._server = net.createServer();
  this._discover = mdns.createBrowser(this._key);
  this._announce = null;
}
util.inherits(Service, events.EventEmitter);
module.exports = Service;

Service.prototype.listen = function () {
  var self = this;
  var args = Array.prototype.slice.call(arguments);

  var callback, port, address;

  // Extract callback
  if (typeof args[args.length - 1] === 'function') {
    callback = args.pop();
  }

  // Extract port
  port = args.pop() || 0;

  // Extract address
  address = args.pop() || "0.0.0.0";

  // Start service server
  this._server.listen(port, address);

  // Server is online
  this._server.once('listening', function () {
    port = self._address.port = self._server.address().port;

    // Start announceing and discovering
    self._announce = mdns.createAdvertisement(
      self._key, port, {
        // workaround for some wird bug
        // ref: https://github.com/agnat/node_mdns/issues/51
        name: self._uuidString
      }
    );

    self._announce.start();
    self._discover.start();

    // Listening event will be emited once an
    // self announcement has been made, see
    // serviceUp handler below
  });

  // Got connection start handshake
  this._server.on('connection', function (socket) {

    // The first data chunk should be a JSON string, containing information
    // about the remote service.
    socket.once('data', function (chunk) {
      var remote = JSON.parse(chunk.toString());

      // Done, not ready to claim this a connection
      self._addSocket(socket, remote);
    });
  });

  // Used when this service is online but an announcement was made
  function offline(service) {

    // Use self announcement to catch broadcasted public ip
    if (self._selfAnnouncement(service)) {
      self._address.addresses = service.addresses;

      // Switch to online mode
      self._discover.removeListener('serviceUp', offline);
      self._discover.on('serviceUp', online);

      // Emit listening
      self.emit('listening');

      // Connect to buffered services
      self._serviceBuffer.forEach(self._addService.bind(self));
      self._serviceBuffer = [];

      return;
    }

    // When offline, then store service in buffer
    self._serviceBuffer.push(service);
  }
  this._discover.on('serviceUp', offline);

  // Used when this service is online
  function online(service) {

    // Skip self announcement
    if (self._selfAnnouncement(service)) return;

    self._addService(service);
  }
};

Service.prototype._addService = function (service) {
  var self = this;

  // Get IPv4 addresses
  // TODO: look intro IPv6 support
  var addresses = service.addresses.filter(net.isIPv4.bind(net));

  // Create remote connection object
  var remote = {
    port: service.port,
    addresses: service.addresses
  };

  // Prevent double connection
  this._services.push(remote);

  // The service worker with the highest number gets the pleasure of
  // initiating the TPC connection.
  if (smallerHex(this._uuidString, service.name)) return;

  // Don't allow multiply connection to same service worker
  if (this._existConnection(remote)) return;

  // Connect to remote and start handshake
  var socket = net.connect({ port: service.port, host: addresses[0] });
  socket.once('connect', function () {

    // Inform remote about the service it just connected to
    socket.write(JSON.stringify(self._address), function () {

      // Done, not ready to claim this a connection
      self._addSocket(socket, remote);
    });
  });
};

// Remove remote object from services list
Service.prototype._removeService = function (remote) {
    var index = this._services.indexOf(remote);
    if (index === -1) return;

    this._services.splice(index, 1);
};

Service.prototype._addSocket = function (socket, remote) {
  var self = this;

  // Add remote info to socket object
  socket.remote = remote;

  // Add socket to connections list now
  this.connections.push(socket);

  // Remove socket from connection list once closed
  socket.once('close', function () {
    self._removeService(remote);
    self._removeSocket(socket);
  });

  // Done, emit connection event
  this.emit('connection', socket);
};

// Remove socket from connections list
Service.prototype._removeSocket = function (socket) {
    var index = this.connections.indexOf(socket);
    if (index === -1) return;

    this.connections.splice(index, 1);
};

Service.prototype._selfAnnouncement = function (service) {
  return (service.addresses.some(isme) && service.port == this._address.port);
};

Service.prototype._existConnection = function (remote) {
  var connections = this.connections;
  var i = connections.length;

  // If the remote object was found in connections list, then it exist
  while (i--) {
    var serviceInfo = this._services[i];

    if (matchArray(serviceInfo.addresses, remote.addresses) &&
        serviceInfo.port == remote.port) {
      return true;
    }
  }

  // No matching remote object was found
  return false;
};

function matchArray(arr1, arr2) {
  if (arr1.length !== arr2.length) return false;

  var i = arr1.length;
  while (i--) {
    if (arr1[i] !== arr2[i]) return false;
  }
}

Service.prototype.address = function () {
  // addresses is the last property there will be set
  if (this._address.addresses === null) {
    return null;
  }

  return this._server.address();
};

Service.prototype.close = function (callback) {
  var self = this;

  // reset address info
  this._address = {
    port: null,
    addresses: null
  };

  // Callback is just a close event handler
  if (callback) this.once('close', callback),

  // Stop announceing and discovering
  this._announce.stop();
  this._discover.stop();

  async.parallel([

    // Close all sockets
    function (done) {
      async.forEach(self.connections.slice(0), function(socket, eachDone) {
        socket.end();
        socket.once('close', function () { eachDone(null); });
      }, done);
    },

    // Close connection server, note the server.close callback
    // won't be executed before all connected sockets are closed.
    function (done) {
      self._server.close(done);
    }
  ], function () {
    self.emit('close');
  });
};
<commit_msg>[fix] an issue where the comparing digits wasn't at the same position<commit_after>
var net = require('net');
var util = require('util');
var events = require('events');
var async = require('async');

var mdns = require('mdns');
var isme = require('isme');
var gmid = require('gmid');

function compareHex(hexA, hexB) {
  for (var i = 0, l = hexA.length; i < l; i++) {
    // if hex digits are the same nothing can be concluded
    if (hexA[i] === hexB[i]) continue;

    // At this point the hex digits arn't the same, compare the size
    // and return true if hexA was greater than hexB
    return (parseInt(hexA[i], 16) > parseInt(hexB[i], 16));
  }

  // If they where totally equal, something went wrong and
  // it should be ignored, thats a `return true`.
  return true;
}

function Service(serviceName, connectionHandler) {
  if (!(this instanceof Service)) {
    return new Service(serviceName, connectionHandler);
  }

  // Collection of online sockets
  this.connections = [];

  // Add connection handler if given
  if (connectionHandler) this.on('connection', connectionHandler);

  // UUID: we need a string to send and a number to compare
  this._uuidString = gmid();

  // Collection of services
  this._services = [];
  this._serviceBuffer = [];

  // Keeps a key and port
  this._key = mdns.tcp(serviceName);
  this._address = {
    addresses: null,
    port: null
  };

  // Keeps main objects, unfortunately the announce object
  // can't be created before the service server is online
  this._server = net.createServer();
  this._discover = mdns.createBrowser(this._key);
  this._announce = null;
}
util.inherits(Service, events.EventEmitter);
module.exports = Service;

Service.prototype.listen = function () {
  var self = this;
  var args = Array.prototype.slice.call(arguments);

  var callback, port, address;

  // Extract callback
  if (typeof args[args.length - 1] === 'function') {
    callback = args.pop();
  }

  // Extract port
  port = args.pop() || 0;

  // Extract address
  address = args.pop() || "0.0.0.0";

  // Start service server
  this._server.listen(port, address);

  // Server is online
  this._server.once('listening', function () {
    port = self._address.port = self._server.address().port;

    // Start announceing and discovering
    self._announce = mdns.createAdvertisement(
      self._key, port, {
        // workaround for some wird bug
        // ref: https://github.com/agnat/node_mdns/issues/51
        name: self._uuidString
      }
    );

    self._announce.start();
    self._discover.start();

    // Listening event will be emited once an
    // self announcement has been made, see
    // serviceUp handler below
  });

  // Got connection start handshake
  this._server.on('connection', function (socket) {

    // The first data chunk should be a JSON string, containing information
    // about the remote service.
    socket.once('data', function (chunk) {
      var remote = JSON.parse(chunk.toString());

      // Done, not ready to claim this a connection
      self._addSocket(socket, remote);
    });
  });

  // Used when this service is online but an announcement was made
  function offline(service) {

    // Use self announcement to catch broadcasted public ip
    if (self._selfAnnouncement(service)) {
      self._address.addresses = service.addresses;

      // Switch to online mode
      self._discover.removeListener('serviceUp', offline);
      self._discover.on('serviceUp', online);

      // Emit listening
      self.emit('listening');

      // Connect to buffered services
      self._serviceBuffer.forEach(self._addService.bind(self));
      self._serviceBuffer = [];

      return;
    }

    // When offline, then store service in buffer
    self._serviceBuffer.push(service);
  }
  this._discover.on('serviceUp', offline);

  // Used when this service is online
  function online(service) {

    // Skip self announcement
    if (self._selfAnnouncement(service)) return;

    self._addService(service);
  }
};

Service.prototype._addService = function (service) {
  var self = this;

  // Get IPv4 addresses
  // TODO: look intro IPv6 support
  var addresses = service.addresses.filter(net.isIPv4.bind(net));

  // Create remote connection object
  var remote = {
    port: service.port,
    addresses: service.addresses
  };

  // Prevent double connection
  this._services.push(remote);

  // The service worker with the highest number gets the pleasure of
  // initiating the TPC connection.
  if (compareHex(this._uuidString, service.name)) return;

  // Don't allow multiply connection to same service worker
  if (this._existConnection(remote)) return;

  // Connect to remote and start handshake
  var socket = net.connect({ port: service.port, host: addresses[0] });
  socket.once('connect', function () {

    // Inform remote about the service it just connected to
    socket.write(JSON.stringify(self._address), function () {

      // Done, not ready to claim this a connection
      self._addSocket(socket, remote);
    });
  });
};

// Remove remote object from services list
Service.prototype._removeService = function (remote) {
    var index = this._services.indexOf(remote);
    if (index === -1) return;

    this._services.splice(index, 1);
};

Service.prototype._addSocket = function (socket, remote) {
  var self = this;

  // Add remote info to socket object
  socket.remote = remote;

  // Add socket to connections list now
  this.connections.push(socket);

  // Remove socket from connection list once closed
  socket.once('close', function () {
    self._removeService(remote);
    self._removeSocket(socket);
  });

  // Done, emit connection event
  this.emit('connection', socket);
};

// Remove socket from connections list
Service.prototype._removeSocket = function (socket) {
    var index = this.connections.indexOf(socket);
    if (index === -1) return;

    this.connections.splice(index, 1);
};

Service.prototype._selfAnnouncement = function (service) {
  return (service.addresses.some(isme) &&
          service.port == this._address.port &&
          service.name === this._uuidString);
};

Service.prototype._existConnection = function (remote) {
  var connections = this.connections;
  var i = connections.length;

  // If the remote object was found in connections list, then it exist
  while (i--) {
    var serviceInfo = this._services[i];

    if (matchArray(serviceInfo.addresses, remote.addresses) &&
        serviceInfo.port == remote.port) {
      return true;
    }
  }

  // No matching remote object was found
  return false;
};

function matchArray(arr1, arr2) {
  if (arr1.length !== arr2.length) return false;

  var i = arr1.length;
  while (i--) {
    if (arr1[i] !== arr2[i]) return false;
  }
}

Service.prototype.address = function () {
  // addresses is the last property there will be set
  if (this._address.addresses === null) {
    return null;
  }

  return this._address;
};

Service.prototype.close = function (callback) {
  var self = this;

  // reset address info
  this._address = {
    port: null,
    addresses: null
  };

  // Callback is just a close event handler
  if (callback) this.once('close', callback),

  // Stop announceing and discovering
  this._announce.stop();
  this._discover.stop();

  async.parallel([

    // Close all sockets
    function (done) {
      async.forEach(self.connections.slice(0), function(socket, eachDone) {
        socket.end();
        socket.once('close', function () { eachDone(null); });
      }, done);
    },

    // Close connection server, note the server.close callback
    // won't be executed before all connected sockets are closed.
    function (done) {
      self._server.close(done);
    }
  ], function () {
    self.emit('close');
  });
};
<|endoftext|>


<commit_before>// http://mochajs.org/
// @see http://chaijs.com/api/bdd/
// @see https://github.com/domenic/sinon-chai
describe('Router', function() {
  var App;
  var Router;
  var testRouter;

  beforeEach(function() {
    App = window.App;
    Router = App.Router;
  });

  afterEach(function() {
    App = null;
    Router = null;

    testRouter = null;
  });

  it('should call handler once', function() {
    testRouter = new Router();
    var spyHandler = sinon.spy();

    testRouter.all('/', spyHandler);
    testRouter.route('/');

    expect(spyHandler).to.have.been.calledOnce;
  });
});
<commit_msg>Добавил тест для метода _init<commit_after>// http://mochajs.org/
// @see http://chaijs.com/api/bdd/
// @see https://github.com/domenic/sinon-chai
describe('Router', function() {
  var App;
  var Router;
  var testRouter;

  beforeEach(function() {
    App = window.App;
    Router = App.Router;
  });

  afterEach(function() {
    App = null;
    Router = null;

    testRouter = null;
  });

  it('should call handler once', function() {
    testRouter = new Router();
    var spyHandler = sinon.spy();

    testRouter.all('/', spyHandler);
    testRouter.route('/');

    expect(spyHandler).to.have.been.calledOnce;
  });

  it('should call event on popstate', function () {
    testRouter = new Router();
    var spyHandler = sinon.spy();

    testRouter.all('/', spyHandler);
    testRouter.route('/');
    testRouter._init();

    expect(spyHandler).to(_init.called).to.equal(true);
  })
});
<|endoftext|>


<commit_before>// -------------------------------------------------------------------------- \\
// File: PopOverView.js                                                       \\
// Module: PanelViews                                                         \\
// Requires: Core, Foundation, DOM, View                                      \\
// Author: Neil Jenkins                                                       \\
// License: © 2010–2013 Opera Software ASA. All rights reserved.              \\
// -------------------------------------------------------------------------- \\

"use strict";

( function ( NS ) {

var PopOverView = NS.Class({

    Extends: NS.View,

    className: 'PopOverView',

    positioning: 'absolute',

    isVisible: false,
    parentPopOverView: null,

    /*
        Options
        - view -> The view to append to the pop over
        - alignWithView -> the view to align to
        - atNode -> the node within the view to align to
        - positionToThe -> 'bottom'/'top'/'left'/'right'
        - alignEdge -> 'left'/'centre'/'right'/'top'/'middle'/'bottom'
        - inParent -> The view to insert the pop over in (optional)
        - showCallout -> true/false
        - offsetLeft
        - offsetTop
        - onHide: fn
    */
    show: function ( options ) {
        if ( options.alignWithView === this ) {
            return this.get( 'subPopOverView' ).show( options );
        }
        this.hide();

        this._options = options;

        // Set layout and insert in the right place
        var eventHandler = this.get( 'eventHandler' ),
            view = options.view,
            alignWithView = options.alignWithView,
            atNode = options.atNode || alignWithView.get( 'layer' ),
            atNodeWidth = atNode.offsetWidth,
            atNodeHeight = atNode.offsetHeight,
            positionToThe = options.positionToThe || 'bottom',
            alignEdge = options.alignEdge || 'left',
            parent = options.inParent,
            deltaLeft = 0,
            deltaTop = 0,
            calloutDelta = 0,
            calloutIsAtTopOrBottom =
                ( positionToThe === 'top' || positionToThe === 'bottom' ),
            callout, layout, position, gap, layer,
            Element = NS.Element,
            el = Element.create,
            getPosition = Element.getPosition;

        // Want nearest parent scroll view (or root view if none).
        // Special case parent == parent pop-over view.
        if ( !parent ) {
            parent = options.atNode ?
                alignWithView : alignWithView.get( 'parentView' );
            while ( !( parent instanceof NS.RootView ) &&
                    !( parent instanceof NS.ScrollView ) &&
                    !( parent instanceof PopOverView ) ) {
                parent = parent.get( 'parentView' );
            }
        }

        // Now find out our offsets;
        layout = getPosition( atNode, parent.get( 'layer' ) );

        switch ( positionToThe ) {
        case 'right':
            layout.left += atNodeWidth;
            /* falls through */
        case 'left':
            switch ( alignEdge ) {
            // case 'top':
            //    break; // nothing to do
            case 'middle':
                atNodeHeight = atNodeHeight >> 1;
                /* falls through */
            case 'bottom':
                layout.top += atNodeHeight;
                break;
            }
            break;
        case 'bottom':
            layout.top += atNodeHeight;
            /* falls through */
        case 'top':
            switch ( alignEdge ) {
            // case 'left':
            //     break; // nothing to do
            case 'centre':
                atNodeWidth = atNodeWidth >> 1;
                /* falls through */
            case 'right':
                layout.left += atNodeWidth;
                break;
            }
            break;
        }

        layout.top += options.offsetTop || 0;
        layout.left += options.offsetLeft || 0;
        layout.zIndex = 1000;

        // Set layout
        this.set( 'layout', layout );

        // Insert view
        this.insertView( view );
        this.render();

        // Callout
        layer = this.get( 'layer' );
        if ( options.showCallout ) {
            layer.appendChild(
                el( 'b', {
                    className: 'callout ' +
                        positionToThe.charAt( 0 ) + ' ' + alignEdge
                }, [
                    callout = el( 'b', {
                        className: 'callout-inner ' + positionToThe.charAt( 0 )
                    })
                ])
            );
        }

        // Insert into parent.
        parent.insertView( this );

        // Adjust positioning
        switch ( positionToThe ) {
        case 'left':
            deltaLeft -= layer.offsetWidth;
            /* falls through */
        case 'right':
            switch ( alignEdge ) {
            // case 'top':
            //    break; // nothing to do
            case 'middle':
                deltaTop -= layer.offsetHeight >> 1;
                break;
            case 'bottom':
                deltaTop -= layer.offsetHeight;
                break;
            }
            break;
        case 'top':
            deltaTop -= layer.offsetHeight;
            /* falls through */
        case 'bottom':
            switch ( alignEdge ) {
            // case 'left':
            //     break; // nothing to do
            case 'centre':
                deltaLeft -= layer.offsetWidth >> 1;
                break;
            case 'right':
                deltaLeft -= layer.offsetWidth;
                break;
            }
            break;
        }

        // Check not run off screen.
        if ( parent instanceof NS.RootView ) {
            position = getPosition( layer );
            // Check left edge
            gap = position.left + deltaLeft;
            if ( gap < 0 ) {
                deltaLeft -= gap;
                deltaLeft += 10;
                if ( callout && calloutIsAtTopOrBottom ) {
                    calloutDelta -= gap;
                    calloutDelta += 10;
                }
            }
            // Check right edge
            gap = parent.get( 'pxWidth' ) - gap - layer.offsetWidth;
            // If gap is negative, move the view.
            if ( gap < 0 ) {
                deltaLeft += gap;
                deltaLeft -= 10;
                if ( callout && calloutIsAtTopOrBottom ) {
                    calloutDelta += gap;
                    calloutDelta -= 10;
                }
            }
            // Check top edge
            gap = position.top + deltaTop;
            if ( gap < 0 ) {
                deltaTop -= gap;
                deltaTop += 10;
                if ( callout && !calloutIsAtTopOrBottom ) {
                    calloutDelta -= gap;
                    calloutDelta += 10;
                }
            }
            // Check bottom edge
            gap = parent.get( 'pxHeight' )  - gap - layer.offsetHeight;
            if ( gap < 0 ) {
                deltaTop += gap;
                deltaTop -= 10;
                if ( callout && !calloutIsAtTopOrBottom ) {
                    calloutDelta += gap;
                    calloutDelta -= 10;
                }
            }
        }

        if ( deltaLeft || deltaTop ) {
            layout.left += deltaLeft;
            layout.top += deltaTop;
            this.propertyDidChange( 'layout' );
        }
        if ( calloutDelta ) {
            Element.setStyle( callout,
                calloutIsAtTopOrBottom ? 'left' : 'top',
                -calloutDelta + 'px'
            );
        }

        if ( eventHandler ) {
            NS.ViewEventsController.addEventTarget( eventHandler, 10 );
        }
        this.set( 'isVisible', true );

        return this;
    },

    hide: function () {
        if ( this.get( 'isVisible' ) ) {
            var subPopOverView = this.hasSubView() ?
                    this.get( 'subPopOverView' ) : null,
                eventHandler = this.get( 'eventHandler' ),
                options = this._options,
                onHide, view, layer;
            if ( subPopOverView ) {
                subPopOverView.hide();
            }
            this.set( 'isVisible', false );
            view = options.view;
            this.get( 'parentView' ).removeView( this );
            this.removeView( view );
            if ( options.showCallout ) {
                layer = this.get( 'layer' );
                layer.removeChild( layer.firstChild );
            }
            if ( eventHandler ) {
                NS.ViewEventsController.removeEventTarget( eventHandler );
                eventHandler._seenMouseDown = false;
            }
            this._options = null;
            if ( onHide = options.onHide ) {
                onHide();
            }
        }
        return this;
    },

    hasSubView: function () {
        return !!NS.meta( this ).cache.subPopOverView &&
            this.get( 'subPopOverView' ).get( 'isVisible' );
    },

    subPopOverView: function () {
        return new NS.PopOverView({ parentPopOverView: this });
    }.property(),

    eventHandler: function () {
        return this.get( 'parentPopOverView' ) ?
            null : new NS.ModalEventHandler({ view: this });
    }.property(),

    clickedOutside: function () {
        this.hide();
    },

    keyOutside: function ( event ) {
        var view = this;
        while ( view.hasSubView() ) {
            view = view.get( 'subPopOverView' );
        }
        view._options.view.fire( event.type, event );
        if ( event.type === 'keydown' &&
                NS.DOMEvent.lookupKey( event ) === 'esc' ) {
            view.hide();
        }
    },

    stopEvents: function ( event ) {
        event.stopPropagation();
    }.on( 'click', 'mousedown', 'mouseup',
        'keypress', 'keydown', 'keyup', 'tap' )
});

NS.PopOverView = PopOverView;

}( this.O ) );
<commit_msg>Position PopOver view correctly in iOS.<commit_after>// -------------------------------------------------------------------------- \\
// File: PopOverView.js                                                       \\
// Module: PanelViews                                                         \\
// Requires: Core, Foundation, DOM, View                                      \\
// Author: Neil Jenkins                                                       \\
// License: © 2010–2013 Opera Software ASA. All rights reserved.              \\
// -------------------------------------------------------------------------- \\

"use strict";

( function ( NS ) {

var PopOverView = NS.Class({

    Extends: NS.View,

    className: 'PopOverView',

    positioning: 'absolute',

    isVisible: false,
    parentPopOverView: null,

    /*
        Options
        - view -> The view to append to the pop over
        - alignWithView -> the view to align to
        - atNode -> the node within the view to align to
        - positionToThe -> 'bottom'/'top'/'left'/'right'
        - alignEdge -> 'left'/'centre'/'right'/'top'/'middle'/'bottom'
        - inParent -> The view to insert the pop over in (optional)
        - showCallout -> true/false
        - offsetLeft
        - offsetTop
        - onHide: fn
    */
    show: function ( options ) {
        if ( options.alignWithView === this ) {
            return this.get( 'subPopOverView' ).show( options );
        }
        this.hide();

        this._options = options;

        // Set layout and insert in the right place
        var eventHandler = this.get( 'eventHandler' ),
            view = options.view,
            alignWithView = options.alignWithView,
            atNode = options.atNode || alignWithView.get( 'layer' ),
            atNodeWidth = atNode.offsetWidth,
            atNodeHeight = atNode.offsetHeight,
            positionToThe = options.positionToThe || 'bottom',
            alignEdge = options.alignEdge || 'left',
            parent = options.inParent,
            deltaLeft = 0,
            deltaTop = 0,
            calloutDelta = 0,
            calloutIsAtTopOrBottom =
                ( positionToThe === 'top' || positionToThe === 'bottom' ),
            callout, layout, position, gap, layer,
            Element = NS.Element,
            el = Element.create,
            getPosition = Element.getPosition;

        // Want nearest parent scroll view (or root view if none).
        // Special case parent == parent pop-over view.
        if ( !parent ) {
            parent = options.atNode ?
                alignWithView : alignWithView.get( 'parentView' );
            while ( !( parent instanceof NS.RootView ) &&
                    !( parent instanceof NS.ScrollView ) &&
                    !( parent instanceof PopOverView ) ) {
                parent = parent.get( 'parentView' );
            }
        }

        // Now find out our offsets;
        layout = getPosition( atNode, parent instanceof NS.ScrollView ?
            parent.get( 'scrollLayer' ) : parent.get( 'layer' ) );

        switch ( positionToThe ) {
        case 'right':
            layout.left += atNodeWidth;
            /* falls through */
        case 'left':
            switch ( alignEdge ) {
            // case 'top':
            //    break; // nothing to do
            case 'middle':
                atNodeHeight = atNodeHeight >> 1;
                /* falls through */
            case 'bottom':
                layout.top += atNodeHeight;
                break;
            }
            break;
        case 'bottom':
            layout.top += atNodeHeight;
            /* falls through */
        case 'top':
            switch ( alignEdge ) {
            // case 'left':
            //     break; // nothing to do
            case 'centre':
                atNodeWidth = atNodeWidth >> 1;
                /* falls through */
            case 'right':
                layout.left += atNodeWidth;
                break;
            }
            break;
        }

        layout.top += options.offsetTop || 0;
        layout.left += options.offsetLeft || 0;
        layout.zIndex = 1000;

        // Set layout
        this.set( 'layout', layout );

        // Insert view
        this.insertView( view );
        this.render();

        // Callout
        layer = this.get( 'layer' );
        if ( options.showCallout ) {
            layer.appendChild(
                el( 'b', {
                    className: 'callout ' +
                        positionToThe.charAt( 0 ) + ' ' + alignEdge
                }, [
                    callout = el( 'b', {
                        className: 'callout-inner ' + positionToThe.charAt( 0 )
                    })
                ])
            );
        }

        // Insert into parent.
        parent.insertView( this );

        // Adjust positioning
        switch ( positionToThe ) {
        case 'left':
            deltaLeft -= layer.offsetWidth;
            /* falls through */
        case 'right':
            switch ( alignEdge ) {
            // case 'top':
            //    break; // nothing to do
            case 'middle':
                deltaTop -= layer.offsetHeight >> 1;
                break;
            case 'bottom':
                deltaTop -= layer.offsetHeight;
                break;
            }
            break;
        case 'top':
            deltaTop -= layer.offsetHeight;
            /* falls through */
        case 'bottom':
            switch ( alignEdge ) {
            // case 'left':
            //     break; // nothing to do
            case 'centre':
                deltaLeft -= layer.offsetWidth >> 1;
                break;
            case 'right':
                deltaLeft -= layer.offsetWidth;
                break;
            }
            break;
        }

        // Check not run off screen.
        if ( parent instanceof NS.RootView ) {
            position = getPosition( layer );
            // Check left edge
            gap = position.left + deltaLeft;
            if ( gap < 0 ) {
                deltaLeft -= gap;
                deltaLeft += 10;
                if ( callout && calloutIsAtTopOrBottom ) {
                    calloutDelta -= gap;
                    calloutDelta += 10;
                }
            }
            // Check right edge
            gap = parent.get( 'pxWidth' ) - gap - layer.offsetWidth;
            // If gap is negative, move the view.
            if ( gap < 0 ) {
                deltaLeft += gap;
                deltaLeft -= 10;
                if ( callout && calloutIsAtTopOrBottom ) {
                    calloutDelta += gap;
                    calloutDelta -= 10;
                }
            }
            // Check top edge
            gap = position.top + deltaTop;
            if ( gap < 0 ) {
                deltaTop -= gap;
                deltaTop += 10;
                if ( callout && !calloutIsAtTopOrBottom ) {
                    calloutDelta -= gap;
                    calloutDelta += 10;
                }
            }
            // Check bottom edge
            gap = parent.get( 'pxHeight' )  - gap - layer.offsetHeight;
            if ( gap < 0 ) {
                deltaTop += gap;
                deltaTop -= 10;
                if ( callout && !calloutIsAtTopOrBottom ) {
                    calloutDelta += gap;
                    calloutDelta -= 10;
                }
            }
        }

        if ( deltaLeft || deltaTop ) {
            layout.left += deltaLeft;
            layout.top += deltaTop;
            this.propertyDidChange( 'layout' );
        }
        if ( calloutDelta ) {
            Element.setStyle( callout,
                calloutIsAtTopOrBottom ? 'left' : 'top',
                -calloutDelta + 'px'
            );
        }

        if ( eventHandler ) {
            NS.ViewEventsController.addEventTarget( eventHandler, 10 );
        }
        this.set( 'isVisible', true );

        return this;
    },

    hide: function () {
        if ( this.get( 'isVisible' ) ) {
            var subPopOverView = this.hasSubView() ?
                    this.get( 'subPopOverView' ) : null,
                eventHandler = this.get( 'eventHandler' ),
                options = this._options,
                onHide, view, layer;
            if ( subPopOverView ) {
                subPopOverView.hide();
            }
            this.set( 'isVisible', false );
            view = options.view;
            this.get( 'parentView' ).removeView( this );
            this.removeView( view );
            if ( options.showCallout ) {
                layer = this.get( 'layer' );
                layer.removeChild( layer.firstChild );
            }
            if ( eventHandler ) {
                NS.ViewEventsController.removeEventTarget( eventHandler );
                eventHandler._seenMouseDown = false;
            }
            this._options = null;
            if ( onHide = options.onHide ) {
                onHide();
            }
        }
        return this;
    },

    hasSubView: function () {
        return !!NS.meta( this ).cache.subPopOverView &&
            this.get( 'subPopOverView' ).get( 'isVisible' );
    },

    subPopOverView: function () {
        return new NS.PopOverView({ parentPopOverView: this });
    }.property(),

    eventHandler: function () {
        return this.get( 'parentPopOverView' ) ?
            null : new NS.ModalEventHandler({ view: this });
    }.property(),

    clickedOutside: function () {
        this.hide();
    },

    keyOutside: function ( event ) {
        var view = this;
        while ( view.hasSubView() ) {
            view = view.get( 'subPopOverView' );
        }
        view._options.view.fire( event.type, event );
        if ( event.type === 'keydown' &&
                NS.DOMEvent.lookupKey( event ) === 'esc' ) {
            view.hide();
        }
    },

    stopEvents: function ( event ) {
        event.stopPropagation();
    }.on( 'click', 'mousedown', 'mouseup',
        'keypress', 'keydown', 'keyup', 'tap' )
});

NS.PopOverView = PopOverView;

}( this.O ) );
<|endoftext|>


<commit_before>'use strict';
var models = require('../../models');
var _ = require('lodash');
var security = require('../../core/utils/security');

var proto = module.exports = function (){
  function AppManager() {

  }
  AppManager.__proto__ = proto;
  return AppManager;
};

proto.findAppByName = function (uid, appName) {
  return models.Apps.findOne({where: {name: appName, uid: uid}});
};

proto.addApp = function (uid, appName, identical) {
  return models.sequelize.transaction(function (t) {
    return models.Apps.create({
      name: appName,
      uid: uid
    },{
      transaction: t
    })
    .then(function (apps) {
      var appId = apps.id;
      var deployments = [];
      var deploymentKey = security.randToken(28) + identical;
      deployments.push({
        appid: appId,
        name: 'Production',
        last_deployment_version_id: 0,
        label_id: 0,
        deployment_key: deploymentKey
      });
      deploymentKey = security.randToken(28) + identical;
      deployments.push({
        appid: appId,
        name: 'Staging',
        last_deployment_version_id: 0,
        label_id: 0,
        deployment_key: deploymentKey
      });
      return Promise.all([
        models.Collaborators.create({appid: appId, uid: uid, roles: "Owner"}, {transaction: t}),
        models.Deployments.bulkCreate(deployments, {transaction: t})
      ]);
    });
  });
};

proto.deleteApp = function (appId) {
  return models.sequelize.transaction(function (t) {
    return Promise.all([
      models.Apps.destroy({where: {id: appId}, transaction: t}),
      models.Collaborators.destroy({where: {appid: appId}, transaction: t}),
      models.Deployments.destroy({where: {appid: appId}, transaction: t})
    ]);
  });
};

proto.modifyApp = function (appId, params) {
  return models.Apps.update(params, {where: {id:appId}})
  .spread(function (affectedCount, affectedRows) {
    if (!_.gt(affectedCount, 0)) {
      throw Error('modify errors');
    }
    return affectedCount;
  });
};

proto.transferApp = function (appId, fromUid, toUid) {
  return models.sequelize.transaction(function (t) {
    return Promise.all([
      models.Apps.update({uid: toUid}, {where: {id: appId}, transaction: t}),
      models.Collaborators.destroy({where: {appid: appId, uid: fromUid}, transaction: t}),
      models.Collaborators.destroy({where: {appid: appId, uid: toUid}, transaction: t}),
      models.Collaborators.create({appid: appId, uid: toUid, roles: "Owner"}, {transaction: t})
    ]);
  });
};

proto.listApps = function (uid) {
  return models.Collaborators.findAll({where : {uid: uid}})
  .then(function(data){
    if (_.isEmpty(data)){
      throw new Error('You are not Collaborator in any apps.');
    }else {
      return [data, data.map(function(v){ return v.appid })];
    }
  })
  .spread(function (collaboratorInfos, appids) {
    return models.Apps.findAll({where: {id: {in: appids}}})
    .then(function(appInfos) {
      if (_.isEmpty(appInfos)) {
        throw new Error('can\'t find apps info.');
      }else {
        var appInfos = _.reduce(appInfos, function(result, value, key) {
          result[value.id] = value;
          return result;
        }, {});
        return [collaboratorInfos, appInfos];
      }
    });
  })
  .spread(function (collaboratorInfos, appInfos) {
    var ownerIds = _.map(appInfos, function (v) {
      return v.uid;
    });
    return models.Users.findAll({where: {id: {in: ownerIds}}})
    .then(function (data) {
      var userInfos = _.reduce(data, function(result, value, key) {
        result[value.id] = value;
        return result;
      }, {});
      return userInfos;
    })
    .then(function (userInfos) {
      var rs = _.map(appInfos, function(v, key){
        var collaborators = {};
        if (_.eq(v.uid, uid)) {
           var bo = {permission: 'Owner', isCurrentAccount: true};
        } else {
           var bo = {permission: 'Owner', isCurrentAccount: false};
        }
        var email = userInfos[v.uid] ? userInfos[v.uid].email : req.users.email;
        collaborators[email + ""] = bo;
        return {collaborators: collaborators, name: v.name};
      });
      return rs;
    });
  });
};
<commit_msg>fix list app<commit_after>'use strict';
var Promise = require('bluebird');
var models = require('../../models');
var _ = require('lodash');
var security = require('../../core/utils/security');

var proto = module.exports = function (){
  function AppManager() {

  }
  AppManager.__proto__ = proto;
  return AppManager;
};

proto.findAppByName = function (uid, appName) {
  return models.Apps.findOne({where: {name: appName, uid: uid}});
};

proto.addApp = function (uid, appName, identical) {
  return models.sequelize.transaction(function (t) {
    return models.Apps.create({
      name: appName,
      uid: uid
    },{
      transaction: t
    })
    .then(function (apps) {
      var appId = apps.id;
      var deployments = [];
      var deploymentKey = security.randToken(28) + identical;
      deployments.push({
        appid: appId,
        name: 'Production',
        last_deployment_version_id: 0,
        label_id: 0,
        deployment_key: deploymentKey
      });
      deploymentKey = security.randToken(28) + identical;
      deployments.push({
        appid: appId,
        name: 'Staging',
        last_deployment_version_id: 0,
        label_id: 0,
        deployment_key: deploymentKey
      });
      return Promise.all([
        models.Collaborators.create({appid: appId, uid: uid, roles: "Owner"}, {transaction: t}),
        models.Deployments.bulkCreate(deployments, {transaction: t})
      ]);
    });
  });
};

proto.deleteApp = function (appId) {
  return models.sequelize.transaction(function (t) {
    return Promise.all([
      models.Apps.destroy({where: {id: appId}, transaction: t}),
      models.Collaborators.destroy({where: {appid: appId}, transaction: t}),
      models.Deployments.destroy({where: {appid: appId}, transaction: t})
    ]);
  });
};

proto.modifyApp = function (appId, params) {
  return models.Apps.update(params, {where: {id:appId}})
  .spread(function (affectedCount, affectedRows) {
    if (!_.gt(affectedCount, 0)) {
      throw Error('modify errors');
    }
    return affectedCount;
  });
};

proto.transferApp = function (appId, fromUid, toUid) {
  return models.sequelize.transaction(function (t) {
    return Promise.all([
      models.Apps.update({uid: toUid}, {where: {id: appId}, transaction: t}),
      models.Collaborators.destroy({where: {appid: appId, uid: fromUid}, transaction: t}),
      models.Collaborators.destroy({where: {appid: appId, uid: toUid}, transaction: t}),
      models.Collaborators.create({appid: appId, uid: toUid, roles: "Owner"}, {transaction: t})
    ]);
  });
};

proto.listApps = function (uid) {
  return models.Collaborators.findAll({where : {uid: uid}})
  .then(function(data){
    if (_.isEmpty(data)){
      throw new Error('You are not Collaborator in any apps.');
    }else {
      return [data, data.map(function(v){ return v.appid })];
    }
  })
  .spread(function (collaboratorInfos, appids) {
    return models.Apps.findAll({where: {id: {in: appids}}})
    .then(function(appInfos) {
      if (_.isEmpty(appInfos)) {
        throw new Error(`can't find apps info.`);
      }else {
        var appInfos = _.reduce(appInfos, function(result, value, key) {
          result[value.id] = value;
          return result;
        }, {});
        return [collaboratorInfos, appInfos];
      }
    });
  })
  .spread(function (collaboratorInfos, appInfos) {
    var ownerIds = _.map(appInfos, function (v) {
      return v.uid;
    });
    return models.Users.findAll({where: {id: {in: ownerIds}}})
    .then(function (data) {
      var userInfos = _.reduce(data, function(result, value, key) {
        result[value.id] = value;
        return result;
      }, {});
      return userInfos;
    })
    .then(function (userInfos) {
      var rs = Promise.map(_.values(appInfos), function(v){
        var appId = v.get('id');
        var email = userInfos[v.uid] ? userInfos[v.uid].email : req.users.email;
        var isCurrentAccount = false;
        if (_.eq(v.uid, uid)) {
          isCurrentAccount = true;
        }
        return models.Deployments.findAll({where: {appid: appId}})
        .then(function (deploymentInfos) {
          return {
            collaborators: {[email]: {permission: 'Owner', isCurrentAccount: isCurrentAccount}},
            deployments: _.map(deploymentInfos, function (item) {
              return _.get(item, 'name');
            }),
            name: v.name
          };
        });
      });
      return rs;
    })
  });
};
<|endoftext|>


<commit_before>/*
The latest version of this package is available at:
<http://github.com/jantman/biweeklybudget>

################################################################################
Copyright 2016 Jason Antman <jason@jasonantman.com> <http://www.jasonantman.com>

    This file is part of biweeklybudget, also known as biweeklybudget.

    biweeklybudget is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    biweeklybudget is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with biweeklybudget.  If not, see <http://www.gnu.org/licenses/>.

The Copyright and Authors attributions contained herein may not be removed or
otherwise altered, except to add the Author attribution of a contributor to
this work. (Additional Terms pursuant to Section 7b of the AGPL v3)
################################################################################
While not legally required, I sincerely request that anyone who finds
bugs please submit them at <https://github.com/jantman/biweeklybudget> or
to me via email, and that you send any contributions or improvements
either as a pull request on GitHub, or to me via email.
################################################################################

AUTHORS:
Jason Antman <jason@jasonantman.com> <http://www.jasonantman.com>
################################################################################
*/

// for the DataTable
var mytable;

$(document).ready(function() {
    mytable = $('#table-transactions').dataTable({
        processing: true,
        serverSide: true,
        ajax: "/ajax/transactions",
        columns: [
            { data: "date" },
            {
                data: "amount",
                "render": function(data, type, row) {
                    return type === "display" || type === "filter" ?
                        fmt_currency(data) :
                        data;
                }
            },
            {
                data: "description",
                "render": function(data, type, row) {
                    return $("
").append($("").attr("href", "javascript:transModal(" + row.DT_RowData.id + ", mytable)").text(data)).html();
                }
            },
            {
                data: "account",
                "render": function(data, type, row) {
                    return $("
").append($("").attr("href", "/accounts/" + row.DT_RowData.acct_id).text(data)).html();
                }
            },
            {
                data: "DT_RowData.budget",
                "render": function(data, type, row) {
                    if(row.DT_RowData.budgets.length == 1) {
                        var budg = row.DT_RowData.budgets[0];
                        var txt = budg['name'];
                        if(budg['is_income'] === true) { txt = txt + ' (income)'; }
                        txt = txt + ' (' + budg['id'] + ')';
                        return $("
").append($("").attr("href", "/budgets/" + budg['id']).text(txt)).html();
                    } else {
                        var div = $("
");
                        for (index = 0; index < row.DT_RowData.budgets.length; ++index) {
                            var budg = row.DT_RowData.budgets[index];
                            console.log(budg);
                            console.log(index);
                            var txt = budg['name'];
                            if(budg['is_income'] === true) { txt = txt + ' (income)'; }
                            txt = txt + ' (' + budg['id'] + ')';
                            txt = txt + ' (' + fmt_currency(budg['amount']) + ')';
                            div.append($("").attr("href", "/budgets/" + budg['id']).text(txt));
                            if(index < row.DT_RowData.budgets.length - 1) { div.append($('
')); }
                        }
                        return div.html();
                    }
                }
            },
            {
                data: "scheduled",
                "render": function(data, type, row) {
                    if(data != null) {
                        return $("
").append($("").attr("href", "/scheduled/" + data).text("Yes (" + data + ")")).html();
                    } else {
                        return ' ';
                    }
                }
            },
            {
                data: "budgeted_amount",
                "render": function(data, type, row) {
                    if(type === "display" || type === "filter") {
                        if(data === row.amount || data === null ) {
                            return '&nbsp';
                        } else {
                            return fmt_currency(data);
                        }
                    } else {
                        return data;
                    }
                }
            },
            {
                data: "reconcile_id",
                "render": function(data, type, row) {
                    if(data != null) {
                        return $("
").append($("").attr("href", "javascript:txnReconcileModal(" + data + ")").text("Yes (" + data + ")")).html();
                    } else {
                        return ' ';
                    }
                }
            }
        ],
        order: [[ 0, "desc"], [ 1, "asc"]],
        bInfo: true
    });

    $('#table-transactions_length').parent().removeClass('col-sm-6');
    $('#table-transactions_length').parent().addClass('col-sm-3');
    $('#table-transactions_filter').parent().removeClass('col-sm-6');
    $('#table-transactions_filter').parent().addClass('col-sm-3');
    var acctsel = '

Account: <select name="account_filter" id="account_filter" class="form-control input-sm" aria-controls="table-transactions">';
    acctsel += '<option value="None" selected="selected"></option>';
    Object.keys(acct_names_to_id).forEach(function (key) {
        acctsel += '<option value="' + acct_names_to_id[key] + '">' + key + '</option>';
    });
    acctsel += '</select>

';
    $(acctsel).insertAfter($('#table-transactions_length').parent());
    $('#account_filter').on('change', function() {
        var selectedVal = $(this).val();
        mytable.fnFilter(selectedVal, 3, false);
    });

    var budgsel = '

Budget: <select name="budget_filter" id="budget_filter" class="form-control input-sm" aria-controls="table-transactions">';
    budgsel += '<option value="None" selected="selected"></option>';
    Object.keys(budget_names_to_id).forEach(function (key) {
        budgsel += '<option value="' + budget_names_to_id[key] + '">' + key + '</option>';
    });
    budgsel += '</select>

';
    $(budgsel).insertAfter($('#table-transactions_length').parent());
    $('#budget_filter').on('change', function() {
        var selectedVal = $(this).val();
        mytable.fnFilter(selectedVal, 4, false);
    });

    $('#btn_add_trans').click(function() {
        transModal(null, mytable);
    });
});
<commit_msg>issue #178 - split-budget transactions breaks Transactions datatable sorting; disable ordering on budget column<commit_after>/*
The latest version of this package is available at:
<http://github.com/jantman/biweeklybudget>

################################################################################
Copyright 2016 Jason Antman <jason@jasonantman.com> <http://www.jasonantman.com>

    This file is part of biweeklybudget, also known as biweeklybudget.

    biweeklybudget is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    biweeklybudget is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with biweeklybudget.  If not, see <http://www.gnu.org/licenses/>.

The Copyright and Authors attributions contained herein may not be removed or
otherwise altered, except to add the Author attribution of a contributor to
this work. (Additional Terms pursuant to Section 7b of the AGPL v3)
################################################################################
While not legally required, I sincerely request that anyone who finds
bugs please submit them at <https://github.com/jantman/biweeklybudget> or
to me via email, and that you send any contributions or improvements
either as a pull request on GitHub, or to me via email.
################################################################################

AUTHORS:
Jason Antman <jason@jasonantman.com> <http://www.jasonantman.com>
################################################################################
*/

// for the DataTable
var mytable;

$(document).ready(function() {
    mytable = $('#table-transactions').dataTable({
        processing: true,
        serverSide: true,
        ajax: "/ajax/transactions",
        columns: [
            { data: "date" },
            {
                data: "amount",
                "render": function(data, type, row) {
                    return type === "display" || type === "filter" ?
                        fmt_currency(data) :
                        data;
                }
            },
            {
                data: "description",
                "render": function(data, type, row) {
                    return $("
").append($("").attr("href", "javascript:transModal(" + row.DT_RowData.id + ", mytable)").text(data)).html();
                }
            },
            {
                data: "account",
                "render": function(data, type, row) {
                    return $("
").append($("").attr("href", "/accounts/" + row.DT_RowData.acct_id).text(data)).html();
                }
            },
            {
                data: "DT_RowData.budget",
                orderable: false,  // split-budget transactions breaks sorting
                "render": function(data, type, row) {
                    if(row.DT_RowData.budgets.length == 1) {
                        var budg = row.DT_RowData.budgets[0];
                        var txt = budg['name'];
                        if(budg['is_income'] === true) { txt = txt + ' (income)'; }
                        txt = txt + ' (' + budg['id'] + ')';
                        return $("
").append($("").attr("href", "/budgets/" + budg['id']).text(txt)).html();
                    } else {
                        var div = $("
");
                        for (index = 0; index < row.DT_RowData.budgets.length; ++index) {
                            var budg = row.DT_RowData.budgets[index];
                            console.log(budg);
                            console.log(index);
                            var txt = budg['name'];
                            if(budg['is_income'] === true) { txt = txt + ' (income)'; }
                            txt = txt + ' (' + budg['id'] + ')';
                            txt = txt + ' (' + fmt_currency(budg['amount']) + ')';
                            div.append($("").attr("href", "/budgets/" + budg['id']).text(txt));
                            if(index < row.DT_RowData.budgets.length - 1) { div.append($('
')); }
                        }
                        return div.html();
                    }
                }
            },
            {
                data: "scheduled",
                "render": function(data, type, row) {
                    if(data != null) {
                        return $("
").append($("").attr("href", "/scheduled/" + data).text("Yes (" + data + ")")).html();
                    } else {
                        return ' ';
                    }
                }
            },
            {
                data: "budgeted_amount",
                "render": function(data, type, row) {
                    if(type === "display" || type === "filter") {
                        if(data === row.amount || data === null ) {
                            return '&nbsp';
                        } else {
                            return fmt_currency(data);
                        }
                    } else {
                        return data;
                    }
                }
            },
            {
                data: "reconcile_id",
                "render": function(data, type, row) {
                    if(data != null) {
                        return $("
").append($("").attr("href", "javascript:txnReconcileModal(" + data + ")").text("Yes (" + data + ")")).html();
                    } else {
                        return ' ';
                    }
                }
            }
        ],
        order: [[ 0, "desc"], [ 1, "asc"]],
        bInfo: true
    });

    $('#table-transactions_length').parent().removeClass('col-sm-6');
    $('#table-transactions_length').parent().addClass('col-sm-3');
    $('#table-transactions_filter').parent().removeClass('col-sm-6');
    $('#table-transactions_filter').parent().addClass('col-sm-3');
    var acctsel = '

Account: <select name="account_filter" id="account_filter" class="form-control input-sm" aria-controls="table-transactions">';
    acctsel += '<option value="None" selected="selected"></option>';
    Object.keys(acct_names_to_id).forEach(function (key) {
        acctsel += '<option value="' + acct_names_to_id[key] + '">' + key + '</option>';
    });
    acctsel += '</select>

';
    $(acctsel).insertAfter($('#table-transactions_length').parent());
    $('#account_filter').on('change', function() {
        var selectedVal = $(this).val();
        mytable.fnFilter(selectedVal, 3, false);
    });

    var budgsel = '

Budget: <select name="budget_filter" id="budget_filter" class="form-control input-sm" aria-controls="table-transactions">';
    budgsel += '<option value="None" selected="selected"></option>';
    Object.keys(budget_names_to_id).forEach(function (key) {
        budgsel += '<option value="' + budget_names_to_id[key] + '">' + key + '</option>';
    });
    budgsel += '</select>

';
    $(budgsel).insertAfter($('#table-transactions_length').parent());
    $('#budget_filter').on('change', function() {
        var selectedVal = $(this).val();
        mytable.fnFilter(selectedVal, 4, false);
    });

    $('#btn_add_trans').click(function() {
        transModal(null, mytable);
    });
});
<|endoftext|>


<commit_before>var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
var Schema = mongoose.Schema;
var request = require('request');
var _ = require('underscore');

var UserSchema = new Schema({
  subscriptionId: {
    type: String,
    unique: true,
    required: true
  },
  apiKey: {
    type: String,
    unique: true,
    required: true
  }
});

var User = mongoose.model('User', UserSchema);

module.exports = function (opts) {
    var IP_CALLBACK = opts.IP_CALLBACK;

    module.registerOrionInstance = function (req, res) {
        var orionIp = req.body.ip;
        var userApiKey = req.body.apiKey;
        var regex = req.body.pattern || "";

        console.log(orionIp, userApiKey, regex);

        if (! orionIp || ! userApiKey) {
            res.send(400, {status: "error", message: "missing OrionIp, pattern or apiKey"});
        } else {
            // if its already registered update
            User.findOne({ apiKey: userApiKey }, function (err, user) {
                if (err){
                    res.send(500, {status: "error", message: "userkey not found"});
                }

                if (user) { // exists update

                } else {
                    console.log('ApiKey does not exist, creating subscription for this');
                     // if does not exist create subscription
                    request.post({
                    url: 'http://' + orionIp + '/v1/queryContext',
                    json: true,
                    body: {
                            "entities": [
                                {
                                    "type": [],
                                    "isPattern": "true",
                                    "id": ".*" + regex
                                }
                            ]
                        }
                    }, function (error, response, body) {
                        if (error) console.log('err', error);
                        if (body.contextResponses[0].statusCode.code == 200) {
                            var attrs = [];
                            _.each( body.contextResponses[0].contextElement.attributes, function (attribute){
                                attrs.push(attribute.name);
                            });

                            // Register to all changes in structures in ORION each 30 secs
                            request.post({
                                url: 'http://' + orionIp + '/v1/subscribeContext',
                                json: true,
                                body: {
                                    "entities": [
                                    {
                                        "type": [],
                                        "isPattern": "true",
                                        "id": ".*" + regex
                                    }
                                    ],
                                    "attributes": [],
                                    "reference": "http://" + IP_CALLBACK + "/api/subscriptions",
                                    "duration": "P12M",
                                    "notifyConditions": [
                                    {
                                        "type": "ONCHANGE",
                                        "attributes" : attrs
                                    }
                                    ]
                                }
                            },
                            function (error, response, body) {
                                if (error) {
                                    console.log('err', error);
                                } else {
                                    var user = new User({ subscriptionId : body.subscribeResponse.subscriptionId, apiKey : userApiKey});
                                    user.save(function (err) { console.log(err) });
                                    console.log('registering apikey: ' + userApiKey + ' withRegId: ' + body.subscribeResponse.subscriptionId);
                                    res.send(200, {status: "ok", message: "registered to attributes: " + attrs.toString() });
                                }
                            });
                        } else {
                            res.send(500, {status: "error", message: "no entities subscribed to" });
                        }
                    });
                }
            });
        }
    }

    module.updatesFromOrion = function (req, res) {
        var updatedElement = req.body.contextResponses[0].contextElement;
        console.log('received update from ORION--------------------------------------------------');
        var channels = [];
        if (_.findLastIndex(updatedElement.attributes, { name: 'channels' }) != -1) {
            channels = updatedElement.attributes[_.findLastIndex(updatedElement.attributes, { name: 'channels' })].value;
        } else {
            channels.push("orion");
        }

        //get APIKEY FROM SUBSCRIPTION ID AND PUT IT TO THE NEXT CALL FOR CHANNELS
        var apiKey = req.body.subscriptionId;

        _.each(channels, function (channel) {
            //TODO verify apikey, from is ORION?
            bastly.sendMessage(channel, "ORION", apiKey, updatedElement, function(err, reply){
                console.log('message sent from ORION to client', err, reply);
            });
        });

        res.sendStatus(200);
    }

return module;
}

<commit_msg>log body<commit_after>var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
var Schema = mongoose.Schema;
var request = require('request');
var _ = require('underscore');

var UserSchema = new Schema({
  subscriptionId: {
    type: String,
    unique: true,
    required: true
  },
  apiKey: {
    type: String,
    unique: true,
    required: true
  }
});

var User = mongoose.model('User', UserSchema);

module.exports = function (opts) {
    var IP_CALLBACK = opts.IP_CALLBACK;

    module.registerOrionInstance = function (req, res) {
        var orionIp = req.body.ip;
        var userApiKey = req.body.apiKey;
        var regex = req.body.pattern || "";

        console.log(orionIp, userApiKey, regex);

        if (! orionIp || ! userApiKey) {
            res.send(400, {status: "error", message: "missing OrionIp, pattern or apiKey"});
        } else {
            // if its already registered update
            User.findOne({ apiKey: userApiKey }, function (err, user) {
                if (err){
                    res.send(500, {status: "error", message: "userkey not found"});
                }

                if (user) { // exists update

                } else {
                    console.log('ApiKey does not exist, creating subscription for this');
                     // if does not exist create subscription
                    request.post({
                    url: 'http://' + orionIp + '/v1/queryContext',
                    json: true,
                    body: {
                            "entities": [
                                {
                                    "type": [],
                                    "isPattern": "true",
                                    "id": ".*" + regex
                                }
                            ]
                        }
                    }, function (error, response, body) {
                        if (error) console.log('err', error);
                        if (body.contextResponses[0].statusCode.code == 200) {
                            var attrs = [];
                            _.each( body.contextResponses[0].contextElement.attributes, function (attribute){
                                attrs.push(attribute.name);
                            });

                            // Register to all changes in structures in ORION each 30 secs
                            request.post({
                                url: 'http://' + orionIp + '/v1/subscribeContext',
                                json: true,
                                body: {
                                    "entities": [
                                    {
                                        "type": [],
                                        "isPattern": "true",
                                        "id": ".*" + regex
                                    }
                                    ],
                                    "attributes": [],
                                    "reference": "http://" + IP_CALLBACK + "/api/subscriptions",
                                    "duration": "P12M",
                                    "notifyConditions": [
                                    {
                                        "type": "ONCHANGE",
                                        "attributes" : attrs
                                    }
                                    ]
                                }
                            },
                            function (error, response, body) {
                                if (error) {
                                    console.log('err', error);
                                } else {
                                    console.log(body);
                                    var user = new User({ subscriptionId : body.subscribeResponse.subscriptionId, apiKey : userApiKey});
                                    user.save(function (err) { console.log(err) });
                                    console.log('registering apikey: ' + userApiKey + ' withRegId: ' + body.subscribeResponse.subscriptionId);
                                    res.send(200, {status: "ok", message: "registered to attributes: " + attrs.toString() });
                                }
                            });
                        } else {
                            res.send(500, {status: "error", message: "no entities subscribed to" });
                        }
                    });
                }
            });
        }
    }

    module.updatesFromOrion = function (req, res) {
        var updatedElement = req.body.contextResponses[0].contextElement;
        console.log('received update from ORION--------------------------------------------------');
        var channels = [];
        if (_.findLastIndex(updatedElement.attributes, { name: 'channels' }) != -1) {
            channels = updatedElement.attributes[_.findLastIndex(updatedElement.attributes, { name: 'channels' })].value;
        } else {
            channels.push("orion");
        }

        //get APIKEY FROM SUBSCRIPTION ID AND PUT IT TO THE NEXT CALL FOR CHANNELS
        var apiKey = req.body.subscriptionId;

        _.each(channels, function (channel) {
            //TODO verify apikey, from is ORION?
            bastly.sendMessage(channel, "ORION", apiKey, updatedElement, function(err, reply){
                console.log('message sent from ORION to client', err, reply);
            });
        });

        res.sendStatus(200);
    }

return module;
}

<|endoftext|>


<commit_before>/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Famous Industries Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

'use strict';

var Transitionable = require('../transitions/Transitionable');

/**
 * Size component used for managing the size of the Node it is attached to.
 * Supports absolute and relative (proportional and differential) sizing.
 *
 * @class Size
 *
 * @param {Node} node Node that the Size component is attached to
 */
function Size(node) {
    this._node = node;
    this._id = node.addComponent(this);
    this._requestingUpdate = false;

    var initialProportionalSize = node.getProportionalSize();
    var initialDifferentialSize = node.getDifferentialSize();
    var initialAbsoluteSize = node.getAbsoluteSize();

    this._proportional = {
        x: new Transitionable(initialProportionalSize[0]),
        y: new Transitionable(initialProportionalSize[1]),
        z: new Transitionable(initialProportionalSize[2])
    };
    this._differential = {
        x: new Transitionable(initialDifferentialSize[0]),
        y: new Transitionable(initialDifferentialSize[1]),
        z: new Transitionable(initialDifferentialSize[2])
    };
    this._absolute = {
        x: new Transitionable(initialAbsoluteSize[0]),
        y: new Transitionable(initialAbsoluteSize[1]),
        z: new Transitionable(initialAbsoluteSize[2])
    };
}

Size.RELATIVE = 0;
Size.ABSOLUTE = 1;
Size.RENDER = 2;
Size.DEFAULT = Size.RELATIVE;

/**
 * Set which mode each axis of Size will have its dimensions
 * calculated by.  Size can be calculated by absolute pixel definitions,
 * relative to its parent, or by the size of its renderables
 *
 * @method
 *
 * @param {Number} x the mode of size for the width
 * @param {Number} y the mode of size for the height
 * @param {Number} z the mode of size for the depth
 *
 * @return {Size} this
 */
Size.prototype.setMode = function setMode(x, y, z) {
    this._node.setSizeMode(x, y, z);
    return this;
};

/**
 * Return the name of the Size component
 *
 * @method
 *
 * @return {String} Name of the component
 */
Size.prototype.toString = function toString() {
    return 'Size';
};

/**
 * @typedef absoluteSizeValue
 * @type {Object}
 * @property {String} type current type of sizing being applied ('absolute')
 * @property {String} component component name ('Size')
 * @property {number} x
 * @property {number} y
 * @property {number} z
 */

/**
 * @typedef relativeSizeValue
 * @type {Object}
 * @property {String} type current type of sizing being applied ('relative')
 * @property {String} component component name ('Size')
 * @property {Object} differential
 * @property {number} differential.x
 * @property {number} differential.y
 * @property {number} differential.z
 * @property {Object} proportional
 * @property {number} proportional.x
 * @property {number} proportional.y
 * @property {number} proportional.z
 */

/**
 * Returns serialized state of the component.
 *
 * @method
 *
 * @return {Object} the internal state of the component
 */
Size.prototype.getValue = function getValue() {
    return {
        sizeMode: this._node.value.sizeMode,
        absolute: {
            x: this._absolute.x.get(),
            y: this._absolute.y.get(),
            z: this._absolute.z.get()
        },
        differential: {
            x: this._differential.x.get(),
            y: this._differential.y.get(),
            z: this._differential.z.get()
        },
        proportional: {
            x: this._proportional.x.get(),
            y: this._proportional.y.get(),
            z: this._proportional.z.get()
        }
    };
};

/**
 * Updates state of component.
 *
 * @method
 *
 * @param {Object} state state encoded in same format as state retrieved through `getValue`
 *
 * @return {Boolean} boolean indicating whether the new state has been applied
 */
Size.prototype.setValue = function setValue(state) {
    if (this.toString() === state.component) {
        this.setMode.apply(this, state.sizeMode);
        if (state.absolute) {
            this.setAbsolute(state.absolute.x, state.absolute.y, state.absolute.z);
        }
        if (state.differential) {
            this.setAbsolute(state.differential.x, state.differential.y, state.differential.z);
        }
        if (state.proportional) {
            this.setAbsolute(state.proportional.x, state.proportional.y, state.proportional.z);
        }
    }
    return false;
};

/**
 * Helper function that grabs the activity of a certain type of size.
 *
 * @method
 * @private
 *
 * @param {Object} type Representation of a type of the sizing model
 *
 * @return {Boolean} boolean indicating whether the new state has been applied
 */
Size.prototype._isActive = function _isActive(type) {
    return type.x.isActive() || type.y.isActive() || type.z.isActive();
};

/**
 * Helper function that grabs the activity of a certain type of size.
 *
 * @method
 *
 * @param {String} type Type of size
 *
 * @return {Boolean} boolean indicating whether the new state has been applied
 */

Size.prototype.isActive = function isActive(){
    return (
        this._isActive(this._absolute) ||
        this._isActive(this._proportional) ||
        this._isActive(this._differential)
    );
};

/**
 * When the node this component is attached to updates, update the value
 * of the Node's size.
 *
 * @method
 *
 * @return {undefined} undefined
 */
Size.prototype.onUpdate = function onUpdate() {
    var abs = this._absolute;
    this._node.setAbsoluteSize(
        abs.x.get(),
        abs.y.get(),
        abs.z.get()
    );
    var prop = this._proportional;
    var diff = this._differential;
    this._node.setProportionalSize(
        prop.x.get(),
        prop.y.get(),
        prop.z.get()
    );
    this._node.setDifferentialSize(
        diff.x.get(),
        diff.y.get(),
        diff.z.get()
    );

    if (this.isActive()) this._node.requestUpdateOnNextTick(this._id);
    else this._requestingUpdate = false;
};

/**
* Applies absolute size.
*
* @method
*
* @param {Number} x used to set absolute size in x-direction (width)
* @param {Number} y used to set absolute size in y-direction (height)
* @param {Number} z used to set absolute size in z-direction (depth)
* @param {Object} options options hash
* @param {Function} callback callback function to be executed after the
*                            transitions have been completed
* @return {Size} this
*/
Size.prototype.setAbsolute = function setAbsolute(x, y, z, options, callback) {
    if (!this._requestingUpdate) {
        this._node.requestUpdate(this._id);
        this._requestingUpdate = true;
    }

    var xCallback;
    var yCallback;
    var zCallback;

    if (z != null) {
        zCallback = callback;
    }
    else if (y != null) {
        yCallback = callback;
    }
    else if (x != null) {
        xCallback = callback;
    }

    var abs = this._absolute;
    if (x != null) {
        abs.x.set(x, options, xCallback);
    }
    if (y != null) {
        abs.y.set(y, options, yCallback);
    }
    if (z != null) {
        abs.z.set(z, options, zCallback);
    }
};

/**
* Applies proportional size.
*
* @method
*
* @param {Number} x used to set proportional size in x-direction (width)
* @param {Number} y used to set proportional size in y-direction (height)
* @param {Number} z used to set proportional size in z-direction (depth)
* @param {Object} options options hash
* @param {Function} callback callback function to be executed after the
*                            transitions have been completed
* @return {Size} this
*/
Size.prototype.setProportional = function setProportional(x, y, z, options, callback) {
    if (!this._requestingUpdate) {
        this._node.requestUpdate(this._id);
        this._requestingUpdate = true;
    }

    var xCallback;
    var yCallback;
    var zCallback;

    if (z != null) {
        zCallback = callback;
    }
    else if (y != null) {
        yCallback = callback;
    }
    else if (x != null) {
        xCallback = callback;
    }

    var prop = this._proportional;
    if (x != null) {
        prop.x.set(x, options, xCallback);
    }
    if (y != null) {
        prop.y.set(y, options, yCallback);
    }
    if (z != null) {
        prop.z.set(z, options, zCallback);
    }
    return this;
};

/**
* Applies differential size to Size component.
*
* @method
*
* @param {Number} x used to set differential size in x-direction (width)
* @param {Number} y used to set differential size in y-direction (height)
* @param {Number} z used to set differential size in z-direction (depth)
* @param {Object} options options hash
* @param {Function} callback callback function to be executed after the
*                            transitions have been completed
* @return {Size} this
*/
Size.prototype.setDifferential = function setDifferential(x, y, z, options, callback) {
    if (!this._requestingUpdate) {
        this._node.requestUpdate(this._id);
        this._requestingUpdate = true;
    }

    var xCallback;
    var yCallback;
    var zCallback;

    if (z != null) {
        zCallback = callback;
    }
    else if (y != null) {
        yCallback = callback;
    }
    else if (x != null) {
        xCallback = callback;
    }

    var diff = this._differential;
    if (x != null) {
        diff.x.set(x, options, xCallback);
    }
    if (y != null) {
        diff.y.set(y, options, yCallback);
    }
    if (z != null) {
        diff.z.set(z, options, zCallback);
    }
    return this;
};

/**
 * Retrieves the computed size applied to the underlying Node.
 *
 * @method
 *
 * @return {Array} size three dimensional computed size
 */
Size.prototype.get = function get () {
    return this._node.getSize();
};

/**
 * Halts all currently active size transitions.
 *
 * @method
 *
 * @return {Size} this
 */
Size.prototype.halt = function halt () {
    this._proportional.x.halt();
    this._proportional.y.halt();
    this._proportional.z.halt();
    this._differential.x.halt();
    this._differential.y.halt();
    this._differential.z.halt();
    this._absolute.x.halt();
    this._absolute.y.halt();
    this._absolute.z.halt();
    return this;
};

module.exports = Size;
<commit_msg>fix: update breaking reference to the node's sizeMode<commit_after>/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Famous Industries Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

'use strict';

var Transitionable = require('../transitions/Transitionable');
var SizeSystem = require('../core/SizeSystem');

/**
 * Size component used for managing the size of the Node it is attached to.
 * Supports absolute and relative (proportional and differential) sizing.
 *
 * @class Size
 *
 * @param {Node} node Node that the Size component is attached to
 */
function Size(node) {
    this._node = node;
    this._id = node.addComponent(this);
    this._requestingUpdate = false;

    var initialProportionalSize = node.getProportionalSize();
    var initialDifferentialSize = node.getDifferentialSize();
    var initialAbsoluteSize = node.getAbsoluteSize();

    this._proportional = {
        x: new Transitionable(initialProportionalSize[0]),
        y: new Transitionable(initialProportionalSize[1]),
        z: new Transitionable(initialProportionalSize[2])
    };
    this._differential = {
        x: new Transitionable(initialDifferentialSize[0]),
        y: new Transitionable(initialDifferentialSize[1]),
        z: new Transitionable(initialDifferentialSize[2])
    };
    this._absolute = {
        x: new Transitionable(initialAbsoluteSize[0]),
        y: new Transitionable(initialAbsoluteSize[1]),
        z: new Transitionable(initialAbsoluteSize[2])
    };
}

Size.RELATIVE = 0;
Size.ABSOLUTE = 1;
Size.RENDER = 2;
Size.DEFAULT = Size.RELATIVE;

/**
 * Set which mode each axis of Size will have its dimensions
 * calculated by.  Size can be calculated by absolute pixel definitions,
 * relative to its parent, or by the size of its renderables
 *
 * @method
 *
 * @param {Number} x the mode of size for the width
 * @param {Number} y the mode of size for the height
 * @param {Number} z the mode of size for the depth
 *
 * @return {Size} this
 */
Size.prototype.setMode = function setMode(x, y, z) {
    this._node.setSizeMode(x, y, z);
    return this;
};

/**
 * Return the name of the Size component
 *
 * @method
 *
 * @return {String} Name of the component
 */
Size.prototype.toString = function toString() {
    return 'Size';
};

/**
 * @typedef absoluteSizeValue
 * @type {Object}
 * @property {String} type current type of sizing being applied ('absolute')
 * @property {String} component component name ('Size')
 * @property {number} x
 * @property {number} y
 * @property {number} z
 */

/**
 * @typedef relativeSizeValue
 * @type {Object}
 * @property {String} type current type of sizing being applied ('relative')
 * @property {String} component component name ('Size')
 * @property {Object} differential
 * @property {number} differential.x
 * @property {number} differential.y
 * @property {number} differential.z
 * @property {Object} proportional
 * @property {number} proportional.x
 * @property {number} proportional.y
 * @property {number} proportional.z
 */

/**
 * Returns serialized state of the component.
 *
 * @method
 *
 * @return {Object} the internal state of the component
 */
Size.prototype.getValue = function getValue() {
    return {
        sizeMode: SizeSystem.get(this._node.getLocation()).getSizeMode(),
        absolute: {
            x: this._absolute.x.get(),
            y: this._absolute.y.get(),
            z: this._absolute.z.get()
        },
        differential: {
            x: this._differential.x.get(),
            y: this._differential.y.get(),
            z: this._differential.z.get()
        },
        proportional: {
            x: this._proportional.x.get(),
            y: this._proportional.y.get(),
            z: this._proportional.z.get()
        }
    };
};

/**
 * Updates state of component.
 *
 * @method
 *
 * @param {Object} state state encoded in same format as state retrieved through `getValue`
 *
 * @return {Boolean} boolean indicating whether the new state has been applied
 */
Size.prototype.setValue = function setValue(state) {
    if (this.toString() === state.component) {
        this.setMode.apply(this, state.sizeMode);
        if (state.absolute) {
            this.setAbsolute(state.absolute.x, state.absolute.y, state.absolute.z);
        }
        if (state.differential) {
            this.setAbsolute(state.differential.x, state.differential.y, state.differential.z);
        }
        if (state.proportional) {
            this.setAbsolute(state.proportional.x, state.proportional.y, state.proportional.z);
        }
    }
    return false;
};

/**
 * Helper function that grabs the activity of a certain type of size.
 *
 * @method
 * @private
 *
 * @param {Object} type Representation of a type of the sizing model
 *
 * @return {Boolean} boolean indicating whether the new state has been applied
 */
Size.prototype._isActive = function _isActive(type) {
    return type.x.isActive() || type.y.isActive() || type.z.isActive();
};

/**
 * Helper function that grabs the activity of a certain type of size.
 *
 * @method
 *
 * @param {String} type Type of size
 *
 * @return {Boolean} boolean indicating whether the new state has been applied
 */

Size.prototype.isActive = function isActive(){
    return (
        this._isActive(this._absolute) ||
        this._isActive(this._proportional) ||
        this._isActive(this._differential)
    );
};

/**
 * When the node this component is attached to updates, update the value
 * of the Node's size.
 *
 * @method
 *
 * @return {undefined} undefined
 */
Size.prototype.onUpdate = function onUpdate() {
    var abs = this._absolute;
    this._node.setAbsoluteSize(
        abs.x.get(),
        abs.y.get(),
        abs.z.get()
    );
    var prop = this._proportional;
    var diff = this._differential;
    this._node.setProportionalSize(
        prop.x.get(),
        prop.y.get(),
        prop.z.get()
    );
    this._node.setDifferentialSize(
        diff.x.get(),
        diff.y.get(),
        diff.z.get()
    );

    if (this.isActive()) this._node.requestUpdateOnNextTick(this._id);
    else this._requestingUpdate = false;
};

/**
* Applies absolute size.
*
* @method
*
* @param {Number} x used to set absolute size in x-direction (width)
* @param {Number} y used to set absolute size in y-direction (height)
* @param {Number} z used to set absolute size in z-direction (depth)
* @param {Object} options options hash
* @param {Function} callback callback function to be executed after the
*                            transitions have been completed
* @return {Size} this
*/
Size.prototype.setAbsolute = function setAbsolute(x, y, z, options, callback) {
    if (!this._requestingUpdate) {
        this._node.requestUpdate(this._id);
        this._requestingUpdate = true;
    }

    var xCallback;
    var yCallback;
    var zCallback;

    if (z != null) {
        zCallback = callback;
    }
    else if (y != null) {
        yCallback = callback;
    }
    else if (x != null) {
        xCallback = callback;
    }

    var abs = this._absolute;
    if (x != null) {
        abs.x.set(x, options, xCallback);
    }
    if (y != null) {
        abs.y.set(y, options, yCallback);
    }
    if (z != null) {
        abs.z.set(z, options, zCallback);
    }
};

/**
* Applies proportional size.
*
* @method
*
* @param {Number} x used to set proportional size in x-direction (width)
* @param {Number} y used to set proportional size in y-direction (height)
* @param {Number} z used to set proportional size in z-direction (depth)
* @param {Object} options options hash
* @param {Function} callback callback function to be executed after the
*                            transitions have been completed
* @return {Size} this
*/
Size.prototype.setProportional = function setProportional(x, y, z, options, callback) {
    if (!this._requestingUpdate) {
        this._node.requestUpdate(this._id);
        this._requestingUpdate = true;
    }

    var xCallback;
    var yCallback;
    var zCallback;

    if (z != null) {
        zCallback = callback;
    }
    else if (y != null) {
        yCallback = callback;
    }
    else if (x != null) {
        xCallback = callback;
    }

    var prop = this._proportional;
    if (x != null) {
        prop.x.set(x, options, xCallback);
    }
    if (y != null) {
        prop.y.set(y, options, yCallback);
    }
    if (z != null) {
        prop.z.set(z, options, zCallback);
    }
    return this;
};

/**
* Applies differential size to Size component.
*
* @method
*
* @param {Number} x used to set differential size in x-direction (width)
* @param {Number} y used to set differential size in y-direction (height)
* @param {Number} z used to set differential size in z-direction (depth)
* @param {Object} options options hash
* @param {Function} callback callback function to be executed after the
*                            transitions have been completed
* @return {Size} this
*/
Size.prototype.setDifferential = function setDifferential(x, y, z, options, callback) {
    if (!this._requestingUpdate) {
        this._node.requestUpdate(this._id);
        this._requestingUpdate = true;
    }

    var xCallback;
    var yCallback;
    var zCallback;

    if (z != null) {
        zCallback = callback;
    }
    else if (y != null) {
        yCallback = callback;
    }
    else if (x != null) {
        xCallback = callback;
    }

    var diff = this._differential;
    if (x != null) {
        diff.x.set(x, options, xCallback);
    }
    if (y != null) {
        diff.y.set(y, options, yCallback);
    }
    if (z != null) {
        diff.z.set(z, options, zCallback);
    }
    return this;
};

/**
 * Retrieves the computed size applied to the underlying Node.
 *
 * @method
 *
 * @return {Array} size three dimensional computed size
 */
Size.prototype.get = function get () {
    return this._node.getSize();
};

/**
 * Halts all currently active size transitions.
 *
 * @method
 *
 * @return {Size} this
 */
Size.prototype.halt = function halt () {
    this._proportional.x.halt();
    this._proportional.y.halt();
    this._proportional.z.halt();
    this._differential.x.halt();
    this._differential.y.halt();
    this._differential.z.halt();
    this._absolute.x.halt();
    this._absolute.y.halt();
    this._absolute.z.halt();
    return this;
};

module.exports = Size;
<|endoftext|>