<commit_before>'use strict';

angular.module('Lunch.profile', ['openfb', 'Lunch.factory.Geo', 'Lunch.factory.storedUserData', 'Lunch.factory.requests', 'Lunch.service.matchData'])
.config(function($stateProvider) {
  $stateProvider
  .state('app.profile', {
    url: '/profile',
    views: {
      'menuContent' :{
        templateUrl: 'templates/profile.html',
        controller: 'ProfileCtrl'
      }
    }
  });
})

.controller('ProfileCtrl', function($rootScope, $scope, $ionicSlideBoxDelegate, storedUserData, OpenFB, Geo, localStore, requests, matchData) {
  // Store data in scope
  $scope.userData = storedUserData;

  $scope.getLikes = function() {
    OpenFB.get('/me/likes')
      .success(function(fbLikeObj){
        // need to keep track of old ids
        var idTrack = {};
        angular.forEach(fbLikeObj.data, function(value){
          //if a new like / id
         if(!$scope.userData.likes[value.id] && value.name){
            //inform the database if a new id
            //add the new id and like data locally
            $scope.userData.likes[value.id] = value.name;

            postLikes(value);
          }
          //track id
          idTrack[value.id] = true;
        });
        //check ids fetched from fb to local ids.  if like stored locally no longer
        //avaiable from fb, inform the db and (then) delete locally
        for(var like in $scope.userData.likes) {
          if(!idTrack[like]){
            console.info('Deleteing like: ' + like + '; no longer in facebook');
            requests.deleteLike(like.id, {'userId': $scope.userData.id});
            delete $scope.userData.likes[like]; // since the like no longer exists
            //inform database using like.id and user.id and like.name ($scope.userData.likes[likeId])
          }
        }
      $rootScope.$emit('userDataChanged', $scope.userData);
    });
  };

  var postLikes = function(like) {
    requests.postLike({
      'userId' : $scope.userData.id,
      'id': like.id,
      'name': like.name
    });
  };

  var postTags = function() {
    angular.forEach($scope.userData.tags, function(value, key){
      if(value){ // only posts if tag is selected
        requests.postTag({
          'userId' : $scope.userData.id,
          'id': key,
          'name': key
        });
      }
    });
  };

  var getPicture = function() {
    OpenFB.get('/me/picture?redirect=0&height=133&type=normal&width=100')//'/me/picture')
    .success(function(data){
      if(data !== $scope.userData.photo_url){
        var image = "

";
        angular.element(document.querySelector('#userimage')).html(image);
        $scope.userData.photo_url = data.data.url;
        //tell the database the image associated with the user has changed
        postUser();
        $rootScope.$emit('userDataChanged', $scope.userData);
      }
    });
  };

  var getDetails = function() {
    OpenFB.get('/me')
    .success(function(data){
      $scope.userData.first_name = data.first_name;
      $scope.userData.last_name = data.last_name;
      $scope.userData.id = data.id;
      $scope.userData.updated_time = data.updated_time;
      //update the database with user information from fb
      postUser();
      //store updates to data locally
      $rootScope.$emit('userDataChanged', $scope.userData);
    })
    .error(function(data){
      //if we have data for the user, and fb api is not available
      // still post user data and tags
      if($scope.userData.id){
        postUser();
        postTags();
      }
    });
  };

  $scope.tagClick = function(e){
    var clickedText = e.toElement.innerText;
    var pressed = $scope.userData.tags[clickedText];
    if(pressed){
      //toggle
      $scope.userData.tags[clickedText] = false;
    } else {
      $scope.userData.tags[clickedText] = true;
    }
    $rootScope.$emit('userDataChanged', $scope.userData);
  };

  var postUser = function(){
    requests.postBasicDetails({
      'id' : $scope.userData.id,
      'firstname': $scope.userData.first_name,
      'lastname': $scope.userData.last_name,
      'profileImage': $scope.userData.photo_url
    });
    postTags();
  };

  var postLocation = function(pos) {
    $scope.userData.geolocation = pos.coords;
      OpenFB.checkLogin().then(function(id) {
        requests.postLocation({
          'userId': id,
          'lat': pos.coords.latitude,
          'lng': pos.coords.longitude
        }).then(function(res) {
          console.info('You are in ' + angular.fromJson(res).data.city);
          $scope.userData.location = angular.fromJson(res).data.city;
          $rootScope.$emit('userDataChanged', $scope.userData);
        });
    });
  };

  // var getMatches = function() {
  //   requests.getMatches({
  //     'userId': $scope.userData.id
  //   }).then(function(data){
  //     var matchedUsers = angular.fromJson(data);
  //     matchData.processMatchData(data);
  //   });
  // };

  $scope.$on('$stateChangeSuccess', function(e, state) { // this triggers every time we go to the profile page, may need something else
    getDetails();
    getPicture();
    $scope.getLikes();

    Geo.getCurrentPosition()
      .then(function(pos) { postLocation(pos); })
      .catch(function(err) { console.error(err); });

  });
});
<commit_msg>Removed dead code<commit_after>'use strict';

angular.module('Lunch.profile', ['openfb', 'Lunch.factory.Geo', 'Lunch.factory.storedUserData', 'Lunch.factory.requests', 'Lunch.service.matchData'])
.config(function($stateProvider) {
  $stateProvider
  .state('app.profile', {
    url: '/profile',
    views: {
      'menuContent' :{
        templateUrl: 'templates/profile.html',
        controller: 'ProfileCtrl'
      }
    }
  });
})

.controller('ProfileCtrl', function($rootScope, $scope, $ionicSlideBoxDelegate, storedUserData, OpenFB, Geo, localStore, requests, matchData) {
  // Store data in scope
  $scope.userData = storedUserData;

  $scope.getLikes = function() {
    OpenFB.get('/me/likes')
      .success(function(fbLikeObj){
        // need to keep track of old ids
        var idTrack = {};
        angular.forEach(fbLikeObj.data, function(value){
          //if a new like / id
         if(!$scope.userData.likes[value.id] && value.name){
            //inform the database if a new id
            //add the new id and like data locally
            $scope.userData.likes[value.id] = value.name;

            postLikes(value);
          }
          //track id
          idTrack[value.id] = true;
        });
        //check ids fetched from fb to local ids.  if like stored locally no longer
        //avaiable from fb, inform the db and (then) delete locally
        for(var like in $scope.userData.likes) {
          if(!idTrack[like]){
            console.info('Deleteing like: ' + like + '; no longer in facebook');
            requests.deleteLike(like.id, {'userId': $scope.userData.id});
            delete $scope.userData.likes[like]; // since the like no longer exists
            //inform database using like.id and user.id and like.name ($scope.userData.likes[likeId])
          }
        }
      $rootScope.$emit('userDataChanged', $scope.userData);
    });
  };

  var postLikes = function(like) {
    requests.postLike({
      'userId' : $scope.userData.id,
      'id': like.id,
      'name': like.name
    });
  };

  var postTags = function() {
    angular.forEach($scope.userData.tags, function(value, key){
      if(value){ // only posts if tag is selected
        requests.postTag({
          'userId' : $scope.userData.id,
          'id': key,
          'name': key
        });
      }
    });
  };

  var getPicture = function() {
    OpenFB.get('/me/picture?redirect=0&height=133&type=normal&width=100')//'/me/picture')
    .success(function(data){
      if(data !== $scope.userData.photo_url){
        var image = "

";
        angular.element(document.querySelector('#userimage')).html(image);
        $scope.userData.photo_url = data.data.url;
        //tell the database the image associated with the user has changed
        postUser();
        $rootScope.$emit('userDataChanged', $scope.userData);
      }
    });
  };

  var getDetails = function() {
    OpenFB.get('/me')
    .success(function(data){
      $scope.userData.first_name = data.first_name;
      $scope.userData.last_name = data.last_name;
      $scope.userData.id = data.id;
      $scope.userData.updated_time = data.updated_time;
      //update the database with user information from fb
      postUser();
      //store updates to data locally
      $rootScope.$emit('userDataChanged', $scope.userData);
    })
    .error(function(data){
      //if we have data for the user, and fb api is not available
      // still post user data and tags
      if($scope.userData.id){
        postUser();
        postTags();
      }
    });
  };

  $scope.tagClick = function(e){
    var clickedText = e.toElement.innerText;
    var pressed = $scope.userData.tags[clickedText];
    if(pressed){
      //toggle
      $scope.userData.tags[clickedText] = false;
    } else {
      $scope.userData.tags[clickedText] = true;
    }
    $rootScope.$emit('userDataChanged', $scope.userData);
  };

  var postUser = function(){
    requests.postBasicDetails({
      'id' : $scope.userData.id,
      'firstname': $scope.userData.first_name,
      'lastname': $scope.userData.last_name,
      'profileImage': $scope.userData.photo_url
    });
    postTags();
  };

  var postLocation = function(pos) {
    $scope.userData.geolocation = pos.coords;
      OpenFB.checkLogin().then(function(id) {
        requests.postLocation({
          'userId': id,
          'lat': pos.coords.latitude,
          'lng': pos.coords.longitude
        }).then(function(res) {
          console.info('You are in ' + angular.fromJson(res).data.city);
          $scope.userData.location = angular.fromJson(res).data.city;
          $rootScope.$emit('userDataChanged', $scope.userData);
        });
    });
  };

  $scope.$on('$stateChangeSuccess', function(e, state) { // this triggers every time we go to the profile page, may need something else
    getDetails();
    getPicture();
    $scope.getLikes();

    Geo.getCurrentPosition()
      .then(function(pos) { postLocation(pos); })
      .catch(function(err) { console.error(err); });

  });
});
<|endoftext|>


<commit_before>
// Get a stream and attach it to the activity.
function get_stream(activity) {
    var activity_id = activity.activity_id;
    return XHR('/api/streams?activity_id=' + activity_id)
        .then(function(streams) {
            var stream = streams.result.streams[activity_id];
            console.log("Got stream from ", activity_id, ": ", streams);
            activity.stream = stream;
            return stream;
        });
}

// generate metadata about all the streams
function index_streams(activities) {
    return new Promise(function(resolve, reject) {
        var results = [];
        for (var i = 0; i < activities.length; ++i) {
            if (!activities[i].stream)
                results.push(get_stream(activities[i]).then(index_stream));
            else
                results.push(index_stream(activities[i].stream));
        }
        Promise.all(results).then(function(stream_indexes) {
            console.log("All streams ready! ", stream_indexes);
            resolve(stream_indexes);
        }).catch(function(ex) {
            console.error("oops, rejected: ", ex);
        });
    });
}

// generate metadata about a single stream
function index_stream(stream) {
    if (!stream) {
        return Promise.reject("Missing stream");
    }
    return new Promise(function(resolve, reject) {
        var metadata = {domain: {}};

        var latlng_stream = stream.latlng;
        var alt_stream = stream.altitude;

        if (latlng_stream) {
            metadata.domain.lat = d3.extent(latlng_stream.data, accessor(0));
            metadata.domain.lng = d3.extent(latlng_stream.data, accessor(1));
        }
        if (alt_stream) {
            metadata.domain.alt = d3.extent(alt_stream.data);
        }
        resolve(metadata);
    });
}

