-
-
Save runspired/3526501 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. | |
*/ |
@cohengal @ssamuel68 @serenamichelle I believe this issue has now been fixed, easiest way to reach me if it isn't is on twitter @runspired
I've completely rewritten both scripts to make them more future proof.
Hi I am trying to get run garmin2nike PART of your script I was able to retreive all the garmin activities from my garmin.connect account. when I run the second script from http://www.awsmithson.com/tcx2nikeplus/ chrome console. I get the following error: SyntaxError: Unexpected token ILLEGAL
I not sure what this error means. I try to hard my username and password and enter the the activitiy
garmin2nike( nikePlusEmailInQuotes , nikePlusPasswordInQuotes , stringFromPartOne ) function
I still get the error.
Can you et me know what this is?
Hi,
exact same issue here, not sure whats missing or where it went wrong. Would love to import all my garmin activity's to nike+. great idea !