Skip to content

Instantly share code, notes, and snippets.

@zrzka
Forked from runspired/garmin2nike
Created July 2, 2014 15:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zrzka/245aeacbe08768c2322a to your computer and use it in GitHub Desktop.
Save zrzka/245aeacbe08768c2322a to your computer and use it in GitHub Desktop.
/*
As of 5/13/2013 3:06PM Central Time this script is working. Tweet @runspired to report malfunctions.
*/
/*
Use the Javascript console in Google Chrome and the tcx2nikeplus converter located here: http://www.awsmithson.com/tcx2nikeplus/
This will take some time to run but will port all of your garmin data to nike+.
you will need to set all of your garmin plus workouts to public for this to work properly. The first function converts all of your garmin connect data to a public privacy setting after it gathers the activity urls, if you would like to return your activities to private, tweet me @runspired and I'll show you how to do so.
*/
//------------------------- Garmin Connect set privacy to public and gather activity urls PART ---------------------------
/*
Run this in chrome's javascript console
on Garmin Connect's activities page. By opening the console, copy pasting this
function, and hitting the return key.
*/
(function(){
function garminWalker() {
var walkerInstance = this
, activities = new Array()
, sendRequest = function(index) {
jQuery.ajax({
type : 'POST'
, url : 'http://connect.garmin.com/proxy/activity-service-1.2/json/privacy/' + activities[index].match(/^\/activity\/(\d+)\/?$/ )[1]
, processData : false
, data : 'value=public&_='
, success : function(m,s,r) { if( index == 0 ) console.log( '["' + activities.join('","') + '"]' ); else sendRequest( index-1); }
});
}
, makePublic = function() {
console.log( "making all activities public" );
sendRequest( activities.length - 1 );
}
, getActivities = function() {
console.log( "gathering activities from this frame" );
var wrappers = document.getElementsByClassName('activityName')
, index = wrappers.length;
while(index--)
activities.push(wrappers[index].childNodes[0].getAttribute('href'));
var next = document.getElementsByClassName('rich-datascr-button')[3];
if( next.hasAttribute('onclick') && next.getAttribute('onclick') != '')
wait( wrappers[0], 0 );
else
makePublic();
}
, wait = function( compare , count ) {
//infinite recursion preventer
var count = !!count? count : 0;
//ensure the necessary action is taking place
if( count == 30 )
document.getElementsByClassName('rich-datascr-button')[3].click();
//hard cap on recursion
if( count > 100 )
return;
//continue recursion if the val has not changed
if( document.getElementsByClassName('activityName')[0] === compare )
setTimeout( (function(){ wait(compare,++count); }), 100);
else
getActivities();
};
getActivities();
}
//start the script running
new garminWalker();
}).call(this);
/*
After the page stops loading new results and sets all activities to public it will wait ~5s and then print
a string (probably very long) that looks something like
["/activity/216534341","/activity/21554341","/activity/13456341"]
Copy this string and paste it somewhere you can copy it from again in a moment.
*/
//------------------------- garmin2nike PART ---------------------------
/*
uploader, run this in Chrome's javascript console on the garmin2nike+ page located
here http://www.awsmithson.com/tcx2nikeplus/
by copy pasting it into the console and hitting return.
This Script will take a VERY long time to complete, just leave the window open and let it run.
Periodically some of the activities will fail to upload, the most common reason is the lack
of a GPS track or not enout (at least 3) data points on the GPS track.
The script will continue executing, you can manually go look at the activities that failed if you
would like to verify the reason they didn't convert. There is (unfortunately) nothing I can do about
Garmin data being bad.
*/
(function() {
function tcxConverter(u,p,a) {
var requestURL = 'http://www.awsmithson.com/tcx2nikeplus/tcx2nikeplus/convert'
, baseUrl = "http://connect.garmin.com"
, activities = a || false
, username = u || ''
, password = p || ''
, inputElement = document.getElementById('garminActivityId')
, submitButton = document.getElementById('ext-gen38')
, buildFormString = function(id){
var text = ''
, EOL = "\r\n"
, boundary = "------WebKitFormBoundarywZcuQVboZzlXS7Yv";
text += boundary + EOL + 'Content-Disposition: form-data; name="fsGarminId-checkbox"' + EOL + EOL + 'on' + EOL;
text += boundary + EOL + 'Content-Disposition: form-data; name="garminActivityId"' + EOL + EOL + id + EOL;
text += boundary + EOL + 'Content-Disposition: form-data; name="nikeEmail"' + EOL + EOL + username + EOL;
text += boundary + EOL + 'Content-Disposition: form-data; name="nikePassword"' + EOL + EOL + password + EOL;
text += boundary + EOL + 'Content-Disposition: form-data; name="chkSaveCookies"' + EOL + EOL + 'on' + EOL;
text += boundary + EOL + 'Content-Disposition: form-data; name="clientTimeZoneOffset"' + EOL + EOL +(-1*(new Date()).getTimezoneOffset())+ EOL;
text += boundary + '--' + EOL;
return text;
}
, ajax = function(o) {
var xhr;
if( window.XMLHttpRequest )
xhr = new XMLHttpRequest();
else {
console.log("error establishing server connection");
return false;
}
//ensure readiness
xhr.onreadystatechange = function() {
if(xhr.readyState < 4) {
return;
}
if(xhr.status !== 200) {
o.error( xhr );
return;
}
// all is well
if(xhr.readyState === 4)
o.success(xhr);
};
xhr.open( o.type , o.url , true );
xhr.setRequestHeader("Content-Type",o.contentType);
xhr.setRequestHeader("Accept",'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8');
xhr.send( o.data );
return false;
}
, sendRequest = function(index) {
console.log("transferring activity: "+activities[index]);
ajax({
type : 'POST'
, url : requestURL
, contentType : 'multipart/form-data; boundary=----WebKitFormBoundarywZcuQVboZzlXS7Yv'
, data : buildFormString(activities[index])
, success : function(xhr ) {
xhr.data = JSON.parse( xhr.responseText );
if( xhr.data.success == false ) {
console.log("Transfer failed for "+activities[index], xhr.data.data.errorMessage);
}
if( index > 0 ) sendRequest( index-1 );
else console.log("all activities transferred");
}
, error : function(xhr) { console.log("aborting process, unable to reach server");}
});
};
sendRequest( activities.length - 1 );
}
this.garmin2nike = function(u,p,a) { new tcxConverter(u,p,a); };
}).call(this);
/*
Enter your Nike+ email and password.
after you get the string from running the script on Garmin Connect,
type the line below and copy paste the string from Garmin Connect
where it says 'stringFromPartOne'. Remove the outter layer of quotes (ie it should
look something like ["/url","/url"] not "["/url","/url"]"), make sure you entered your
Nike+ email and password, and hit return.
*/
garmin2nike( nikePlusEmailInQuotes , nikePlusPasswordInQuotes , stringFromPartOne );
/*
Sit back and let the script do it's thing, note, error messages may occassionally pop up that you will need to dismiss,
such as "Error message: 1 is smaller than the minimum (3): number of points (1)"
This is something to do with the script located here not being able to convert the data for a particular workout, not
something I can change.
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment