<commit_before>module.exports = function(server) {

    var io = require('socket.io')(server);

    var Game = require('./game');
    var Player = require('./player');

    var games = [];

    io.on('connection', function(socket) {

        /**
         * A new client connected, which wants to host a game.
         */
        socket.on('game', function(key) {
            var game = new Game(key, socket);

            // Make sure we can later identify the socket (i.e. on disconnection).
            game.socket.type = 'game';

            // Add the newly created game to the list of games.
            games.push(game);

            console.log('(Game ' + key + '): Game created!');
        });

        /**
         * A new client connected, which wants to attend to a game as a player.
         */
        socket.on('player', function(data) {
            var player = new Player(data.name, socket);

            // Find the game the user wants to join.
            var gameIndex = null;
            for (var i = 0; i < games.length; ++i) {
                if (games[i].key == data.key) {
                    gameIndex = i;
                }
            }

            // Check if the game exists.
            if (gameIndex === null) {
                socket.emit('game-does-not-exist');

                return;
            }

            // Check if there is room for the player in the game.
            if (games[gameIndex].players.length >= 2) {
                socket.emit('no-more-room');

                return;
            }

            // Make sure we can later identify the socket (i.e. on disconnection).
            player.socket.type = 'player';
            player.socket.gameIndex = gameIndex;

            // Add the player to a game.
            games[gameIndex].addPlayer(player);

            // Inform the game a new player is added.
            games[gameIndex].socket.emit('player-joined', games[gameIndex].players.length);

            console.log('(' + data.name + '): Joined ' + games[gameIndex].key + '!');
        });

        /**
         * A client has just disconnected and we need to handle that.
         */
        socket.on('disconnect', function() {
            if (socket.type === 'game') {
                var gameIndex = null;

                // Find the game which disconnected.
                for (var i = 0; i < games.length; ++i) {
                    if (games[i].socket.id === socket.id) {
                        gameIndex = i;
                    }
                }

                console.log('(Game ' + games[gameIndex].key + '): Disconnected.');

                // Inform every player in the game that it doesn't exist anymore.
                games[gameIndex].broadcast('game-destroyed');

                // Get rid of the game.
                games.splice(gameIndex, 1);
            } else if (socket.type === 'player') {
                var playerIndex = null;

                // Check if the game still exists.
                if (typeof games[socket.gameIndex] === 'undefined') {
                    return;
                }

                // Find the player which disconnected.
                for (var i = 0; i < games[socket.gameIndex].players.length; ++i) {
                    if (games[socket.gameIndex].players[i].socket.id == socket.id) {
                        playerIndex = i;
                    }
                }

                console.log('(' + games[socket.gameIndex].players[playerIndex].name + '): Disconnected.');

                // Inform the game the player left.
                games[socket.gameIndex].socket.emit('player-left', games[socket.gameIndex].players.length);

                // Get rid of the player.
                games[socket.gameIndex].players.splice(playerIndex, 1);
            } else {
                console.log('wtf?');
            }
        });

    });

};
<commit_msg>Change some logging things.<commit_after>module.exports = function(server) {

    var io = require('socket.io')(server);

    var Game = require('./game');
    var Player = require('./player');

    var games = [];

    io.on('connection', function(socket) {

        /**
         * A new client connected, which wants to host a game.
         */
        socket.on('game', function(key) {
            var game = new Game(key, socket);

            // Make sure we can later identify the socket (i.e. on disconnection).
            game.socket.type = 'game';

            // Add the newly created game to the list of games.
            games.push(game);

            console.log('(Game ' + key + '): Game created!');
        });

        /**
         * A new client connected, which wants to attend to a game as a player.
         */
        socket.on('player', function(data) {
            var player = new Player(data.name, socket);

            // Find the game the user wants to join.
            var gameIndex = null;
            for (var i = 0; i < games.length; ++i) {
                if (games[i].key == data.key) {
                    gameIndex = i;
                }
            }

            // Check if the game exists.
            if (gameIndex === null) {
                socket.emit('game-does-not-exist');

                return;
            }

            // Check if there is room for the player in the game.
            if (games[gameIndex].players.length >= 2) {
                socket.emit('no-more-room');

                return;
            }

            // Make sure we can later identify the socket (i.e. on disconnection).
            player.socket.type = 'player';
            player.socket.gameIndex = gameIndex;

            // Add the player to a game.
            games[gameIndex].addPlayer(player);

            // Inform the game a new player is added.
            games[gameIndex].socket.emit('player-joined', games[gameIndex].players.length);

            console.log('(Player ' + data.name + '): Joined ' + games[gameIndex].key + '!');
        });

        /**
         * A client has just disconnected and we need to handle that.
         */
        socket.on('disconnect', function() {
            if (socket.type === 'game') {
                var gameIndex = null;

                // Find the game which disconnected.
                for (var i = 0; i < games.length; ++i) {
                    if (games[i].socket.id === socket.id) {
                        gameIndex = i;
                    }
                }

                console.log('(Game ' + games[gameIndex].key + '): Disconnected.');

                // Inform every player in the game that it doesn't exist anymore.
                games[gameIndex].broadcast('game-destroyed');

                // Get rid of the game.
                games.splice(gameIndex, 1);
            } else if (socket.type === 'player') {
                var playerIndex = null;

                // Check if the game still exists.
                if (typeof games[socket.gameIndex] === 'undefined') {
                    return;
                }

                // Find the player which disconnected.
                for (var i = 0; i < games[socket.gameIndex].players.length; ++i) {
                    if (games[socket.gameIndex].players[i].socket.id == socket.id) {
                        playerIndex = i;
                    }
                }

                console.log('(Player ' + games[socket.gameIndex].players[playerIndex].name + '): Disconnected.');

                // Inform the game the player left.
                games[socket.gameIndex].socket.emit('player-left', games[socket.gameIndex].players.length);

                // Get rid of the player.
                games[socket.gameIndex].players.splice(playerIndex, 1);
            } else {
                console.log('wtf?');
            }
        });

    });

};
<|endoftext|>


<commit_before>'use strict';

/**
 * @ngdoc function
 * @name Pear2Pear.service:Pear
 * @description
 * # Pear service
 * Provides controllers with a data model for pear to pear app
 * It serves as an abstraction between Pear data and backend (SwellRT)
 */

angular.module('Pear2Pear')
  .factory('pear', [
           '$rootScope', 'swellRT', '$q', '$timeout', 'base64',
           function($rootScope, swellRT, $q, $timeout, base64) {

    var proxy = {
    };

    var DATATYPES = {
      PROJECT: 'PROJECT',
      COMMUNITY: 'COMMUNITY'
    };

    // FIXME model prototype
    var urlId = function(id) {
      if (id === undefined) { return ''; }

      return base64.encode(id);
    };

    // map of opened projects
    var openedProjects = {};
    // map of opened communities
    var openedCommunities = {};

    var def = $q.defer();

    function makeModelPublic(model){
      model.addParticipant('@' + SwellRTConfig.swellrtServerDomain,
                           null,
                           function(err){
                             console.log('ERROR: ' + err);
                           }
                          );
    }

    var communities = {

      all: function() {
        var foundCommunities = $q.defer();
        SwellRT.query(
          {
            'root.type': DATATYPES.COMMUNITY
          },
          function(result){
            var comms = [];
            angular.forEach(result.result, function(val) {
              comms.push(val.root);
            });
            foundCommunities.resolve(comms);
          },
          function(e){
            foundCommunities.reject(e);
          }
        );
        return foundCommunities.promise;
      },

      find: function(urlId) {

        var id = base64.decode(urlId);
        var comDef = $q.defer();
        var community = comDef.promise;

        if (!openedCommunities[id]) {
          openedCommunities[id] = comDef.promise;
          window.SwellRT.openModel(id, function(model){
            var pr = swellRT.proxy(model);
            comDef.resolve(pr);
          }, function(error){
            console.log(error);
            comDef.reject(error);
          });
        } else {
          openedCommunities[id].then(
            function(r){
              comDef.resolve(r);
            });
        }

        var allSnapshot = function(){
              var foundProjects = $q.defer();
              SwellRT.query(
                {
                  'root.type': DATATYPES.PROJECT,
                  'root.communities': id,
                  $and: [{
                    $or: [
                      {'root.shareMode': 'public'},
                      {'root.supporters': users.current()},
                      {'root.contributors': users.current()}
                    ]
                  }]
                },
                function(result){
                  var projs = [];
                  angular.forEach(result.result, function(val) {
                    projs.push(val.root);
                  });
                 foundProjects.resolve(projs);
                },
                function(e){
                  foundProjects.reject(e);
                }
              );
              return foundProjects.promise;
      };
      return {
          community: comDef.promise,
          projects: {

            allSnapshot: allSnapshot,

            all: function() {
              var projsDef = $q.defer();
              allSnapshot().then(function(all){
                var promises = {};
                if (all.length > 0){
                  angular.forEach(all, function(val){
                    var projDef = $q.defer();
                    promises[val.id] = projDef.promise;
                    projects.find(base64.encode(val.id)).then(function(model){
                      $timeout(function(){
                        projDef.resolve(model);
                      });
                    });
                  });
                  projsDef.resolve($q.all(promises));
                } else {
                  projsDef.resolve([]);
                }
              });

              return projsDef.promise;
            },
            destroy: function(projId){
              var i = community.projects.indexOf(projId);
              if (i > -1){
                community.projects.splice(i,1);
              }
            }
          }
        };
      },
      create: function(data, callback) {
        var id = window.SwellRT.createModel(function(model){

          makeModelPublic(model);
          var p = swellRT.proxy(model);

          $timeout(function(){
            p.type = DATATYPES.COMMUNITY;
            p.name = data.name;
            p.id = id;
            p.projects = [];
            callback(p);
          });
        });
      },
      destroy: function(urlId) {
        var id = base64.decode(urlId);
        // currently not supported by SwellRT
        return urlId;
      }
    };

    var findProjects = function(urlId) {
        var id = base64.decode(urlId);
        var def = $q.defer();
        if (!openedProjects[id]) {
          openedProjects[id] = def.promise;
          window.SwellRT.openModel(id, function(model){
            var pr = swellRT.proxy(model);
            def.resolve(pr);
          }, function(error){
            console.log(error);
            def.reject(error);
          });
        } else {
          openedProjects[id].then(
            function(r){
              def.resolve(r);
            });
        }
        return def.promise;
    };
    var projects = {

      find: findProjects,

      create: function(callback, communityId) {
        var id = window.SwellRT.createModel(function(model){

          makeModelPublic(model);
          var proxyProj = swellRT.proxy(model);
          $timeout(function(){
            proxyProj.type = DATATYPES.PROJECT;
            proxyProj.communities = (communityId) ? [communityId] : [];
            proxyProj.id = id;
            proxyProj.title = '';
            proxyProj.chat = [];
            proxyProj.pad = new swellRT.TextObject();
            proxyProj.needs = [];
            proxyProj.promoter = users.current();
            proxyProj.supporters = [];
            proxyProj.contributors = [users.current()];
            proxyProj.shareMode = 'link';
            var d = $q.defer();
            d.resolve(proxyProj);
            openedProjects[id] = d.promise;
            callback(proxyProj);
          });
        });
      },

      setShareMode: function(projId, shareMode){
        projects.find(projId).then(
          function(project){
            $timeout(function(){
              project.shareMode = shareMode;
            });
          }
        );
      },

      addContributor: function(projId, user){
        if (!user){
          user = users.current();
        }
        console.log('user',user);
        projects.find(projId).then(
          function(p){
            if (user && p.contributors.indexOf(user) < 0){
              p.contributors.push(user);
            }
          });
      }
    };

    var users = {
      current: function() {
        return window.sessionStorage.getItem('userId');
      },
      setCurrent: function(name) {
        var cleanedName = name ? name.trim() : name;

        return window.sessionStorage.setItem('userId', cleanedName);
      },
      isCurrent: function(user) {
        return user === users.current();
      },
      loggedIn: function() {
        return users.current() !== 'undefined' && users.current() !== null;
      }
    };

    var addChatMessage = function(projectId, message) {
      projects.find(projectId).then(function(project){
        project.chat.push({
          text: message,
          who: users.current(),
          time: (new Date()).toJSON()
        });
        projects.addContributor(projectId);
      }, function(error){
        console.log(error);
      });
    };

    var toggleSupport = function(projectId) {
      projects.find(base64.encode(projectId)).then(
        function(model){
          var index = model.supporters.indexOf(users.current());

          $timeout(function(){
            if (index > -1) {
              model.supporters.splice(index, 1);
            } else {
              model.supporters.push(users.current());
            }
          });
        },
        function(error){
          console.log(error);
        }
      );
    };

    var registerUser = function(userName, password, onSuccess, onError){
      window.SwellRT.registerUser(SwellRTConfig.server, userName, password, onSuccess, onError);
    };

    var startSession = function(userName, password, onSuccess, onError){

      // if login with user Name, close previous anonimous session
      if (userName){
        window.SwellRT.stopSession();
      }
      window.SwellRT.startSession(
        SwellRTConfig.server, userName || SwellRT.user.ANONYMOUS, password || '',
        function(){
          SwellRT.on(SwellRT.events.NETWORK_CONNECTED, onSuccess);
        }, onError);
    };

    window.onSwellRTReady = function () {
      startSession(null, null, function(){
          $timeout(
            function() {
              communities.all();
              def.resolve(SwellRT);
            });
        },
        function(error) {
          console.log(error);
        });
      // to avoid multiple calls
      window.onSwellRTReady = null;
    };

    if (window.SwellRT && typeof window.onSwellRTReady === 'function') {
      window.onSwellRTReady();
    }

    return {
      communities: communities,
      projects: projects,
      users: users,
      urlId: urlId,
      addChatMessage: addChatMessage,
      toggleSupport: toggleSupport,
      startSession: startSession,
      registerUser: registerUser,
      onLoad: function(f) {
        def.promise.then(f);
      }
    };
  }]);