function accessor(attr) {
    return function(d, i) {
        return d[attr];
    };
}

// an object that represents the bounds of a map
// note that there is no projection here
function Bounds(stream_index) {
    var extents = {
        min_lat: d3.extent(stream_index, function(d) { return d.domain.lat[0]; })[0],
        max_lat: d3.extent(stream_index, function(d) { return d.domain.lat[1]; })[1],
        min_lng: d3.extent(stream_index, function(d) { return d.domain.lng[0]; })[0],
        max_lng: d3.extent(stream_index, function(d) { return d.domain.lng[1]; })[1],
        min_alt: d3.extent(stream_index, function(d) { return d.domain.alt[0]; })[0],
        max_alt: d3.extent(stream_index, function(d) { return d.domain.alt[1]; })[1],
    };
    console.log("Now have extents.domain = ", extents);

    var padding_x = (extents.max_lng - extents.min_lng) * 0.1;
    var padding_y = (extents.max_lat - extents.min_lat) * 0.1;
    this.x = extents.min_lng - padding_x;
    this.y = extents.min_lat - padding_y;
    this.width = extents.max_lng - extents.min_lng + 2*padding_x;
    this.height = extents.max_lat - extents.min_lat + 2*padding_y;

    // todo: use a projection to make sure the x/y are scaled
    // appropriately. For demonstration purposes, a 1:1 projection
    // looks fine at my latitude.

    this.scale_x = d3.scale.linear().domain([this.x, this.x+this.width]);
    this.scale_y = d3.scale.linear().domain([this.y+this.height, this.y]);
    this.scale_z = d3.scale.linear().domain([extents.min_alt,
                                             extents.max_alt]);
}

Bounds.prototype.setSize = function(width, height) {
    this.scale_x.range([0, width]);
    this.scale_y.range([0, height]);
    this.scale_z.range([100, 200]); // lower number = darker = lower altitude
};

function drawmap(activities) {
    index_streams(activities).then(function(index) {
        I = index;
        var bounds = new Bounds(index);
        console.log("have bounds", bounds);
        B= bounds;

        drawWithBounds(bounds, activities);
    });
}

function Dataset() {
    this._pending_activities =
        XHR('/api/activities').then(function(response) {
            console.log("Got activities: ", response);
            return response.result.activities;
        });
}

Dataset.prototype.activities = function() {
    return this._pending_activities.then(function(activities) {
        return run_filter(activities);
    });
};

function drawWithBounds(bounds, activities) {
    var canvas = document.querySelector('#map');

    bounds.setSize(canvas.width, canvas.height);
    var color = d3.scale.ordinal().range(colorbrewer.Set3[12]);
    var ctx = canvas.getContext('2d');
    ctx.clearRect(0,0,canvas.width, canvas.height);

    // draw altitude gradients first - this is terribly inefficient
    var gradientRadius = canvas.width * 0.02;
    console.log("radius: ", gradientRadius);
    var gradientCache = {};
    for (var i = 0; i < activities.length; ++i) {
        if (!activities[i].stream)
            continue;
        for (var j = 0; j < activities[i].stream.altitude.data.length; ++j) {
            var point = activities[i].stream.latlng.data[j];
            var altitude = activities[i].stream.altitude.data[j];
            var x = Math.round(bounds.scale_x(point[1]));
            var y = Math.round(bounds.scale_y(point[0]));
            var z = Math.round(bounds.scale_z(altitude));
            var zcolor = z + ',' + z + ',' + z;

            var gradient = gradientCache[x + "," + y + "," + z];
            if (gradient) continue;
            if (!gradient) {
                //console.log("creating gradient for ", x, y);
                gradient = ctx.createRadialGradient(x,y,1,x,y,gradientRadius);
                gradient.addColorStop(0, 'rgba(' + zcolor + ',.90)');
                gradient.addColorStop(1, 'rgba(' + zcolor + ',0)');
                gradientCache[x + "," + y + ',' + z] = gradient;
            }
            ctx.fillStyle = gradient;
            ctx.fillRect(x-gradientRadius,y-gradientRadius,x+gradientRadius,y+gradientRadius);
        }
    }

    for (var i = 0; i < activities.length; ++i) {
        if (!activities[i].stream)
            continue;
        ctx.strokeStyle = color(i);
        ctx.beginPath();
        var stream = activities[i].stream.latlng;
        var last_point = stream.data[0];
        ctx.moveTo(bounds.scale_x(last_point[1]),
                   bounds.scale_y(last_point[0]));
        console.log("Drawing ", stream.data.length, " points");
        for (var j = 1; j < stream.data.length; ++j) {
            var point = stream.data[j];
            ctx.lineTo(bounds.scale_x(point[1]),
                       bounds.scale_y(point[0]));
        }
        ctx.stroke();
    }
}

// creates a filter from the UI controls
function make_filter() {
    var test_props = [];
    var test_value = [];

    // generate filter
    var controls = document.querySelectorAll('.map-control');
    for (var i = 0; i < controls.length; ++i) {
        var properties = JSON.parse(controls[i].getAttribute('properties'));
        var criteria = controls[i].querySelectorAll('span.tab');
        for (var j = 0; j < criteria.length; j++) {
            var criterium = criteria[j];
            if (criterium.classList.contains('polymer-selected')) {
                var value = criterium.getAttribute('value');
                console.log(controls[i], criterium, "Got criterium value: ", value);
                if (value != '*') {
                    value = JSON.parse(value);

                    if (value.length != properties.length) {
                        console.warn("prop/value mismatch: ", properties, " vs. ", value);
                        break;
                    }
                    for (var k = 0; k < properties.length; ++k) {
                        test_props.push(properties[k]);
                        test_value.push(value[k]);
                    }
                    console.log("I need to check for ", properties, ' = ', value);
                }
            }
        }
    }

    return [test_props, test_value];
}

function run_filter(activities) {
    var filter = make_filter();
    var props = filter[0];
    var values = filter[1];
    var result = [];
    for (var i = 0; i < activities.length; ++i) {
        var matches = true;
        var activity = activities[i];
        for (var j = 0; j < props.length; ++j) {
            if (activity[props[j]] != values[j]) {
                matches = false;
                break;
            }
        }
        if (matches)
            result.push(activity);
    }
    return result;
}

// activities is just the regular activities list
// properies is an array of arrays of properties:
// [['type'], ['gear_id']]
function create_filter_ui(activities, properties_list) {

    // count of propvals[proname][value] == count, like
    // propval['type']['Ride'] == 12
    var propvals = {};
    for (var i = 0; i < properties_list.length; ++i) {
        var properties = properties_list[i];

        for (var k = 0; k < activities.length; ++k) {
            var activity = activities[k];
            var propkey = [];
            var propval = [];
            for (var j = 0; j < properties.length; j++) {
                var property = properties[j];

                var value = activity[property];
                propkey.push(property);
                propval.push(value);
            }

            propkey = propkey.join(',');
            propval = propval.join(',');
            if (!(propkey in propvals))
                propvals[propkey] = {};
            if (!(propval in propvals[propkey]))
                propvals[propkey][propval] = 0;
            ++propvals[propkey][propval];
        }
    }
    console.log("Generated ", propvals);

    var control_div = document.querySelector('.map-ui');
    var labels = [];
    for (i = 0; i < properties_list.length; ++i) {
        properties = properties_list[i];
        propkey = properties.join(',');
        var container = document.createElement('div');
        container.className = "map-control";
        control_div.appendChild(container);

        // technically this should be properties.join(','), but we're
        // only using the first value because the label is probably
        // prettier.
        var label = properties[0] + ": ";
        container.appendChild(document.createTextNode(label));
        var tabStrip = document.createElement('polymer-ui-tabs');
        tabStrip.properties = properties;
        tabStrip.propkey = propkey;

        function add(propval) {
            var span = document.createElement('span');
            var text = propval;
            if (propval == '*')
                text += " (" + sum(Object.keys(propvals[propkey])
                                   .map(function(k) { return propvals[propkey][k]; }))
                + ")";
            else
                text += " (" + propvals[propkey][propval] + ")";
            span.innerText = text;
            span.propval = propval;
            tabStrip.appendChild(span);
        }

        add('*');
        // just use the first property for now?
        for (var propval in propvals[propkey]) {
            add(propval);
        }

        container.appendChild(tabStrip);
    }

}

function sum(values) {
    return values.reduceRight(function(previous, current) { return previous+current; });
}

// Create an XHR context that fires off progress notifications
// usage:
// function update_progress(waiting, total) {
//   console.log("Updated ", waiting, " / ", total);
// }
// var XHR = xhrContext(update_progress);
// XHR('http://....).then(function(result)) {... });
function xhrContext(progress) {
    var total_started = 0;
    var total_complete = 0;

    function update_progress() {
        progress(total_started, total_complete);
        if (total_started == total_complete)
            total_started = total_complete = 0;
    }

    return function(url) {
        total_started += 1;
        progress(total_started, total_complete);
        return new Promise(function(resolve, reject) {
            try {
                var xhr = new XMLHttpRequest();
                LASTXHR = xhr;
                xhr.open('GET', url, true);
                xhr.onload = function() {
                    // console.error("Got response from ", url, ": ", xhr.responseText);
                    try {
                        var response = JSON.parse(xhr.responseText);
                        resolve(response);
                    } catch(ex) {
                        console.trace(ex, " in ", xhr.responseText);
                        reject(ex);
                    }
                    total_complete += 1;
                    update_progress();
                };
                xhr.onerror = function(e) {
                    total_complete += 1;
                    update_progress();
                    reject(e);
                };
                console.log("Requesting ", url);
                xhr.send();
            } catch (ex) {
                total_complete +=1;
                update_progress();
                reject(ex);
            };
        });
    };
}

function refresh() {
    console.log("refreshing..");
    return D.activities().then(drawmap).catch(function(ex) { console.error(ex); });
}

FILTERS = [
    { name: 'Location',
      shortkey: 'location',
      key: ['location_city', 'location_state', 'location_country'],
    },
    { name: 'Type',
      shortkey: 'type',
      key: ['type'],
    }
];

// given a keylist like ['foo', 'bar'] extracts the corresponding objects from obj, i.e. returns [obj.foo, obj.bar]
function extract_key_value(key_list, obj) {
    var key_value = [];
    key_list.forEach(function(subkey) {
        key_value.push(obj[subkey]);
    });
    return key_value;
}

function update_progress_indicator(waiting, complete) {
    console.log("update_progress(", waiting, ", ", complete);
    if (waiting == complete)
        $('#progress').hide();
    else
        $('#progress').show().text(complete*100/waiting + "%");
}

function init() {
    XHR = xhrContext(update_progress_indicator);

    D = new Dataset();
    D.activities().then(function(activities) {
        FILTERS.forEach(function(filter) {
            var key_values = {};
            console.log("Filter: ", filter);
            var key_string = JSON.stringify(filter.key);
            // extract all possible key values
            activities.forEach(function(activity) {
                var key_value = extract_key_value(filter.key, activity);
                key_values[JSON.stringify(key_value)] = true;
            });

            console.log("Possible values: ", Object.keys(key_values));
            // now build up the filter UI
            // (Note this will get way more complex in a bit)
            Object.keys(key_values).forEach(function(key_value) {
                var sp = $('<span>')
                        .addClass('tab')
                        .attr('value', key_value)
                        .text(JSON.parse(key_value)[0]);
                console.log("Building up ui in ", $('.filter-' + filter.shortkey));
                $('.filter-' + filter.shortkey).append(sp);
            });
        });
        // extract all visible keys
    });

    var controls = document.querySelectorAll('.map-control polymer-ui-tabs');
    for (var i = 0; i < controls.length; ++i) {
        if (!controls[i].hasEventListener) {
            controls[i].addEventListener('polymer-activate', refresh);
            controls[i].hasEventListener = true;
        }
    }

    refresh();
}

// hack hack
document.addEventListener('WebComponentsReady', init);
console.log("profile.js loaded, should be calling other stuff");<commit_msg>clean up and start to generalize filter<commit_after>
// Get a stream and attach it to the activity.
function get_stream(activity) {
    var activity_id = activity.activity_id;
    return XHR('/api/streams?activity_id=' + activity_id)
        .then(function(streams) {
            var stream = streams.result.streams[activity_id];
            activity.stream = stream;
            return stream;
        });
}

// generate metadata about all the streams
function index_streams(activities) {
    return new Promise(function(resolve, reject) {
        var results = [];
        for (var i = 0; i < activities.length; ++i) {
            if (!activities[i].stream)
                results.push(get_stream(activities[i]).then(index_stream));
            else
                results.push(index_stream(activities[i].stream));
        }
        Promise.all(results).then(function(stream_indexes) {
            console.log("All streams ready! ", stream_indexes);
            resolve(stream_indexes);
        }).catch(function(ex) {
            console.error("oops, rejected: ", ex);
        });
    });
}

// generate metadata about a single stream
function index_stream(stream) {
    if (!stream) {
        return Promise.reject("Missing stream");
    }
    return new Promise(function(resolve, reject) {
        var metadata = {domain: {}};

        var latlng_stream = stream.latlng;
        var alt_stream = stream.altitude;

        if (latlng_stream) {
            metadata.domain.lat = d3.extent(latlng_stream.data, accessor(0));
            metadata.domain.lng = d3.extent(latlng_stream.data, accessor(1));
        }
        if (alt_stream) {
            metadata.domain.alt = d3.extent(alt_stream.data);
        }
        resolve(metadata);
    });
}

function accessor(attr) {
    return function(d, i) {
        return d[attr];
    };
}

// an object that represents the bounds of a map
// note that there is no projection here
function Bounds(stream_index) {
    var extents = {
        min_lat: d3.extent(stream_index, function(d) { return d.domain.lat[0]; })[0],
        max_lat: d3.extent(stream_index, function(d) { return d.domain.lat[1]; })[1],
        min_lng: d3.extent(stream_index, function(d) { return d.domain.lng[0]; })[0],
        max_lng: d3.extent(stream_index, function(d) { return d.domain.lng[1]; })[1],
        min_alt: d3.extent(stream_index, function(d) { return d.domain.alt[0]; })[0],
        max_alt: d3.extent(stream_index, function(d) { return d.domain.alt[1]; })[1],
    };
    console.log("Now have extents.domain = ", extents);

    var padding_x = (extents.max_lng - extents.min_lng) * 0.1;
    var padding_y = (extents.max_lat - extents.min_lat) * 0.1;
    this.x = extents.min_lng - padding_x;
    this.y = extents.min_lat - padding_y;
    this.width = extents.max_lng - extents.min_lng + 2*padding_x;
    this.height = extents.max_lat - extents.min_lat + 2*padding_y;

    // todo: use a projection to make sure the x/y are scaled
    // appropriately. For demonstration purposes, a 1:1 projection
    // looks fine at my latitude.

    this.scale_x = d3.scale.linear().domain([this.x, this.x+this.width]);
    this.scale_y = d3.scale.linear().domain([this.y+this.height, this.y]);
    this.scale_z = d3.scale.linear().domain([extents.min_alt,
                                             extents.max_alt]);
}

Bounds.prototype.setSize = function(width, height) {
    this.scale_x.range([0, width]);
    this.scale_y.range([0, height]);
    this.scale_z.range([100, 200]); // lower number = darker = lower altitude
};

function drawmap(activities) {
    index_streams(activities).then(function(index) {
        I = index;
        var bounds = new Bounds(index);
        console.log("have bounds", bounds);
        B= bounds;

        drawWithBounds(bounds, activities);
    });
}

function Dataset() {
    this._pending_activities =
        XHR('/api/activities').then(function(response) {
            console.log("Got activities: ", response);
            return response.result.activities;
        });
}

Dataset.prototype.activities = function() {
    return this._pending_activities.then(function(activities) {
        return run_filter(activities);
    });
};

function drawWithBounds(bounds, activities) {
    var canvas = document.querySelector('#map');

    bounds.setSize(canvas.width, canvas.height);
    var color = d3.scale.ordinal().range(colorbrewer.Set3[12]);
    var ctx = canvas.getContext('2d');
    ctx.clearRect(0,0,canvas.width, canvas.height);

    // draw altitude gradients first - this is terribly inefficient
    var gradientRadius = canvas.width * 0.02;
    console.log("radius: ", gradientRadius);
    var gradientCache = {};
    for (var i = 0; i < activities.length; ++i) {
        if (!activities[i].stream)
            continue;
        for (var j = 0; j < activities[i].stream.altitude.data.length; ++j) {
            var point = activities[i].stream.latlng.data[j];
            var altitude = activities[i].stream.altitude.data[j];
            var x = Math.round(bounds.scale_x(point[1]));
            var y = Math.round(bounds.scale_y(point[0]));
            var z = Math.round(bounds.scale_z(altitude));
            var zcolor = z + ',' + z + ',' + z;

            var gradient = gradientCache[x + "," + y + "," + z];
            if (gradient) continue;
            if (!gradient) {
                //console.log("creating gradient for ", x, y);
                gradient = ctx.createRadialGradient(x,y,1,x,y,gradientRadius);
                gradient.addColorStop(0, 'rgba(' + zcolor + ',.90)');
                gradient.addColorStop(1, 'rgba(' + zcolor + ',0)');
                gradientCache[x + "," + y + ',' + z] = gradient;
            }
            ctx.fillStyle = gradient;
            ctx.fillRect(x-gradientRadius,y-gradientRadius,x+gradientRadius,y+gradientRadius);
        }
    }

    for (var i = 0; i < activities.length; ++i) {
        if (!activities[i].stream)
            continue;
        ctx.strokeStyle = color(i);
        ctx.beginPath();
        var stream = activities[i].stream.latlng;
        var last_point = stream.data[0];
        ctx.moveTo(bounds.scale_x(last_point[1]),
                   bounds.scale_y(last_point[0]));
        console.log("Drawing ", stream.data.length, " points");
        for (var j = 1; j < stream.data.length; ++j) {
            var point = stream.data[j];
            ctx.lineTo(bounds.scale_x(point[1]),
                       bounds.scale_y(point[0]));
        }
        ctx.stroke();
    }
}

// creates a filter from the UI controls, where the filter is in the form
// [[key, value], [key, value]]
function make_filter() {
    var test_props = [];
    var test_value = [];
    var test_compare = [];

    // generate filter
    var controls = document.querySelectorAll('.map-control');
    for (var i = 0; i < controls.length; ++i) {
        var properties = JSON.parse(controls[i].getAttribute('properties'));
        var criteria = controls[i].querySelectorAll('span.tab');
        for (var j = 0; j < criteria.length; j++) {
            var criterium = criteria[j];
            if (criterium.classList.contains('polymer-selected')) {
                var value = criterium.getAttribute('value');
                var filter_id = criterium.getAttribute('filter');
                var filter = FILTERS_BY_ID[filter_id];
                if (value != '*') {
                    value = JSON.parse(value);

                    // XXX use filter.compare here
                    if (value.length != properties.length) {
                        console.warn("prop/value mismatch: ", properties, " vs. ", value);
                        break;
                    }
                    for (var k = 0; k < properties.length; ++k) {
                        test_props.push(properties[k]);
                        test_value.push(value[k]);
                    }
                    console.log("I need to check for ", properties, ' = ', value);
                }
            }
        }
    }

    return [test_props, test_value];
}

function run_filter(activities) {
    var filter = make_filter();
    var props = filter[0];
    var values = filter[1];
    var result = [];
    for (var i = 0; i < activities.length; ++i) {
        var matches = true;
        var activity = activities[i];
        for (var j = 0; j < props.length; ++j) {
            if (activity[props[j]] != values[j]) {
                matches = false;
                break;
            }
        }
        if (matches)
            result.push(activity);
    }
    return result;
}

// activities is just the regular activities list
// properies is an array of arrays of properties:
// [['type'], ['gear_id']]
function create_filter_ui(activities, properties_list) {

    // count of propvals[proname][value] == count, like
    // propval['type']['Ride'] == 12
    var propvals = {};
    for (var i = 0; i < properties_list.length; ++i) {
        var properties = properties_list[i];

        for (var k = 0; k < activities.length; ++k) {
            var activity = activities[k];
            var propkey = [];
            var propval = [];
            for (var j = 0; j < properties.length; j++) {
                var property = properties[j];

                var value = activity[property];
                propkey.push(property);
                propval.push(value);
            }

            propkey = propkey.join(',');
            propval = propval.join(',');
            if (!(propkey in propvals))
                propvals[propkey] = {};
            if (!(propval in propvals[propkey]))
                propvals[propkey][propval] = 0;
            ++propvals[propkey][propval];
        }
    }
    console.log("Generated ", propvals);

    var control_div = document.querySelector('.map-ui');
    var labels = [];
    for (i = 0; i < properties_list.length; ++i) {
        properties = properties_list[i];
        propkey = properties.join(',');
        var container = document.createElement('div');
        container.className = "map-control";
        control_div.appendChild(container);

        // technically this should be properties.join(','), but we're
        // only using the first value because the label is probably
        // prettier.
        var label = properties[0] + ": ";
        container.appendChild(document.createTextNode(label));
        var tabStrip = document.createElement('polymer-ui-tabs');
        tabStrip.properties = properties;
        tabStrip.propkey = propkey;

        function add(propval) {
            var span = document.createElement('span');
            var text = propval;
            if (propval == '*')
                text += " (" + sum(Object.keys(propvals[propkey])
                                   .map(function(k) { return propvals[propkey][k]; }))
                + ")";
            else
                text += " (" + propvals[propkey][propval] + ")";
            span.innerText = text;
            span.propval = propval;
            tabStrip.appendChild(span);
        }

        add('*');
        // just use the first property for now?
        for (var propval in propvals[propkey]) {
            add(propval);
        }

        container.appendChild(tabStrip);
    }

}

