-
-
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. | |
| */ |
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 !
@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?
Im having the same issue: TypeError: Object [object global] has no method 'activityUrls'