<commit_msg>Simplify wave datatypes<commit_after>'use strict';

/**
 * @ngdoc function
 * @name Pear2Pear.service:Pear
 * @description
 * # Pear service
 * Provides controllers with a data model for pear to pear app
 * It serves as an abstraction between Pear data and backend (SwellRT)
 */

angular.module('Pear2Pear')
  .factory('pear', [
           '$rootScope', 'swellRT', '$q', '$timeout', 'base64',
           function($rootScope, swellRT, $q, $timeout, base64) {

    var proxy = {
    };

    // FIXME model prototype
    var urlId = function(id) {
      if (id === undefined) { return ''; }

      return base64.encode(id);
    };

    // map of opened projects
    var openedProjects = {};
    // map of opened communities
    var openedCommunities = {};

    var def = $q.defer();

    function makeModelPublic(model){
      model.addParticipant('@' + SwellRTConfig.swellrtServerDomain,
                           null,
                           function(err){
                             console.log('ERROR: ' + err);
                           }
                          );
    }

    var communities = {

      all: function() {
        var foundCommunities = $q.defer();
        SwellRT.query(
          {
            'root.type': 'community'
          },
          function(result){
            var comms = [];
            angular.forEach(result.result, function(val) {
              comms.push(val.root);
            });
            foundCommunities.resolve(comms);
          },
          function(e){
            foundCommunities.reject(e);
          }
        );
        return foundCommunities.promise;
      },

      find: function(urlId) {

        var id = base64.decode(urlId);
        var comDef = $q.defer();
        var community = comDef.promise;

        if (!openedCommunities[id]) {
          openedCommunities[id] = comDef.promise;
          window.SwellRT.openModel(id, function(model){
            var pr = swellRT.proxy(model);
            comDef.resolve(pr);
          }, function(error){
            console.log(error);
            comDef.reject(error);
          });
        } else {
          openedCommunities[id].then(
            function(r){
              comDef.resolve(r);
            });
        }

        var allSnapshot = function(){
              var foundProjects = $q.defer();
              SwellRT.query(
                {
                  'root.type': 'project',
                  'root.communities': id,
                  $and: [{
                    $or: [
                      {'root.shareMode': 'public'},
                      {'root.supporters': users.current()},
                      {'root.contributors': users.current()}
                    ]
                  }]
                },
                function(result){
                  var projs = [];
                  angular.forEach(result.result, function(val) {
                    projs.push(val.root);
                  });
                 foundProjects.resolve(projs);
                },
                function(e){
                  foundProjects.reject(e);
                }
              );
              return foundProjects.promise;
      };
      return {
          community: comDef.promise,
          projects: {

            allSnapshot: allSnapshot,

            all: function() {
              var projsDef = $q.defer();
              allSnapshot().then(function(all){
                var promises = {};
                if (all.length > 0){
                  angular.forEach(all, function(val){
                    var projDef = $q.defer();
                    promises[val.id] = projDef.promise;
                    projects.find(base64.encode(val.id)).then(function(model){
                      $timeout(function(){
                        projDef.resolve(model);
                      });
                    });
                  });
                  projsDef.resolve($q.all(promises));
                } else {
                  projsDef.resolve([]);
                }
              });

              return projsDef.promise;
            },
            destroy: function(projId){
              var i = community.projects.indexOf(projId);
              if (i > -1){
                community.projects.splice(i,1);
              }
            }
          }
        };
      },
      create: function(data, callback) {
        var id = window.SwellRT.createModel(function(model){

          makeModelPublic(model);
          var p = swellRT.proxy(model);

          $timeout(function(){
            p.type = 'community';
            p.name = data.name;
            p.id = id;
            p.projects = [];
            callback(p);
          });
        });
      },
      destroy: function(urlId) {
        var id = base64.decode(urlId);
        // currently not supported by SwellRT
        return urlId;
      }
    };

    var findProjects = function(urlId) {
        var id = base64.decode(urlId);
        var def = $q.defer();
        if (!openedProjects[id]) {
          openedProjects[id] = def.promise;
          window.SwellRT.openModel(id, function(model){
            var pr = swellRT.proxy(model);
            def.resolve(pr);
          }, function(error){
            console.log(error);
            def.reject(error);
          });
        } else {
          openedProjects[id].then(
            function(r){
              def.resolve(r);
            });
        }
        return def.promise;
    };
    var projects = {

      find: findProjects,

      create: function(callback, communityId) {
        var id = window.SwellRT.createModel(function(model){

          makeModelPublic(model);
          var proxyProj = swellRT.proxy(model);
          $timeout(function(){
            proxyProj.type = 'project';
            proxyProj.communities = (communityId) ? [communityId] : [];
            proxyProj.id = id;
            proxyProj.title = '';
            proxyProj.chat = [];
            proxyProj.pad = new swellRT.TextObject();
            proxyProj.needs = [];
            proxyProj.promoter = users.current();
            proxyProj.supporters = [];
            proxyProj.contributors = [users.current()];
            proxyProj.shareMode = 'link';
            var d = $q.defer();
            d.resolve(proxyProj);
            openedProjects[id] = d.promise;
            callback(proxyProj);
          });
        });
      },

      setShareMode: function(projId, shareMode){
        projects.find(projId).then(
          function(project){
            $timeout(function(){
              project.shareMode = shareMode;
            });
          }
        );
      },

      addContributor: function(projId, user){
        if (!user){
          user = users.current();
        }
        console.log('user',user);
        projects.find(projId).then(
          function(p){
            if (user && p.contributors.indexOf(user) < 0){
              p.contributors.push(user);
            }
          });
      }
    };

    var users = {
      current: function() {
        return window.sessionStorage.getItem('userId');
      },
      setCurrent: function(name) {
        var cleanedName = name ? name.trim() : name;

        return window.sessionStorage.setItem('userId', cleanedName);
      },
      isCurrent: function(user) {
        return user === users.current();
      },
      loggedIn: function() {
        return users.current() !== 'undefined' && users.current() !== null;
      }
    };

    var addChatMessage = function(projectId, message) {
      projects.find(projectId).then(function(project){
        project.chat.push({
          text: message,
          who: users.current(),
          time: (new Date()).toJSON()
        });
        projects.addContributor(projectId);
      }, function(error){
        console.log(error);
      });
    };

    var toggleSupport = function(projectId) {
      projects.find(base64.encode(projectId)).then(
        function(model){
          var index = model.supporters.indexOf(users.current());

          $timeout(function(){
            if (index > -1) {
              model.supporters.splice(index, 1);
            } else {
              model.supporters.push(users.current());
            }
          });
        },
        function(error){
          console.log(error);
        }
      );
    };

    var registerUser = function(userName, password, onSuccess, onError){
      window.SwellRT.registerUser(SwellRTConfig.server, userName, password, onSuccess, onError);
    };

    var startSession = function(userName, password, onSuccess, onError){

      // if login with user Name, close previous anonimous session
      if (userName){
        window.SwellRT.stopSession();
      }
      window.SwellRT.startSession(
        SwellRTConfig.server, userName || SwellRT.user.ANONYMOUS, password || '',
        function(){
          SwellRT.on(SwellRT.events.NETWORK_CONNECTED, onSuccess);
        }, onError);
    };

    window.onSwellRTReady = function () {
      startSession(null, null, function(){
          $timeout(
            function() {
              communities.all();
              def.resolve(SwellRT);
            });
        },
        function(error) {
          console.log(error);
        });
      // to avoid multiple calls
      window.onSwellRTReady = null;
    };

    if (window.SwellRT && typeof window.onSwellRTReady === 'function') {
      window.onSwellRTReady();
    }

    return {
      communities: communities,
      projects: projects,
      users: users,
      urlId: urlId,
      addChatMessage: addChatMessage,
      toggleSupport: toggleSupport,
      startSession: startSession,
      registerUser: registerUser,
      onLoad: function(f) {
        def.promise.then(f);
      }
    };
  }]);