function sum(values) {
    return values.reduceRight(function(previous, current) { return previous+current; });
}

// Create an XHR context that fires off progress notifications
// usage:
// function update_progress(waiting, total) {
//   console.log("Updated ", waiting, " / ", total);
// }
// var XHR = xhrContext(update_progress);
// XHR('http://....).then(function(result)) {... });
function xhrContext(progress) {
    var total_started = 0;
    var total_complete = 0;

    function update_progress() {
        progress(total_started, total_complete);
        if (total_started == total_complete)
            total_started = total_complete = 0;
    }

    return function(url) {
        total_started += 1;
        progress(total_started, total_complete);
        return new Promise(function(resolve, reject) {
            try {
                var xhr = new XMLHttpRequest();
                LASTXHR = xhr;
                xhr.open('GET', url, true);
                xhr.onload = function() {
                    // console.error("Got response from ", url, ": ", xhr.responseText);
                    try {
                        var response = JSON.parse(xhr.responseText);
                        resolve(response);
                    } catch(ex) {
                        console.trace(ex, " in ", xhr.responseText);
                        reject(ex);
                    }
                    total_complete += 1;
                    update_progress();
                };
                xhr.onerror = function(e) {
                    total_complete += 1;
                    update_progress();
                    reject(e);
                };
                console.log("Requesting ", url);
                xhr.send();
            } catch (ex) {
                total_complete +=1;
                update_progress();
                reject(ex);
            };
        });
    };
}

function refresh() {
    console.log("refreshing..");
    return D.activities().then(drawmap).catch(function(ex) { console.error(ex); });
}

// given a keylist like ['foo', 'bar'] extracts the corresponding
// objects from obj, i.e. returns [obj.foo, obj.bar]
function extract_key_value(key_list, obj) {
    var key_value = [];
    key_list.forEach(function(subkey) {
        key_value.push(obj[subkey]);
    });
    return key_value;
}

function equals(a, b) {
    return a == b;
}

FILTERS = [
    { name: 'Location',
      id: 'location',
      key: ['location_city', 'location_state', 'location_country'],
      extract: extract_key_value,
      compare: equals,
    },
    { name: 'Type',
      id: 'type',
      key: ['type'],
      extract: extract_key_value,
      compare: equals,
    }
];

FILTERS_BY_ID = {};
FILTERS.forEach(function(filter) {
    FILTERS_BY_ID[filter.id] = filter;
});

function update_progress_indicator(waiting, complete) {
    console.log("update_progress(", waiting, ", ", complete);
    if (waiting == complete)
        $('#progress').hide();
    else
        $('#progress').show().text(complete*100/waiting + "%");
}

function init() {
    XHR = xhrContext(update_progress_indicator);

    D = new Dataset();
    D.activities().then(function(activities) {
        FILTERS.forEach(function(filter) {
            var key_values = {};
            var key_string = JSON.stringify(filter.key);
            // extract all possible key values
            activities.forEach(function(activity) {
                var key_value = filter.extract(filter.key, activity);
                key_values[JSON.stringify(key_value)] = true;
            });

            // now build up the filter UI
            // (Note this will get way more complex in a bit)
            Object.keys(key_values).forEach(function(key_value) {
                var sp = $('<span>')
                        .addClass('tab')
                        .attr('value', key_value)
                        .attr('filter', filter.id)
                        .text(JSON.parse(key_value)[0]);
                $('.filter-' + filter.id).append(sp);
            });
        });
        // extract all visible keys
    });

    var controls = document.querySelectorAll('.map-control polymer-ui-tabs');
    for (var i = 0; i < controls.length; ++i) {
        if (!controls[i].hasEventListener) {
            controls[i].addEventListener('polymer-activate', refresh);
            controls[i].hasEventListener = true;
        }
    }

    refresh();
}

// hack hack
document.addEventListener('WebComponentsReady', init);
console.log("profile.js loaded, should be calling other stuff");<|endoftext|>


