<commit_before>define([
    '/common/messages.js'
], function (Messages) {

    /** Id of the element for getting debug info. */
    var DEBUG_LINK_CLS = 'rtwysiwyg-debug-link';

    /** Id of the div containing the user list. */
    var USER_LIST_CLS = 'rtwysiwyg-user-list';

    /** Id of the button to change my username. */
    var USERNAME_BUTTON_ID = 'rtwysiwyg-change-username';

    /** Id of the div containing the lag info. */
    var LAG_ELEM_CLS = 'rtwysiwyg-lag';

    /** The toolbar class which contains the user list, debug link and lag. */
    var TOOLBAR_CLS = 'rtwysiwyg-toolbar';

    /** Key in the localStore which indicates realtime activity should be disallowed. */
    var LOCALSTORAGE_DISALLOW = 'rtwysiwyg-disallow';

    var SPINNER_DISAPPEAR_TIME = 3000;
    var SPINNER = [ '-', '\\', '|', '/' ];

    var uid = function () {
        return 'rtwysiwyg-uid-' + String(Math.random()).substring(2);
    };

    var createRealtimeToolbar = function ($container) {
        var id = uid();
        $container.prepend(
            '
' +
                '

' +
                '

' +
            '
'
        );
        var toolbar = $container.find('#'+id);
        toolbar.append([
            ''
         ].join('\n'));
        return toolbar;
    };

    var createEscape = function ($container) {
        var id = uid();
        $container.append('
⇐ Back
');
        var $ret = $container.find('#'+id);
        $ret.on('click', function () {
            window.location.href = '/';
        });
        return $ret[0];
    };

    var createSpinner = function ($container) {
        var id = uid();
        $container.append('

');
        return $container.find('#'+id)[0];
    };

    var kickSpinner = function (spinnerElement, reversed) {
        var txt = spinnerElement.textContent || '-';
        var inc = (reversed) ? -1 : 1;
        spinnerElement.textContent = SPINNER[(SPINNER.indexOf(txt) + inc) % SPINNER.length];
        if (spinnerElement.timeout) { clearTimeout(spinnerElement.timeout); }
        spinnerElement.timeout = setTimeout(function () {
            spinnerElement.textContent = '';
        }, SPINNER_DISAPPEAR_TIME);
    };

    var createUserList = function ($container) {
        var id = uid();
        $container.append('

');
        return $container.find('#'+id)[0];
    };

    var getOtherUsers = function(myUserName, userList, userData) {
      var i = 0;
      var list = '';
      userList.forEach(function(user) {
        if(user !== myUserName) {
          var data = (userData) ? (userData[user] || null) : null;
          var userName = (data) ? data.name : user;
          if(userName) {
            if(i === 0) list = ' : ';
            list += userName + ', ';
            i++;
          }
        }
      });
      return (i > 0) ? list.slice(0, -2) : list;
    }

    var createChangeName = function($container, userList, buttonID) {
        var id = uid();
        userList.innerHTML = 'Change name';
        return $container.find('#'+id)[0];
    }

    var updateUserList = function (myUserName, listElement, userList, userData) {
        var meIdx = userList.indexOf(myUserName);
        if (meIdx === -1) {
            listElement.textContent = Messages.synchronizing;
            return;
        }
        if (userList.length === 1) {
            listElement.innerHTML = Messages.editingAlone;
        } else if (userList.length === 2) {
            listElement.innerHTML = Messages.editingWithOneOtherPerson + getOtherUsers(myUserName, userList, userData);
        } else {
            listElement.innerHTML = Messages.editingWith + ' ' + (userList.length - 1) + ' ' + Messages.otherPeople + getOtherUsers(myUserName, userList, userData);
        }
    };

    var createLagElement = function ($container) {
        var id = uid();
        $container.append('

');
        return $container.find('#'+id)[0];
    };

    var checkLag = function (getLag, lagElement) {
        if(typeof getLag !== "function") { return; }
        var lag = getLag();
        var lagMsg = Messages.lag + ' ';
        if(lag) {
          var lagSec = lag/1000;
          if (lag.waiting && lagSec > 1) {
              lagMsg += "?? " + Math.floor(lagSec);
          } else {
              lagMsg += lagSec;
          }
        }
        else {
          lagMsg += "??";
        }
        lagElement.textContent = lagMsg;
    };

    // this is a little hack, it should go in it's own file.
    // FIXME ok, so let's put it in its own file then
    // TODO there should also be a 'clear recent pads' button
    var rememberPad = function () {
        // FIXME, this is overly complicated, use array methods
        var recentPadsStr = localStorage['CryptPad_RECENTPADS'];
        var recentPads = [];
        if (recentPadsStr) { recentPads = JSON.parse(recentPadsStr); }
        // TODO use window.location.hash or something like that
        if (window.location.href.indexOf('#') === -1) { return; }
        var now = new Date();
        var out = [];
        for (var i = recentPads.length; i >= 0; i--) {
            if (recentPads[i] &&
                // TODO precompute this time value, maybe make it configurable?
                // FIXME precompute the date too, why getTime every time?
                now.getTime() - recentPads[i][1] < (1000*60*60*24*30) &&
                recentPads[i][0] !== window.location.href)
            {
                out.push(recentPads[i]);
            }
        }
        out.push([window.location.href, now.getTime()]);
        localStorage['CryptPad_RECENTPADS'] = JSON.stringify(out);
    };

    var create = function ($container, myUserName, realtime, getLag, userList, config) {
        var toolbar = createRealtimeToolbar($container);
        createEscape(toolbar.find('.rtwysiwyg-toolbar-leftside'));
        var userListElement = createUserList(toolbar.find('.rtwysiwyg-toolbar-leftside'));
        var spinner = createSpinner(toolbar.find('.rtwysiwyg-toolbar-rightside'));
        var lagElement = createLagElement(toolbar.find('.rtwysiwyg-toolbar-rightside'));
        var userData = config.userData;
        var changeNameID = config.changeNameID;

        // Check if the user is allowed to change his name
        if(changeNameID) {
            // Create the button and update the element containing the user list
            userListElement = createChangeName($container, userListElement, changeNameID);
        }

        rememberPad();

        var connected = false;

        userList.onChange = function(newUserData) {
          var users = userList.users;
          if (users.indexOf(myUserName) !== -1) { connected = true; }
          if (!connected) { return; }
          if(newUserData) { // Someone has changed his name/color
            userData = newUserData;
          }
          updateUserList(myUserName, userListElement, users, userData);
        }

        var ks = function () {
            if (connected) { kickSpinner(spinner, false); }
        };

        realtime.onPatch(ks);
        // Try to filter out non-patch messages, doesn't have to be perfect this is just the spinner
        realtime.onMessage(function (msg) { if (msg.indexOf(':[2,') > -1) { ks(); } });

        setInterval(function () {
            if (!connected) { return; }
            checkLag(getLag, lagElement);
        }, 3000);

        return {
            failed: function () {
                connected = false;
                userListElement.textContent = '';
                lagElement.textContent = '';
            },
            reconnecting: function () {
                connected = false;
                userListElement.textContent = Messages.reconnecting;
                lagElement.textContent = '';
            },
            connected: function () {
                connected = true;
            }
        };
    };

    return { create: create };
});
<commit_msg>Hide the usernames when they have not been changed<commit_after>define([
    '/common/messages.js'
], function (Messages) {

    /** Id of the element for getting debug info. */
    var DEBUG_LINK_CLS = 'rtwysiwyg-debug-link';

    /** Id of the div containing the user list. */
    var USER_LIST_CLS = 'rtwysiwyg-user-list';

    /** Id of the button to change my username. */
    var USERNAME_BUTTON_ID = 'rtwysiwyg-change-username';

    /** Id of the div containing the lag info. */
    var LAG_ELEM_CLS = 'rtwysiwyg-lag';

    /** The toolbar class which contains the user list, debug link and lag. */
    var TOOLBAR_CLS = 'rtwysiwyg-toolbar';

    /** Key in the localStore which indicates realtime activity should be disallowed. */
    var LOCALSTORAGE_DISALLOW = 'rtwysiwyg-disallow';

    var SPINNER_DISAPPEAR_TIME = 3000;
    var SPINNER = [ '-', '\\', '|', '/' ];

    var uid = function () {
        return 'rtwysiwyg-uid-' + String(Math.random()).substring(2);
    };

    var createRealtimeToolbar = function ($container) {
        var id = uid();
        $container.prepend(
            '
' +
                '

' +
                '

' +
            '
'
        );
        var toolbar = $container.find('#'+id);
        toolbar.append([
            ''
         ].join('\n'));
        return toolbar;
    };

    var createEscape = function ($container) {
        var id = uid();
        $container.append('
⇐ Back
');
        var $ret = $container.find('#'+id);
        $ret.on('click', function () {
            window.location.href = '/';
        });
        return $ret[0];
    };

    var createSpinner = function ($container) {
        var id = uid();
        $container.append('

');
        return $container.find('#'+id)[0];
    };

    var kickSpinner = function (spinnerElement, reversed) {
        var txt = spinnerElement.textContent || '-';
        var inc = (reversed) ? -1 : 1;
        spinnerElement.textContent = SPINNER[(SPINNER.indexOf(txt) + inc) % SPINNER.length];
        if (spinnerElement.timeout) { clearTimeout(spinnerElement.timeout); }
        spinnerElement.timeout = setTimeout(function () {
            spinnerElement.textContent = '';
        }, SPINNER_DISAPPEAR_TIME);
    };

    var createUserList = function ($container) {
        var id = uid();
        $container.append('

');
        return $container.find('#'+id)[0];
    };

    var getOtherUsers = function(myUserName, userList, userData) {
      var i = 0;
      var list = '';
      userList.forEach(function(user) {
        if(user !== myUserName) {
          var data = (userData) ? (userData[user] || null) : null;
          var userName = (data) ? data.name : null;
          if(userName) {
            if(i === 0) list = ' : ';
            list += userName + ', ';
            i++;
          }
        }
      });
      return (i > 0) ? list.slice(0, -2) : list;
    }

    var createChangeName = function($container, userList, buttonID) {
        var id = uid();
        userList.innerHTML = 'Change name';
        return $container.find('#'+id)[0];
    }

    var updateUserList = function (myUserName, listElement, userList, userData) {
        var meIdx = userList.indexOf(myUserName);
        if (meIdx === -1) {
            listElement.textContent = Messages.synchronizing;
            return;
        }
        if (userList.length === 1) {
            listElement.innerHTML = Messages.editingAlone;
        } else if (userList.length === 2) {
            listElement.innerHTML = Messages.editingWithOneOtherPerson + getOtherUsers(myUserName, userList, userData);
        } else {
            listElement.innerHTML = Messages.editingWith + ' ' + (userList.length - 1) + ' ' + Messages.otherPeople + getOtherUsers(myUserName, userList, userData);
        }
    };

    var createLagElement = function ($container) {
        var id = uid();
        $container.append('

');
        return $container.find('#'+id)[0];
    };

    var checkLag = function (getLag, lagElement) {
        if(typeof getLag !== "function") { return; }
        var lag = getLag();
        var lagMsg = Messages.lag + ' ';
        if(lag) {
          var lagSec = lag/1000;
          if (lag.waiting && lagSec > 1) {
              lagMsg += "?? " + Math.floor(lagSec);
          } else {
              lagMsg += lagSec;
          }
        }
        else {
          lagMsg += "??";
        }
        lagElement.textContent = lagMsg;
    };

    // this is a little hack, it should go in it's own file.
    // FIXME ok, so let's put it in its own file then
    // TODO there should also be a 'clear recent pads' button
    var rememberPad = function () {
        // FIXME, this is overly complicated, use array methods
        var recentPadsStr = localStorage['CryptPad_RECENTPADS'];
        var recentPads = [];
        if (recentPadsStr) { recentPads = JSON.parse(recentPadsStr); }
        // TODO use window.location.hash or something like that
        if (window.location.href.indexOf('#') === -1) { return; }
        var now = new Date();
        var out = [];
        for (var i = recentPads.length; i >= 0; i--) {
            if (recentPads[i] &&
                // TODO precompute this time value, maybe make it configurable?
                // FIXME precompute the date too, why getTime every time?
                now.getTime() - recentPads[i][1] < (1000*60*60*24*30) &&
                recentPads[i][0] !== window.location.href)
            {
                out.push(recentPads[i]);
            }
        }
        out.push([window.location.href, now.getTime()]);
        localStorage['CryptPad_RECENTPADS'] = JSON.stringify(out);
    };

    var create = function ($container, myUserName, realtime, getLag, userList, config) {
        var toolbar = createRealtimeToolbar($container);
        createEscape(toolbar.find('.rtwysiwyg-toolbar-leftside'));
        var userListElement = createUserList(toolbar.find('.rtwysiwyg-toolbar-leftside'));
        var spinner = createSpinner(toolbar.find('.rtwysiwyg-toolbar-rightside'));
        var lagElement = createLagElement(toolbar.find('.rtwysiwyg-toolbar-rightside'));
        var userData = config.userData;
        var changeNameID = config.changeNameID;

        // Check if the user is allowed to change his name
        if(changeNameID) {
            // Create the button and update the element containing the user list
            userListElement = createChangeName($container, userListElement, changeNameID);
        }

        rememberPad();

        var connected = false;

        userList.onChange = function(newUserData) {
          var users = userList.users;
          if (users.indexOf(myUserName) !== -1) { connected = true; }
          if (!connected) { return; }
          if(newUserData) { // Someone has changed his name/color
            userData = newUserData;
          }
          updateUserList(myUserName, userListElement, users, userData);
        }

        var ks = function () {
            if (connected) { kickSpinner(spinner, false); }
        };

        realtime.onPatch(ks);
        // Try to filter out non-patch messages, doesn't have to be perfect this is just the spinner
        realtime.onMessage(function (msg) { if (msg.indexOf(':[2,') > -1) { ks(); } });

        setInterval(function () {
            if (!connected) { return; }
            checkLag(getLag, lagElement);
        }, 3000);

        return {
            failed: function () {
                connected = false;
                userListElement.textContent = '';
                lagElement.textContent = '';
            },
            reconnecting: function () {
                connected = false;
                userListElement.textContent = Messages.reconnecting;
                lagElement.textContent = '';
            },
            connected: function () {
                connected = true;
            }
        };
    };

    return { create: create };
});
<|endoftext|>


<commit_before>/*global cordova, module*/

var exec = require("cordova/exec")

/*
 * Format of the returned value:
 * {
 *    "isDutyCycling": true/false,
 *    "accuracy": "high/balanced/hundredmeters/..",
 *    "geofenceRadius": 1234,
 *    "accuracyThreshold": 1234,
 *    "filter": "time/distance",
 *    "filterValue": 1234,
 *    "tripEndStationaryMins": 1234
 * }
 */

var DataCollection = {
    /*
     * Launch init: registers all the callbacks necessary on launch, mainly for
     * iOS, where we have to re-register the listeners for fine location
     * tracking every time the app is launched.
     */
    launchInit: function (successCallback, errorCallback) {
        exec(successCallback, errorCallback, "DataCollection", "launchInit", []);
    },
    markConsented: function (newConsent) {
        return new Promise(function(resolve, reject) {
            exec(resolve, reject, "DataCollection", "markConsented", [newConsent]);
        });
    },
    // Switching both the get and set config to a promise to experiment with promises!!
    getConfig: function () {
        return new Promise(function(resolve, reject) {
            exec(resolve, reject, "DataCollection", "getConfig", []);
        });
    },
    setConfig: function (newConfig) {
        return new Promise(function(resolve, reject) {
            exec(resolve, reject, "DataCollection", "setConfig", [newConfig]);
        });
    },
    getAccuracyOptions: function () {
        return new Promise(function(resolve, reject) {
            exec(resolve, reject, "DataCollection", "getAccuracyOptions", []);
        });
    },
    getState: function (successCallback, errorCallback) {
        exec(successCallback, errorCallback, "DataCollection", "getState", []);
    },
    forceTransition: function (generalTransitionName) {
        return new Promise(function(resolve, reject) {
            exec(resolve, reject, "DataCollection", "forceTransition", [generalTransitionName]);
        });
    }
}

module.exports = DataCollection;
<commit_msg>added promise to getState function<commit_after>/*global cordova, module*/

var exec = require("cordova/exec")

/*
 * Format of the returned value:
 * {
 *    "isDutyCycling": true/false,
 *    "accuracy": "high/balanced/hundredmeters/..",
 *    "geofenceRadius": 1234,
 *    "accuracyThreshold": 1234,
 *    "filter": "time/distance",
 *    "filterValue": 1234,
 *    "tripEndStationaryMins": 1234
 * }
 */

var DataCollection = {
    /*
     * Launch init: registers all the callbacks necessary on launch, mainly for
     * iOS, where we have to re-register the listeners for fine location
     * tracking every time the app is launched.
     */
    launchInit: function (successCallback, errorCallback) {
        exec(successCallback, errorCallback, "DataCollection", "launchInit", []);
    },
    markConsented: function (newConsent) {
        return new Promise(function(resolve, reject) {
            exec(resolve, reject, "DataCollection", "markConsented", [newConsent]);
        });
    },
    // Switching both the get and set config to a promise to experiment with promises!!
    getConfig: function () {
        return new Promise(function(resolve, reject) {
            exec(resolve, reject, "DataCollection", "getConfig", []);
        });
    },
    setConfig: function (newConfig) {
        return new Promise(function(resolve, reject) {
            exec(resolve, reject, "DataCollection", "setConfig", [newConfig]);
        });
    },
    getAccuracyOptions: function () {
        return new Promise(function(resolve, reject) {
            exec(resolve, reject, "DataCollection", "getAccuracyOptions", []);
        });
    },
    getState: function () {
        return new Promise(function(resolve, reject) {
            exec(resolve, reject, "DataCollection", "getState", []);
        });
    },
    forceTransition: function (generalTransitionName) {
        return new Promise(function(resolve, reject) {
            exec(resolve, reject, "DataCollection", "forceTransition", [generalTransitionName]);
        });
    }
}

module.exports = DataCollection;
<|endoftext|>


<commit_before>angular.module('omniwallet')
  .factory('enumerated_addresses', function($http, $q, $timeout, $injector) {
    var count = 1;
    return {
      "getData": function() {
        var deferred = $q.defer();

        _.defer(function() {
          var wallet = $injector.get('userService').getWallet();
          if (wallet && wallet.addresses.length > 0) {

            deferred.resolve({
              addresses: wallet.addresses
            });
          } else {
            deferred.resolve({
              addresses: []
            });
          }
        });
        return deferred.promise;
      }
    };
  })
  .controller('AddAddressController', function($modal, $injector, $scope, userService, enumerated_addresses) {

  function decodeAddressFromPrivateKey(key) {

    //  Return the address decoded from the private key.
    var eckey = new Bitcoin.ECKey(key);
    var addr = eckey.getBitcoinAddress().toString();

    return addr;
  }
  ;

  function encodePrivateKey(key, passphrase) {

    //  Return encoded key.  Forget the passphrase forever.
    var eckey = new Bitcoin.ECKey(key);
    var enc = eckey.getEncryptedFormat(passphrase);

    return enc;
  }
  ;

  $scope.backupWallet = function() {
    $scope.login = {
      uuid: userService.data.wallet.uuid,
      action: 'verify',
      disable: true //disable UUID field in template
    }
    var modalInstance = $modal.open({
      templateUrl: '/partials/login_modal.html',
      controller: LoginController,
      scope: $scope
    });

    modalInstance.result.then(function(wallet) {
      var walletAddresses = userService.data.wallet.addresses;
      var blob = {
        addresses: []
      };
      walletAddresses.forEach(function(obj) {
        console.log('wallet addrs', obj)
        var ecKey = Bitcoin.ECKey.decodeEncryptedFormat(obj.privkey, obj.address);
        var addr = ecKey.getBitcoinAddress().toString();
        var key = ecKey.getWalletImportFormat();
        blob.addresses.push({ address: addr, privkey: key });
      })
      var exportBlob = new Blob([JSON.stringify(blob)], {
        type: 'application/json;charset=utf-8'
      });
      saveAs(exportBlob, "wallet.json");
    });
  }

  // Begin Import watch only Form Code
  $scope.openImportWatchOnlyForm = function() {
    var modalInstance = $modal.open({
      templateUrl: '/partials/import_watch_only.html',
      controller: AddBtcAddressModal
    });

    modalInstance.result.then(function(result) {

      if (result.address) {
        $injector.get('userService').addAddress(result.address);
      }
      $scope.refresh();

    }, function() {});
  };

  var AddBtcAddressModal = function($scope, $modalInstance) {
    $scope.validate = function(address) {
      return Bitcoin.Address.validate(address);
    };

    $scope.ok = function(result) {
      if (Bitcoin.Address.validate(result.address))
        $modalInstance.close(result);
      else
        console.log('*** Invalid address: ' + result.address);
    };

    $scope.cancel = function() {
      $modalInstance.dismiss('cancel');
    };
  };
  // Done Import Watch Only Form Code.

  // Begin Import Private Key Form Code
  $scope.openImportPrivateKeyForm = function() {
    var modalInstance = $modal.open({
      templateUrl: '/partials/import_private.html',
      controller: AddPrivateKeyModal
    });

    modalInstance.result.then(function(result) {

      if (result.privKey) {
        // Use address as passphrase for now
        var addr = decodeAddressFromPrivateKey(result.privKey);
        $injector.get('userService').addAddress(
        addr,
        encodePrivateKey(result.privKey, addr));
      }
      $scope.refresh();

    }, function() {});
  };

  var AddPrivateKeyModal = function($scope, $modalInstance) {
    $scope.validate = function(address) {
      if (!address) return false;

      try {
        var eckey = new Bitcoin.ECKey(address);
        var addr = eckey.getBitcoinAddress().toString();
        return true;
      } catch (e) {
        return false;
      }
    };

    $scope.ok = function(result) {
      $modalInstance.close(result);
    };

    $scope.cancel = function() {
      $modalInstance.dismiss('cancel');
    };
  };
  // Done Import Private Key Form Code.
  // Begin Import Encrypted Key Form Code
  $scope.openImportEncryptedKeyForm = function() {
    var modalInstance = $modal.open({
      templateUrl: '/partials/import_encrypted_private.html',
      controller: AddEncryptedPrivateModal
    });

    modalInstance.result.then(function(result) {

      if (result.encryptedPrivKey && result.password) {
        // Decrypt key then store with default password of addr
        var key = Bitcoin.ECKey.decodeEncryptedFormat(result.encryptedPrivKey, result.password);
        var privkey = Bitcoin.Util.bytesToHex(key.getPrivateKeyByteArray());
        // Use address as passphrase for now
        var addr = decodeAddressFromPrivateKey(privkey);
        $injector.get('userService').addAddress(
        addr,
        encodePrivateKey(privkey, addr));
      }
      $scope.refresh();

    }, function() {});
  };

  var AddEncryptedPrivateModal = function($scope, $modalInstance) {
    $scope.ok = function(result) {
      $modalInstance.close(result);
    };

    $scope.cancel = function() {
      $modalInstance.dismiss('cancel');
    };
  };
  // Done Import Encrypted Private Key Code.

  // Begine Create Form Code.
  $scope.createBTCAddress = createBTCAddress;
  function createBTCAddress() {
    var ecKey = new Bitcoin.ECKey();
    var address = ecKey.getBitcoinAddress().toString();
    var encryptedPrivateKey = ecKey.getEncryptedFormat(address);
    $injector.get('userService').addAddress(address, encryptedPrivateKey);
    $scope.refresh();
  }
  ;
  // Done Create Form Code.

  $scope.enumerateAddresses = function() {

    $scope.items = enumerated_addresses.getData().then(function(result) {
      if (result.addresses.length == 0) {
        createBTCAddress();
      }
      $scope.addresses = result.addresses;
    });
  };

});

<commit_msg>remove debug and check for privkey before dump<commit_after>angular.module('omniwallet')
  .factory('enumerated_addresses', function($http, $q, $timeout, $injector) {
    var count = 1;
    return {
      "getData": function() {
        var deferred = $q.defer();

        _.defer(function() {
          var wallet = $injector.get('userService').getWallet();
          if (wallet && wallet.addresses.length > 0) {

            deferred.resolve({
              addresses: wallet.addresses
            });
          } else {
            deferred.resolve({
              addresses: []
            });
          }
        });
        return deferred.promise;
      }
    };
  })
  .controller('AddAddressController', function($modal, $injector, $scope, userService, enumerated_addresses) {

  function decodeAddressFromPrivateKey(key) {

    //  Return the address decoded from the private key.
    var eckey = new Bitcoin.ECKey(key);
    var addr = eckey.getBitcoinAddress().toString();

    return addr;
  }
  ;

  function encodePrivateKey(key, passphrase) {

    //  Return encoded key.  Forget the passphrase forever.
    var eckey = new Bitcoin.ECKey(key);
    var enc = eckey.getEncryptedFormat(passphrase);

    return enc;
  }
  ;

  $scope.backupWallet = function() {
    $scope.login = {
      uuid: userService.data.wallet.uuid,
      action: 'verify',
      disable: true //disable UUID field in template
    }
    var modalInstance = $modal.open({
      templateUrl: '/partials/login_modal.html',
      controller: LoginController,
      scope: $scope
    });

    modalInstance.result.then(function(wallet) {
      var walletAddresses = userService.data.wallet.addresses;
      var blob = {
        addresses: []
      };
      walletAddresses.forEach(function(obj) {
        if(obj.privkey) {
          var ecKey = Bitcoin.ECKey.decodeEncryptedFormat(obj.privkey, obj.address);
          var addr = ecKey.getBitcoinAddress().toString();
          var key = ecKey.getWalletImportFormat();
          blob.addresses.push({ address: addr, privkey: key });
        }
      })
      var exportBlob = new Blob([JSON.stringify(blob)], {
        type: 'application/json;charset=utf-8'
      });
      saveAs(exportBlob, "wallet.json");
    });
  }

  // Begin Import watch only Form Code
  $scope.openImportWatchOnlyForm = function() {
    var modalInstance = $modal.open({
      templateUrl: '/partials/import_watch_only.html',
      controller: AddBtcAddressModal
    });

    modalInstance.result.then(function(result) {

      if (result.address) {
        $injector.get('userService').addAddress(result.address);
      }
      $scope.refresh();

    }, function() {});
  };

  var AddBtcAddressModal = function($scope, $modalInstance) {
    $scope.validate = function(address) {
      return Bitcoin.Address.validate(address);
    };

    $scope.ok = function(result) {
      if (Bitcoin.Address.validate(result.address))
        $modalInstance.close(result);
      else
        console.log('*** Invalid address: ' + result.address);
    };

    $scope.cancel = function() {
      $modalInstance.dismiss('cancel');
    };
  };
  // Done Import Watch Only Form Code.

  // Begin Import Private Key Form Code
  $scope.openImportPrivateKeyForm = function() {
    var modalInstance = $modal.open({
      templateUrl: '/partials/import_private.html',
      controller: AddPrivateKeyModal
    });

    modalInstance.result.then(function(result) {

      if (result.privKey) {
        // Use address as passphrase for now
        var addr = decodeAddressFromPrivateKey(result.privKey);
        $injector.get('userService').addAddress(
        addr,
        encodePrivateKey(result.privKey, addr));
      }
      $scope.refresh();

    }, function() {});
  };

  var AddPrivateKeyModal = function($scope, $modalInstance) {
    $scope.validate = function(address) {
      if (!address) return false;

      try {
        var eckey = new Bitcoin.ECKey(address);
        var addr = eckey.getBitcoinAddress().toString();
        return true;
      } catch (e) {
        return false;
      }
    };

    $scope.ok = function(result) {
      $modalInstance.close(result);
    };

    $scope.cancel = function() {
      $modalInstance.dismiss('cancel');
    };
  };
  // Done Import Private Key Form Code.
  // Begin Import Encrypted Key Form Code
  $scope.openImportEncryptedKeyForm = function() {
    var modalInstance = $modal.open({
      templateUrl: '/partials/import_encrypted_private.html',
      controller: AddEncryptedPrivateModal
    });

    modalInstance.result.then(function(result) {

      if (result.encryptedPrivKey && result.password) {
        // Decrypt key then store with default password of addr
        var key = Bitcoin.ECKey.decodeEncryptedFormat(result.encryptedPrivKey, result.password);
        var privkey = Bitcoin.Util.bytesToHex(key.getPrivateKeyByteArray());
        // Use address as passphrase for now
        var addr = decodeAddressFromPrivateKey(privkey);
        $injector.get('userService').addAddress(
        addr,
        encodePrivateKey(privkey, addr));
      }
      $scope.refresh();

    }, function() {});
  };

  var AddEncryptedPrivateModal = function($scope, $modalInstance) {
    $scope.ok = function(result) {
      $modalInstance.close(result);
    };

    $scope.cancel = function() {
      $modalInstance.dismiss('cancel');
    };
  };
  // Done Import Encrypted Private Key Code.

  // Begine Create Form Code.
  $scope.createBTCAddress = createBTCAddress;
  function createBTCAddress() {
    var ecKey = new Bitcoin.ECKey();
    var address = ecKey.getBitcoinAddress().toString();
    var encryptedPrivateKey = ecKey.getEncryptedFormat(address);
    $injector.get('userService').addAddress(address, encryptedPrivateKey);
    $scope.refresh();
  }
  ;
  // Done Create Form Code.

  $scope.enumerateAddresses = function() {

    $scope.items = enumerated_addresses.getData().then(function(result) {
      if (result.addresses.length == 0) {
        createBTCAddress();
      }
      $scope.addresses = result.addresses;
    });
  };

});

<|endoftext|>


<commit_before>angular.module('buurtmeter.controllers', ['leaflet-directive', 'ionic'])

.controller('MapController', function($scope, $timeout, AreaService, StorageService, CameraService){
	var cameraOptions = {
		destinationType : 1, // 0 : base64-encoded, 1 : image file URI, 2 : image native URI
		sourceType : 2, // 0 : PHOTOLIBRARY, 1 : CAMERA, 2 : SAVEDPHOTOALBUM
		//quality: 90, // less than 50 to avoid memory problems for older iPhones
		encodingType: 1, // 0 : jpeg, 1 : png
		correctOrientation: true, // rotate the image to correct for the orientation of the device during capture
		//targetWidth: 100,
		//targetHeight: 100,
		saveToPhotoAlbum: true
	};

	var center = StorageService.getObject('center');
	// defaults to 'grote markt'
	if(JSON.stringify(center) == '{}'){
		center.lat = 51.221311;
		center.lng = 4.399160;
	}

    var map = {
        defaults: {
        	// MapQuest
        	tileLayer: 'http://otile4.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png',
        	// OpenStreetMap
            //tileLayer: 'http://{s}.tile.osm.org/{z}/{x}/{y}.png',
            maxZoom: 20,
            zoomControl: false,
            doubleClickZoom: false,
            scrollWheelZoom: true,
            touchZoom: true,
            path: {
                weight: 10,
                color: '#800000',
                opacity: 1
            }
        },
        markers: StorageService.getObject('mapMarkers'),
        center: {
            lat: center.lat,
            lng: center.lng,
            zoom: 17
        }
    };

	$scope.map = map;
	$scope.markerCount = $scope.map.markers.length;
	$scope.myDataSets = StorageService.getObject('myDataSets');

	// normal click
	$scope.$on('leafletDirectiveMap.click', function(event, locationEvent){
		setTimeout(addMarker(locationEvent), 1000);
	});

	// double click
	// $scope.$on('leafletDirectiveMap.dblclick', function(event, locationEvent){ });

	// right-click
	$scope.$on('leafletDirectiveMap.contextmenu', function(event, locationEvent){
		$scope.map.markers = [];
    	$scope.markerCount = 0;
    	StorageService.setObject('mapMarkers', $scope.map.markers);
	});

	setTitle = function(msg){
		document.getElementById('title').innerHTML = '
' + msg + '
';
	};

	locate = function(){
		setTitle('Even geduld, positie wordt bepaald...');
		navigator.geolocation.getCurrentPosition(function(position){
			var center = {
				lat: position.coords.latitude,
				lng: position.coords.longitude,
				zoom : 17
			};
			$timeout(function(){
        		$scope.map.center = center;
        		setTitle('');
    		}, 3000);
			StorageService.setObject('center', center);
		}, function(err){ console.log(err); });
	};

	/* http://alienryderflex.com/polygon/
	The basic idea is to find all edges of the polygon that span the 'x' position of the point you're testing against.
	Then you find how many of them intersect the vertical line that extends above your point. If an even number cross above the point,
	then you're outside the polygon. If an odd number crosses above, then you're inside. */
	inPolygon = function(location, polyLoc){
		var lastPoint = polyLoc[polyLoc.length-1];
		var isInside = false;
		var x = location[0];

		for(var i = 0; i < polyLoc.length; i++){
			var point = polyLoc[i];
			var x1 = lastPoint[0];
			var x2 = point[0];
			var dx = x2 - x1;

			if(Math.abs(dx) > 180.0){
				if(x > 0){
					while(x1 < 0)
						x1 += 360;
					while(x2 < 0)
						x2 += 360;
				}
				else{
					while(x1 > 0)
						x1 -= 360;
					while(x2 > 0)
						x2 -= 360;
				}
				dx = x2 - x1;
			}

			if((x1 <= x && x2 > x) || (x1 >= x && x2 < x)){
				var grad = (point[1] - lastPoint[1]) / dx;
				var intersectAtLat = lastPoint[1] + ((x - x1) * grad);

				if(intersectAtLat > location[1])
					isInside = !isInside;
			}
			lastPoint = point;
		}
		return isInside;
	};

	getAreaScore = function(lat, lng){
		var totalResult = {};
		var totalScore = 0;
		for(var key in $scope.myDataSets){
  			if($scope.myDataSets.hasOwnProperty(key) && $scope.myDataSets[key].used){
   				var tmpScore = 0;
  				try{
					var set = JSON.parse(StorageService.get(key + '.json'));
					var geo = JSON.parse(set.data[0].geometry);
					if(geo.type == 'Polygon'){
						var polygons = geo.coordinates;
						for(var i = 0; i < polygons.length; i++){
							if(inPolygon([lng, lat], polygons[i])){
								tmpScore += 1;
							}
						}
						totalScore += tmpScore * ($scope.myDataSets[key].range / 10);
						totalResult[key] = {'name':$scope.myDataSets[key].type, 'score':tmpScore * ($scope.myDataSets[key].range / 10)};
					}
				}
				catch(err){
					console.log(err);
				}
			}
		}
		totalResult.total = totalScore;
		return totalResult;
	};

	addMarker = function(locationEvent){
		var lat = locationEvent.leafletEvent.latlng.lat;
		var lng = locationEvent.leafletEvent.latlng.lng;
		var areas = AreaService.all();
		for(var i = 0; i < areas.length; i++){
			var geo = JSON.parse(areas[i].geometry);
			var coordinates = geo.coordinates[0];
			if(inPolygon([lng, lat], coordinates)){
				var scores = getAreaScore(lat, lng);
				var msg = '' + areas[i].wijknaam + '
' + 'Score : ' + scores.total + '
';
				if(scores.total > 0){
					msg += '
';
					for(var score in scores){
						if(score != 'total'){
							msg += '
' + scores[score].name + ' : ' + scores[score].score;
						}
					}
				}
				var marker = {
			        lat: lat,
			        lng: lng,
			        message: msg,
					focus: true,
					draggable: false
				};
				updateMap = function(){
			        $scope.map.markers[$scope.markerCount] = marker;
			        $scope.markerCount += 1;
			        StorageService.setObject('mapMarkers', $scope.map.markers);
				};
				if(ionic.Platform.isCordova()){
					CameraService.getPicture(cameraOptions).then(function(imageURI){
				    	var img = document.createElement('img');
				    	var canvas = document.createElement('canvas');
				        var ctx = canvas.getContext('2d');
						canvas.width = 100;
				        canvas.height = 100;
				        img.onload = function(){
				            ctx.drawImage(this, 0, 0, 100, 100);
				     		marker.message += '

';
				     		// dunno why, but timeout necessary for update of map
							$timeout(updateMap(), 500);
				        };
				        img.src = imageURI;
		    		}, function(err){
		    			updateMap();
		    		});
				}
				// desktop browser
				else{
					updateMap();
					//console.log(StorageService.getObject('mapMarkers'));
				}
	    		break;
			}
		}
	};
})

.controller('DataController', function($scope, $http, DataSetService, StorageService){
	setTitle = function(msg){
		document.getElementById('title').innerHTML = '
' + msg + '
';
	};

	locate = function(){};

	$scope.myDataSets = StorageService.getObject('myDataSets');
	if(JSON.stringify($scope.myDataSets) == '{}'){
		setTitle('Even geduld, datasets worden opgehaald...');
		DataSetService.get().then(function(sets){
			document.getElementById('dsTitle').innerHTML = '
DatSets(' + Object.keys(sets).length + ')
';
			$scope.myDataSets = sets;
			StorageService.setObject('myDataSets', $scope.myDataSets);
			setTitle('');
		});
	}

  	$scope.saveRange = function(){
    	StorageService.setObject('myDataSets', $scope.myDataSets);
  	};

	$scope.load = function(set){
		StorageService.setObject('myDataSets', $scope.myDataSets);
		if($scope.myDataSets[set.resource].used){
			if(!StorageService.get(set.resource + '.json')){
				$http.get('http://datasets.antwerpen.be/v4/gis/' + set.resource + '.json')
				    .success(function(data) {
				       StorageService.set(set.resource + '.json', JSON.stringify(data));
				    });
			}
		}
	};

});
<commit_msg>Corrected bug with nr of datasets in title<commit_after>angular.module('buurtmeter.controllers', ['leaflet-directive', 'ionic'])

.controller('MapController', function($scope, $timeout, AreaService, StorageService, CameraService){
	var cameraOptions = {
		destinationType : 1, // 0 : base64-encoded, 1 : image file URI, 2 : image native URI
		sourceType : 2, // 0 : PHOTOLIBRARY, 1 : CAMERA, 2 : SAVEDPHOTOALBUM
		//quality: 90, // less than 50 to avoid memory problems for older iPhones
		encodingType: 1, // 0 : jpeg, 1 : png
		correctOrientation: true, // rotate the image to correct for the orientation of the device during capture
		//targetWidth: 100,
		//targetHeight: 100,
		saveToPhotoAlbum: true
	};

	var center = StorageService.getObject('center');
	// defaults to 'grote markt'
	if(JSON.stringify(center) == '{}'){
		center.lat = 51.221311;
		center.lng = 4.399160;
	}

    var map = {
        defaults: {
        	// MapQuest
        	tileLayer: 'http://otile4.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png',
        	// OpenStreetMap
            //tileLayer: 'http://{s}.tile.osm.org/{z}/{x}/{y}.png',
            maxZoom: 20,
            zoomControl: false,
            doubleClickZoom: false,
            scrollWheelZoom: true,
            touchZoom: true,
            path: {
                weight: 10,
                color: '#800000',
                opacity: 1
            }
        },
        markers: StorageService.getObject('mapMarkers'),
        center: {
            lat: center.lat,
            lng: center.lng,
            zoom: 17
        }
    };

	$scope.map = map;
	$scope.markerCount = $scope.map.markers.length;
	$scope.myDataSets = StorageService.getObject('myDataSets');

	// normal click
	$scope.$on('leafletDirectiveMap.click', function(event, locationEvent){
		setTimeout(addMarker(locationEvent), 1000);
	});

	// double click
	// $scope.$on('leafletDirectiveMap.dblclick', function(event, locationEvent){ });

	// right-click
	$scope.$on('leafletDirectiveMap.contextmenu', function(event, locationEvent){
		$scope.map.markers = [];
    	$scope.markerCount = 0;
    	StorageService.setObject('mapMarkers', $scope.map.markers);
	});

	setTitle = function(msg){
		document.getElementById('title').innerHTML = '
' + msg + '
';
	};

	locate = function(){
		setTitle('Even geduld, positie wordt bepaald...');
		navigator.geolocation.getCurrentPosition(function(position){
			var center = {
				lat: position.coords.latitude,
				lng: position.coords.longitude,
				zoom : 17
			};
			$timeout(function(){
        		$scope.map.center = center;
        		setTitle('');
    		}, 3000);
			StorageService.setObject('center', center);
		}, function(err){ console.log(err); });
	};

	/* http://alienryderflex.com/polygon/
	The basic idea is to find all edges of the polygon that span the 'x' position of the point you're testing against.
	Then you find how many of them intersect the vertical line that extends above your point. If an even number cross above the point,
	then you're outside the polygon. If an odd number crosses above, then you're inside. */
	inPolygon = function(location, polyLoc){
		var lastPoint = polyLoc[polyLoc.length-1];
		var isInside = false;
		var x = location[0];

		for(var i = 0; i < polyLoc.length; i++){
			var point = polyLoc[i];
			var x1 = lastPoint[0];
			var x2 = point[0];
			var dx = x2 - x1;

			if(Math.abs(dx) > 180.0){
				if(x > 0){
					while(x1 < 0)
						x1 += 360;
					while(x2 < 0)
						x2 += 360;
				}
				else{
					while(x1 > 0)
						x1 -= 360;
					while(x2 > 0)
						x2 -= 360;
				}
				dx = x2 - x1;
			}

			if((x1 <= x && x2 > x) || (x1 >= x && x2 < x)){
				var grad = (point[1] - lastPoint[1]) / dx;
				var intersectAtLat = lastPoint[1] + ((x - x1) * grad);

				if(intersectAtLat > location[1])
					isInside = !isInside;
			}
			lastPoint = point;
		}
		return isInside;
	};

	getAreaScore = function(lat, lng){
		var totalResult = {};
		var totalScore = 0;
		for(var key in $scope.myDataSets){
  			if($scope.myDataSets.hasOwnProperty(key) && $scope.myDataSets[key].used){
   				var tmpScore = 0;
  				try{
					var set = JSON.parse(StorageService.get(key + '.json'));
					var geo = JSON.parse(set.data[0].geometry);
					if(geo.type == 'Polygon'){
						var polygons = geo.coordinates;
						for(var i = 0; i < polygons.length; i++){
							if(inPolygon([lng, lat], polygons[i])){
								tmpScore += 1;
							}
						}
						totalScore += tmpScore * ($scope.myDataSets[key].range / 10);
						totalResult[key] = {'name':$scope.myDataSets[key].type, 'score':tmpScore * ($scope.myDataSets[key].range / 10)};
					}
				}
				catch(err){
					console.log(err);
				}
			}
		}
		totalResult.total = totalScore;
		return totalResult;
	};

	addMarker = function(locationEvent){
		var lat = locationEvent.leafletEvent.latlng.lat;
		var lng = locationEvent.leafletEvent.latlng.lng;
		var areas = AreaService.all();
		for(var i = 0; i < areas.length; i++){
			var geo = JSON.parse(areas[i].geometry);
			var coordinates = geo.coordinates[0];
			if(inPolygon([lng, lat], coordinates)){
				var scores = getAreaScore(lat, lng);
				var msg = '' + areas[i].wijknaam + '
' + 'Score : ' + scores.total + '
';
				if(scores.total > 0){
					msg += '
';
					for(var score in scores){
						if(score != 'total'){
							msg += '
' + scores[score].name + ' : ' + scores[score].score;
						}
					}
				}
				var marker = {
			        lat: lat,
			        lng: lng,
			        message: msg,
					focus: true,
					draggable: false
				};
				updateMap = function(){
			        $scope.map.markers[$scope.markerCount] = marker;
			        $scope.markerCount += 1;
			        StorageService.setObject('mapMarkers', $scope.map.markers);
				};
				if(ionic.Platform.isCordova()){
					CameraService.getPicture(cameraOptions).then(function(imageURI){
				    	var img = document.createElement('img');
				    	var canvas = document.createElement('canvas');
				        var ctx = canvas.getContext('2d');
						canvas.width = 100;
				        canvas.height = 100;
				        img.onload = function(){
				            ctx.drawImage(this, 0, 0, 100, 100);
				     		marker.message += '

';
				     		// dunno why, but timeout necessary for update of map
							$timeout(updateMap(), 500);
				        };
				        img.src = imageURI;
		    		}, function(err){
		    			updateMap();
		    		});
				}
				// desktop browser
				else{
					updateMap();
					//console.log(StorageService.getObject('mapMarkers'));
				}
	    		break;
			}
		}
	};
})

.controller('DataController', function($scope, $timeout, $http, DataSetService, StorageService){
	setTitle = function(msg){
		document.getElementById('title').innerHTML = '
' + msg + '
';
	};

	locate = function(){};

	$scope.myDataSets = StorageService.getObject('myDataSets');
	if(JSON.stringify($scope.myDataSets) == '{}'){
		setTitle('Even geduld, datasets worden opgehaald...');
		DataSetService.get().then(function(sets){
			document.getElementById('dsTitle').innerHTML = '
DatSets(' + Object.keys(sets).length + ')
';
			$scope.myDataSets = sets;
			StorageService.setObject('myDataSets', $scope.myDataSets);
			setTitle('');
		});
	}
	else{
		$timeout(function(){
			document.getElementById('dsTitle').innerHTML = '
DatSets(' + Object.keys($scope.myDataSets).length + ')
';
		}
		, 100);
	}

  	$scope.saveRange = function(){
    	StorageService.setObject('myDataSets', $scope.myDataSets);
  	};

	$scope.load = function(set){
		StorageService.setObject('myDataSets', $scope.myDataSets);
		if($scope.myDataSets[set.resource].used){
			if(!StorageService.get(set.resource + '.json')){
				$http.get('http://datasets.antwerpen.be/v4/gis/' + set.resource + '.json')
				    .success(function(data) {
				       StorageService.set(set.resource + '.json', JSON.stringify(data));
				    });
			}
		}
	};

});
<|endoftext|>


<commit_before>
var __ff_ui;
$(function() {
  // Load the Facebook SDK, then initialize FirefeedUI;
  window.fbAsyncInit = function() {
    FB.init({
      appId      : "104907529680402",
      channelUrl : "channel.html"
    });
    __ff_ui = new FirefeedUI();
  };
  (function(d, debug){
    var js, id = "facebook-jssdk", ref = d.getElementsByTagName("script")[0];
    if (d.getElementById(id)) {return;}
    js = d.createElement("script"); js.id = id; js.async = true;
    js.src = "//connect.facebook.net/en_US/all" + (debug ? "/debug" : "") + ".js";
    ref.parentNode.insertBefore(js, ref);
  }(document, /*debug*/ false));
});

function FirefeedUI() {
  this._limit = 120;
  this._loggedIn = false;
  this._spinner = new Spinner();
  this._firefeed = new Firefeed("http://firefeed.firebaseio.com/");

  // Figure out if the user is logged in or not, with silent login.
  var self = this;
  self.login(function(info) {
    self._loggedIn = info;
    self._pageController(window.location.href);
  });
}

FirefeedUI.prototype._pageController = function(url) {
  // Extract sub page from URL, if any.
  var idx = url.indexOf("?");
  var hash = (idx > 0) ? url.slice(idx + 1) : "";
  var value = hash.split("=");

  switch (value[0]) {
    case "profile":
      if (!value[1]) {
        this.render404({}, "", "/?404");
      } else {
        this.renderProfile(value[1]);
      }
      break;
    case "status":
      if (!value[1]) {
        this.render404({}, "", "/?404");
      } else {
        this.renderStatus(value[1]);
      }
      break;
    case "timeline":
    default:
      if (this._loggedIn) {
        this.renderTimeline(this._loggedIn);
      } else {
        this.renderHome();
      }
      break;
  }
};

FirefeedUI.prototype._postHandler = function(e) {
  var sparkText = $("#spark-input");
  var sparkButton = $("#spark-button");
  var containerEl = $("#spark-button-div");
  var message = $("<div>", { class: "msg" }).html("Posting...");

  var self = this;
  e.preventDefault();
  sparkButton.replaceWith(message);
  self._spinner.spin(containerEl.get(0));
  self._firefeed.post(sparkText.val(), function(err, done) {
    if (!err) {
      message.html("Posted!").css("background", "#008000");
      sparkText.val("");
    } else {
      message.html("Posting failed!").css("background", "#FF6347");
    }
    self._spinner.stop();
    $("#c-count").val(self._limit);
    message.css("visibility", "visible");
    message.fadeOut(1500, function() {
      message.replaceWith(sparkButton);
      sparkButton.click(self._postHandler.bind(self));
    });
  });
};

FirefeedUI.prototype._handleNewSpark = function(listId, limit, func) {
  var self = this;
  func(
    limit,
    function(sparkId, spark) {
      spark.sparkId = sparkId;
      spark.friendlyTimestamp = self._formatDate(
        new Date(spark.timestamp || 0)
      );
      var sparkEl = $(Mustache.to_html($("#tmpl-spark").html(), spark)).hide();
      $("#" + listId).prepend(sparkEl);
      sparkEl.slideDown("slow");
    }, function(sparkId) {
      $("#spark-" + sparkId).slideToggle("slow", function() {
        $(this).remove();
      });
    }
  );
};

FirefeedUI.prototype._formatDate = function(date) {
  var localeDate = date.toLocaleString();
  // Remove GMT offset if it's there.
  var gmtIndex = localeDate.indexOf(' GMT');
  if (gmtIndex > 0) {
    localeDate = localeDate.substr(0, gmtIndex);
  }
  return localeDate;
};

FirefeedUI.prototype._editableHandler = function(id, value) {
  if (id == "inputLocation") {
    this._firefeed.setProfileField("location", value);
  }
  if (id == "inputBio") {
    this._firefeed.setProfileField("bio", value);
  }
  return true;
}

FirefeedUI.prototype.login = function(cb) {
  // Try silent login in case the user is already logged in.
  var self = this;
  self._firefeed.login(true, function(err, info) {
    if (!err && info) {
      cb(info);
    } else {
      cb(false);
    }
  });
};

FirefeedUI.prototype.logout = function(e) {
  if (e) {
    e.preventDefault();
  }
  this._firefeed.logout();
  this._loggedIn = false;
  this.renderHome();
};

FirefeedUI.prototype.render404 == function() {
  // TODO: Add 404 page.
  this.renderHome();
};

FirefeedUI.prototype.renderHome = function(e) {
  if (e) {
    e.preventDefault();
  }
  if (this._loggedIn) {
    this.renderTimeline(this._loggedIn);
    return;
  }

  $("#header").html($("#tmpl-index-header").html());

  // Preload animation.
  var path = "img/curl-animate.gif";
  var img = new Image();
  img.src = path;

  // Setup curl on hover.
  $(".ribbon-curl").find("img").hover(function() {
    $(this).attr("src", path);
  }, function() {
    $(this).attr("src", "img/curl-static.gif");
  });

  var body = Mustache.to_html($("#tmpl-content").html(), {
    classes: "cf home", content: $("#tmpl-index-content").html()
  });
  $("#body").html(body);

  var self = this;
  var loginButton = $("#login-button");
  loginButton.click(function(e) {
    e.preventDefault();
    loginButton.css("visibility", "hidden");
    self._spinner.spin($("#login-div").get(0));
    self._firefeed.login(false, function(err, info) {
      if (err) {
        self._spinner.stop();
        loginButton.css("visibility", "visible");
        console.log(err);
      } else {
        self.renderTimeline(info);
      }
    });
  });

  $("#about-link").remove();

  // Attach handler to display the latest 5 sparks.
  self._handleNewSpark(
    'spark-index-list', 5, self._firefeed.onLatestSpark.bind(self._firefeed)
  );
};

FirefeedUI.prototype.renderTimeline = function(info) {
  $("#header").html($("#tmpl-page-header").html());
  $("#top-logo").click(this.renderHome.bind(this));
  $("#logout-button").click(this.logout.bind(this));

  // Render placeholders for location / bio if not filled in.
  info.location = info.location || "Your Location...";
  info.bio = info.bio || "Your Bio...";

  // Render body.
  var content = Mustache.to_html($("#tmpl-timeline-content").html(), info);
  var body = Mustache.to_html($("#tmpl-content").html(), {
    classes: "cf", content: content
  });
  $("#body").html(body);

  // Attach textarea handlers.
  var self = this;
  var charCount = $("#c-count");
  var sparkText = $("#spark-input");
  $("#spark-button").css("visibility", "hidden");
  function _textAreaHandler() {
    var text = sparkText.val();
    charCount.text("" + (self._limit - text.length));
    if (text.length > self._limit) {
      charCount.css("color", "#FF6347");
      $("#spark-button").css("visibility", "hidden");
    } else if (text.length == 0) {
      $("#spark-button").css("visibility", "hidden");
    } else {
      charCount.css("color", "#999");
      $("#spark-button").css("visibility", "visible");
    }
  };
  charCount.text(self._limit);
  sparkText.keyup(_textAreaHandler);
  sparkText.blur(_textAreaHandler);

  // Attach post spark button.
  $("#spark-button").click(self._postHandler.bind(self));

  // Attach new spark event handler, capped to 10 for now.
  self._handleNewSpark(
    'spark-timeline-list', 10, self._firefeed.onNewSpark.bind(self._firefeed)
  );

  // Get some "suggested" users.
  self._firefeed.getSuggestedUsers(function(userid, info) {
    info.id = userid;
    $(Mustache.to_html($("#tmpl-suggested-user").html(), info)).
      appendTo("#suggested-users");
    var button = $("#followBtn-" + userid);
    // Fade out the suggested user if they were followed successfully.
    button.click(function(e) {
      e.preventDefault();
      button.remove();
      self._firefeed.follow(userid, function(err, done) {
        // TODO FIXME: Check for errors!
        $("#followBox-" + userid).fadeOut(1500);
      });
    });
  });

  // Make profile fields editable.
  $(".editable").editable(function(value, settings) {
    self._editableHandler($(this).attr("id"), value);
    return value;
  });
};

FirefeedUI.prototype.renderProfile = function(uid) {
  var self = this;
  $("#header").html($("#tmpl-page-header").html());
  $("#top-logo").click(this.renderHome.bind(this));
  if (self._loggedIn) {
    $("#logout-button").click(self.logout.bind(self));
  } else {
    $("#logout-button").remove();
  }

  // Render profile page body.
  self._firefeed.getUserInfo(uid, function(info) {
    var content = Mustache.to_html($("#tmpl-profile-content").html(), info);
    var body = Mustache.to_html($("#tmpl-content").html(), {
      classes: "cf", content: content
    });
    $("#body").html(body);
  });

  // Render this user's tweets. Capped to 5 for now.
  self._handleNewSpark(
    'spark-profile-list', 5, self._firefeed.onNewSparkFor.bind(self._firefeed, uid)
  );
};

FirefeedUI.prototype.renderStatus = function(id) {
  $("#header").html($("#tmpl-page-header").html());
  $("#top-logo").click(this.renderHome.bind(this));
  $("#logout-button").click(this.logout.bind(this));

  // Render spark page body.
  var self = this;
  self._firefeed.getSpark(id, function(info) {
    if (info !== null && info.author) {
      self._firefeed.getUserInfo(info.author, function(authorInfo) {
        for (var key in authorInfo) {
          info[key] = authorInfo[key];
        }
        info.friendlyTimestamp = self._formatDate(
          new Date(info.timestamp || 0)
        );
        var content = Mustache.to_html($("#tmpl-spark-content").html(), info);
        var body = Mustache.to_html($("#tmpl-content").html(), {
          classes: "cf", content: content
        });
        $("#body").html(body);
      });
    }
  });
};
<commit_msg>Limit spark display length to disincentivize length overflowing<commit_after>
var __ff_ui;
$(function() {
  // Load the Facebook SDK, then initialize FirefeedUI;
  window.fbAsyncInit = function() {
    FB.init({
      appId      : "104907529680402",
      channelUrl : "channel.html"
    });
    __ff_ui = new FirefeedUI();
  };
  (function(d, debug){
    var js, id = "facebook-jssdk", ref = d.getElementsByTagName("script")[0];
    if (d.getElementById(id)) {return;}
    js = d.createElement("script"); js.id = id; js.async = true;
    js.src = "//connect.facebook.net/en_US/all" + (debug ? "/debug" : "") + ".js";
    ref.parentNode.insertBefore(js, ref);
  }(document, /*debug*/ false));
});

function FirefeedUI() {
  this._limit = 141;
  this._loggedIn = false;
  this._spinner = new Spinner();
  this._firefeed = new Firefeed("http://firefeed.firebaseio.com/");

  // Figure out if the user is logged in or not, with silent login.
  var self = this;
  self.login(function(info) {
    self._loggedIn = info;
    self._pageController(window.location.href);
  });
}

FirefeedUI.prototype._pageController = function(url) {
  // Extract sub page from URL, if any.
  var idx = url.indexOf("?");
  var hash = (idx > 0) ? url.slice(idx + 1) : "";
  var value = hash.split("=");

  switch (value[0]) {
    case "profile":
      if (!value[1]) {
        this.render404({}, "", "/?404");
      } else {
        this.renderProfile(value[1]);
      }
      break;
    case "status":
      if (!value[1]) {
        this.render404({}, "", "/?404");
      } else {
        this.renderStatus(value[1]);
      }
      break;
    case "timeline":
    default:
      if (this._loggedIn) {
        this.renderTimeline(this._loggedIn);
      } else {
        this.renderHome();
      }
      break;
  }
};

FirefeedUI.prototype._postHandler = function(e) {
  var sparkText = $("#spark-input");
  var sparkButton = $("#spark-button");
  var containerEl = $("#spark-button-div");
  var message = $("<div>", { class: "msg" }).html("Posting...");

  var self = this;
  e.preventDefault();
  sparkButton.replaceWith(message);
  self._spinner.spin(containerEl.get(0));
  self._firefeed.post(sparkText.val(), function(err, done) {
    if (!err) {
      message.html("Posted!").css("background", "#008000");
      sparkText.val("");
    } else {
      message.html("Posting failed!").css("background", "#FF6347");
    }
    self._spinner.stop();
    $("#c-count").val(self._limit);
    message.css("visibility", "visible");
    message.fadeOut(1500, function() {
      message.replaceWith(sparkButton);
      sparkButton.click(self._postHandler.bind(self));
    });
  });
};

FirefeedUI.prototype._handleNewSpark = function(listId, limit, func) {
  var self = this;
  func(
    limit,
    function(sparkId, spark) {
      spark.content = spark.content.substring(0, self._limit);
      spark.sparkId = sparkId;
      spark.friendlyTimestamp = self._formatDate(
        new Date(spark.timestamp || 0)
      );
      var sparkEl = $(Mustache.to_html($("#tmpl-spark").html(), spark)).hide();
      $("#" + listId).prepend(sparkEl);
      sparkEl.slideDown("slow");
    }, function(sparkId) {
      $("#spark-" + sparkId).slideToggle("slow", function() {
        $(this).remove();
      });
    }
  );
};

FirefeedUI.prototype._formatDate = function(date) {
  var localeDate = date.toLocaleString();
  // Remove GMT offset if it's there.
  var gmtIndex = localeDate.indexOf(' GMT');
  if (gmtIndex > 0) {
    localeDate = localeDate.substr(0, gmtIndex);
  }
  return localeDate;
};

FirefeedUI.prototype._editableHandler = function(id, value) {
  if (id == "inputLocation") {
    this._firefeed.setProfileField("location", value);
  }
  if (id == "inputBio") {
    this._firefeed.setProfileField("bio", value);
  }
  return true;
}

FirefeedUI.prototype.login = function(cb) {
  // Try silent login in case the user is already logged in.
  var self = this;
  self._firefeed.login(true, function(err, info) {
    if (!err && info) {
      cb(info);
    } else {
      cb(false);
    }
  });
};

FirefeedUI.prototype.logout = function(e) {
  if (e) {
    e.preventDefault();
  }
  this._firefeed.logout();
  this._loggedIn = false;
  this.renderHome();
};

FirefeedUI.prototype.render404 == function() {
  // TODO: Add 404 page.
  this.renderHome();
};

FirefeedUI.prototype.renderHome = function(e) {
  if (e) {
    e.preventDefault();
  }
  if (this._loggedIn) {
    this.renderTimeline(this._loggedIn);
    return;
  }

  $("#header").html($("#tmpl-index-header").html());

  // Preload animation.
  var path = "img/curl-animate.gif";
  var img = new Image();
  img.src = path;

  // Setup curl on hover.
  $(".ribbon-curl").find("img").hover(function() {
    $(this).attr("src", path);
  }, function() {
    $(this).attr("src", "img/curl-static.gif");
  });

  var body = Mustache.to_html($("#tmpl-content").html(), {
    classes: "cf home", content: $("#tmpl-index-content").html()
  });
  $("#body").html(body);

  var self = this;
  var loginButton = $("#login-button");
  loginButton.click(function(e) {
    e.preventDefault();
    loginButton.css("visibility", "hidden");
    self._spinner.spin($("#login-div").get(0));
    self._firefeed.login(false, function(err, info) {
      if (err) {
        self._spinner.stop();
        loginButton.css("visibility", "visible");
        console.log(err);
      } else {
        self.renderTimeline(info);
      }
    });
  });

  $("#about-link").remove();

  // Attach handler to display the latest 5 sparks.
  self._handleNewSpark(
    'spark-index-list', 5, self._firefeed.onLatestSpark.bind(self._firefeed)
  );
};

FirefeedUI.prototype.renderTimeline = function(info) {
  $("#header").html($("#tmpl-page-header").html());
  $("#top-logo").click(this.renderHome.bind(this));
  $("#logout-button").click(this.logout.bind(this));

  // Render placeholders for location / bio if not filled in.
  info.location = info.location || "Your Location...";
  info.bio = info.bio || "Your Bio...";

  // Render body.
  var content = Mustache.to_html($("#tmpl-timeline-content").html(), info);
  var body = Mustache.to_html($("#tmpl-content").html(), {
    classes: "cf", content: content
  });
  $("#body").html(body);

  // Attach textarea handlers.
  var self = this;
  var charCount = $("#c-count");
  var sparkText = $("#spark-input");
  $("#spark-button").css("visibility", "hidden");
  function _textAreaHandler() {
    var text = sparkText.val();
    charCount.text("" + (self._limit - text.length));
    if (text.length > self._limit) {
      charCount.css("color", "#FF6347");
      $("#spark-button").css("visibility", "hidden");
    } else if (text.length == 0) {
      $("#spark-button").css("visibility", "hidden");
    } else {
      charCount.css("color", "#999");
      $("#spark-button").css("visibility", "visible");
    }
  };
  charCount.text(self._limit);
  sparkText.keyup(_textAreaHandler);
  sparkText.blur(_textAreaHandler);

  // Attach post spark button.
  $("#spark-button").click(self._postHandler.bind(self));

  // Attach new spark event handler, capped to 10 for now.
  self._handleNewSpark(
    'spark-timeline-list', 10, self._firefeed.onNewSpark.bind(self._firefeed)
  );

  // Get some "suggested" users.
  self._firefeed.getSuggestedUsers(function(userid, info) {
    info.id = userid;
    $(Mustache.to_html($("#tmpl-suggested-user").html(), info)).
      appendTo("#suggested-users");
    var button = $("#followBtn-" + userid);
    // Fade out the suggested user if they were followed successfully.
    button.click(function(e) {
      e.preventDefault();
      button.remove();
      self._firefeed.follow(userid, function(err, done) {
        // TODO FIXME: Check for errors!
        $("#followBox-" + userid).fadeOut(1500);
      });
    });
  });

  // Make profile fields editable.
  $(".editable").editable(function(value, settings) {
    self._editableHandler($(this).attr("id"), value);
    return value;
  });
};

FirefeedUI.prototype.renderProfile = function(uid) {
  var self = this;
  $("#header").html($("#tmpl-page-header").html());
  $("#top-logo").click(this.renderHome.bind(this));
  if (self._loggedIn) {
    $("#logout-button").click(self.logout.bind(self));
  } else {
    $("#logout-button").remove();
  }

  // Render profile page body.
  self._firefeed.getUserInfo(uid, function(info) {
    var content = Mustache.to_html($("#tmpl-profile-content").html(), info);
    var body = Mustache.to_html($("#tmpl-content").html(), {
      classes: "cf", content: content
    });
    $("#body").html(body);
  });

  // Render this user's tweets. Capped to 5 for now.
  self._handleNewSpark(
    'spark-profile-list', 5, self._firefeed.onNewSparkFor.bind(self._firefeed, uid)
  );
};

FirefeedUI.prototype.renderStatus = function(id) {
  $("#header").html($("#tmpl-page-header").html());
  $("#top-logo").click(this.renderHome.bind(this));
  $("#logout-button").click(this.logout.bind(this));

  // Render spark page body.
  var self = this;
  self._firefeed.getSpark(id, function(spark) {
    if (spark !== null && spark.author) {
      self._firefeed.getUserInfo(spark.author, function(authorInfo) {
        for (var key in authorInfo) {
          spark[key] = authorInfo[key];
        }
        spark.content = spark.content.substring(0, self._limit);
        spark.friendlyTimestamp = self._formatDate(
          new Date(spark.timestamp || 0)
        );
        var content = Mustache.to_html($("#tmpl-spark-content").html(), spark);
        var body = Mustache.to_html($("#tmpl-content").html(), {
          classes: "cf", content: content
        });
        $("#body").html(body);
      });
    }
  });
};
<|endoftext|>


<commit_before>/*
 * Hammer.JS
 * version 0.2
 * author: Eight Media
 * https://github.com/EightMedia/hammer.js
 */
function Hammer(element, options, undefined)
{
    var self = this;

    var defaults = {
        // prevent the default event or not... might be buggy when false
        prevent_default    : false,

        drag               : true,
        drag_vertical      : true,
        drag_horizontal    : true,
        // minimum distance before the drag event starts
        drag_min_distance  : 20, // pixels

        // pinch zoom and rotation
        transform          : true,
        scale_treshold     : 0.1,
        rotation_treshold  : 15, // degrees

        tap                : true,
        tap_double         : true,
        tap_max_interval   : 300,
        tap_double_distance: 20,

        hold               : true,
        hold_timeout       : 500
    };
    options = mergeObject(defaults, options);

    // some css hacks
    var vendors = ['-webkit-','-moz-','-ms-','-o-',''];
    var css = '';
    var css_props = {
        "user-select": "none",
        "touch-callout": "none",
        "user-drag": "none",
        "tap-highlight-color": "rgba(0,0,0,0)"
    }

    var i = 0, l = vendors.length;
    for(; i<l; ++i){
        if( supports(vendors[i] + 'user-select') ) {
            for(var prop in css_props) {
                css += vendors[i] + prop + ': ' + css_props[prop] + ';';
            }
        }
    }
    element.setAttribute('style', css);

    // holds the distance that has been moved
    var _distance = 0;

    // holds the exact angle that has been moved
    var _angle = 0;

    // holds the diraction that has been moved
    var _direction = 0;

    // holds position movement for sliding
    var _pos = { };

    // how many fingers are on the screen
    var _fingers = 0;

    var _first = false;

    var _gesture = null;
    var _prev_gesture = null;

    var _touch_start_time = null;
    var _prev_tap_pos = {x: 0, y: 0};
    var _prev_tap_end_time = null;

    var _hold_timer = null;

    var _offset = {};

    // keep track of the mouse status
    var _mousedown = false;

    /**
     * angle to direction define
     * @param  float    angle
     * @return string   direction
     */
    this.getDirectionFromAngle = function( angle )
    {
        var directions = {
            down: angle >= 45 && angle < 135, //90
            left: angle >= 135 || angle <= -135, //180
            up: angle < -45 && angle > -135, //270
            right: angle >= -45 && angle <= 45 //0
        };

        var direction, key;
        for(key in directions){
            if(directions[key]){
                direction = key;
                break;
            }
        }
        return direction;
    };

    /**
     * count the number of fingers in the event
     * when no fingers are detected, one finger is returned (mouse pointer)
     * @param  event
     * @return int  fingers
     */
    function countFingers( event )
    {
        // there is a bug on android (until v4?) that touches is always 1,
        // so no multitouch is supported, e.g. no, zoom and rotation...
        return event.touches ? event.touches.length : 1;
    }

    /**
     * get the x and y positions from the event object
     * @param  event
     * @return array  [{ x: int, y: int }]
     */
    function getXYfromEvent( event )
    {
        // no touches, use the event pageX and pageY
        if(!event.touches) {
            return [{ x: event.pageX, y: event.pageY }];
        }
        // multitouch, return array with positions
        else {
            var pos = [], src;
            for(var t=0, len=event.touches.length; t<len; t++) {
                src = event.touches[t];
                pos.push({ x: src.pageX, y: src.pageY });
            }
            return pos;
        }
    }

    /**
     * calculate the angle between two points
     * @param object pos1 { x: int, y: int }
     * @param object pos2 { x: int, y: int }
     */
    function getAngle( pos1, pos2 )
    {
        return Math.atan2(pos2.y - pos1.y, pos2.x - pos1.x) * 180 / Math.PI;
    }

    /**
     * trigger an event/callback by name with params
     * @param string name
     * @param array  params
     */
    function triggerEvent( eventName, params )
    {
        // return touches object
        params.touches = getXYfromEvent(params.originalEvent);
        params.type = eventName;

        // trigger callback
        if(isFunction(self["on"+ eventName])) {
            self["on"+ eventName].call(self, params);
        }
    }

    /**
     * reset the internal vars to the start values
     */
    function reset()
    {
        _pos = {};
        _first = false;
        _fingers = 0;
        _distance = 0;
        _angle = 0;
        _gesture = null;
    }

    var gestures = {
        // hold gesture
        // fired on touchstart
        hold : function(event)
        {
            // only when one finger is on the screen
            if(options.hold && _fingers == 1) {
                _gesture = 'hold';
                clearTimeout(_hold_timer);

                _hold_timer = setTimeout(function() {
                    if(_gesture == 'hold' && _fingers == 1) {
                        triggerEvent("hold", {
                            originalEvent   : event,
                            position        : _pos.start
                        });
                    }
                }, options.hold_timeout);
            }
        },

        // drag gesture
        // fired on mousemove
        drag : function(event)
        {
            // get the distance we moved
            var _distance_x = _pos.move[0].x - _pos.start[0].x;
            var _distance_y = _pos.move[0].y - _pos.start[0].y;
            _distance = Math.sqrt(_distance_x * _distance_x + _distance_y * _distance_y);

            // drag
            // minimal movement required
            if(options.drag && (_distance > options.drag_min_distance) || _gesture == 'drag') {
                // calculate the angle
                _angle = getAngle(_pos.start[0], _pos.move[0]);
                _direction = self.getDirectionFromAngle(_angle);

                // check the movement and stop if we go in the wrong direction
                var is_vertical = (_direction == 'up' || _direction == 'down');
                if(((is_vertical && !options.drag_vertical) || (!is_vertical && !options.drag_horizontal))
                    && (_distance > options.drag_min_distance)) {
                    return;
                }

                _gesture = 'drag';

                var position = { x: _pos.move[0].x - _offset.left,
                                 y: _pos.move[0].y - _offset.top };

                var event_obj = {
                    originalEvent   : event,
                    position        : position,
                    direction       : _direction,
                    distance        : _distance,
                    distanceX       : _distance_x,
                    distanceY       : _distance_y,
                    angle           : _angle
                };

                // on the first time trigger the start event
                if(_first) {
                    triggerEvent("dragstart", event_obj);

                    _first = false;
                }

                // normal slide event
                triggerEvent("drag", event_obj);

                event.preventDefault();
            }
        },

        // transform gesture
        // fired on touchmove
        transform : function(event)
        {
            if(options.transform) {
                var scale = event.scale || 1;
                var rotation = event.rotation || 0;

                if(_gesture != 'drag' &&
                    (_gesture == 'transform' || Math.abs(1-scale) > options.scale_treshold
                        || Math.abs(rotation) > options.rotation_treshold)) {
                    _gesture = 'transform';

                    _pos.center = {  x: ((_pos.move[0].x + _pos.move[1].x) / 2) - _offset.left,
                        y: ((_pos.move[0].y + _pos.move[1].y) / 2) - _offset.top };

                    var event_obj = {
                        originalEvent   : event,
                        position        : _pos.center,
                        scale           : scale,
                        rotation        : rotation
                    };

                    // on the first time trigger the start event
                    if(_first) {
                        triggerEvent("transformstart", event_obj);
                        _first = false;
                    }

                    triggerEvent("transform", event_obj);

                    event.preventDefault();

                    return true;
                }
            }

            return false;
        },

        // tap and double tap gesture
        // fired on touchend
        tap : function(event)
        {
            // compare the kind of gesture by time
            var now = new Date().getTime();
            var touch_time = now - _touch_start_time;
            var is_double_tap = function () {
                if (_prev_tap_pos && options.tap_double && _prev_gesture == 'tap' &&
                    (_touch_start_time - _prev_tap_end_time) < options.tap_max_interval) {
                    var x_distance = Math.abs(_prev_tap_pos[0].x - _pos.start[0].x);
                    var y_distance = Math.abs(_prev_tap_pos[0].y - _pos.start[0].y);
                    return (_prev_tap_pos && _pos.start &&
                        Math.max(x_distance, y_distance) < options.tap_double_distance);
                }
                return false;
            }

            // dont fire when hold is fired
            if(options.hold && !(options.hold && options.hold_timeout > touch_time)) {
                return;
            }

            // when previous event was tap and the tap was max_interval ms ago

            if(is_double_tap()) {
                _gesture = 'double_tap';
                _prev_tap_end_time = null;

                triggerEvent("doubletap", {
                    originalEvent   : event,
                    position        : _pos.start
                });
                event.preventDefault();
            }

            // single tap is single touch
            else {
                _gesture = 'tap';
                _prev_tap_end_time = now;
                _prev_tap_pos = _pos.start;

                if(options.tap) {
                    triggerEvent("tap", {
                        originalEvent   : event,
                        position        : _pos.start
                    });
                    event.preventDefault();
                }
            }

        }

    };

    function handleEvents(event)
    {
        switch(event.type)
        {
            case 'mousedown':
            case 'touchstart':
                _pos.start = getXYfromEvent(event);
                _touch_start_time = new Date().getTime();
                _fingers = countFingers(event);
                _first = true;

                // borrowed from jquery offset https://github.com/jquery/jquery/blob/master/src/offset.js
                // needs to be tested yet
                var box = element.getBoundingClientRect();
                var clientTop  = element.clientTop  || document.body.clientTop  || 0;
                var clientLeft = element.clientLeft || document.body.clientLeft || 0;
                var scrollTop  = window.pageYOffset || element.scrollTop  || document.body.scrollTop;
                var scrollLeft = window.pageXOffset || element.scrollLeft || document.body.scrollLeft;

                _offset = {
                    top: box.top + scrollTop  - clientTop,
                    left: box.left + scrollLeft - clientLeft
                };

                _mousedown = true;

                // hold gesture
                gestures.hold(event);

                if(options.prevent_default) {
                    event.preventDefault();
                }
                break;

            case 'mousemove':
            case 'touchmove':
                if(!_mousedown) {
                    return false;
                }
                _pos.move = getXYfromEvent(event);

                if(!gestures.transform(event)) {
                    gestures.drag(event);
                }
                break;

            case 'mouseup':
            case 'touchcancel':
            case 'touchend':
                _mousedown = false;

                // drag gesture
                // dragstart is triggered, so dragend is possible
                if(_gesture == 'drag') {
                    triggerEvent("dragend", {
                        originalEvent   : event,
                        direction       : _direction,
                        distance        : _distance,
                        angle           : _angle
                    });
                }

                // transform
                // transformstart is triggered, so transformed is possible
                else if(_gesture == 'transform') {
                    triggerEvent("transformend", {
                        originalEvent   : event,
                        position        : _pos.center,
                        scale           : event.scale,
                        rotation        : event.rotation
                    });
                }
                else {
                    gestures.tap(event);
                }

                _prev_gesture = _gesture;

                // reset vars
                reset();
                break;
        }
    }

    // bind events for touch devices
    // except for windows phone 7.5, it doenst support touch events..!
    if('ontouchstart' in window) {
        element.addEventListener("touchstart", handleEvents, false);
        element.addEventListener("touchmove", handleEvents, false);
        element.addEventListener("touchend", handleEvents, false);
        element.addEventListener("touchcancel", handleEvents, false);
    }
    // for non-touch
    else {
        // Listen for mouseup on the document so we know it happens
        // even if the mouse has left the element.
        document.addEventListener("mouseup", handleEvents, false);
        element.addEventListener("mousedown", handleEvents, false);
        element.addEventListener("mousemove", handleEvents, false);
    }

    /**
     * merge 2 objects into a new object
     * @param   object  obj1
     * @param   object  obj2
     * @return  object  merged object
     */
    function mergeObject(obj1, obj2) {
        var output = {};

        if(!obj2) {
            return obj1;
        }

        for (var prop in obj1) {
            if (prop in obj2) {
                output[prop] = obj2[prop];
            } else {
                output[prop] = obj1[prop];
            }
        }
        return output;
    }

    function supports( property ){
        return document.body.style[property] !== undefined;
    }

    function isFunction( obj ){
        return Object.prototype.toString.call( obj ) == "[object Function]";
    }
}<commit_msg>added marioestrada\s vendor styles<commit_after>/*
 * Hammer.JS
 * version 0.2
 * author: Eight Media
 * https://github.com/EightMedia/hammer.js
 */
function Hammer(element, options, undefined)
{
    var self = this;

    var defaults = {
        // prevent the default event or not... might be buggy when false
        prevent_default    : false,

        drag               : true,
        drag_vertical      : true,
        drag_horizontal    : true,
        // minimum distance before the drag event starts
        drag_min_distance  : 20, // pixels

        // pinch zoom and rotation
        transform          : true,
        scale_treshold     : 0.1,
        rotation_treshold  : 15, // degrees

        tap                : true,
        tap_double         : true,
        tap_max_interval   : 300,
        tap_double_distance: 20,

        hold               : true,
        hold_timeout       : 500
    };
    options = mergeObject(defaults, options);

    // some css hacks
    var vendors = ['-webkit-','-moz-','-ms-','-o-',''];
    var css_props = {
        "user-select": "none",
        "touch-callout": "none",
        "user-drag": "none",
        "tap-highlight-color": "rgba(0,0,0,0)"
    }

    for(i = 0; i < vendors.length; i++)
    {
        if( supports(vendors[i] + 'user-select') ) {
            for(var prop in css_props) {
                for(j = 0; j < elements.length; j++)
                {
                    elements[j].style[vendors[i] + prop] = css_props[prop];
                }
            }
        }
    }

    // holds the distance that has been moved
    var _distance = 0;

    // holds the exact angle that has been moved
    var _angle = 0;

    // holds the diraction that has been moved
    var _direction = 0;

    // holds position movement for sliding
    var _pos = { };

    // how many fingers are on the screen
    var _fingers = 0;

    var _first = false;

    var _gesture = null;
    var _prev_gesture = null;

    var _touch_start_time = null;
    var _prev_tap_pos = {x: 0, y: 0};
    var _prev_tap_end_time = null;

    var _hold_timer = null;

    var _offset = {};

    // keep track of the mouse status
    var _mousedown = false;

    /**
     * angle to direction define
     * @param  float    angle
     * @return string   direction
     */
    this.getDirectionFromAngle = function( angle )
    {
        var directions = {
            down: angle >= 45 && angle < 135, //90
            left: angle >= 135 || angle <= -135, //180
            up: angle < -45 && angle > -135, //270
            right: angle >= -45 && angle <= 45 //0
        };

        var direction, key;
        for(key in directions){
            if(directions[key]){
                direction = key;
                break;
            }
        }
        return direction;
    };

    /**
     * count the number of fingers in the event
     * when no fingers are detected, one finger is returned (mouse pointer)
     * @param  event
     * @return int  fingers
     */
    function countFingers( event )
    {
        // there is a bug on android (until v4?) that touches is always 1,
        // so no multitouch is supported, e.g. no, zoom and rotation...
        return event.touches ? event.touches.length : 1;
    }

    /**
     * get the x and y positions from the event object
     * @param  event
     * @return array  [{ x: int, y: int }]
     */
    function getXYfromEvent( event )
    {
        // no touches, use the event pageX and pageY
        if(!event.touches) {
            return [{ x: event.pageX, y: event.pageY }];
        }
        // multitouch, return array with positions
        else {
            var pos = [], src;
            for(var t=0, len=event.touches.length; t<len; t++) {
                src = event.touches[t];
                pos.push({ x: src.pageX, y: src.pageY });
            }
            return pos;
        }
    }

    /**
     * calculate the angle between two points
     * @param object pos1 { x: int, y: int }
     * @param object pos2 { x: int, y: int }
     */
    function getAngle( pos1, pos2 )
    {
        return Math.atan2(pos2.y - pos1.y, pos2.x - pos1.x) * 180 / Math.PI;
    }

    /**
     * trigger an event/callback by name with params
     * @param string name
     * @param array  params
     */
    function triggerEvent( eventName, params )
    {
        // return touches object
        params.touches = getXYfromEvent(params.originalEvent);
        params.type = eventName;

        // trigger callback
        if(isFunction(self["on"+ eventName])) {
            self["on"+ eventName].call(self, params);
        }
    }

    /**
     * reset the internal vars to the start values
     */
    function reset()
    {
        _pos = {};
        _first = false;
        _fingers = 0;
        _distance = 0;
        _angle = 0;
        _gesture = null;
    }

    var gestures = {
        // hold gesture
        // fired on touchstart
        hold : function(event)
        {
            // only when one finger is on the screen
            if(options.hold && _fingers == 1) {
                _gesture = 'hold';
                clearTimeout(_hold_timer);

                _hold_timer = setTimeout(function() {
                    if(_gesture == 'hold' && _fingers == 1) {
                        triggerEvent("hold", {
                            originalEvent   : event,
                            position        : _pos.start
                        });
                    }
                }, options.hold_timeout);
            }
        },

        // drag gesture
        // fired on mousemove
        drag : function(event)
        {
            // get the distance we moved
            var _distance_x = _pos.move[0].x - _pos.start[0].x;
            var _distance_y = _pos.move[0].y - _pos.start[0].y;
            _distance = Math.sqrt(_distance_x * _distance_x + _distance_y * _distance_y);

            // drag
            // minimal movement required
            if(options.drag && (_distance > options.drag_min_distance) || _gesture == 'drag') {
                // calculate the angle
                _angle = getAngle(_pos.start[0], _pos.move[0]);
                _direction = self.getDirectionFromAngle(_angle);

                // check the movement and stop if we go in the wrong direction
                var is_vertical = (_direction == 'up' || _direction == 'down');
                if(((is_vertical && !options.drag_vertical) || (!is_vertical && !options.drag_horizontal))
                    && (_distance > options.drag_min_distance)) {
                    return;
                }

                _gesture = 'drag';

                var position = { x: _pos.move[0].x - _offset.left,
                                 y: _pos.move[0].y - _offset.top };

                var event_obj = {
                    originalEvent   : event,
                    position        : position,
                    direction       : _direction,
                    distance        : _distance,
                    distanceX       : _distance_x,
                    distanceY       : _distance_y,
                    angle           : _angle
                };

                // on the first time trigger the start event
                if(_first) {
                    triggerEvent("dragstart", event_obj);

                    _first = false;
                }

                // normal slide event
                triggerEvent("drag", event_obj);

                event.preventDefault();
            }
        },

        // transform gesture
        // fired on touchmove
        transform : function(event)
        {
            if(options.transform) {
                var scale = event.scale || 1;
                var rotation = event.rotation || 0;

                if(_gesture != 'drag' &&
                    (_gesture == 'transform' || Math.abs(1-scale) > options.scale_treshold
                        || Math.abs(rotation) > options.rotation_treshold)) {
                    _gesture = 'transform';

                    _pos.center = {  x: ((_pos.move[0].x + _pos.move[1].x) / 2) - _offset.left,
                        y: ((_pos.move[0].y + _pos.move[1].y) / 2) - _offset.top };

                    var event_obj = {
                        originalEvent   : event,
                        position        : _pos.center,
                        scale           : scale,
                        rotation        : rotation
                    };

                    // on the first time trigger the start event
                    if(_first) {
                        triggerEvent("transformstart", event_obj);
                        _first = false;
                    }

                    triggerEvent("transform", event_obj);

                    event.preventDefault();

                    return true;
                }
            }

            return false;
        },

        // tap and double tap gesture
        // fired on touchend
        tap : function(event)
        {
            // compare the kind of gesture by time
            var now = new Date().getTime();
            var touch_time = now - _touch_start_time;
            var is_double_tap = function () {
                if (_prev_tap_pos && options.tap_double && _prev_gesture == 'tap' &&
                    (_touch_start_time - _prev_tap_end_time) < options.tap_max_interval) {
                    var x_distance = Math.abs(_prev_tap_pos[0].x - _pos.start[0].x);
                    var y_distance = Math.abs(_prev_tap_pos[0].y - _pos.start[0].y);
                    return (_prev_tap_pos && _pos.start &&
                        Math.max(x_distance, y_distance) < options.tap_double_distance);
                }
                return false;
            }

            // dont fire when hold is fired
            if(options.hold && !(options.hold && options.hold_timeout > touch_time)) {
                return;
            }

            // when previous event was tap and the tap was max_interval ms ago

            if(is_double_tap()) {
                _gesture = 'double_tap';
                _prev_tap_end_time = null;

                triggerEvent("doubletap", {
                    originalEvent   : event,
                    position        : _pos.start
                });
                event.preventDefault();
            }

            // single tap is single touch
            else {
                _gesture = 'tap';
                _prev_tap_end_time = now;
                _prev_tap_pos = _pos.start;

                if(options.tap) {
                    triggerEvent("tap", {
                        originalEvent   : event,
                        position        : _pos.start
                    });
                    event.preventDefault();
                }
            }

        }

    };

    function handleEvents(event)
    {
        switch(event.type)
        {
            case 'mousedown':
            case 'touchstart':
                _pos.start = getXYfromEvent(event);
                _touch_start_time = new Date().getTime();
                _fingers = countFingers(event);
                _first = true;

                // borrowed from jquery offset https://github.com/jquery/jquery/blob/master/src/offset.js
                // needs to be tested yet
                var box = element.getBoundingClientRect();
                var clientTop  = element.clientTop  || document.body.clientTop  || 0;
                var clientLeft = element.clientLeft || document.body.clientLeft || 0;
                var scrollTop  = window.pageYOffset || element.scrollTop  || document.body.scrollTop;
                var scrollLeft = window.pageXOffset || element.scrollLeft || document.body.scrollLeft;

                _offset = {
                    top: box.top + scrollTop  - clientTop,
                    left: box.left + scrollLeft - clientLeft
                };

                _mousedown = true;

                // hold gesture
                gestures.hold(event);

                if(options.prevent_default) {
                    event.preventDefault();
                }
                break;

            case 'mousemove':
            case 'touchmove':
                if(!_mousedown) {
                    return false;
                }
                _pos.move = getXYfromEvent(event);

                if(!gestures.transform(event)) {
                    gestures.drag(event);
                }
                break;

            case 'mouseup':
            case 'touchcancel':
            case 'touchend':
                _mousedown = false;

                // drag gesture
                // dragstart is triggered, so dragend is possible
                if(_gesture == 'drag') {
                    triggerEvent("dragend", {
                        originalEvent   : event,
                        direction       : _direction,
                        distance        : _distance,
                        angle           : _angle
                    });
                }

                // transform
                // transformstart is triggered, so transformed is possible
                else if(_gesture == 'transform') {
                    triggerEvent("transformend", {
                        originalEvent   : event,
                        position        : _pos.center,
                        scale           : event.scale,
                        rotation        : event.rotation
                    });
                }
                else {
                    gestures.tap(event);
                }

                _prev_gesture = _gesture;

                // reset vars
                reset();
                break;
        }
    }

    // bind events for touch devices
    // except for windows phone 7.5, it doenst support touch events..!
    if('ontouchstart' in window) {
        element.addEventListener("touchstart", handleEvents, false);
        element.addEventListener("touchmove", handleEvents, false);
        element.addEventListener("touchend", handleEvents, false);
        element.addEventListener("touchcancel", handleEvents, false);
    }
    // for non-touch
    else {
        // Listen for mouseup on the document so we know it happens
        // even if the mouse has left the element.
        document.addEventListener("mouseup", handleEvents, false);
        element.addEventListener("mousedown", handleEvents, false);
        element.addEventListener("mousemove", handleEvents, false);
    }

    /**
     * merge 2 objects into a new object
     * @param   object  obj1
     * @param   object  obj2
     * @return  object  merged object
     */
    function mergeObject(obj1, obj2) {
        var output = {};

        if(!obj2) {
            return obj1;
        }

        for (var prop in obj1) {
            if (prop in obj2) {
                output[prop] = obj2[prop];
            } else {
                output[prop] = obj1[prop];
            }
        }
        return output;
    }

    function supports( property ){
        return document.body.style[property] !== undefined;
    }

    function isFunction( obj ){
        return Object.prototype.toString.call( obj ) == "[object Function]";
    }
}<|endoftext|>


<commit_before>#!/usr/bin/env node

"use strict";

var path = require('path');

// this must be set before "server.js" is loaded
process.env.HARAKA = process.env.HARAKA || path.resolve('.');
try {
    require.paths.push(path.join(process.env.HARAKA, 'node_modules'));
}
catch(e) {
    process.env.NODE_PATH += ':' + path.join(process.env.HARAKA, 'node_modules');
}

var fs     = require('fs');
var logger = require('./logger');
var server = require('./server');

exports.version = JSON.parse(
        fs.readFileSync(path.join(__dirname, './package.json'), 'utf8')
    ).version;

process.on('uncaughtException', function (err) {
    if (err.stack) {
        err.stack.split("\n").forEach(function (line) {
            logger.logcrit(line);
        });
    }
    else {
        logger.logcrit('Caught exception: ' + err);
    }
    logger.dump_logs();
    process.exit(1);
});

logger.log("INFO", "Starting up Haraka version " + exports.version);

server.createServer();
<commit_msg>Fix paths not working on node 0.6 (hack alert)<commit_after>#!/usr/bin/env node

"use strict";

var path = require('path');

// this must be set before "server.js" is loaded
process.env.HARAKA = process.env.HARAKA || path.resolve('.');
try {
    require.paths.push(path.join(process.env.HARAKA, 'node_modules'));
}
catch(e) {
    process.env.NODE_PATH = process.env.NODE_PATH ?
            (process.env.NODE_PATH + ':' + path.join(process.env.HARAKA, 'node_modules'))
            :
            (path.join(process.env.HARAKA, 'node_modules'));
    require('module')._initPaths(); // Horrible hack
}

var fs     = require('fs');
var logger = require('./logger');
var server = require('./server');

exports.version = JSON.parse(
        fs.readFileSync(path.join(__dirname, './package.json'), 'utf8')
    ).version;

process.on('uncaughtException', function (err) {
    if (err.stack) {
        err.stack.split("\n").forEach(function (line) {
            logger.logcrit(line);
        });
    }
    else {
        logger.logcrit('Caught exception: ' + err);
    }
    logger.dump_logs();
    process.exit(1);
});

logger.log("INFO", "Starting up Haraka version " + exports.version);

server.createServer();
<|endoftext|>