<|endoftext|>


<commit_before>var util = require('util'),
    webutil = require('../util/web'),
    Tab = require('../client/tab').Tab,
    settings = require('../util/settings'),
    Currency = ripple.Currency;

var AdvancedTab = function ()
{
  Tab.call(this);
};

util.inherits(AdvancedTab, Tab);

AdvancedTab.prototype.tabName = 'advanced';
AdvancedTab.prototype.mainMenu = 'advanced';

AdvancedTab.prototype.generateHtml = function ()
{
  return require('../../jade/tabs/advanced.jade')();
};

AdvancedTab.prototype.angular = function(module)
{
  module.controller('AdvancedCtrl', ['$scope', 'rpId', 'rpKeychain', 'rpNetwork',
                                    function ($scope, id, keychain, network)
  {
    // XRP currency object.
    // {name: 'XRP - Ripples', order: 146, value: 'XRP'}
    var xrpCurrency = Currency.from_json('XRP');

    $scope.xrp = {
      name: xrpCurrency.to_human({full_name:$scope.currencies_all_keyed.XRP.name}),
      code: xrpCurrency.get_iso(),
      currency: xrpCurrency
    };

    $scope.options = Options;
    $scope.optionsBackup = $.extend(true, {}, Options);
    $scope.passwordProtection = !settings.getSetting($scope.userBlob, 'persistUnlock', false);
    $scope.editConfirmation = {
      send: false,
      exchange: false,
      trade: false
    };
    $scope.edit = {
      advanced_feature_switch: false,
      blobvault: false,
      bridge: false,
      maxNetworkFee: false,
      historyApi: false
    };
    $scope.max_tx_network_fee_human = ripple.Amount.from_json($scope.options.max_tx_network_fee).to_human();
    $scope.advancedFeatureSwitchChanged = false;
    $scope.confirmationChanged = {
      send: false,
      exchange: false,
      trade: false
    };

    // Initialize the notification object
    $scope.success = {};

    $scope.save = function(type) {
      switch (type) {
        case 'maxNetworkFee':
          $scope.options.max_tx_network_fee = ripple.Amount.from_human($scope.max_tx_network_fee_human).to_json();
          // This has to be updated manually because the network object is not
          // recreated unless we do location.reload()
          network.remote.max_fee = $scope.options.max_tx_network_fee;
          break;
        case 'advanced_feature_switch':
          // Ignore it if we are not going to change anything
          if (!$scope.advancedFeatureSwitchChanged) {
            $scope.edit[type] = false;
            return;
          }
          $scope.advancedFeatureSwitchChanged = false;
          $scope.userBlob.set('/clients/rippletradecom/trust/advancedMode', $scope.options.advanced_feature_switch);
          break;
        case 'historyApi':
          $scope.userBlob.set('/clients/rippletradecom/historyApi', $scope.options.historyApi);
          break;
        default:
          // Save in local storage
          if (!store.disabled) {
            store.set('ripple_settings', JSON.stringify($scope.options));
          }
      }

      $scope.edit[type] = false;

      if (type === 'blobvault') {
        location.reload();
      }

      // Notify the user
      $scope.success[type] = true;
    };

    $scope.saveConfirmation = function(transactionType) {
      // ignore it if we are not going to change anything
      if (!$scope.confirmationChanged[transactionType]) {
        $scope.editConfirmation[transactionType] = false;
        return;
      }
      $scope.confirmationChanged[transactionType] = false;

      $scope.userBlob.set('/clients/rippletradecom/confirmation', $scope.options.confirmation);

      $scope.editConfirmation[transactionType] = false;

      // Notify the user
      $scope.success.saveConfirmation[transactionType] = true;
    };

    $scope.deleteUrl = function(type) {
      switch (type) {
        case 'blobvault':
          $scope.options.blobvault = '';
          break;
        case 'bridge':
          $scope.options.bridge.out.bitcoin = '';
      }

      // Save in local storage
      if (!store.disabled) {
        store.set('ripple_settings', JSON.stringify($scope.options));
      }
    };

    $scope.cancelEdit = function(type) {
      $scope.edit[type] = false;
      if (type === 'maxNetworkFee') {
        $scope.options.max_tx_network_fee = $scope.optionsBackup.max_tx_network_fee;
        $scope.max_tx_network_fee_human = ripple.Amount.from_json($scope.options.max_tx_network_fee).to_human();
      } else if (type === 'bridge') {
        $scope.options.bridge.out.bitcoin = $scope.optionsBackup.bridge.out.bitcoin;
      } else {
        $scope.options[type] = $scope.optionsBackup[type];
      }
    };

    $scope.cancelEditConfirmation = function(transactionType) {
      $scope.editConfirmation[transactionType] = false;
      $scope.options.confirmation[transactionType] = $scope.optionsBackup.confirmation[transactionType];
    };

    $scope.$on('$blobUpdate', function() {
      $scope.passwordProtection = !settings.getSetting($scope.userBlob, 'persistUnlock', false);

      // we assume that some fields in Options are updated in rpId service $blobUpdate handler
      $scope.optionsBackup = $.extend(true, {}, Options);
    });

    // Add a new server
    $scope.addServer = function() {
      // Create a new server line
      if (!$scope.options.server.servers.isEmptyServer)
        $scope.options.server.servers.push({isEmptyServer: true, secure: false});

      // Set editing to true
      $scope.editing = true;

      // Notify the user on save later
      $scope.success.addServer = true;
    };
  }]);

  module.controller('ServerRowCtrl', ['$scope',
    function ($scope) {
      $scope.editing = $scope.server.isEmptyServer;

      // Delete the server
      $scope.remove = function () {
        $scope.options.server.servers.splice($scope.index, 1);

        // Save in local storage
        if (!store.disabled) {
          store.set('ripple_settings', JSON.stringify($scope.options));
        }

        // Notify the user
        $scope.success.removeServer = true;
      };

      $scope.hasRemove = function () {
        return !$scope.server.isEmptyServer && $scope.options.server.servers.length !== 1;
      };

      $scope.cancel = function () {
        if ($scope.server.isEmptyServer) {
          $scope.remove();
          return;
        }

        $scope.editing = false;
        $scope.server = $.extend({}, $scope.optionsBackup.server.servers[$scope.index]);
      };

      $scope.noCancel = function () {
        return $scope.server.isEmptyServer && $scope.options.server.servers.length === 1;
      };

      $scope.save = function () {
        $scope.server.isEmptyServer = false;
        $scope.editing = false;

        // Save in local storage
        if (!store.disabled) {
          store.set('ripple_settings', JSON.stringify($scope.options));
        }

        // Notify the user
        $scope.success.saveServer = true;

        // Reload
        // A force reload is necessary here because we have to re-initialize
        // the network object with the new server list.
        location.reload();
      };
    }
  ]);
};

module.exports = AdvancedTab;
<commit_msg>[TASK] Advanced: Default ports for server settings<commit_after>var util = require('util'),
    webutil = require('../util/web'),
    Tab = require('../client/tab').Tab,
    settings = require('../util/settings'),
    Currency = ripple.Currency;

var AdvancedTab = function ()
{
  Tab.call(this);
};

util.inherits(AdvancedTab, Tab);

AdvancedTab.prototype.tabName = 'advanced';
AdvancedTab.prototype.mainMenu = 'advanced';

AdvancedTab.prototype.generateHtml = function ()
{
  return require('../../jade/tabs/advanced.jade')();
};

