-
-
Save zrzka/245aeacbe08768c2322a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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