<commit_before>'use strict';
var uiFuncs = function() {}
uiFuncs.getTxData = function($scope) {
    return {
        to: $scope.tx.to,
        value: $scope.tx.value,
        unit: $scope.tx.unit,
        gasLimit: $scope.tx.gasLimit,
        data: $scope.tx.data,
        from: $scope.wallet.getAddressString(),
        privKey: $scope.wallet.privKey ? $scope.wallet.getPrivateKeyString() : '',
        path: $scope.wallet.getPath(),
        hwType: $scope.wallet.getHWType(),
        hwTransport: $scope.wallet.getHWTransport()
    };
}
uiFuncs.isTxDataValid = function(txData) {
    if (txData.to != "0xCONTRACT" && !ethFuncs.validateEtherAddress(txData.to)) throw globalFuncs.errorMsgs[5];
    else if (!globalFuncs.isNumeric(txData.value) || parseFloat(txData.value) < 0) throw globalFuncs.errorMsgs[0];
    else if (!globalFuncs.isNumeric(txData.gasLimit) || parseFloat(txData.gasLimit) <= 0) throw globalFuncs.errorMsgs[8];
    else if (!ethFuncs.validateHexString(txData.data)) throw globalFuncs.errorMsgs[9];
    if (txData.to == "0xCONTRACT") txData.to = '';
}
uiFuncs.signTxTrezor = function(rawTx, txData, callback) {
    var localCallback = function(result) {
        if (!result.success) {
            if (callback !== undefined) {
                callback({
                    isError: true,
                    error: result.error
                });
            }
            return;
        }

        rawTx.v = "0x" + ethFuncs.decimalToHex(result.v);
        rawTx.r = "0x" + result.r;
        rawTx.s = "0x" + result.s;
        var eTx = new ethUtil.Tx(rawTx);
        rawTx.rawTx = JSON.stringify(rawTx);
        rawTx.signedTx = '0x' + eTx.serialize().toString('hex');
        rawTx.isError = false;
        if (callback !== undefined) callback(rawTx);
    }

    TrezorConnect.signEthereumTx(
        txData.path,
        ethFuncs.getNakedAddress(rawTx.nonce),
        ethFuncs.getNakedAddress(rawTx.gasPrice),
        ethFuncs.getNakedAddress(rawTx.gasLimit),
        ethFuncs.getNakedAddress(rawTx.to),
        ethFuncs.getNakedAddress(rawTx.value),
        ethFuncs.getNakedAddress(rawTx.data),
        rawTx.chainId,
        localCallback
    );
}
uiFuncs.signTxLedger = function(app, eTx, rawTx, txData, old, callback) {
    eTx.raw[6] = Buffer.from([rawTx.chainId]);
    eTx.raw[7] = eTx.raw[8] = 0;
    var toHash = old ? eTx.raw.slice(0, 6) : eTx.raw;
    var txToSign = ethUtil.rlp.encode(toHash);
    var localCallback = function(result, error) {
        if (typeof error != "undefined") {
            error = error.errorCode ? u2f.getErrorByCode(error.errorCode) : error;
            if (callback !== undefined) callback({
                isError: true,
                error: error
            });
            return;
        }
        rawTx.v = "0x" + result['v'];
        rawTx.r = "0x" + result['r'];
        rawTx.s = "0x" + result['s'];
        eTx = new ethUtil.Tx(rawTx);
        rawTx.rawTx = JSON.stringify(rawTx);
        rawTx.signedTx = '0x' + eTx.serialize().toString('hex');
        rawTx.isError = false;
        if (callback !== undefined) callback(rawTx);
    }
    app.signTransaction(txData.path, txToSign.toString('hex'), localCallback);
}
uiFuncs.signTxDigitalBitbox = function(eTx, rawTx, txData, callback) {
    var localCallback = function(result, error) {
        if (typeof error != "undefined") {
            error = error.errorCode ? u2f.getErrorByCode(error.errorCode) : error;
            if (callback !== undefined) callback({
                isError: true,
                error: error
            });
            return;
        }
        uiFuncs.notifier.info("The transaction was signed but not sent. Click the blue 'Send Transaction' button to continue.");
        rawTx.v = ethFuncs.sanitizeHex(result['v']);
        rawTx.r = ethFuncs.sanitizeHex(result['r']);
        rawTx.s = ethFuncs.sanitizeHex(result['s']);
        var eTx_ = new ethUtil.Tx(rawTx);
        rawTx.rawTx = JSON.stringify(rawTx);
        rawTx.signedTx = ethFuncs.sanitizeHex(eTx_.serialize().toString('hex'));
        rawTx.isError = false;
        if (callback !== undefined) callback(rawTx);
    }
    uiFuncs.notifier.info("Touch the LED for 3 seconds to sign the transaction. Or tap the LED to cancel.");
    var app = new DigitalBitboxEth(txData.hwTransport, '');
    app.signTransaction(txData.path, eTx, localCallback);
}
uiFuncs.signTxSecalot = function(eTx, rawTx, txData, callback) {

    var localCallback = function(result, error) {
        if (typeof error != "undefined") {
            error = error.errorCode ? u2f.getErrorByCode(error.errorCode) : error;
            if (callback !== undefined) callback({
                isError: true,
                error: error
            });
            return;
        }
        uiFuncs.notifier.info("The transaction was signed but not sent. Click the blue 'Send Transaction' button to continue.");
        rawTx.v = ethFuncs.sanitizeHex(result['v']);
        rawTx.r = ethFuncs.sanitizeHex(result['r']);
        rawTx.s = ethFuncs.sanitizeHex(result['s']);

        var eTx_ = new ethUtil.Tx(rawTx);
        rawTx.rawTx = JSON.stringify(rawTx);
        rawTx.signedTx = ethFuncs.sanitizeHex(eTx_.serialize().toString('hex'));
        rawTx.isError = false;
        if (callback !== undefined) callback(rawTx);
    }
    uiFuncs.notifier.info("Tap a touch button on your device to confirm signing.");
    var app = new SecalotEth(txData.hwTransport);
    app.signTransaction(txData.path, eTx, localCallback);
}
uiFuncs.trezorUnlockCallback = function(txData, callback) {
    TrezorConnect.open(function(error) {
        if (error) {
            if (callback !== undefined) callback({
                isError: true,
                error: error
            });
        } else {
            txData.trezorUnlocked = true;
            uiFuncs.generateTx(txData, callback);
        }
    });
}
uiFuncs.generateTx = function(txData, callback) {
    if ((typeof txData.hwType != "undefined") && (txData.hwType == "trezor") && !txData.trezorUnlocked) {
        uiFuncs.trezorUnlockCallback(txData, callback);
        return;
    }
    try {
        uiFuncs.isTxDataValid(txData);
        var genTxWithInfo = function(data) {
            var rawTx = {
                nonce: ethFuncs.sanitizeHex(data.nonce),
                gasPrice: data.isOffline ? ethFuncs.sanitizeHex(data.gasprice) : ethFuncs.sanitizeHex(ethFuncs.addTinyMoreToGas(data.gasprice)),
                gasLimit: ethFuncs.sanitizeHex(ethFuncs.decimalToHex(txData.gasLimit)),
                to: ethFuncs.sanitizeHex(txData.to),
                value: ethFuncs.sanitizeHex(ethFuncs.decimalToHex(etherUnits.toWei(txData.value, txData.unit))),
                data: ethFuncs.sanitizeHex(txData.data)
            }
            if(txData.kyberGasPrice){
              rawTx.gasPrice = txData.kyberGasPrice;
            }
            if (ajaxReq.eip155) rawTx.chainId = ajaxReq.chainId;
            rawTx.data = rawTx.data == '' ? '0x' : rawTx.data;
            var eTx = new ethUtil.Tx(rawTx);
            if ((typeof txData.hwType != "undefined") && (txData.hwType == "ledger")) {
                var app = new ledgerEth(txData.hwTransport);
                var EIP155Supported = false;
                var localCallback = function(result, error) {
                    if (typeof error != "undefined") {
                        if (callback !== undefined) callback({
                            isError: true,
                            error: error
                        });
                        return;
                    }
                    var splitVersion = result['version'].split('.');
                    if (parseInt(splitVersion[0]) > 1) {
                        EIP155Supported = true;
                    } else
                    if (parseInt(splitVersion[1]) > 0) {
                        EIP155Supported = true;
                    } else
                    if (parseInt(splitVersion[2]) > 2) {
                        EIP155Supported = true;
                    }
                    uiFuncs.signTxLedger(app, eTx, rawTx, txData, !EIP155Supported, callback);
                }
                app.getAppConfiguration(localCallback);
            } else if ((typeof txData.hwType != "undefined") && (txData.hwType == "trezor")) {
                uiFuncs.signTxTrezor(rawTx, txData, callback);
            } else if ((typeof txData.hwType != "undefined") && (txData.hwType == "web3")) {
                // for web3, we dont actually sign it here
                // instead we put the final params in the "signedTx" field and
                // wait for the confirmation dialogue / sendTx method
                var txParams = Object.assign({
                    from: txData.from,
                    gas: ethFuncs.sanitizeHex(ethFuncs.decimalToHex(txData.gasLimit)) // MetaMask and Web3 v1.0 use 'gas' not 'gasLimit'
                }, rawTx)
                rawTx.rawTx = JSON.stringify(rawTx);
                rawTx.signedTx = JSON.stringify(txParams);
                rawTx.isError = false;
                callback(rawTx)
            } else if ((typeof txData.hwType != "undefined") && (txData.hwType == "digitalBitbox")) {
                uiFuncs.signTxDigitalBitbox(eTx, rawTx, txData, callback);
            } else if ((typeof txData.hwType != "undefined") && (txData.hwType == "secalot")) {
                uiFuncs.signTxSecalot(eTx, rawTx, txData, callback);
            } else {
                eTx.sign(new Buffer(txData.privKey, 'hex'));
                rawTx.rawTx = JSON.stringify(rawTx);
                rawTx.signedTx = '0x' + eTx.serialize().toString('hex');
                rawTx.isError = false;
                if (callback !== undefined) callback(rawTx);
            }
        }
        if (txData.nonce || txData.gasPrice) {
            var data = {
                nonce: txData.nonce,
                gasprice: txData.gasPrice
            }
            data.isOffline = txData.isOffline ? txData.isOffline : false;
            genTxWithInfo(data);
        } else {
            ajaxReq.getTransactionData(txData.from, function(data) {
                if (data.error && callback !== undefined) {
                    callback({
                        isError: true,
                        error: e
                    });
                } else {
                    data = data.data;
                    data.isOffline = txData.isOffline ? txData.isOffline : false;
                    genTxWithInfo(data);
                }
            });
        }
    } catch (e) {
        if (callback !== undefined) callback({
            isError: true,
            error: e
        });
    }
}
uiFuncs.sendTx = function(signedTx, callback) {
    // check for web3 late signed tx
    if (signedTx.slice(0, 2) !== '0x') {
        var txParams = JSON.parse(signedTx)
        window.web3.eth.sendTransaction(txParams, function(err, txHash) {
            if (err) {
                return callback({
                    isError: true,
                    error: err.stack,
                })
            }
            callback({
                data: txHash
            })
        });
        return
    }

    ajaxReq.sendRawTx(signedTx, function(data) {
        var resp = {};
        if (data.error) {
            resp = {
                isError: true,
                error: data.msg
            };
        } else {
            resp = {
                isError: false,
                data: data.data
            };
        }
        if (callback !== undefined) callback(resp);
    });
}
uiFuncs.transferAllBalance = function(fromAdd, gasLimit, callback) {
    try {
        ajaxReq.getTransactionData(fromAdd, function(data) {
            if (data.error) throw data.msg;
            data = data.data;
            var gasPrice = new BigNumber(ethFuncs.sanitizeHex(ethFuncs.addTinyMoreToGas(data.gasprice))).times(gasLimit);
            var maxVal = new BigNumber(data.balance).minus(gasPrice);
            maxVal = etherUnits.toEther(maxVal, 'wei') < 0 ? 0 : etherUnits.toEther(maxVal, 'wei');
            if (callback !== undefined) callback({
                isError: false,
                unit: "ether",
                value: maxVal
            });
        });
    } catch (e) {
        if (callback !== undefined) callback({
            isError: true,
            error: e
        });
    }
}
uiFuncs.notifier = {
    alerts: {},
    warning: function(msg, duration = 5000) {
        this.addAlert("warning", msg, duration);
    },
    info: function(msg, duration = 5000) {
        this.addAlert("info", msg, duration);
    },
    danger: function(msg, duration = 7000) {
        msg = msg.message ? msg.message : msg;
        // Danger messages can be translated based on the type of node
        msg = globalFuncs.getEthNodeMsg(msg);
        this.addAlert("danger", msg, duration);
    },
    success: function(msg, duration = 5000) {
        this.addAlert("success", msg, duration);
    },
    addAlert: function(type, msg, duration) {
        if (duration == undefined) duration = 7000;
        // Save all messages by unique id for removal
        var id = Date.now();
        alert = this.buildAlert(id, type, msg);
        this.alerts[id] = alert
        var that = this;
        if (duration > 0) { // Support permanent messages
            setTimeout(alert.close, duration);
        }
        if (!this.scope.$$phase) this.scope.$apply();
    },
    buildAlert: function(id, type, msg) {
        var that = this;
        return {
            show: true,
            type: type,
            message: msg,
            close: function() {
                delete that.alerts[id];
                if (!that.scope.$$phase) that.scope.$apply();
            }
        }
    },
}
module.exports = uiFuncs;
<commit_msg>support 32bits chainId with the latest Ledger app<commit_after>'use strict';
var uiFuncs = function() {}
uiFuncs.getTxData = function($scope) {
    return {
        to: $scope.tx.to,
        value: $scope.tx.value,
        unit: $scope.tx.unit,
        gasLimit: $scope.tx.gasLimit,
        data: $scope.tx.data,
        from: $scope.wallet.getAddressString(),
        privKey: $scope.wallet.privKey ? $scope.wallet.getPrivateKeyString() : '',
        path: $scope.wallet.getPath(),
        hwType: $scope.wallet.getHWType(),
        hwTransport: $scope.wallet.getHWTransport()
    };
}
uiFuncs.isTxDataValid = function(txData) {
    if (txData.to != "0xCONTRACT" && !ethFuncs.validateEtherAddress(txData.to)) throw globalFuncs.errorMsgs[5];
    else if (!globalFuncs.isNumeric(txData.value) || parseFloat(txData.value) < 0) throw globalFuncs.errorMsgs[0];
    else if (!globalFuncs.isNumeric(txData.gasLimit) || parseFloat(txData.gasLimit) <= 0) throw globalFuncs.errorMsgs[8];
    else if (!ethFuncs.validateHexString(txData.data)) throw globalFuncs.errorMsgs[9];
    if (txData.to == "0xCONTRACT") txData.to = '';
}
uiFuncs.signTxTrezor = function(rawTx, txData, callback) {
    var localCallback = function(result) {
        if (!result.success) {
            if (callback !== undefined) {
                callback({
                    isError: true,
                    error: result.error
                });
            }
            return;
        }

        rawTx.v = "0x" + ethFuncs.decimalToHex(result.v);
        rawTx.r = "0x" + result.r;
        rawTx.s = "0x" + result.s;
        var eTx = new ethUtil.Tx(rawTx);
        rawTx.rawTx = JSON.stringify(rawTx);
        rawTx.signedTx = '0x' + eTx.serialize().toString('hex');
        rawTx.isError = false;
        if (callback !== undefined) callback(rawTx);
    }

    TrezorConnect.signEthereumTx(
        txData.path,
        ethFuncs.getNakedAddress(rawTx.nonce),
        ethFuncs.getNakedAddress(rawTx.gasPrice),
        ethFuncs.getNakedAddress(rawTx.gasLimit),
        ethFuncs.getNakedAddress(rawTx.to),
        ethFuncs.getNakedAddress(rawTx.value),
        ethFuncs.getNakedAddress(rawTx.data),
        rawTx.chainId,
        localCallback
    );
}
uiFuncs.signTxLedger = function(app, eTx, rawTx, txData, old, callback) {
    eTx.raw[6] = rawTx.chainId;
    eTx.raw[7] = eTx.raw[8] = 0;
    var toHash = old ? eTx.raw.slice(0, 6) : eTx.raw;
    var txToSign = ethUtil.rlp.encode(toHash);
    var localCallback = function(result, error) {
        if (typeof error != "undefined") {
            error = error.errorCode ? u2f.getErrorByCode(error.errorCode) : error;
            if (callback !== undefined) callback({
                isError: true,
                error: error
            });
            return;
        }
        var v = result['v'].toString(16);
        if (!old) {
            // EIP155 support. check/recalc signature v value.
            var rv = parseInt(v, 16);
            var cv = rawTx.chainId * 2 + 35;
            if (rv !== cv && (rv & cv) !== rv) {
                cv += 1; // add signature v bit.
            }
            v = cv.toString(16);
        }
        rawTx.v = "0x" + v;
        rawTx.r = "0x" + result['r'];
        rawTx.s = "0x" + result['s'];
        eTx = new ethUtil.Tx(rawTx);
        rawTx.rawTx = JSON.stringify(rawTx);
        rawTx.signedTx = '0x' + eTx.serialize().toString('hex');
        rawTx.isError = false;
        if (callback !== undefined) callback(rawTx);
    }
    app.signTransaction(txData.path, txToSign.toString('hex'), localCallback);
}
uiFuncs.signTxDigitalBitbox = function(eTx, rawTx, txData, callback) {
    var localCallback = function(result, error) {
        if (typeof error != "undefined") {
            error = error.errorCode ? u2f.getErrorByCode(error.errorCode) : error;
            if (callback !== undefined) callback({
                isError: true,
                error: error
            });
            return;
        }
        uiFuncs.notifier.info("The transaction was signed but not sent. Click the blue 'Send Transaction' button to continue.");
        rawTx.v = ethFuncs.sanitizeHex(result['v']);
        rawTx.r = ethFuncs.sanitizeHex(result['r']);
        rawTx.s = ethFuncs.sanitizeHex(result['s']);
        var eTx_ = new ethUtil.Tx(rawTx);
        rawTx.rawTx = JSON.stringify(rawTx);
        rawTx.signedTx = ethFuncs.sanitizeHex(eTx_.serialize().toString('hex'));
        rawTx.isError = false;
        if (callback !== undefined) callback(rawTx);
    }
    uiFuncs.notifier.info("Touch the LED for 3 seconds to sign the transaction. Or tap the LED to cancel.");
    var app = new DigitalBitboxEth(txData.hwTransport, '');
    app.signTransaction(txData.path, eTx, localCallback);
}
uiFuncs.signTxSecalot = function(eTx, rawTx, txData, callback) {

    var localCallback = function(result, error) {
        if (typeof error != "undefined") {
            error = error.errorCode ? u2f.getErrorByCode(error.errorCode) : error;
            if (callback !== undefined) callback({
                isError: true,
                error: error
            });
            return;
        }
        uiFuncs.notifier.info("The transaction was signed but not sent. Click the blue 'Send Transaction' button to continue.");
        rawTx.v = ethFuncs.sanitizeHex(result['v']);
        rawTx.r = ethFuncs.sanitizeHex(result['r']);
        rawTx.s = ethFuncs.sanitizeHex(result['s']);

        var eTx_ = new ethUtil.Tx(rawTx);
        rawTx.rawTx = JSON.stringify(rawTx);
        rawTx.signedTx = ethFuncs.sanitizeHex(eTx_.serialize().toString('hex'));
        rawTx.isError = false;
        if (callback !== undefined) callback(rawTx);
    }
    uiFuncs.notifier.info("Tap a touch button on your device to confirm signing.");
    var app = new SecalotEth(txData.hwTransport);
    app.signTransaction(txData.path, eTx, localCallback);
}
uiFuncs.trezorUnlockCallback = function(txData, callback) {
    TrezorConnect.open(function(error) {
        if (error) {
            if (callback !== undefined) callback({
                isError: true,
                error: error
            });
        } else {
            txData.trezorUnlocked = true;
            uiFuncs.generateTx(txData, callback);
        }
    });
}
uiFuncs.generateTx = function(txData, callback) {
    if ((typeof txData.hwType != "undefined") && (txData.hwType == "trezor") && !txData.trezorUnlocked) {
        uiFuncs.trezorUnlockCallback(txData, callback);
        return;
    }
    try {
        uiFuncs.isTxDataValid(txData);
        var genTxWithInfo = function(data) {
            var rawTx = {
                nonce: ethFuncs.sanitizeHex(data.nonce),
                gasPrice: data.isOffline ? ethFuncs.sanitizeHex(data.gasprice) : ethFuncs.sanitizeHex(ethFuncs.addTinyMoreToGas(data.gasprice)),
                gasLimit: ethFuncs.sanitizeHex(ethFuncs.decimalToHex(txData.gasLimit)),
                to: ethFuncs.sanitizeHex(txData.to),
                value: ethFuncs.sanitizeHex(ethFuncs.decimalToHex(etherUnits.toWei(txData.value, txData.unit))),
                data: ethFuncs.sanitizeHex(txData.data)
            }
            if(txData.kyberGasPrice){
              rawTx.gasPrice = txData.kyberGasPrice;
            }
            if (ajaxReq.eip155) rawTx.chainId = ajaxReq.chainId;
            rawTx.data = rawTx.data == '' ? '0x' : rawTx.data;
            var eTx = new ethUtil.Tx(rawTx);
            if ((typeof txData.hwType != "undefined") && (txData.hwType == "ledger")) {
                var app = new ledgerEth(txData.hwTransport);
                var EIP155Supported = false;
                var localCallback = function(result, error) {
                    if (typeof error != "undefined") {
                        if (callback !== undefined) callback({
                            isError: true,
                            error: error
                        });
                        return;
                    }
                    var splitVersion = result['version'].split('.');
                    if (parseInt(splitVersion[0]) > 1) {
                        EIP155Supported = true;
                    } else
                    if (parseInt(splitVersion[1]) > 0) {
                        EIP155Supported = true;
                    } else
                    if (parseInt(splitVersion[2]) > 2) {
                        EIP155Supported = true;
                    }
                    uiFuncs.signTxLedger(app, eTx, rawTx, txData, !EIP155Supported, callback);
                }
                app.getAppConfiguration(localCallback);
            } else if ((typeof txData.hwType != "undefined") && (txData.hwType == "trezor")) {
                uiFuncs.signTxTrezor(rawTx, txData, callback);
            } else if ((typeof txData.hwType != "undefined") && (txData.hwType == "web3")) {
                // for web3, we dont actually sign it here
                // instead we put the final params in the "signedTx" field and
                // wait for the confirmation dialogue / sendTx method
                var txParams = Object.assign({
                    from: txData.from,
                    gas: ethFuncs.sanitizeHex(ethFuncs.decimalToHex(txData.gasLimit)) // MetaMask and Web3 v1.0 use 'gas' not 'gasLimit'
                }, rawTx)
                rawTx.rawTx = JSON.stringify(rawTx);
                rawTx.signedTx = JSON.stringify(txParams);
                rawTx.isError = false;
                callback(rawTx)
            } else if ((typeof txData.hwType != "undefined") && (txData.hwType == "digitalBitbox")) {
                uiFuncs.signTxDigitalBitbox(eTx, rawTx, txData, callback);
            } else if ((typeof txData.hwType != "undefined") && (txData.hwType == "secalot")) {
                uiFuncs.signTxSecalot(eTx, rawTx, txData, callback);
            } else {
                eTx.sign(new Buffer(txData.privKey, 'hex'));
                rawTx.rawTx = JSON.stringify(rawTx);
                rawTx.signedTx = '0x' + eTx.serialize().toString('hex');
                rawTx.isError = false;
                if (callback !== undefined) callback(rawTx);
            }
        }
        if (txData.nonce || txData.gasPrice) {
            var data = {
                nonce: txData.nonce,
                gasprice: txData.gasPrice
            }
            data.isOffline = txData.isOffline ? txData.isOffline : false;
            genTxWithInfo(data);
        } else {
            ajaxReq.getTransactionData(txData.from, function(data) {
                if (data.error && callback !== undefined) {
                    callback({
                        isError: true,
                        error: e
                    });
                } else {
                    data = data.data;
                    data.isOffline = txData.isOffline ? txData.isOffline : false;
                    genTxWithInfo(data);
                }
            });
        }
    } catch (e) {
        if (callback !== undefined) callback({
            isError: true,
            error: e
        });
    }
}
uiFuncs.sendTx = function(signedTx, callback) {
    // check for web3 late signed tx
    if (signedTx.slice(0, 2) !== '0x') {
        var txParams = JSON.parse(signedTx)
        window.web3.eth.sendTransaction(txParams, function(err, txHash) {
            if (err) {
                return callback({
                    isError: true,
                    error: err.stack,
                })
            }
            callback({
                data: txHash
            })
        });
        return
    }

    ajaxReq.sendRawTx(signedTx, function(data) {
        var resp = {};
        if (data.error) {
            resp = {
                isError: true,
                error: data.msg
            };
        } else {
            resp = {
                isError: false,
                data: data.data
            };
        }
        if (callback !== undefined) callback(resp);
    });
}
uiFuncs.transferAllBalance = function(fromAdd, gasLimit, callback) {
    try {
        ajaxReq.getTransactionData(fromAdd, function(data) {
            if (data.error) throw data.msg;
            data = data.data;
            var gasPrice = new BigNumber(ethFuncs.sanitizeHex(ethFuncs.addTinyMoreToGas(data.gasprice))).times(gasLimit);
            var maxVal = new BigNumber(data.balance).minus(gasPrice);
            maxVal = etherUnits.toEther(maxVal, 'wei') < 0 ? 0 : etherUnits.toEther(maxVal, 'wei');
            if (callback !== undefined) callback({
                isError: false,
                unit: "ether",
                value: maxVal
            });
        });
    } catch (e) {
        if (callback !== undefined) callback({
            isError: true,
            error: e
        });
    }
}
uiFuncs.notifier = {
    alerts: {},
    warning: function(msg, duration = 5000) {
        this.addAlert("warning", msg, duration);
    },
    info: function(msg, duration = 5000) {
        this.addAlert("info", msg, duration);
    },
    danger: function(msg, duration = 7000) {
        msg = msg.message ? msg.message : msg;
        // Danger messages can be translated based on the type of node
        msg = globalFuncs.getEthNodeMsg(msg);
        this.addAlert("danger", msg, duration);
    },
    success: function(msg, duration = 5000) {
        this.addAlert("success", msg, duration);
    },
    addAlert: function(type, msg, duration) {
        if (duration == undefined) duration = 7000;
        // Save all messages by unique id for removal
        var id = Date.now();
        alert = this.buildAlert(id, type, msg);
        this.alerts[id] = alert
        var that = this;
        if (duration > 0) { // Support permanent messages
            setTimeout(alert.close, duration);
        }
        if (!this.scope.$$phase) this.scope.$apply();
    },
    buildAlert: function(id, type, msg) {
        var that = this;
        return {
            show: true,
            type: type,
            message: msg,
            close: function() {
                delete that.alerts[id];
                if (!that.scope.$$phase) that.scope.$apply();
            }
        }
    },
}
module.exports = uiFuncs;
<|endoftext|>