AdvancedTab.prototype.angular = function(module)
{
  module.controller('AdvancedCtrl', ['$scope', 'rpId', 'rpKeychain', 'rpNetwork',
                                    function ($scope, id, keychain, network)
  {
    // XRP currency object.
    // {name: 'XRP - Ripples', order: 146, value: 'XRP'}
    var xrpCurrency = Currency.from_json('XRP');

    $scope.xrp = {
      name: xrpCurrency.to_human({full_name:$scope.currencies_all_keyed.XRP.name}),
      code: xrpCurrency.get_iso(),
      currency: xrpCurrency
    };

    $scope.options = Options;
    $scope.optionsBackup = $.extend(true, {}, Options);
    $scope.passwordProtection = !settings.getSetting($scope.userBlob, 'persistUnlock', false);
    $scope.editConfirmation = {
      send: false,
      exchange: false,
      trade: false
    };
    $scope.edit = {
      advanced_feature_switch: false,
      blobvault: false,
      bridge: false,
      maxNetworkFee: false,
      historyApi: false
    };
    $scope.max_tx_network_fee_human = ripple.Amount.from_json($scope.options.max_tx_network_fee).to_human();
    $scope.advancedFeatureSwitchChanged = false;
    $scope.confirmationChanged = {
      send: false,
      exchange: false,
      trade: false
    };

    // Initialize the notification object
    $scope.success = {};

    $scope.save = function(type) {
      switch (type) {
        case 'maxNetworkFee':
          $scope.options.max_tx_network_fee = ripple.Amount.from_human($scope.max_tx_network_fee_human).to_json();
          // This has to be updated manually because the network object is not
          // recreated unless we do location.reload()
          network.remote.max_fee = $scope.options.max_tx_network_fee;
          break;
        case 'advanced_feature_switch':
          // Ignore it if we are not going to change anything
          if (!$scope.advancedFeatureSwitchChanged) {
            $scope.edit[type] = false;
            return;
          }
          $scope.advancedFeatureSwitchChanged = false;
          $scope.userBlob.set('/clients/rippletradecom/trust/advancedMode', $scope.options.advanced_feature_switch);
          break;
        case 'historyApi':
          $scope.userBlob.set('/clients/rippletradecom/historyApi', $scope.options.historyApi);
          break;
        default:
          // Save in local storage
          if (!store.disabled) {
            store.set('ripple_settings', JSON.stringify($scope.options));
          }
      }

      $scope.edit[type] = false;

      if (type === 'blobvault') {
        location.reload();
      }

      // Notify the user
      $scope.success[type] = true;
    };

    $scope.saveConfirmation = function(transactionType) {
      // ignore it if we are not going to change anything
      if (!$scope.confirmationChanged[transactionType]) {
        $scope.editConfirmation[transactionType] = false;
        return;
      }
      $scope.confirmationChanged[transactionType] = false;

      $scope.userBlob.set('/clients/rippletradecom/confirmation', $scope.options.confirmation);

      $scope.editConfirmation[transactionType] = false;

      // Notify the user
      $scope.success.saveConfirmation[transactionType] = true;
    };

    $scope.deleteUrl = function(type) {
      switch (type) {
        case 'blobvault':
          $scope.options.blobvault = '';
          break;
        case 'bridge':
          $scope.options.bridge.out.bitcoin = '';
      }

      // Save in local storage
      if (!store.disabled) {
        store.set('ripple_settings', JSON.stringify($scope.options));
      }
    };

    $scope.cancelEdit = function(type) {
      $scope.edit[type] = false;
      if (type === 'maxNetworkFee') {
        $scope.options.max_tx_network_fee = $scope.optionsBackup.max_tx_network_fee;
        $scope.max_tx_network_fee_human = ripple.Amount.from_json($scope.options.max_tx_network_fee).to_human();
      } else if (type === 'bridge') {
        $scope.options.bridge.out.bitcoin = $scope.optionsBackup.bridge.out.bitcoin;
      } else {
        $scope.options[type] = $scope.optionsBackup[type];
      }
    };

    $scope.cancelEditConfirmation = function(transactionType) {
      $scope.editConfirmation[transactionType] = false;
      $scope.options.confirmation[transactionType] = $scope.optionsBackup.confirmation[transactionType];
    };

    $scope.$on('$blobUpdate', function() {
      $scope.passwordProtection = !settings.getSetting($scope.userBlob, 'persistUnlock', false);

      // we assume that some fields in Options are updated in rpId service $blobUpdate handler
      $scope.optionsBackup = $.extend(true, {}, Options);
    });

    // Add a new server
    $scope.addServer = function() {
      // Create a new server line
      if (!$scope.options.server.servers.isEmptyServer)
        $scope.options.server.servers.push({isEmptyServer: true, secure: false});

      // Set editing to true
      $scope.editing = true;

      // Notify the user on save later
      $scope.success.addServer = true;
    };
  }]);

  module.controller('ServerRowCtrl', ['$scope',
    function ($scope) {
      $scope.editing = $scope.server.isEmptyServer;

      // Delete the server
      $scope.remove = function () {
        $scope.options.server.servers.splice($scope.index, 1);

        // Save in local storage
        if (!store.disabled) {
          store.set('ripple_settings', JSON.stringify($scope.options));
        }

        // Notify the user
        $scope.success.removeServer = true;
      };

      $scope.hasRemove = function () {
        return !$scope.server.isEmptyServer && $scope.options.server.servers.length !== 1;
      };

      $scope.cancel = function () {
        if ($scope.server.isEmptyServer) {
          $scope.remove();
          return;
        }

        $scope.editing = false;
        $scope.server = $.extend({}, $scope.optionsBackup.server.servers[$scope.index]);
      };

      $scope.noCancel = function () {
        return $scope.server.isEmptyServer && $scope.options.server.servers.length === 1;
      };

      $scope.save = function () {
        $scope.server.isEmptyServer = false;
        $scope.editing = false;

        // Determine port if empty
        if (!$scope.server.port) {
          $scope.server.port = $scope.server.secure ? '443' : '80'
        }

        // Save in local storage
        if (!store.disabled) {
          store.set('ripple_settings', JSON.stringify($scope.options));
        }

        // Notify the user
        $scope.success.saveServer = true;

        // Reload
        // A force reload is necessary here because we have to re-initialize
        // the network object with the new server list.
        location.reload();
      };
    }
  ]);
};

module.exports = AdvancedTab;
<|endoftext|>


<commit_before>var LZString = require('lz-string');

var Persist = {
	getItem: function(key, decompress) {
		decompress = (decompress === undefined) ? true : decompress;

		var item = (decompress) ? LZString.decompress(localStorage.getItem(key)) : localStorage.getItem(key);

		return (item === '' || item === null) ? null : JSON.parse(item);
	},

	getChromeSyncItem: function(item, callback) {
		chrome.storage.sync.get(item, callback);
	},

	setItem: function(key, item, compress) {
		compress = (compress === undefined) ? true : compress;

		item = (compress) ? LZString.compress(JSON.stringify(item)) : JSON.stringify(item);

		localStorage.setItem(key, item);
	},

	removeItem: function(key) {
		localStorage.removeItem(key);
	},

	clear: function() {
		localStorage.clear();
	}
};

module.exports = Persist;
<commit_msg>clear chrome sync<commit_after>var LZString = require('lz-string');

var Persist = {
	getItem: function(key, decompress) {
		decompress = (decompress === undefined) ? true : decompress;

		var item = (decompress) ? LZString.decompress(localStorage.getItem(key)) : localStorage.getItem(key);

		return (item === '' || item === null) ? null : JSON.parse(item);
	},

	getChromeSyncItem: function(item, callback) {
		chrome.storage.sync.get(item, callback);
	},

	setItem: function(key, item, compress) {
		compress = (compress === undefined) ? true : compress;

		item = (compress) ? LZString.compress(JSON.stringify(item)) : JSON.stringify(item);

		localStorage.setItem(key, item);
	},

	removeItem: function(key) {
		localStorage.removeItem(key);
	},

	clear: function() {
		localStorage.clear();
		chrome.storage.sync.clear();
	}
};

module.exports = Persist;
<|endoftext|>


<commit_before>import { Point } from './Point';

function Marker( ctx, count ) {

	const materials = ctx.materials;

	Point.call( this, materials.getClusterMaterial( count ), ctx );

	return this;

}

Marker.prototype = Object.create( Point.prototype );

Marker.prototype.isMarker = true;

Marker.prototype.adjustHeight = function ( func ) {

	this.position.setZ( func( this.position ) + 10 );

};

export { Marker };<commit_msg>fix render order of cluster markers<commit_after>import { Point } from './Point';

function Marker( ctx, count ) {

	const materials = ctx.materials;

	Point.call( this, materials.getClusterMaterial( count ), ctx );
	this.renderOrder = 1;

	return this;

}

Marker.prototype = Object.create( Point.prototype );

Marker.prototype.isMarker = true;

Marker.prototype.adjustHeight = function ( func ) {

	this.position.setZ( func( this.position ) + 10 );

};

export { Marker };<|endoftext|>


<commit_before>/*jslint nomen: true */
/*globals define: true */
/*globals console: true */

define(function (require) {
  'use strict';

  var $ = require('jquery');
  var _ = require('lib/lodash');
  var Backbone = require('backbone');
  var _kmq = require('lib/kissmetrics');

  var api = require('api');
  var template = require('text!templates/surveys/form-editor.html');
  var Form = require('models/forms');

  var Builder = {};

  Builder.BuilderView = Backbone.View.extend({
    template: _.template(template),

    el: '#builder-view-container',

    boxAnswersByQuestionId: {},
    formQuestions: $('#editor'),
    questionsByParentId: {},
    repeatCounter: {},
    templates: {
      question: _.template($('#question-edit-template').html()),
      photoQuestion: _.template($('#photo-question-edit-template').html()),
      textQuestion: _.template($('#text-question-edit-template').html()),
      answer: _.template($('#answer-edit-template').html())
    },

    events: {
      'click .save': 'save'
    },

    initialize: function(options) {
      _.bindAll(this,
        'render',
        'save',

        'makeBlankQuestion',
        'renderForm',
        'suffix',
        'slugify',

        'editQuestionFactory',
        'setQuestionType',
        'deleteQuestion',
        'createQuestionFactory',
        'createPhotoQuestion',
        'addSubQuestionFactory',
        'renderQuestion',

        'createAnswer',
        'editAnswer',
        'deleteAnswer'
      );

      this.forms = options.forms;
    },

    render: function() {
      this.renderForm();
    },

    save: function(event) {
      event.preventDefault();
      console.log('Saving form');
      _kmq.push(['record', 'Survey questions saved']);

      api.createForm(this.forms.getMostRecentForm(), function(){
        console.log('Form successfully saved');
        $(".saved").fadeIn().css("display","inline-block").delay(2000).fadeOut();
      });
    },

    // The blank question template.
    makeBlankQuestion: function(){
      return {
        "text": "",
        "value": "",
        "answers": [
          {
            "text": "",
            "value": ""
          },
          {
            "text": "",
            "value": ""
          }
        ]
      };
    },

    renderForm: function() {
      // Render the editor template
      this.$el.html(this.template({}));

      // Clear out the editor, just in case.
      this.formQuestions = $('#editor');
      this.formQuestions.html('');

      var form = this.forms.getMostRecentForm();
      var formData;

      // Default to a blank question if the form is empty
      if (form === undefined) {
        formData = {};
        formData.questions = [];
        formData.questions.push(this.makeBlankQuestion());
        this.forms.add(new Form.Model({ questions: formData.questions }));
      } else {
        formData = {
          questions: form.get('questions')
        };
      }

      // Render form
      _.each(formData.questions, function (question, questionIndex) {
        this.renderQuestion(question, undefined, undefined, undefined, undefined, questionIndex, formData.questions);
      }, this);
    },

    suffix: function(name) {
      if(_.has(this.repeatCounter, name)) {
        this.repeatCounter[name] += 1;
        return '-' + this.repeatCounter[name].toString();
      }

      this.repeatCounter[name] = 1;
      return '';
    },

    editQuestionFactory: function(question) {
      return function(event) {
        console.log('Updating question');
        _kmq.push(['record', 'Question edited']);
        var text = $(event.target).val(); //$(this).val();
        var name = this.slugify(text);

        question.text = text;
        question.name = name;

        if(question.type === 'checkbox') {
          _.each(question.answers, function(answer){
            answer.name = question.name + '-' + this.slugify(answer.text);
            answer.value = 'yes';
          }.bind(this));
        }
      };
    },

    setQuestionType: function(question, dataRole) {
      console.log(dataRole);

      if (dataRole === 'radio-question') {
        delete question.type;
        _.each(question.answers, function(answer){
          answer.value = this.slugify(answer.text);
        }.bind(this));
      }

      if (dataRole === 'checkbox-question') {
        question.type = "checkbox";
        // Make sure each answer has a name
        _.each(question.answers, function(answer){
          answer.name = question.name + '-' + this.slugify(answer.text);
          answer.value = 'yes';
        }.bind(this));
      }

      if (dataRole === 'file-question') {
        question.type = 'file';
        delete question.answers;
      }

      if (dataRole === 'text-question') {
        question.type = 'text';
        delete question.answers;
      }
    },

    deleteQuestion: function($question, parent, questionIndex) {
      return function(event) {
        console.log("removing question");
        _kmq.push(['record', 'Question deleted']);

        // Remove it from the DOM.
        $question.remove();

        // Remove it from the json
        parent.splice(questionIndex, 1);
      };
    },

    createQuestionFactory: function(parent, questionIndex) {
      return function(event) {
        event.preventDefault();
        var type = $(event.currentTarget).attr('data-type');

        console.log("Adding a new question");
        _kmq.push(['record', 'Question added']);

        var newQuestion = this.makeBlankQuestion();
        this.setQuestionType(newQuestion, type);
        parent.splice(questionIndex + 1, 0, newQuestion);

        this.renderForm();
      };
    },

    createPhotoQuestion: function(parent, questionIndex) {
      return function(event) {
        console.log("Adding a new photo question");
        _kmq.push(['record', 'Question added']);

        var newQuestion = this.makeBlankQuestion();
        newQuestion.type = 'file';
        delete newQuestion.answers;
        parent.splice(questionIndex + 1, 0, newQuestion);

        this.renderForm();
      };
    },

    createAnswer: function(question) {
      return function(event) {
        _kmq.push(['record', 'Answer added']);
        question.answers.push({
          'text': '',
          'value': ''
        });
        this.setQuestionLayout(question);

        this.renderForm();
      };
    },

    addSubQuestionFactory: function(question, index) {
      return function(event) {
        event.preventDefault();
        var type = $(event.currentTarget).attr('data-type');

        console.log("Adding sub-question", type);
        _kmq.push(['record', 'Sub-question added']);

        var newQuestion = this.makeBlankQuestion();
        this.setQuestionType(newQuestion, type);

        // Add the question to the end of the array, or create a new array
        if (_.has(question.answers[index], 'questions')) {
          question.answers[index].questions.unshift(newQuestion);
        }else {
          question.answers[index].questions = [newQuestion];
        }

        this.renderForm();
      };
    },

    editAnswer: function(question, index) {
      return function(event){
        _kmq.push(['record', 'Answer edited']);
        var text = $(event.target).val();
        question.answers[index].text = text;
        question.answers[index].value = this.slugify(text);

        this.setQuestionLayout(question);
        if(question.type === 'checkbox') {
          question.answers[index].name = question.name + '-' + this.slugify(text);
          question.answers[index].value = 'yes';
        }

      };
    },

    deleteAnswer: function(question, index, $answer) {
      return function(event) {
        console.log("removing answer");
        _kmq.push(['record', 'Answer deleted']);

        // Remove it from the json
        question.answers.splice(index, 1);

        this.setQuestionLayout(question);

        // Remove it from the DON.
        $answer.remove();
      };
    },

    // Display a question horizontally if it doesn't have many answers.
    setQuestionLayout: function(question) {
      if (question.answers.length !== 2) {
        delete question.layout;
      }else {
        if (question.type !== 'checkbox') {
          question.layout = 'horizontal';
        }
      }
    },

    renderQuestion: function(question, visible, parentID, triggerID, appendTo, questionIndex, parent) {
      // TODO:
      // This should pass around a well-documented options object
      // Instead of 30 different parameters
      // var options = {
      //   visible:
      //   parentId:
      //   triggerId:
      //   appendTo:
      //   questionIndex:
      //   parent:
      // }

      // Set default values for questions
      if (visible === undefined) {
        visible = true;
      }
      if (parentID === undefined) {
        parentID = '';
      }
      if (triggerID === undefined) {
        triggerID = '';
      }

      // Give the question an ID based on its name
      var id = _.uniqueId(question.name);

      // Render the questions .................................................
      var $question = $(this.templates.question({question: question}));
      if(question.type === 'file') {
        $question = $(this.templates.photoQuestion({question: question}));
      }
      if(question.type === 'text') {
        $question = $(this.templates.textQuestion({question: question}));
      }

      // Listen for changes to the question ...................................
      // TODO: make it less verbose

      // Listen for changes to the question text
      var editQuestionProxy = $.proxy(this.editQuestionFactory(question), this);
      $question.find('> div input').keyup(editQuestionProxy);

      // Listen for a request to remove a question
      // TODO: confirm delete
      var deleteQuestionProxy = $.proxy(this.deleteQuestion($question, parent, questionIndex), this);
      $question.find('> div .remove').click(deleteQuestionProxy);

      // Add a question
      // Show add sub-question
      $question.find('.add-question').click(function(event) {
        event.preventDefault();
        $(this).parent().find('> .question-choices').slideDown();
      });

      // Hide add sub-question
      $question.find('.close-add-sub-question').click(function(event) {
        event.preventDefault();
        $(this).parents('.question-choices').slideUp();
      });

      // Listen for a request to add a question
      var createQuestionProxy = $.proxy(this.createQuestionFactory(parent, questionIndex), this);
      $question.find('.add-sub-question').click(createQuestionProxy);

      // // Add a sub-question
      // var $addSubQuestion = $answer.find('.add-sub-question');
      // var addSubQuestionProxy = $.proxy(this.addSubQuestionFactory(question, index), this);
      // $addSubQuestion.click(addSubQuestionProxy);

      // // Listen for a request to add a photo question
      // var createPhotoQuestionProxy = $.proxy(this.createPhotoQuestion(parent, questionIndex), this);
      // $question.find('.add-photo-question').click(createPhotoQuestionProxy);

      // Listen for a request to add an answer
      var createAnswerProxy = $.proxy(this.createAnswer(question), this);
      $question.find('.add-answer').click(createAnswerProxy);

      // Some stuff with siblings -- forget what this does.
      var siblings = this.questionsByParentId[parentID];
      if (siblings === undefined) {
        siblings = [];
        this.questionsByParentId[parentID] = siblings;
      }
      siblings.push($question);

      // Figure out the right place to append the rendered template
      if (appendTo !== undefined) {
        $(appendTo).append($question);
      }else {
        this.formQuestions.append($question);
      }

      var suffixed_name = question.name + this.suffix(question.name);

      // TODO Infoboxes (aka help text for questions)
      // if(question.info !== undefined) {
      // }

      // Show question tools on hover
      // TODO: this selector sucks. Let's try to make it generic.
      $question.hover(function(event) {
        // if (event.currentTarget !== event.target) return;
        $(this).find('> .input-append .remove').fadeIn(150);
      }, function() {
        $(this).find('> .input-append.remove').fadeOut(150);
      });

      // Deal with answers ....................................................
      var questionID = id;

      // Add each answer to the question
      _.each(question.answers, function (answer, index) {
        // The triggerID is used to hide/show other question groups
        var triggerID = _.uniqueId(question.name);

        if(question.type === "checkbox") {
          suffixed_name = answer.name + this.suffix(answer.name);
          triggerID = suffixed_name;
          id = suffixed_name;
        }

        // Set the data used to render the answer
        var answerData = {
          questionName: suffixed_name,
          question: question,
          id: triggerID,
          theme: (answer.theme || "c"),
          value: answer.value,
          text: answer.text
        };

        // Render the answer template...
        var $answer;
        var referencesToAnswersForQuestion;

        $answer = $(this.templates.answer(answerData));
        referencesToAnswersForQuestion = this.boxAnswersByQuestionId[questionID];
        if (referencesToAnswersForQuestion === undefined) {
          referencesToAnswersForQuestion = [];
          this.boxAnswersByQuestionId[questionID] = referencesToAnswersForQuestion;
        }

        // ... and append it to the fieldset.
        $question.find('> .answers').append($answer);

        // Tools related to editing answers ------------------------------------

        // Change the answer text when updated
        var $input = $answer.find('input');
        var editAnswerProxy = $.proxy(this.editAnswer(question, index), this);
        $input.keyup(editAnswerProxy);

        // Show tools on hover
        $answer.hover(function() {
          $(this).find('> .btn').fadeIn(150);
        }, function() {
          $(this).find('> .btn').fadeOut(150);
        });

        $answer.find('> .show-add-sub-question').hover(function() {
          $(this).find('span').fadeIn(150);
        }, function() {
          $(this).find('span').fadeOut(150);
        });

        // Show add sub-question
        $answer.find('.show-add-sub-question').click(function(event) {
          event.preventDefault();
          $(this).parent().find('> .question-choices').slideDown();
        });

        // Hide add sub-question
        $answer.find('.close-add-sub-question').click(function(event) {
          event.preventDefault();
          $(this).parents('.question-choices').slideUp();
        });

        // Add a sub-question
        var $addSubQuestion = $answer.find('.add-sub-question');
        var addSubQuestionProxy = $.proxy(this.addSubQuestionFactory(question, index), this);
        $addSubQuestion.click(addSubQuestionProxy);

        // Remove an answer
        var $removeButton = $answer.find('.remove');
        var deleteAnswerProxy = $.proxy(this.deleteAnswer(question, index, $answer), this);
        $removeButton.click(deleteAnswerProxy);

        // If there are conditional questions, add them. -----------------------
        if (answer.questions !== undefined) {
          appendTo = $answer;
          _.each(answer.questions, function (subq, questionIndex) {
            // Add the sub questions before the repeatButton
            if(appendTo !== undefined){
              this.renderQuestion(subq, false, id, triggerID, appendTo, questionIndex, answer.questions);
            }else {
              this.renderQuestion(subq, false, id, triggerID, undefined, questionIndex, answer.questions);
            }
          }, this);
        } // end check for sub-answers

      }, this);
    }, // end renderQuestion

    // Slugify a string
    // Used to generate the name attribute of forms
    slugify: function(text) {
      text = text.replace(/[^-a-zA-Z0-9,&\s]+/ig, '');
      text = text.replace(/\s/gi, "-");
      text = text.replace(/,/gi, '');
      text = text.replace(/&/g, 'and');
      return text;
    }

  }); // end BuilderView{}

  return Builder;

});
<commit_msg>Send plain objects to the API method<commit_after>/*jslint nomen: true */
/*globals define: true */
/*globals console: true */