<commit_before>
import {createContext} from './context';
import {Loader as LoaderClass} from './loader';

import {
  decodePointerComponent,
  resolveReference,
  normalizeId,
  typeOf,
  isSchema
} from './util';

export var $validators = Symbol("$validators");
export var $schema     = Symbol("$schema");
export var $ref        = Symbol("$ref");
export var $id         = Symbol("$id");
export var $validate   = Symbol("$validate");
export var $rescoped   = Symbol("$rescoped");
export var $guard      = Symbol("$guard");
var $getChildSchema    = Symbol("$getChildSchema");

export var Loader = new LoaderClass();

export class Schema {

  static import(opts) {
    return Loader.import(opts);
  }

  static build(definition, id) {
    return Loader.build(definition, id);
  }

  static register(schema, id) {
    return Loader.register(schema, id);
  }

  constructor(id, guard) {
    if (guard !== $guard) {
      throw Error("please use Schema.import() to get schemas");
    }

    this[$id] = id;
  }

  get(id) {
    id = normalizeId(id);
    id = resolveReference(this[$id], id);

    let rootId   = id.slice(0, id.indexOf('#')+1);
    let fragment = id.slice(id.indexOf('#')+2);
    let context  = this;

    if (rootId !== this[$id]) {
      context = this[$rescoped][rootId];
    }

    if (context === undefined) {
      return null;
    }

    return context[$getChildSchema](fragment);
  }