define(function (require) {
  'use strict';

  var $ = require('jquery');
  var _ = require('lib/lodash');
  var Backbone = require('backbone');
  var _kmq = require('lib/kissmetrics');

  var api = require('api');
  var template = require('text!templates/surveys/form-editor.html');
  var Form = require('models/forms');

  var Builder = {};

  Builder.BuilderView = Backbone.View.extend({
    template: _.template(template),

    el: '#builder-view-container',

    boxAnswersByQuestionId: {},
    formQuestions: $('#editor'),
    questionsByParentId: {},
    repeatCounter: {},
    templates: {
      question: _.template($('#question-edit-template').html()),
      photoQuestion: _.template($('#photo-question-edit-template').html()),
      textQuestion: _.template($('#text-question-edit-template').html()),
      answer: _.template($('#answer-edit-template').html())
    },

    events: {
      'click .save': 'save'
    },

    initialize: function(options) {
      _.bindAll(this,
        'render',
        'save',

        'makeBlankQuestion',
        'renderForm',
        'suffix',
        'slugify',

        'editQuestionFactory',
        'setQuestionType',
        'deleteQuestion',
        'createQuestionFactory',
        'createPhotoQuestion',
        'addSubQuestionFactory',
        'renderQuestion',

        'createAnswer',
        'editAnswer',
        'deleteAnswer'
      );

      this.forms = options.forms;
    },

    render: function() {
      this.renderForm();
    },

    save: function(event) {
      event.preventDefault();
      console.log('Saving form');
      _kmq.push(['record', 'Survey questions saved']);

      api.createForm(this.forms.getMostRecentForm().toJSON(), function(){
        console.log('Form successfully saved');
        $(".saved").fadeIn().css("display","inline-block").delay(2000).fadeOut();
      });
    },

    // The blank question template.
    makeBlankQuestion: function(){
      return {
        "text": "",
        "value": "",
        "answers": [
          {
            "text": "",
            "value": ""
          },
          {
            "text": "",
            "value": ""
          }
        ]
      };
    },

    renderForm: function() {
      // Render the editor template
      this.$el.html(this.template({}));

      // Clear out the editor, just in case.
      this.formQuestions = $('#editor');
      this.formQuestions.html('');

      var form = this.forms.getMostRecentForm();
      var formData;

      // Default to a blank question if the form is empty
      if (form === undefined) {
        formData = {};
        formData.questions = [];
        formData.questions.push(this.makeBlankQuestion());
        this.forms.add(new Form.Model({ questions: formData.questions }));
      } else {
        formData = {
          questions: form.get('questions')
        };
      }

      // Render form
      _.each(formData.questions, function (question, questionIndex) {
        this.renderQuestion(question, undefined, undefined, undefined, undefined, questionIndex, formData.questions);
      }, this);
    },

    suffix: function(name) {
      if(_.has(this.repeatCounter, name)) {
        this.repeatCounter[name] += 1;
        return '-' + this.repeatCounter[name].toString();
      }

      this.repeatCounter[name] = 1;
      return '';
    },

    editQuestionFactory: function(question) {
      return function(event) {
        console.log('Updating question');
        _kmq.push(['record', 'Question edited']);
        var text = $(event.target).val(); //$(this).val();
        var name = this.slugify(text);

        question.text = text;
        question.name = name;

        if(question.type === 'checkbox') {
          _.each(question.answers, function(answer){
            answer.name = question.name + '-' + this.slugify(answer.text);
            answer.value = 'yes';
          }.bind(this));
        }
      };
    },

    setQuestionType: function(question, dataRole) {
      console.log(dataRole);

      if (dataRole === 'radio-question') {
        delete question.type;
        _.each(question.answers, function(answer){
          answer.value = this.slugify(answer.text);
        }.bind(this));
      }

      if (dataRole === 'checkbox-question') {
        question.type = "checkbox";
        // Make sure each answer has a name
        _.each(question.answers, function(answer){
          answer.name = question.name + '-' + this.slugify(answer.text);
          answer.value = 'yes';
        }.bind(this));
      }

      if (dataRole === 'file-question') {
        question.type = 'file';
        delete question.answers;
      }

      if (dataRole === 'text-question') {
        question.type = 'text';
        delete question.answers;
      }
    },

    deleteQuestion: function($question, parent, questionIndex) {
      return function(event) {
        console.log("removing question");
        _kmq.push(['record', 'Question deleted']);

        // Remove it from the DOM.
        $question.remove();

        // Remove it from the json
        parent.splice(questionIndex, 1);
      };
    },

    createQuestionFactory: function(parent, questionIndex) {
      return function(event) {
        event.preventDefault();
        var type = $(event.currentTarget).attr('data-type');

        console.log("Adding a new question");
        _kmq.push(['record', 'Question added']);

        var newQuestion = this.makeBlankQuestion();
        this.setQuestionType(newQuestion, type);
        parent.splice(questionIndex + 1, 0, newQuestion);

        this.renderForm();
      };
    },

    createPhotoQuestion: function(parent, questionIndex) {
      return function(event) {
        console.log("Adding a new photo question");
        _kmq.push(['record', 'Question added']);

        var newQuestion = this.makeBlankQuestion();
        newQuestion.type = 'file';
        delete newQuestion.answers;
        parent.splice(questionIndex + 1, 0, newQuestion);

        this.renderForm();
      };
    },

    createAnswer: function(question) {
      return function(event) {
        _kmq.push(['record', 'Answer added']);
        question.answers.push({
          'text': '',
          'value': ''
        });
        this.setQuestionLayout(question);

        this.renderForm();
      };
    },

    addSubQuestionFactory: function(question, index) {
      return function(event) {
        event.preventDefault();
        var type = $(event.currentTarget).attr('data-type');

        console.log("Adding sub-question", type);
        _kmq.push(['record', 'Sub-question added']);

        var newQuestion = this.makeBlankQuestion();
        this.setQuestionType(newQuestion, type);

        // Add the question to the end of the array, or create a new array
        if (_.has(question.answers[index], 'questions')) {
          question.answers[index].questions.unshift(newQuestion);
        }else {
          question.answers[index].questions = [newQuestion];
        }

        this.renderForm();
      };
    },

    editAnswer: function(question, index) {
      return function(event){
        _kmq.push(['record', 'Answer edited']);
        var text = $(event.target).val();
        question.answers[index].text = text;
        question.answers[index].value = this.slugify(text);

        this.setQuestionLayout(question);
        if(question.type === 'checkbox') {
          question.answers[index].name = question.name + '-' + this.slugify(text);
          question.answers[index].value = 'yes';
        }

      };
    },

    deleteAnswer: function(question, index, $answer) {
      return function(event) {
        console.log("removing answer");
        _kmq.push(['record', 'Answer deleted']);

        // Remove it from the json
        question.answers.splice(index, 1);

        this.setQuestionLayout(question);

        // Remove it from the DON.
        $answer.remove();
      };
    },

    // Display a question horizontally if it doesn't have many answers.
    setQuestionLayout: function(question) {
      if (question.answers.length !== 2) {
        delete question.layout;
      }else {
        if (question.type !== 'checkbox') {
          question.layout = 'horizontal';
        }
      }
    },

    renderQuestion: function(question, visible, parentID, triggerID, appendTo, questionIndex, parent) {
      // TODO:
      // This should pass around a well-documented options object
      // Instead of 30 different parameters
      // var options = {
      //   visible:
      //   parentId:
      //   triggerId:
      //   appendTo:
      //   questionIndex:
      //   parent:
      // }

      // Set default values for questions
      if (visible === undefined) {
        visible = true;
      }
      if (parentID === undefined) {
        parentID = '';
      }
      if (triggerID === undefined) {
        triggerID = '';
      }

      // Give the question an ID based on its name
      var id = _.uniqueId(question.name);

      // Render the questions .................................................
      var $question = $(this.templates.question({question: question}));
      if(question.type === 'file') {
        $question = $(this.templates.photoQuestion({question: question}));
      }
      if(question.type === 'text') {
        $question = $(this.templates.textQuestion({question: question}));
      }

      // Listen for changes to the question ...................................
      // TODO: make it less verbose

      // Listen for changes to the question text
      var editQuestionProxy = $.proxy(this.editQuestionFactory(question), this);
      $question.find('> div input').keyup(editQuestionProxy);

      // Listen for a request to remove a question
      // TODO: confirm delete
      var deleteQuestionProxy = $.proxy(this.deleteQuestion($question, parent, questionIndex), this);
      $question.find('> div .remove').click(deleteQuestionProxy);

      // Add a question
      // Show add sub-question
      $question.find('.add-question').click(function(event) {
        event.preventDefault();
        $(this).parent().find('> .question-choices').slideDown();
      });

      // Hide add sub-question
      $question.find('.close-add-sub-question').click(function(event) {
        event.preventDefault();
        $(this).parents('.question-choices').slideUp();
      });

      // Listen for a request to add a question
      var createQuestionProxy = $.proxy(this.createQuestionFactory(parent, questionIndex), this);
      $question.find('.add-sub-question').click(createQuestionProxy);

      // // Add a sub-question
      // var $addSubQuestion = $answer.find('.add-sub-question');
      // var addSubQuestionProxy = $.proxy(this.addSubQuestionFactory(question, index), this);
      // $addSubQuestion.click(addSubQuestionProxy);

      // // Listen for a request to add a photo question
      // var createPhotoQuestionProxy = $.proxy(this.createPhotoQuestion(parent, questionIndex), this);
      // $question.find('.add-photo-question').click(createPhotoQuestionProxy);

      // Listen for a request to add an answer
      var createAnswerProxy = $.proxy(this.createAnswer(question), this);
      $question.find('.add-answer').click(createAnswerProxy);

      // Some stuff with siblings -- forget what this does.
      var siblings = this.questionsByParentId[parentID];
      if (siblings === undefined) {
        siblings = [];
        this.questionsByParentId[parentID] = siblings;
      }
      siblings.push($question);

      // Figure out the right place to append the rendered template
      if (appendTo !== undefined) {
        $(appendTo).append($question);
      }else {
        this.formQuestions.append($question);
      }

      var suffixed_name = question.name + this.suffix(question.name);

      // TODO Infoboxes (aka help text for questions)
      // if(question.info !== undefined) {
      // }

      // Show question tools on hover
      // TODO: this selector sucks. Let's try to make it generic.
      $question.hover(function(event) {
        // if (event.currentTarget !== event.target) return;
        $(this).find('> .input-append .remove').fadeIn(150);
      }, function() {
        $(this).find('> .input-append.remove').fadeOut(150);
      });

      // Deal with answers ....................................................
      var questionID = id;

      // Add each answer to the question
      _.each(question.answers, function (answer, index) {
        // The triggerID is used to hide/show other question groups
        var triggerID = _.uniqueId(question.name);

        if(question.type === "checkbox") {
          suffixed_name = answer.name + this.suffix(answer.name);
          triggerID = suffixed_name;
          id = suffixed_name;
        }

        // Set the data used to render the answer
        var answerData = {
          questionName: suffixed_name,
          question: question,
          id: triggerID,
          theme: (answer.theme || "c"),
          value: answer.value,
          text: answer.text
        };

        // Render the answer template...
        var $answer;
        var referencesToAnswersForQuestion;

        $answer = $(this.templates.answer(answerData));
        referencesToAnswersForQuestion = this.boxAnswersByQuestionId[questionID];
        if (referencesToAnswersForQuestion === undefined) {
          referencesToAnswersForQuestion = [];
          this.boxAnswersByQuestionId[questionID] = referencesToAnswersForQuestion;
        }

        // ... and append it to the fieldset.
        $question.find('> .answers').append($answer);

        // Tools related to editing answers ------------------------------------

        // Change the answer text when updated
        var $input = $answer.find('input');
        var editAnswerProxy = $.proxy(this.editAnswer(question, index), this);
        $input.keyup(editAnswerProxy);

        // Show tools on hover
        $answer.hover(function() {
          $(this).find('> .btn').fadeIn(150);
        }, function() {
          $(this).find('> .btn').fadeOut(150);
        });

        $answer.find('> .show-add-sub-question').hover(function() {
          $(this).find('span').fadeIn(150);
        }, function() {
          $(this).find('span').fadeOut(150);
        });

        // Show add sub-question
        $answer.find('.show-add-sub-question').click(function(event) {
          event.preventDefault();
          $(this).parent().find('> .question-choices').slideDown();
        });

        // Hide add sub-question
        $answer.find('.close-add-sub-question').click(function(event) {
          event.preventDefault();
          $(this).parents('.question-choices').slideUp();
        });

        // Add a sub-question
        var $addSubQuestion = $answer.find('.add-sub-question');
        var addSubQuestionProxy = $.proxy(this.addSubQuestionFactory(question, index), this);
        $addSubQuestion.click(addSubQuestionProxy);

        // Remove an answer
        var $removeButton = $answer.find('.remove');
        var deleteAnswerProxy = $.proxy(this.deleteAnswer(question, index, $answer), this);
        $removeButton.click(deleteAnswerProxy);

        // If there are conditional questions, add them. -----------------------
        if (answer.questions !== undefined) {
          appendTo = $answer;
          _.each(answer.questions, function (subq, questionIndex) {
            // Add the sub questions before the repeatButton
            if(appendTo !== undefined){
              this.renderQuestion(subq, false, id, triggerID, appendTo, questionIndex, answer.questions);
            }else {
              this.renderQuestion(subq, false, id, triggerID, undefined, questionIndex, answer.questions);
            }
          }, this);
        } // end check for sub-answers

      }, this);
    }, // end renderQuestion

    // Slugify a string
    // Used to generate the name attribute of forms
    slugify: function(text) {
      text = text.replace(/[^-a-zA-Z0-9,&\s]+/ig, '');
      text = text.replace(/\s/gi, "-");
      text = text.replace(/,/gi, '');
      text = text.replace(/&/g, 'and');
      return text;
    }

  }); // end BuilderView{}

  return Builder;

});
<|endoftext|>


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