  [$getChildSchema](fragment) {
    if (this[$schema]) {
      return this[$schema][$getChildSchema](fragment);
    }

    if (fragment === '') {
      return this;
    }

    let slashIdx  = fragment.indexOf('/');
    let component = fragment;
    let remainder = null;
    if (slashIdx >= 0) {
      component = fragment.slice(0, slashIdx);
      remainder = fragment.slice(slashIdx+1)
    }
    component = decodePointerComponent(component);

    let child = this[component];

    if (child === undefined) {
      return null;
    }

    if (!remainder) {
      return child;
    }

    if (typeOf(child) === 'array') {
      return this[$getChildSchema].call(child, remainder);
    }

    if (!isSchema(child)) {
      return null;
    }

    return child[$getChildSchema](remainder);
  }

  validate(value) {
    if (this[$schema]) {
      return this[$schema].validate(value);
    }

    var ctx = createContext(this, value);
    return this[$validate](value, ctx);
  }

  [$validate](value, ctx) {
    if (this[$schema]) {
      return this[$schema][$validate](value, ctx);
    }

    if (this[$validators]) {
      this[$validators].forEach(v => v.validate(value, ctx));
    }

    ctx.valid = (ctx.errors.length === 0);

    return ctx;
  }

}
<commit_msg>Added resolvedSchema to Schema<commit_after>
import {createContext} from './context';
import {Loader as LoaderClass} from './loader';

import {
  decodePointerComponent,
  resolveReference,
  normalizeId,
  typeOf,
  isSchema
} from './util';

export var $validators = Symbol("$validators");
export var $schema     = Symbol("$schema");
export var $ref        = Symbol("$ref");
export var $id         = Symbol("$id");
export var $validate   = Symbol("$validate");
export var $rescoped   = Symbol("$rescoped");
export var $guard      = Symbol("$guard");
var $getChildSchema    = Symbol("$getChildSchema");

export var Loader = new LoaderClass();

export class Schema {

  static import(opts) {
    return Loader.import(opts);
  }

  static build(definition, id) {
    return Loader.build(definition, id);
  }

  static register(schema, id) {
    return Loader.register(schema, id);
  }

  constructor(id, guard) {
    if (guard !== $guard) {
      throw Error("please use Schema.import() to get schemas");
    }

    this[$id] = id;
  }

  get resolvedSchema() {
    return this[$schema] || this;
  }

  get(id) {
    id = normalizeId(id);
    id = resolveReference(this[$id], id);

    let rootId   = id.slice(0, id.indexOf('#')+1);
    let fragment = id.slice(id.indexOf('#')+2);
    let context  = this;

    if (rootId !== this[$id]) {
      context = this[$rescoped][rootId];
    }

    if (context === undefined) {
      return null;
    }

    return context[$getChildSchema](fragment);
  }

  [$getChildSchema](fragment) {
    if (this[$schema]) {
      return this[$schema][$getChildSchema](fragment);
    }

    if (fragment === '') {
      return this;
    }

    let slashIdx  = fragment.indexOf('/');
    let component = fragment;
    let remainder = null;
    if (slashIdx >= 0) {
      component = fragment.slice(0, slashIdx);
      remainder = fragment.slice(slashIdx+1)
    }
    component = decodePointerComponent(component);

    let child = this[component];

    if (child === undefined) {
      return null;
    }

    if (!remainder) {
      return child;
    }

    if (typeOf(child) === 'array') {
      return this[$getChildSchema].call(child, remainder);
    }

    if (!isSchema(child)) {
      return null;
    }

    return child[$getChildSchema](remainder);
  }

  validate(value) {
    if (this[$schema]) {
      return this[$schema].validate(value);
    }

    var ctx = createContext(this, value);
    return this[$validate](value, ctx);
  }

  [$validate](value, ctx) {
    if (this[$schema]) {
      return this[$schema][$validate](value, ctx);
    }

    if (this[$validators]) {
      this[$validators].forEach(v => v.validate(value, ctx));
    }

    ctx.valid = (ctx.errors.length === 0);

    return ctx;
  }

}
<|endoftext|>


<commit_before>app.factory('stocks', ['$http', function($http) {
	return function(symbol) {
		var symbols;
		if (symbol.constructor === Array) {
			symbols = symbol.join('","');
		} else {
			symbols = symbol;
		}

		var endPoint = 'https://query.yahooapis.com/v1/public/yql?q=',
				query = "select * from yahoo.finance.quotes where symbol IN(\"" + symbols + "\")",
				args = "&format=json&env=http://datatables.org/alltables.env",
				url = endPoint + encodeURIComponent(query) + args;

		return $http.get(url)
			.success(function(data) {
				return data;
			})
			.error(function(data) {
				return data;
			});
	}

}]);<commit_msg>Use getAll and add methods to get and create stocks in stocks service<commit_after>app.factory('stocks', ['$http', function($http) {
	return {
		stocks: [],
		getAll: function(symbols) {
			var self = this;
			var response;
			return $http.get('http://localhost:3000/stocks')
				.success(function(data) {
					angular.copy(data, self.stocks);
					// return data;
				})
				.error(function(data) {
					return data;
				});
			// 	var url;
			// 	if (symbols.constructor === Array) {
			// 		url = 'http://localhost:3000/getStocks?symbols=' + symbols.join(',');
			// 	} else {
			// 		url = 'http://localhost:3000/getStock?symbol=' + symbol;
			// 	}

			// 	response = $http.get(url)
			// 		.success(function(data) {
			// 			return data;
			// 		})
			// 		.error(function(data) {
			// 			return data;
			// 		});
			// }

			return response;
		},

		add: function(data) {
			return $http.post('http://localhost:3000/addStock', data)
				.success(function(data) {
					return data;
				})
				.error(function(data) {
					return data;
				})
		}
	}

}]);<|endoftext|>


<commit_before>const {Schema} = require("prosemirror-model")

exports.schema = new Schema({
  nodes: {
    doc: {
      content: "block+"
    },

    paragraph: {
      content: "inline<_>*",
      group: "block",
      parseDOM: [{tag: "p"}],
      toDOM() { return ["p", 0] }
    },

    blockquote: {
      content: "block+",
      group: "block",
      parseDOM: [{tag: "blockquote"}],
      toDOM() { return ["blockquote", 0] }
    },

    horizontal_rule: {
      group: "block",
      parseDOM: [{tag: "hr"}],
      toDOM() { return ["div", ["hr"]] }
    },

    heading: {
      attrs: {level: {default: 1}},
      content: "inline<_>*",
      group: "block",
      parseDOM: [{tag: "h1", attrs: {level: 1}},
                 {tag: "h2", attrs: {level: 2}},
                 {tag: "h3", attrs: {level: 3}},
                 {tag: "h4", attrs: {level: 4}},
                 {tag: "h5", attrs: {level: 5}},
                 {tag: "h6", attrs: {level: 6}}],
      toDOM(node) { return ["h" + node.attrs.level, 0] }
    },

    code_block: {
      content: "text*",
      group: "block",
      code: true,
      attrs: {params: {default: ""}},
      parseDOM: [{tag: "pre", preserveWhitespace: true, getAttrs: node => ({params: node.getAttribute("data-params")})}],
      toDOM(node) { return ["pre", node.attrs.params ? {"data-params": node.attrs.params} : {}, ["code", 0]] }
    },

    ordered_list: {
      content: "list_item+",
      group: "block",
      attrs: {order: {default: 1}, tight: {default: false}},
      parseDOM: [{tag: "ol", getAttrs(dom) {
        return {order: dom.hasAttribute("start") ? +dom.getAttribute("start") : 1,
                tight: dom.hasAttribute("data-tight")}
      }}],
      toDOM(node) {
        return ["ol", {start: node.attrs.order == 1 ? null : node.attrs.order,
                       "data-tight": node.attrs.tight ? "true" : null}, 0]
      }
    },

    bullet_list: {
      content: "list_item+",
      group: "block",
      attrs: {tight: {default: false}},
      parseDOM: [{tag: "ul", getAttrs: dom => ({tight: dom.hasAttribute("data-tight")})}],
      toDOM(node) { return ["ul", {"data-tight": node.attrs.tight ? "true" : null}, 0] }
    },

    list_item: {
      content: "paragraph block*",
      parseDOM: [{tag: "li"}],
      toDOM() { return ["li", 0] }
    },

    text: {
      group: "inline",
      toDOM(node) { return node.text }
    },

    image: {
      inline: true,
      attrs: {
        src: {},
        alt: {default: ""},
        title: {default: ""}
      },
      group: "inline",
      draggable: true,
      parseDOM: [{tag: "img[src]", getAttrs(dom) {
        return {
          src: dom.getAttribute("src"),
          title: dom.getAttribute("title"),
          alt: dom.getAttribute("alt")
        }
      }}],
      toDOM(node) { return ["img", node.attrs] }
    },

    hard_break: {
      inline: true,
      group: "inline",
      selectable: false,
      parseDOM: [{tag: "br"}],
      toDOM() { return ["br"] }
    }
  },

  marks: {
    em: {
      parseDOM: [{tag: "i"}, {tag: "em"},
                 {style: "font-style", getAttrs: value => value == "italic" && null}],
      toDOM() { return ["em"] }
    },

    strong: {
      parseDOM: [{tag: "b"}, {tag: "strong"},
                 {style: "font-weight", getAttrs: value => /^(bold(er)?|[5-9]\d{2,})$/.test(value) && null}],
      toDOM() { return ["strong"] }
    },

    link: {
      attrs: {
        href: {},
        title: {default: ""}
      },
      parseDOM: [{tag: "a[href]", getAttrs(dom) {
        return {href: dom.getAttribute("href"), title: dom.getAttribute("title")}
      }}],
      toDOM(node) { return ["a", node.attrs] }
    },

    code: {
      parseDOM: [{tag: "code"}],
      toDOM() { return ["code"] }
    }
  }
})
<commit_msg>Use null instead of empty string for default attrs<commit_after>const {Schema} = require("prosemirror-model")