var React = require('react'),
    Link = require('../touchstone/link'),
	Tappable = require('../touchstone/tappable'),
	Navigation = require('../touchstone/navigation'),
	UI = require('../touchstone/ui');

var moment = require('moment')

module.exports = React.createClass({
	mixins: [Navigation],

	getInitialState: function() {
		return {
			processing: false,
			formIsValid: false,
			bioValue: this.props.user.bio
		}
	},

	showFlavourList: function() {
		this.showView('radio-list', 'show-from-right', { user: this.props.user, flavour: this.state.flavour });
	},

	handleBioInput: function(event) {
		console.log('bio length', this.state.bioValue);
		this.setState({
			bioValue: event.target.value
		});
		if (this.state.bioValue && this.state.bioValue.length > 0) {
			this.setState({
				formIsValid: true
			});
		}
	},

	processForm: function() {
		this.setState({
			processing: true
		});

		setTimeout(function() {
			this.showView('home', 'fade', {});
		}.bind(this), 750);
	},

	flashAlert: function(alertContent) {
		alert(alertContent);
	},

	render: function() {

		// fields
		return (
			<UI.FlexLayout className={this.props.viewClassName}>
				<UI.FlexBlock height="44px" className="headerbar">
					<Link to="home" viewTransition="reveal-from-right" className="headerbar-button">

Back

					</Link>

{this.props.user.name}

					<UI.LoadingButton loading={this.state.processing} disabled={!this.state.formIsValid} onTap={this.processForm} label="Save" className="headerbar-button headerbar-button-label right is-primary" />
				</UI.FlexBlock>
				<UI.FlexBlock scrollable>
					{/*
Basic details
*/}

						<UI.LabelInput label="Name"     value={this.props.user.name}       placeholder="Full name" first />
						<UI.LabelInput label="Location" value={this.props.user.location}   placeholder="Suburb, Country" />
						<UI.LabelInput label="Joined"   value={this.props.user.joinedDate} noedit />
						<UI.LabelTextarea label="Bio"   value={this.state.bioValue}        placeholder="Bio" onChange={this.handleBioInput} />
						<UI.LabelSelect value={this.state.flavour} onChange={this.handleFlavourChange} options={[
							{ label: 'Vanilla',    value: 'vanilla' },
							{ label: 'Chocolate',  value: 'chocolate' },
							{ label: 'Caramel',    value: 'caramel' },
							{ label: 'Strawberry', value: 'strawberry' },
							{ label: 'Banana',     value: 'banana' },
							{ label: 'Lemon',      value: 'lemon' },
							{ label: 'Pastaccio',  value: 'pastaccio' }
						]} />

						<Tappable onTap={this.showFlavourList} className="list-item is-first" component="div">
							Favourite Icecream

{this.props.user.flavour}

						</Tappable>

					<Tappable onTap={this.flashAlert.bind(this, 'You clicked the Primary Button.')} className="panel-button primary" component="button">
						Primary Button
					</Tappable>
					<Tappable onTap={this.flashAlert.bind(this, 'You clicked the Default Button.')} className="panel-button" component="button">
						Default Button
					</Tappable>
					<Tappable onTap={this.flashAlert.bind(this, 'You clicked the Danger Button.')} className="panel-button danger" component="button">
						Danger Button
					</Tappable>
				</UI.FlexBlock>
			</UI.FlexLayout>
		);
	}
});
<commit_msg>Not sure about this component<commit_after>/** @jsx React.DOM */

var React = require('react'),
    Link = require('../touchstone/link'),
	Tappable = require('../touchstone/tappable'),
	Navigation = require('../touchstone/navigation'),
	UI = require('../touchstone/ui');

var moment = require('moment')

module.exports = React.createClass({
	mixins: [Navigation],

	getInitialState: function() {
		return {
			processing: false,
			formIsValid: false,
			bioValue: this.props.user.bio
		}
	},

	showFlavourList: function() {
		this.showView('radio-list', 'show-from-right', { user: this.props.user, flavour: this.state.flavour });
	},

	handleBioInput: function(event) {
		console.log('bio length', this.state.bioValue);
		this.setState({
			bioValue: event.target.value
		});
		if (this.state.bioValue && this.state.bioValue.length > 0) {
			this.setState({
				formIsValid: true
			});
		}
	},

	processForm: function() {
		this.setState({
			processing: true
		});

		setTimeout(function() {
			this.showView('home', 'fade', {});
		}.bind(this), 750);
	},

	flashAlert: function(alertContent) {
		alert(alertContent);
	},

	render: function() {

		// fields
		return (
			<UI.FlexLayout className={this.props.viewClassName}>
				<UI.FlexBlock height="44px" className="headerbar">
					<Link to="home" viewTransition="reveal-from-right" className="headerbar-button">

Back

					</Link>

{this.props.user.name}

					<UI.LoadingButton loading={this.state.processing} disabled={!this.state.formIsValid} onTap={this.processForm} label="Save" className="headerbar-button headerbar-button-label right is-primary" />
				</UI.FlexBlock>
				<UI.FlexBlock scrollable>
					{/*
Basic details
*/}

						<UI.LabelInput label="Name"     value={this.props.user.name}       placeholder="Full name" first />
						<UI.LabelInput label="Location" value={this.props.user.location}   placeholder="Suburb, Country" />
						<UI.LabelInput label="Joined"   value={this.props.user.joinedDate} noedit />
						<UI.LabelTextarea label="Bio"   value={this.state.bioValue}        placeholder="Bio" onChange={this.handleBioInput} />

						<Tappable onTap={this.showFlavourList} className="list-item is-first" component="div">
							Favourite Icecream

{this.props.user.flavour}

						</Tappable>

					<Tappable onTap={this.flashAlert.bind(this, 'You clicked the Primary Button.')} className="panel-button primary" component="button">
						Primary Button
					</Tappable>
					<Tappable onTap={this.flashAlert.bind(this, 'You clicked the Default Button.')} className="panel-button" component="button">
						Default Button
					</Tappable>
					<Tappable onTap={this.flashAlert.bind(this, 'You clicked the Danger Button.')} className="panel-button danger" component="button">
						Danger Button
					</Tappable>
				</UI.FlexBlock>
			</UI.FlexLayout>
		);
	}
});
<|endoftext|>


<commit_before>/* global Ember, DS */
var get = Ember.get;

/**
 * Keep a record of routes to resources by type.
 */

// null prototype in es5 browsers wont allow collisions with things on the
// global Object.prototype.
DS._routes = Ember.create(null);