exports.schema = new Schema({
  nodes: {
    doc: {
      content: "block+"
    },

    paragraph: {
      content: "inline<_>*",
      group: "block",
      parseDOM: [{tag: "p"}],
      toDOM() { return ["p", 0] }
    },

    blockquote: {
      content: "block+",
      group: "block",
      parseDOM: [{tag: "blockquote"}],
      toDOM() { return ["blockquote", 0] }
    },

    horizontal_rule: {
      group: "block",
      parseDOM: [{tag: "hr"}],
      toDOM() { return ["div", ["hr"]] }
    },

    heading: {
      attrs: {level: {default: 1}},
      content: "inline<_>*",
      group: "block",
      parseDOM: [{tag: "h1", attrs: {level: 1}},
                 {tag: "h2", attrs: {level: 2}},
                 {tag: "h3", attrs: {level: 3}},
                 {tag: "h4", attrs: {level: 4}},
                 {tag: "h5", attrs: {level: 5}},
                 {tag: "h6", attrs: {level: 6}}],
      toDOM(node) { return ["h" + node.attrs.level, 0] }
    },

    code_block: {
      content: "text*",
      group: "block",
      code: true,
      attrs: {params: {default: ""}},
      parseDOM: [{tag: "pre", preserveWhitespace: true, getAttrs: node => ({params: node.getAttribute("data-params")})}],
      toDOM(node) { return ["pre", node.attrs.params ? {"data-params": node.attrs.params} : {}, ["code", 0]] }
    },

    ordered_list: {
      content: "list_item+",
      group: "block",
      attrs: {order: {default: 1}, tight: {default: false}},
      parseDOM: [{tag: "ol", getAttrs(dom) {
        return {order: dom.hasAttribute("start") ? +dom.getAttribute("start") : 1,
                tight: dom.hasAttribute("data-tight")}
      }}],
      toDOM(node) {
        return ["ol", {start: node.attrs.order == 1 ? null : node.attrs.order,
                       "data-tight": node.attrs.tight ? "true" : null}, 0]
      }
    },

    bullet_list: {
      content: "list_item+",
      group: "block",
      attrs: {tight: {default: false}},
      parseDOM: [{tag: "ul", getAttrs: dom => ({tight: dom.hasAttribute("data-tight")})}],
      toDOM(node) { return ["ul", {"data-tight": node.attrs.tight ? "true" : null}, 0] }
    },

    list_item: {
      content: "paragraph block*",
      parseDOM: [{tag: "li"}],
      toDOM() { return ["li", 0] }
    },

    text: {
      group: "inline",
      toDOM(node) { return node.text }
    },

    image: {
      inline: true,
      attrs: {
        src: {},
        alt: {default: null},
        title: {default: null}
      },
      group: "inline",
      draggable: true,
      parseDOM: [{tag: "img[src]", getAttrs(dom) {
        return {
          src: dom.getAttribute("src"),
          title: dom.getAttribute("title"),
          alt: dom.getAttribute("alt")
        }
      }}],
      toDOM(node) { return ["img", node.attrs] }
    },

    hard_break: {
      inline: true,
      group: "inline",
      selectable: false,
      parseDOM: [{tag: "br"}],
      toDOM() { return ["br"] }
    }
  },

  marks: {
    em: {
      parseDOM: [{tag: "i"}, {tag: "em"},
                 {style: "font-style", getAttrs: value => value == "italic" && null}],
      toDOM() { return ["em"] }
    },

    strong: {
      parseDOM: [{tag: "b"}, {tag: "strong"},
                 {style: "font-weight", getAttrs: value => /^(bold(er)?|[5-9]\d{2,})$/.test(value) && null}],
      toDOM() { return ["strong"] }
    },

    link: {
      attrs: {
        href: {},
        title: {default: null}
      },
      parseDOM: [{tag: "a[href]", getAttrs(dom) {
        return {href: dom.getAttribute("href"), title: dom.getAttribute("title")}
      }}],
      toDOM(node) { return ["a", node.attrs] }
    },

    code: {
      parseDOM: [{tag: "code"}],
      toDOM() { return ["code"] }
    }
  }
})
<|endoftext|>


<commit_before>const request = require('superagent');
const cheerio = require('cheerio');

const BASE_URL = 'https://github.com';

const scrapeGithubRepos = (path = 'trending', query = {since: 'today'}) => {
    let url = `${BASE_URL}/${path}`;

    return request
        .get(url)
        .query(query)
        .then(res => {
            let $ = cheerio.load(res.text);
            let records = [];

            $('li.d-block').each(function() {
                let data = $(this);
                let record = {};

                let name = data.children().children().children().attr('href');
                let color = data.children('.mt-2').find('span.repo-language-color');
                let updated =  data.children('.mt-2').find('relative-time').text().trim();

                record.name = name.split('/')[2];
                record.full_name = name.slice(1);
                record.html_url = BASE_URL + name;
                record.description = data.children().find('p').text().trim();
                record.language = data.children('.mt-2').find('span.mr-3').text().trim();
                record.language_color = color.length ? color.css('background-color') : '';
                record.stargazers_count = data.children('.mt-2').find('a.mr-3').eq(0).text().trim();
                record.stargazers_period = data.children('.mt-2').children('.float-right').text().trim();
                record.forks = data.children('.mt-2').find('a.mr-3').eq(1).text().trim();
                record.updated = updated.length ? 'Updated on ' + updated : '';

                records.push(record);
            })

            return records;
        });
};

const scrapeGithubUsers = (query = {type: 'Users'}) => {
    let url = `${BASE_URL}/search`;

    return request
        .get(url)
        .query(query)
        .then(res => {
            let $ = cheerio.load(res.text);
            let records = [];

            $('div.user-list-item').each(function() {
                let data = $(this);
                let record = {};

                record.login = data.children('.user-list-info').find('a').eq(0).text().trim();
                record.avatar_url = data.children().children('.avatar').attr('src');
                record.html_url = BASE_URL + '/' + record.login;
                record.name = data.children('.user-list-info').text().split('\n')[2].trim();
                record.location = data.children().children('.user-list-meta').find('li').eq(0).text().trim();
                record.bio = data.children().children('.user-list-bio').text().trim();
                record.email = data.children().children('.user-list-meta').find('li').eq(1).text().trim();

                records.push(record);
            })

            return records;
        });
};

const scrapeGitHubTrendingUsers = (path = '', query = {since: 'today'}) => {
    let url = `${BASE_URL}/trending/developers/${path}`;

    return request
        .get(url)
        .query(query)
        .then(res => {
            let $ = cheerio.load(res.text);
            let records = [];

            $('li.user-leaderboard-list-item').each(function() {
                let data = $(this);
                let record = {};

                let name = data.children().children('.user-leaderboard-list-name').find('a').children('span.full-name').text().trim();
                let repo = data.children().children('.repo-snipit').attr('href');

                record.login = data.children().children('.user-leaderboard-list-name').find('a').eq(0).text().trim().split('\n')[0];
                record.avatar_url = data.children().children('.leaderboard-gravatar').attr('src');
                record.html_url = BASE_URL + '/' + record.login;
                record.name = name.slice(1, name.length - 1);
                record.repo = {};
                record.repo.name = repo.split('')[2];
                record.repo.full_name = repo.slice(1);
                record.repo.html_url = BASE_URL + repo;
                record.repo.slug = data.children().children('.repo-snipit').children('.repo-snipit-name').text().trim();
                record.repo.description = data.children().children('.repo-snipit').children('.repo-snipit-description').text().trim();

                records.push(record);
            })

            return records;
        });
};

module.exports = {
    scrapeGithubRepos,
    scrapeGithubUsers,
    scrapeGitHubTrendingUsers
};
<commit_msg>Updated scraper to reflect GitHub UI changes<commit_after>const request = require('superagent');
const cheerio = require('cheerio');

const BASE_URL = 'https://github.com';

const scrapeGitHubTrendingUsers = (path = '', query = {since: 'today'}) => {
    let url = `${BASE_URL}/trending/developers/${path}`;

    return request
        .get(url)
        .query(query)
        .then(res => {
            let $ = cheerio.load(res.text);
            let records = [];

            $('li.user-leaderboard-list-item').each(function() {
                let data = $(this);
                let record = {};

                let name = data.children().children('.user-leaderboard-list-name').find('a').children('span.full-name').text().trim();
                let repo = data.children().children('.repo-snipit').attr('href');

                record.login = data.children().children('.user-leaderboard-list-name').find('a').eq(0).text().trim().split('\n')[0];
                record.avatar_url = data.children().children('.leaderboard-gravatar').attr('src');
                record.html_url = BASE_URL + '/' + record.login;
                record.name = name.slice(1, name.length - 1);
                record.repo = {};
                record.repo.name = repo.split('')[2];
                record.repo.full_name = repo.slice(1);
                record.repo.html_url = BASE_URL + repo;
                record.repo.slug = data.children().children('.repo-snipit').children('.repo-snipit-name').text().trim();
                record.repo.description = data.children().children('.repo-snipit').children('.repo-snipit-description').text().trim();

                records.push(record);
            })

            return records;
        });
};

const scrapeGitHubTrendingRepos = (path = 'trending', query = {since: 'today'}) => {
    let url = `${BASE_URL}/${path}`;

    return request
        .get(url)
        .query(query)
        .then(res => {
            let $ = cheerio.load(res.text);
            let records = [];

            $('li.d-block').each(function() {
                let data = $(this);
                let record = {};

                let name = data.children().children().children().attr('href');
                let color = data.children('.mt-2').find('span.repo-language-color');
                let updated =  data.children('.mt-2').find('relative-time').text().trim();

                record.name = name.split('/')[2];
                record.full_name = name.slice(1);
                record.html_url = BASE_URL + name;
                record.description = data.children().find('p').text().trim();
                record.language = data.children('.mt-2').find('span.mr-3').text().trim();
                record.language_color = color.length ? color.css('background-color') : '';
                record.stargazers_count = data.children('.mt-2').find('a.mr-3').eq(0).text().trim();
                record.stargazers_period = data.children('.mt-2').children('.float-right').text().trim();
                record.forks = data.children('.mt-2').find('a.mr-3').eq(1).text().trim();
                record.updated = updated.length ? 'Updated on ' + updated : '';

                records.push(record);
            })

            return records;
        });
};

const scrapeGithubUsers = (query = {type: 'Users'}) => {
    let url = `${BASE_URL}/search`;

    return request
        .get(url)
        .query(query)
        .then(res => {
            let $ = cheerio.load(res.text);
            let records = [];

            $('div.user-list-item').each(function() {
                let data = $(this);
                let record = {};

                record.login = data.find('.user-list-info').find('a').eq(0).text().trim();
                record.avatar_url = data.find('.avatar').attr('src');
                record.html_url = BASE_URL + '/' + record.login;
                record.name = data.find('.user-list-info').find('span.ml-1').text().trim();
                record.location = data.find('.user-list-meta').find('li').eq(0).text().trim();
                record.bio = data.find('.user-list-info').find('p.mt-2').text().trim();
                record.email = data.find('.user-list-meta').find('li').eq(1).text().trim();

                records.push(record);
            })

            return records;
        });
};

const scrapeGithubRepos = (path = 'search', query = {type: 'Repositories'}) => {
    let url = `${BASE_URL}/${path}`;

    return request
        .get(url)
        .query(query)
        .then(res => {
            let $ = cheerio.load(res.text);
            let records = [];

            $('div.repo-list-item').each(function() {
                let data = $(this);
                let record = {};

                let name = data.children().children().children().attr('href');
                let color = data.children().find('span.repo-language-color');
                let updated =  data.children().find('relative-time').text().trim();

                record.name = name.split('/')[2];
                record.full_name = name.slice(1);
                record.html_url = BASE_URL + name;
                record.description = data.children().find('p.col-9').text().trim();
                record.language = color.parent().text().trim();
                record.language_color = color.length ? color.css('background-color') : '';
                record.stargazers_count = data.children().find('a.muted-link').text().trim();
                record.updated = updated.length ? 'Updated on ' + updated : '';

                records.push(record);
            })

            return records;
        });
};

module.exports = {
    scrapeGitHubTrendingUsers,
    scrapeGitHubTrendingRepos,
    scrapeGithubUsers,
    scrapeGithubRepos
};
<|endoftext|>