DS.JsonApiAdapter = DS.RESTAdapter.extend({
  defaultSerializer: 'DS/jsonApi',

  contentType: 'application/vnd.api+json; charset=utf-8',
  accepts: 'application/vnd.api+json, application/json, text/javascript, */*; q=0.01',

  ajaxOptions: function(url, type, options) {
    var hash = this._super(url, type, options);
    if (hash.data && type !== 'GET') {
      hash.contentType = this.contentType;
    }
    // Does not work
    //hash.accepts = this.accepts;
    if(!hash.hasOwnProperty('headers')) { hash.headers = {}; }
    hash.headers.Accept = this.accepts;
    return hash;
  },

  getRoute: function(typeName, id/*, record */) {
    return DS._routes[typeName];
  },

  /**
   * Look up routes based on top-level links.
   */
  buildURL: function(typeName, id, snapshot) {
    // FIXME If there is a record, try and look up the self link
    // - Need to use the function from the serializer to build the self key
    // TODO: this basically only works in the simplest of scenarios
    var route = this.getRoute(typeName, id, snapshot);
    if(!route) {
      return this._super(typeName, id, snapshot);
    }

    var url = [];
    var host = get(this, 'host');
    var prefix = this.urlPrefix();
    var param = /\{(.*?)\}/g;

    if (id) {
      if (param.test(route)) {
        url.push(route.replace(param, id));
      } else {
        url.push(route);
      }
    } else {
      url.push(route.replace(param, ''));
    }

    if (prefix) { url.unshift(prefix); }

    url = url.join('/');
    if (!host && url) { url = '/' + url; }

    return url;
  },

  /**
   * Fix query URL.
   */
  findMany: function(store, type, ids, snapshots) {
    return this.ajax(this.buildURL(type.typeKey, ids.join(','), snapshots, 'findMany'), 'GET');
  },

  /**
   * Cast individual record to array,
   * and match the root key to the route
   */
  createRecord: function(store, type, snapshot) {
    var data = this._serializeData(store, type, snapshot);

    return this.ajax(this.buildURL(type.typeKey), 'POST', {
      data: data
    });
  },

  /**
   * Suppress additional API calls if the relationship was already loaded via an `included` section
   */
  findBelongsTo: function(store, snapshot, url, relationship) {
    var belongsTo = snapshot.belongsTo(relationship.key);
    var belongsToLoaded = belongsTo && !belongsTo.record.get('currentState.isEmpty');

    if(belongsToLoaded) { return; }

    return this._super(store, snapshot, url, relationship);
  },

  /**
   * Suppress additional API calls if the relationship was already loaded via an `included` section
   */
  findHasMany: function(store, snapshot, url, relationship) {
    var hasManyLoaded;
    var hasMany = snapshot.hasMany(relationship.key);
    if (hasMany && !relationship.isReloading) {
      hasManyLoaded = hasMany.filter(function(item) { return !item.record.get('currentState.isEmpty'); });
      if(get(hasManyLoaded, 'length')) {
        return new Ember.RSVP.Promise(function (resolve, reject) { reject(); });
      }
    }
    return this._super(store, snapshot, url, relationship);
  },

  /**
   * Cast individual record to array,
   * and match the root key to the route
   */
  updateRecord: function(store, type, snapshot) {
    var data = this._serializeData(store, type, snapshot);
    if (data.data.links) {
      delete data.data.links;
    }
    var id = get(snapshot, 'id');
    return this.ajax(this.buildURL(type.typeKey, id, snapshot), 'PATCH', {
      data
    });
  },

  _serializeData: function(store, type, snapshot) {
    var serializer = store.serializerFor(type.typeKey);
    var fn = Ember.isArray(snapshot) ? 'serializeArray' : 'serialize';
    var json = {
      data: serializer[fn](snapshot, { includeId:true, type:type.typeKey })
    };

    return json;
  },

  _tryParseErrorResponse:  function(responseText) {
    try {
      return Ember.$.parseJSON(responseText);
    } catch(e) {
      return "Something went wrong";
    }
  },

  ajaxError: function(jqXHR) {
    var error = this._super(jqXHR);
    var response;

    if (jqXHR && typeof jqXHR === 'object') {
      response = this._tryParseErrorResponse(jqXHR.responseText);
      var errors = {};

      if (response &&
          typeof response === 'object' &&
            response.errors !== undefined) {

        Ember.A(Ember.keys(response.errors)).forEach(function(key) {
          errors[Ember.String.camelize(key)] = response.errors[key];
        });
      }

      if (jqXHR.status === 422) {
        return new DS.InvalidError(errors);
      } else{
        return new ServerError(jqXHR.status, response, jqXHR);
      }
    } else {
      return error;
    }
  },

  pathForType: function(type) {
    var dasherized = Ember.String.dasherize(type);
    return Ember.String.pluralize(dasherized);
  }
});

function ServerError(status, message, xhr) {
  this.status = status;
  this.message = message;
  this.xhr = xhr;

  this.stack = new Error().stack;
}

ServerError.prototype = Ember.create(Error.prototype);
ServerError.constructor = ServerError;

DS.JsonApiAdapter.ServerError = ServerError;

export default DS.JsonApiAdapter;
<commit_msg>Add missing `data`<commit_after>/* global Ember, DS */
var get = Ember.get;

/**
 * Keep a record of routes to resources by type.
 */

// null prototype in es5 browsers wont allow collisions with things on the
// global Object.prototype.
DS._routes = Ember.create(null);

DS.JsonApiAdapter = DS.RESTAdapter.extend({
  defaultSerializer: 'DS/jsonApi',

  contentType: 'application/vnd.api+json; charset=utf-8',
  accepts: 'application/vnd.api+json, application/json, text/javascript, */*; q=0.01',

  ajaxOptions: function(url, type, options) {
    var hash = this._super(url, type, options);
    if (hash.data && type !== 'GET') {
      hash.contentType = this.contentType;
    }
    // Does not work
    //hash.accepts = this.accepts;
    if(!hash.hasOwnProperty('headers')) { hash.headers = {}; }
    hash.headers.Accept = this.accepts;
    return hash;
  },

  getRoute: function(typeName, id/*, record */) {
    return DS._routes[typeName];
  },

  /**
   * Look up routes based on top-level links.
   */
  buildURL: function(typeName, id, snapshot) {
    // FIXME If there is a record, try and look up the self link
    // - Need to use the function from the serializer to build the self key
    // TODO: this basically only works in the simplest of scenarios
    var route = this.getRoute(typeName, id, snapshot);
    if(!route) {
      return this._super(typeName, id, snapshot);
    }

    var url = [];
    var host = get(this, 'host');
    var prefix = this.urlPrefix();
    var param = /\{(.*?)\}/g;

    if (id) {
      if (param.test(route)) {
        url.push(route.replace(param, id));
      } else {
        url.push(route);
      }
    } else {
      url.push(route.replace(param, ''));
    }

    if (prefix) { url.unshift(prefix); }

    url = url.join('/');
    if (!host && url) { url = '/' + url; }

    return url;
  },

  /**
   * Fix query URL.
   */
  findMany: function(store, type, ids, snapshots) {
    return this.ajax(this.buildURL(type.typeKey, ids.join(','), snapshots, 'findMany'), 'GET');
  },

  /**
   * Cast individual record to array,
   * and match the root key to the route
   */
  createRecord: function(store, type, snapshot) {
    var data = this._serializeData(store, type, snapshot);

    return this.ajax(this.buildURL(type.typeKey), 'POST', {
      data: data
    });
  },

  /**
   * Suppress additional API calls if the relationship was already loaded via an `included` section
   */
  findBelongsTo: function(store, snapshot, url, relationship) {
    var belongsTo = snapshot.belongsTo(relationship.key);
    var belongsToLoaded = belongsTo && !belongsTo.record.get('currentState.isEmpty');

    if(belongsToLoaded) { return; }

    return this._super(store, snapshot, url, relationship);
  },

  /**
   * Suppress additional API calls if the relationship was already loaded via an `included` section
   */
  findHasMany: function(store, snapshot, url, relationship) {
    var hasManyLoaded;
    var hasMany = snapshot.hasMany(relationship.key);
    if (hasMany && !relationship.isReloading) {
      hasManyLoaded = hasMany.filter(function(item) { return !item.record.get('currentState.isEmpty'); });
      if(get(hasManyLoaded, 'length')) {
        return new Ember.RSVP.Promise(function (resolve, reject) { reject(); });
      }
    }
    return this._super(store, snapshot, url, relationship);
  },

  /**
   * Cast individual record to array,
   * and match the root key to the route
   */
  updateRecord: function(store, type, snapshot) {
    var data = this._serializeData(store, type, snapshot);
    if (data.data.links) {
      delete data.data.links;
    }
    var id = get(snapshot, 'id');
    return this.ajax(this.buildURL(type.typeKey, id, snapshot), 'PATCH', {
      data: data
    });
  },

  _serializeData: function(store, type, snapshot) {
    var serializer = store.serializerFor(type.typeKey);
    var fn = Ember.isArray(snapshot) ? 'serializeArray' : 'serialize';
    var json = {
      data: serializer[fn](snapshot, { includeId:true, type:type.typeKey })
    };

    return json;
  },

  _tryParseErrorResponse:  function(responseText) {
    try {
      return Ember.$.parseJSON(responseText);
    } catch(e) {
      return "Something went wrong";
    }
  },

  ajaxError: function(jqXHR) {
    var error = this._super(jqXHR);
    var response;

    if (jqXHR && typeof jqXHR === 'object') {
      response = this._tryParseErrorResponse(jqXHR.responseText);
      var errors = {};

      if (response &&
          typeof response === 'object' &&
            response.errors !== undefined) {

        Ember.A(Ember.keys(response.errors)).forEach(function(key) {
          errors[Ember.String.camelize(key)] = response.errors[key];
        });
      }

      if (jqXHR.status === 422) {
        return new DS.InvalidError(errors);
      } else{
        return new ServerError(jqXHR.status, response, jqXHR);
      }
    } else {
      return error;
    }
  },

  pathForType: function(type) {
    var dasherized = Ember.String.dasherize(type);
    return Ember.String.pluralize(dasherized);
  }
});

function ServerError(status, message, xhr) {
  this.status = status;
  this.message = message;
  this.xhr = xhr;

  this.stack = new Error().stack;
}

ServerError.prototype = Ember.create(Error.prototype);
ServerError.constructor = ServerError;

DS.JsonApiAdapter.ServerError = ServerError;

export default DS.JsonApiAdapter;
<|endoftext|>