diff options
| author | Gubolin <gubolin@fantasymail.de> | 2014-07-30 18:23:33 +0200 |
|---|---|---|
| committer | Gubolin <gubolin@fantasymail.de> | 2014-07-30 18:23:33 +0200 |
| commit | b9ec43451b9feb0c743196e2b4c3419e5ff611f8 (patch) | |
| tree | 13a695fd0e16de1af79ab87fa2d714e4415f65ca | |
| parent | bfdbeca146f8d58e1f15b2b85f4feae2687d1d9f (diff) | |
| download | snap-b9ec43451b9feb0c743196e2b4c3419e5ff611f8.tar.gz snap-b9ec43451b9feb0c743196e2b4c3419e5ff611f8.zip | |
replace the Snap!Cloud with GitHub
| -rw-r--r-- | cloud.js | 616 | ||||
| -rw-r--r-- | gui.js | 409 | ||||
| -rw-r--r-- | octokit/octokit.js | 1465 | ||||
| -rw-r--r-- | octokit/promise-1.0.0.js | 684 | ||||
| -rwxr-xr-x | snap.html | 2 |
5 files changed, 2300 insertions, 876 deletions
@@ -1,12 +1,11 @@ /* +cloud.js - cloud.js + a GitHub backend API for SNAP! - a backend API for SNAP! + written by Gubolin, based on cloud.js by Jens Mönig - written by Jens Mönig - - Copyright (C) 2014 by Jens Mönig + Copyright (C) 2014 by Jens Mönig, Gubolin This file is part of Snap!. @@ -30,253 +29,73 @@ /*global modules, IDE_Morph, SnapSerializer, hex_sha512, alert, nop, localize*/ -modules.cloud = '2014-May-26'; +modules.cloud = '2014-July-29'; // Global stuff var Cloud; -var SnapCloud = new Cloud( - 'https://snapcloud.miosoft.com/miocon/app/login?_app=SnapCloud' -); +var SnapCloud = new Cloud(); // Cloud ///////////////////////////////////////////////////////////// function Cloud(url) { + this.gh = null; this.username = null; - this.password = null; // hex_sha512 hashed - this.url = url; + this.password = null; // TODO saved as plain text this.session = null; this.api = {}; } Cloud.prototype.clear = function () { + this.gh = null; this.username = null; this.password = null; this.session = null; this.api = {}; }; -Cloud.prototype.hasProtocol = function () { - return this.url.toLowerCase().indexOf('http') === 0; -}; - // Cloud: Snap! API -Cloud.prototype.signup = function ( - username, - email, +Cloud.prototype.getProject = function ( + dict, callBack, errorCall ) { - // both callBack and errorCall are two-argument functions - var request = new XMLHttpRequest(), - myself = this; - try { - request.open( - "GET", - (this.hasProtocol() ? '' : 'http://') - + this.url + 'SignUp' - + '&Username=' - + encodeURIComponent(username) - + '&Email=' - + encodeURIComponent(email), - true - ); - request.setRequestHeader( - "Content-Type", - "application/x-www-form-urlencoded" - ); - request.withCredentials = true; - request.onreadystatechange = function () { - if (request.readyState === 4) { - if (request.responseText) { - if (request.responseText.indexOf('ERROR') === 0) { - errorCall.call( - this, - request.responseText, - 'Signup' - ); - } else { - callBack.call( - null, - request.responseText, - 'Signup' - ); - } - } else { - errorCall.call( - null, - myself.url + 'SignUp', - localize('could not connect to:') - ); - } - } - }; - request.send(null); - } catch (err) { - errorCall.call(this, err.toString(), 'Snap!Cloud'); - } -}; + var myself = this; + var projectName = dict.projectname; + var userName = dict.username; -Cloud.prototype.getPublicProject = function ( - id, - callBack, - errorCall -) { - // id is Username=username&projectName=projectname, - // where the values are url-component encoded - // callBack is a single argument function, errorCall take two args - var request = new XMLHttpRequest(), - responseList, - myself = this; - try { - request.open( - "GET", - (this.hasProtocol() ? '' : 'http://') - + this.url + 'Public' - + '&' - + id, - true - ); - request.setRequestHeader( - "Content-Type", - "application/x-www-form-urlencoded" - ); - request.withCredentials = true; - request.onreadystatechange = function () { - if (request.readyState === 4) { - if (request.responseText) { - if (request.responseText.indexOf('ERROR') === 0) { - errorCall.call( - this, - request.responseText - ); - } else { - responseList = myself.parseResponse( - request.responseText - ); - callBack.call( - null, - responseList[0].SourceCode - ); - } - } else { - errorCall.call( - null, - myself.url + 'Public', - localize('could not connect to:') - ); - } - } - }; - request.send(null); - } catch (err) { - errorCall.call(this, err.toString(), 'Snap!Cloud'); - } -}; + if (myself.gh !== null) { + var repo = myself.gh.getRepo(userName, projectName); + var branch = repo.getBranch(); // master (default) + var media, pdata; + var br; -Cloud.prototype.resetPassword = function ( - username, - callBack, - errorCall -) { - // both callBack and errorCall are two-argument functions - var request = new XMLHttpRequest(), - myself = this; - try { - request.open( - "GET", - (this.hasProtocol() ? '' : 'http://') - + this.url + 'ResetPW' - + '&Username=' - + encodeURIComponent(username), - true - ); - request.setRequestHeader( - "Content-Type", - "application/x-www-form-urlencoded" - ); - request.withCredentials = true; - request.onreadystatechange = function () { - if (request.readyState === 4) { - if (request.responseText) { - if (request.responseText.indexOf('ERROR') === 0) { - errorCall.call( - this, - request.responseText, - 'Reset Password' - ); - } else { + branch.read('snap.xml', false).then( + function (sourceContent) { + branch.read('media.xml', false).then( // true for binary + function (mediaContent) { callBack.call( null, - request.responseText, - 'Reset Password' + sourceContent.content, + mediaContent.content ); + }, + function (error) { + errorCall.call(this, error, 'Github'); } - } else { - errorCall.call( - null, - myself.url + 'ResetPW', - localize('could not connect to:') - ); - } + ); + }, + function (error) { + errorCall.call(this, error, 'Github'); } - }; - request.send(null); - } catch (err) { - errorCall.call(this, err.toString(), 'Snap!Cloud'); - } -}; - -Cloud.prototype.connect = function ( - callBack, - errorCall -) { - // both callBack and errorCall are two-argument functions - var request = new XMLHttpRequest(), - myself = this; - try { - request.open( - "GET", - (this.hasProtocol() ? '' : 'http://') + this.url, - true - ); - request.setRequestHeader( - "Content-Type", - "application/x-www-form-urlencoded" ); - request.withCredentials = true; - request.onreadystatechange = function () { - if (request.readyState === 4) { - if (request.responseText) { - myself.api = myself.parseAPI(request.responseText); - myself.session = request.getResponseHeader('MioCracker') - .split(';')[0]; - if (myself.api.login) { - callBack.call(null, myself.api, 'Snap!Cloud'); - } else { - errorCall.call( - null, - 'connection failed' - ); - } - } else { - errorCall.call( - null, - myself.url, - localize('could not connect to:') - ); - } - } - }; - request.send(null); - } catch (err) { - errorCall.call(this, err.toString(), 'Snap!Cloud'); + } else { + errorCall.call(null, 'Please login', 'Github'); } }; - Cloud.prototype.login = function ( username, password, @@ -284,61 +103,22 @@ Cloud.prototype.login = function ( errorCall ) { var myself = this; - this.connect( - function () { - myself.rawLogin(username, password, callBack, errorCall); - myself.disconnect(); - }, - errorCall - ); -}; -Cloud.prototype.rawLogin = function ( - username, - password, - callBack, - errorCall -) { - // both callBack and errorCall are two-argument functions - var myself = this, - pwHash = hex_sha512("miosoft%20miocon," - + this.session.split('=')[1] + "," - + encodeURIComponent(username.toLowerCase()) + "," - + password // alreadey hex_sha512 hashed - ); - this.callService( - 'login', - function (response, url) { - if (myself.api.logout) { - myself.username = username; - myself.password = password; - callBack.call(null, response, url); - } else { - errorCall.call( - null, - 'Service catalog is not available,\nplease retry', - 'Connection Error:' - ); - } - }, - errorCall, - [username, pwHash] - ); -}; + myself.gh = new Octokit({ + username: username, + password: password + }); -Cloud.prototype.reconnect = function ( - callBack, - errorCall -) { - if (!(this.username && this.password)) { - this.message('You are not logged in'); - return; - } - this.login( - this.username, - this.password, - callBack, - errorCall + myself.gh.getUser().getInfo().then( + function(info) { + myself.username = username; + myself.password = password; + + callBack.call(myself); + }, + function (error) { + errorCall.call(this, error, 'Github'); + } ); }; @@ -346,6 +126,7 @@ Cloud.prototype.saveProject = function (ide, callBack, errorCall) { var myself = this, pdata, media; + var repoName = ide.projectName.replace(/[^\w-]/g, ''); // FIXME validation of project name ide.serializer.isCollectingMedia = true; pdata = ide.serializer.serialize(ide.stage); @@ -372,232 +153,111 @@ Cloud.prototype.saveProject = function (ide, callBack, errorCall) { ide.serializer.isCollectingMedia = false; ide.serializer.flushMedia(); - myself.reconnect( - function () { - myself.callService( - 'saveProject', - function (response, url) { - callBack.call(null, response, url); - myself.disconnect(); - ide.hasChangedMedia = false; - }, - errorCall, - [ - ide.projectName, - pdata, - media, - pdata.length, - media ? media.length : 0 - ] - ); - }, - errorCall - ); -}; - -Cloud.prototype.getProjectList = function (callBack, errorCall) { - var myself = this; - this.reconnect( - function () { - myself.callService( - 'getProjectList', - function (response, url) { - callBack.call(null, response, url); - myself.disconnect(); - }, - errorCall - ); - }, - errorCall - ); -}; + myself.getProjectList( + function (projects) { + var exists = false; -Cloud.prototype.changePassword = function ( - oldPW, - newPW, - callBack, - errorCall -) { - var myself = this; - this.reconnect( - function () { - myself.callService( - 'changePassword', - function (response, url) { - callBack.call(null, response, url); - myself.disconnect(); - }, - errorCall, - [oldPW, newPW] - ); - }, - errorCall - ); -}; + projects.forEach(function (project) { + if (project.ProjectName.indexOf(repoName) > -1) { + exists = true; + return; + } + }); -Cloud.prototype.logout = function (callBack, errorCall) { - this.clear(); - this.callService( - 'logout', - callBack, - errorCall - ); -}; + if (exists === false){ + myself.gh.getUser().createRepo(repoName, { // these should be discussed + 'description': 'Snap! Project', + 'has_wiki': 'false', + 'has_downloads': 'false', + 'auto_init': true, + 'license_template': 'mit' // discuss + }).then( + function () {}, + function (error) { + errorCall.call(this, error, 'Github'); + } + ); + } -Cloud.prototype.disconnect = function () { - this.callService( - 'logout', - nop, - nop - ); -}; + if (myself.gh !== null) { + var repo = myself.gh.getRepo(myself.username, ide.projectName); + var branch = repo.getBranch(); // master (default) + var message = ''; // FIXME optional: specify message -// Cloud: backend communication + var contents = { + 'snap.xml': pdata, + 'media.xml': media // may be binary + }; -Cloud.prototype.callURL = function (url, callBack, errorCall) { - // both callBack and errorCall are optional two-argument functions - var request = new XMLHttpRequest(), - myself = this; - try { - request.open('GET', url, true); - request.withCredentials = true; - request.setRequestHeader( - "Content-Type", - "application/x-www-form-urlencoded" - ); - request.setRequestHeader('MioCracker', this.session); - request.onreadystatechange = function () { - if (request.readyState === 4) { - if (request.responseText) { - var responseList = myself.parseResponse( - request.responseText - ); - callBack.call(null, responseList, url); - } else { - errorCall.call( - null, - url, - 'no response from:' + branch.writeMany(contents, message).then( + function () { + callBack.call() + }, + function (error) { + errorCall.call(this, error, 'Github'); + } ); } + }, + function (error) { + errorCall.call(null, error, 'Github'); } - }; - request.send(null); - } catch (err) { - errorCall.call(this, err.toString(), url); - } + ); }; -Cloud.prototype.callService = function ( - serviceName, - callBack, - errorCall, - args -) { - // both callBack and errorCall are optional two-argument functions - var request = new XMLHttpRequest(), - service = this.api[serviceName], - myself = this, - postDict; +Cloud.prototype.getProjectList = function (callBack, errorCall) { + var myself = this; - if (!this.session) { - errorCall.call(null, 'You are not connected', 'Cloud'); - return; - } - if (!service) { - errorCall.call( - null, - 'service ' + serviceName + ' is not available', - 'API' - ); - return; - } - if (args && args.length > 0) { - postDict = {}; - service.parameters.forEach(function (parm, idx) { - postDict[parm] = args[idx]; - }); - } - try { - request.open(service.method, service.url, true); - request.withCredentials = true; - request.setRequestHeader( - "Content-Type", - "application/x-www-form-urlencoded" - ); - request.setRequestHeader('MioCracker', this.session); - request.onreadystatechange = function () { - if (request.readyState === 4) { - var responseList = []; - if (request.responseText && - request.responseText.indexOf('ERROR') === 0) { - errorCall.call( - this, - request.responseText, - localize('Service:') + ' ' + localize(serviceName) - ); - return; - } - if (serviceName === 'login') { - myself.api = myself.parseAPI(request.responseText); + if (myself.gh !== null) { + myself.gh.getUser().getRepos().then( + function (repos) { + var snapProjects = []; + + repos.forEach(function (repo) { + if (repo.description === 'Snap! Project') { // FIXME nicer detection + var project; + + project = { + 'ProjectName': repo.name + }; + + snapProjects.push(project); + } + }); + + callBack.call(myself, snapProjects); + }, + function (error) { + errorCall.call(this, error, 'Github'); } - responseList = myself.parseResponse( - request.responseText - ); - callBack.call(null, responseList, service.url); - } - }; - request.send(this.encodeDict(postDict)); - } catch (err) { - errorCall.call(this, err.toString(), service.url); + ); + } else { + errorCall.call(myself, 'Please login', 'Github'); } }; -// Cloud: payload transformation - -Cloud.prototype.parseAPI = function (src) { - var api = {}, - services; - services = src.split(" "); - services.forEach(function (service) { - var entries = service.split("&"), - serviceDescription = {}, - parms; - entries.forEach(function (entry) { - var pair = entry.split("="), - key = decodeURIComponent(pair[0]).toLowerCase(), - val = decodeURIComponent(pair[1]); - if (key === "service") { - api[val] = serviceDescription; - } else if (key === "parameters") { - parms = val.split(","); - if (!(parms.length === 1 && !parms[0])) { - serviceDescription.parameters = parms; - } - } else { - serviceDescription[key] = val; - } - }); - }); - return api; +Cloud.prototype.logout = function (callBack) { + this.clear(); }; +// Cloud: backend communication + Cloud.prototype.parseResponse = function (src) { var ans = [], lines; if (!src) {return ans; } lines = src.split(" "); lines.forEach(function (service) { - var entries = service.split("&"), - dict = {}; - entries.forEach(function (entry) { - var pair = entry.split("="), - key = decodeURIComponent(pair[0]), - val = decodeURIComponent(pair[1]); - dict[key] = val; + var entries = service.split("&"), + dict = {}; + entries.forEach(function (entry) { + var pair = entry.split("="), + key = decodeURIComponent(pair[0]), + val = decodeURIComponent(pair[1]); + dict[key] = val; + }); + ans.push(dict); }); - ans.push(dict); - }); return ans; }; @@ -606,8 +266,8 @@ Cloud.prototype.parseDict = function (src) { if (!src) {return dict; } src.split("&").forEach(function (entry) { var pair = entry.split("="), - key = decodeURIComponent(pair[0]), - val = decodeURIComponent(pair[1]); + key = decodeURIComponent(pair[0]), + val = decodeURIComponent(pair[1]); dict[key] = val; }); return dict; @@ -619,10 +279,8 @@ Cloud.prototype.encodeDict = function (dict) { key; if (!dict) {return null; } for (key in dict) { - if (dict.hasOwnProperty(key)) { - pair = encodeURIComponent(key) - + '=' - + encodeURIComponent(dict[key]); + if (dict.hasOwnProperty(key)) { + pair = encodeURIComponent(key) + '=' + encodeURIComponent(dict[key]); if (str.length > 0) { str += '&'; } @@ -631,9 +289,3 @@ Cloud.prototype.encodeDict = function (dict) { } return str; }; - -// Cloud: user messages (to be overridden) - -Cloud.prototype.message = function (string) { - alert(string); -}; @@ -69,7 +69,7 @@ SpeechBubbleMorph*/ // Global stuff //////////////////////////////////////////////////////// -modules.gui = '2014-July-29'; +modules.gui = '2014-July-30'; // Declarations @@ -243,8 +243,8 @@ IDE_Morph.prototype.openIn = function (world) { if (usr) { usr = SnapCloud.parseResponse(usr)[0]; if (usr) { - SnapCloud.username = usr.username || null; - SnapCloud.password = usr.password || null; + SnapCloud.login(usr.username, usr.password, + function() {}, myself.cloudError()); } } } @@ -338,12 +338,11 @@ IDE_Morph.prototype.openIn = function (world) { this.parent.add(this.shield); myself.showMessage('Fetching project\nfrom the cloud...'); - // make sure to lowercase the username dict = SnapCloud.parseDict(location.hash.substr(9)); - dict.Username = dict.Username.toLowerCase(); + dict.Username = dict.Username; SnapCloud.getPublicProject( - SnapCloud.encodeDict(dict), + dict, function (projectData) { var msg; myself.nextSteps([ @@ -375,8 +374,6 @@ IDE_Morph.prototype.openIn = function (world) { urlLanguage = location.hash.substr(6); this.setLanguage(urlLanguage); this.loadNewProject = true; - } else if (location.hash.substr(0, 7) === '#signup') { - this.createCloudAccount(); } } @@ -1935,37 +1932,16 @@ IDE_Morph.prototype.cloudMenu = function () { shiftClicked = (world.currentKey === 16); menu = new MenuMorph(this); - if (shiftClicked) { - menu.addItem( - 'url...', - 'setCloudURL', - null, - new Color(100, 0, 0) - ); - menu.addLine(); - } if (!SnapCloud.username) { menu.addItem( 'Login...', 'initializeCloud' ); - menu.addItem( - 'Signup...', - 'createCloudAccount' - ); - menu.addItem( - 'Reset Password...', - 'resetCloudPassword' - ); } else { menu.addItem( localize('Logout') + ' ' + SnapCloud.username, 'logout' ); - menu.addItem( - 'Change Password...', - 'changeCloudPassword' - ); } if (shiftClicked) { menu.addLine(); @@ -2011,53 +1987,6 @@ IDE_Morph.prototype.cloudMenu = function () { null, new Color(100, 0, 0) ); - menu.addLine(); - menu.addItem( - 'open shared project from cloud...', - function () { - myself.prompt('Author name…', function (usr) { - myself.prompt('Project name...', function (prj) { - var id = 'Username=' + - encodeURIComponent(usr.toLowerCase()) + - '&ProjectName=' + - encodeURIComponent(prj); - myself.showMessage( - 'Fetching project\nfrom the cloud...' - ); - SnapCloud.getPublicProject( - id, - function (projectData) { - var msg; - if (!Process.prototype.isCatchingErrors) { - window.open( - 'data:text/xml,' + projectData - ); - } - myself.nextSteps([ - function () { - msg = myself.showMessage( - 'Opening project...' - ); - }, - function () { - myself.rawOpenCloudDataString( - projectData - ); - }, - function () { - msg.destroy(); - } - ]); - }, - myself.cloudError() - ); - - }, null, 'project'); - }, null, 'project'); - }, - null, - new Color(100, 0, 0) - ); } menu.popup(world, pos); }; @@ -3623,17 +3552,17 @@ IDE_Morph.prototype.initializeCloud = function () { new DialogBoxMorph( null, function (user) { - var pwh = hex_sha512(user.password), + var pw = user.password, str; SnapCloud.login( user.username, - pwh, + pw, function () { if (user.choice) { str = SnapCloud.encodeDict( { username: user.username, - password: pwh + password: pw } ); localStorage['-snap-user'] = str; @@ -3658,128 +3587,14 @@ IDE_Morph.prototype.initializeCloud = function () { ); }; -IDE_Morph.prototype.createCloudAccount = function () { - var myself = this, - world = this.world(); -/* - // force-logout, commented out for now: - delete localStorage['-snap-user']; - SnapCloud.clear(); -*/ - new DialogBoxMorph( - null, - function (user) { - SnapCloud.signup( - user.username, - user.email, - function (txt, title) { - new DialogBoxMorph().inform( - title, - txt + - '.\n\nAn e-mail with your password\n' + - 'has been sent to the address provided', - world, - myself.cloudIcon(null, new Color(0, 180, 0)) - ); - }, - myself.cloudError() - ); - } - ).withKey('cloudsignup').promptCredentials( - 'Sign up', - 'signup', - 'http://snap.berkeley.edu/tos.html', - 'Terms of Service...', - 'http://snap.berkeley.edu/privacy.html', - 'Privacy...', - 'I have read and agree\nto the Terms of Service', - world, - myself.cloudIcon(), - myself.cloudMsg - ); -}; - -IDE_Morph.prototype.resetCloudPassword = function () { - var myself = this, - world = this.world(); -/* - // force-logout, commented out for now: - delete localStorage['-snap-user']; - SnapCloud.clear(); -*/ - new DialogBoxMorph( - null, - function (user) { - SnapCloud.resetPassword( - user.username, - function (txt, title) { - new DialogBoxMorph().inform( - title, - txt + - '.\n\nAn e-mail with a link to\n' + - 'reset your password\n' + - 'has been sent to the address provided', - world, - myself.cloudIcon(null, new Color(0, 180, 0)) - ); - }, - myself.cloudError() - ); - } - ).withKey('cloudresetpassword').promptCredentials( - 'Reset password', - 'resetPassword', - null, - null, - null, - null, - null, - world, - myself.cloudIcon(), - myself.cloudMsg - ); -}; - -IDE_Morph.prototype.changeCloudPassword = function () { - var myself = this, - world = this.world(); - new DialogBoxMorph( - null, - function (user) { - SnapCloud.changePassword( - user.oldpassword, - user.password, - function () { - myself.logout(); - myself.showMessage('password has been changed.', 2); - }, - myself.cloudError() - ); - } - ).withKey('cloudpassword').promptCredentials( - 'Change Password', - 'changePassword', - null, - null, - null, - null, - null, - world, - myself.cloudIcon(), - myself.cloudMsg - ); -}; - IDE_Morph.prototype.logout = function () { var myself = this; delete localStorage['-snap-user']; SnapCloud.logout( function () { - SnapCloud.clear(); myself.showMessage('disconnected.', 2); }, function () { - SnapCloud.clear(); myself.showMessage('disconnected.', 2); } ); @@ -4010,31 +3825,6 @@ IDE_Morph.prototype.cloudIcon = function (height, color) { return icon; }; -IDE_Morph.prototype.setCloudURL = function () { - new DialogBoxMorph( - null, - function (url) { - SnapCloud.url = url; - } - ).withKey('cloudURL').prompt( - 'Cloud URL', - SnapCloud.url, - this.world(), - null, - { - 'Snap!Cloud' : - 'https://snapcloud.miosoft.com/miocon/app/' + - 'login?_app=SnapCloud', - 'local network lab' : - '192.168.2.107:8087/miocon/app/login?_app=SnapCloud', - 'local network office' : - '192.168.186.146:8087/miocon/app/login?_app=SnapCloud', - 'localhost dev' : - 'localhost/miocon/app/login?_app=SnapCloud' - } - ); -}; - // IDE_Morph synchronous Http data fetching IDE_Morph.prototype.getURL = function (url) { @@ -4142,8 +3932,6 @@ ProjectDialogMorph.prototype.init = function (ide, task) { this.notesText = null; this.notesField = null; this.deleteButton = null; - this.shareButton = null; - this.unshareButton = null; // initialize inherited properties: ProjectDialogMorph.uber.init.call( @@ -4283,10 +4071,6 @@ ProjectDialogMorph.prototype.buildContents = function () { this.addButton('saveProject', 'Save'); this.action = 'saveProject'; } - this.shareButton = this.addButton('shareProject', 'Share'); - this.unshareButton = this.addButton('unshareProject', 'Unshare'); - this.shareButton.hide(); - this.unshareButton.hide(); this.deleteButton = this.addButton('deleteProject', 'Delete'); this.addButton('cancel', 'Cancel'); @@ -4516,8 +4300,6 @@ ProjectDialogMorph.prototype.setSource = function (source) { }; } this.body.add(this.listField); - this.shareButton.hide(); - this.unshareButton.hide(); if (this.source === 'local') { this.deleteButton.show(); } else { // examples @@ -4594,12 +4376,7 @@ ProjectDialogMorph.prototype.installCloudProjectList = function (pl) { function (element) { return element.ProjectName; } : null, - [ // format: display shared project names bold - [ - 'bold', - function (proj) {return proj.Public === 'true'; } - ] - ], + [], function () {myself.ok(); } ); this.fixListFieldItemColors(); @@ -4635,20 +4412,11 @@ ProjectDialogMorph.prototype.installCloudProjectList = function (pl) { myself.preview.rightCenter().add(new Point(2, 0)) ); } - if (item.Public === 'true') { - myself.shareButton.hide(); - myself.unshareButton.show(); - } else { - myself.unshareButton.hide(); - myself.shareButton.show(); - } myself.buttons.fixLayout(); myself.fixLayout(); myself.edit(); }; this.body.add(this.listField); - this.shareButton.show(); - this.unshareButton.hide(); this.deleteButton.show(); this.buttons.fixLayout(); this.fixLayout(); @@ -4700,24 +4468,12 @@ ProjectDialogMorph.prototype.openCloudProject = function (project) { ProjectDialogMorph.prototype.rawOpenCloudProject = function (proj) { var myself = this; - SnapCloud.reconnect( - function () { - SnapCloud.callService( - 'getProject', - function (response) { - SnapCloud.disconnect(); - myself.ide.source = 'cloud'; - myself.ide.droppedText(response[0].SourceCode); - if (proj.Public === 'true') { - location.hash = '#present:Username=' + - encodeURIComponent(SnapCloud.username) + - '&ProjectName=' + - encodeURIComponent(proj.ProjectName); - } - }, - myself.ide.cloudError(), - [proj.ProjectName] - ); + SnapCloud.getProject( + {'projectname': proj.ProjectName, 'username': SnapCloud.username}, + function (code, media) { + myself.ide.source = 'cloud'; + myself.ide.droppedText(media); + myself.ide.droppedText(code); }, myself.ide.cloudError() ); @@ -4791,141 +4547,6 @@ ProjectDialogMorph.prototype.saveCloudProject = function () { this.destroy(); }; -ProjectDialogMorph.prototype.deleteProject = function () { - var myself = this, - proj, - idx, - name; - - if (this.source === 'cloud') { - proj = this.listField.selected; - if (proj) { - this.ide.confirm( - localize( - 'Are you sure you want to delete' - ) + '\n"' + proj.ProjectName + '"?', - 'Delete Project', - function () { - SnapCloud.reconnect( - function () { - SnapCloud.callService( - 'deleteProject', - function () { - SnapCloud.disconnect(); - myself.ide.hasChangedMedia = true; - idx = myself.projectList.indexOf(proj); - myself.projectList.splice(idx, 1); - myself.installCloudProjectList( - myself.projectList - ); // refresh list - }, - myself.ide.cloudError(), - [proj.ProjectName] - ); - }, - myself.ide.cloudError() - ); - } - ); - } - } else { // 'local, examples' - if (this.listField.selected) { - name = this.listField.selected.name; - this.ide.confirm( - localize( - 'Are you sure you want to delete' - ) + '\n"' + name + '"?', - 'Delete Project', - function () { - delete localStorage['-snap-project-' + name]; - myself.setSource(myself.source); // refresh list - } - ); - } - } -}; - -ProjectDialogMorph.prototype.shareProject = function () { - var myself = this, - proj = this.listField.selected, - entry = this.listField.active; - - if (proj) { - this.ide.confirm( - localize( - 'Are you sure you want to publish' - ) + '\n"' + proj.ProjectName + '"?', - 'Share Project', - function () { - myself.ide.showMessage('sharing\nproject...'); - SnapCloud.reconnect( - function () { - SnapCloud.callService( - 'publishProject', - function () { - SnapCloud.disconnect(); - proj.Public = 'true'; - myself.unshareButton.show(); - myself.shareButton.hide(); - entry.label.isBold = true; - entry.label.drawNew(); - entry.label.changed(); - myself.buttons.fixLayout(); - myself.drawNew(); - myself.ide.showMessage('shared.', 2); - }, - myself.ide.cloudError(), - [proj.ProjectName] - ); - }, - myself.ide.cloudError() - ); - } - ); - } -}; - -ProjectDialogMorph.prototype.unshareProject = function () { - var myself = this, - proj = this.listField.selected, - entry = this.listField.active; - - - if (proj) { - this.ide.confirm( - localize( - 'Are you sure you want to unpublish' - ) + '\n"' + proj.ProjectName + '"?', - 'Unshare Project', - function () { - myself.ide.showMessage('unsharing\nproject...'); - SnapCloud.reconnect( - function () { - SnapCloud.callService( - 'unpublishProject', - function () { - SnapCloud.disconnect(); - proj.Public = 'false'; - myself.shareButton.show(); - myself.unshareButton.hide(); - entry.label.isBold = false; - entry.label.drawNew(); - entry.label.changed(); - myself.buttons.fixLayout(); - myself.drawNew(); - myself.ide.showMessage('unshared.', 2); - }, - myself.ide.cloudError(), - [proj.ProjectName] - ); - }, - myself.ide.cloudError() - ); - } - ); - } -}; - ProjectDialogMorph.prototype.edit = function () { if (this.nameField) { this.nameField.edit(); diff --git a/octokit/octokit.js b/octokit/octokit.js new file mode 100644 index 0000000..40d8576 --- /dev/null +++ b/octokit/octokit.js @@ -0,0 +1,1465 @@ +(function() { + var Octokit, Promise, XMLHttpRequest, allPromises, createGlobalAndAMD, encode, err, injector, makeOctokit, newPromise, _, _ref, + _this = this, + __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + __slice = [].slice; + + _ = {}; + + _.isEmpty = function(object) { + return Object.keys(object).length === 0; + }; + + _.isArray = function(object) { + return !!(object != null ? object.slice : void 0); + }; + + _.defaults = function(object, values) { + var key, _i, _len, _ref, _results; + _ref = Object.keys(values); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + key = _ref[_i]; + _results.push((function(key) { + return object[key] != null ? object[key] : object[key] = values[key]; + })(key)); + } + return _results; + }; + + _.each = function(object, fn) { + var arr, key, _i, _len, _ref, _results; + if (!object) { + return; + } + if (_.isArray(object)) { + object.forEach(fn); + } + arr = []; + _ref = Object.keys(object); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + key = _ref[_i]; + _results.push((function(key) { + return fn(object[key]); + })(key)); + } + return _results; + }; + + _.pairs = function(object) { + var arr, key, _fn, _i, _len, _ref; + arr = []; + _ref = Object.keys(object); + _fn = function(key) { + return arr.push([key, object[key]]); + }; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + key = _ref[_i]; + _fn(key); + } + return arr; + }; + + _.map = function(object, fn) { + var arr, key, _fn, _i, _len, _ref; + if (_.isArray(object)) { + return object.map(fn); + } + arr = []; + _ref = Object.keys(object); + _fn = function(key) { + return arr.push(fn(object[key])); + }; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + key = _ref[_i]; + _fn(key); + } + return arr; + }; + + _.last = function(object, n) { + var len; + len = object.length; + return object.slice(len - n, len); + }; + + _.select = function(object, fn) { + return object.filter(fn); + }; + + _.extend = function(object, template) { + var key, _fn, _i, _len, _ref; + _ref = Object.keys(template); + _fn = function(key) { + return object[key] = template[key]; + }; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + key = _ref[_i]; + _fn(key); + } + return object; + }; + + _.toArray = function(object) { + return Array.prototype.slice.call(object); + }; + + makeOctokit = function(newPromise, allPromises, XMLHttpRequest, base64encode, userAgent) { + var Octokit, ajax, rejectedPromise, resolvedPromise; + ajax = function(options) { + return newPromise(function(resolve, reject) { + var name, value, xhr, _ref; + xhr = new XMLHttpRequest(); + xhr.dataType = options.dataType; + if (typeof xhr.overrideMimeType === "function") { + xhr.overrideMimeType(options.mimeType); + } + xhr.open(options.type, options.url); + if (options.data && 'GET' !== options.type) { + xhr.setRequestHeader('Content-Type', options.contentType); + } + _ref = options.headers; + for (name in _ref) { + value = _ref[name]; + xhr.setRequestHeader(name, value); + } + xhr.onreadystatechange = function() { + var _name, _ref1; + if (4 === xhr.readyState) { + if ((_ref1 = options.statusCode) != null) { + if (typeof _ref1[_name = xhr.status] === "function") { + _ref1[_name](); + } + } + if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) { + return resolve(xhr); + } else { + return reject(xhr); + } + } + }; + return xhr.send(options.data); + }); + }; + resolvedPromise = function(val) { + return newPromise(function(resolve, reject) { + return resolve(val); + }); + }; + rejectedPromise = function(err) { + return newPromise(function(resolve, reject) { + return reject(err); + }); + }; + Octokit = (function() { + function Octokit(clientOptions) { + var AuthenticatedUser, Branch, ETagResponse, Gist, GitRepo, Organization, Repository, Team, User, clearCache, getCache, notifyEnd, notifyStart, setCache, toQueryString, _cachedETags, _client, _listeners, _request; + if (clientOptions == null) { + clientOptions = {}; + } + _.defaults(clientOptions, { + rootURL: 'https://api.github.com', + useETags: true, + usePostInsteadOfPatch: false + }); + _client = this; + _listeners = []; + ETagResponse = (function() { + function ETagResponse(eTag, data, status) { + this.eTag = eTag; + this.data = data; + this.status = status; + } + + return ETagResponse; + + })(); + _cachedETags = {}; + notifyStart = function(promise, path) { + return typeof promise.notify === "function" ? promise.notify({ + type: 'start', + path: path + }) : void 0; + }; + notifyEnd = function(promise, path) { + return typeof promise.notify === "function" ? promise.notify({ + type: 'end', + path: path + }) : void 0; + }; + _request = function(method, path, data, options) { + var auth, headers, mimeType, promise; + if (options == null) { + options = { + raw: false, + isBase64: false, + isBoolean: false + }; + } + if ('PATCH' === method && clientOptions.usePostInsteadOfPatch) { + method = 'POST'; + } + if (!/^http/.test(path)) { + path = "" + clientOptions.rootURL + path; + } + mimeType = void 0; + if (options.isBase64) { + mimeType = 'text/plain; charset=x-user-defined'; + } + headers = { + 'Accept': 'application/vnd.github.raw' + }; + if (userAgent) { + headers['User-Agent'] = userAgent; + } + if (path in _cachedETags) { + headers['If-None-Match'] = _cachedETags[path].eTag; + } else { + headers['If-Modified-Since'] = 'Thu, 01 Jan 1970 00:00:00 GMT'; + } + if (clientOptions.token || (clientOptions.username && clientOptions.password)) { + if (clientOptions.token) { + auth = "token " + clientOptions.token; + } else { + auth = 'Basic ' + base64encode("" + clientOptions.username + ":" + clientOptions.password); + } + headers['Authorization'] = auth; + } + promise = newPromise(function(resolve, reject) { + var ajaxConfig, always, onError, xhrPromise, + _this = this; + ajaxConfig = { + url: path, + type: method, + contentType: 'application/json', + mimeType: mimeType, + headers: headers, + processData: false, + data: !options.raw && data && JSON.stringify(data) || data, + dataType: !options.raw ? 'json' : void 0 + }; + if (options.isBoolean) { + ajaxConfig.statusCode = { + 204: function() { + return resolve(true); + }, + 404: function() { + return resolve(false); + } + }; + } + xhrPromise = ajax(ajaxConfig); + always = function(jqXHR) { + var listener, rateLimit, rateLimitRemaining, _i, _len, _results; + notifyEnd(_this, path); + rateLimit = parseFloat(jqXHR.getResponseHeader('X-RateLimit-Limit')); + rateLimitRemaining = parseFloat(jqXHR.getResponseHeader('X-RateLimit-Remaining')); + _results = []; + for (_i = 0, _len = _listeners.length; _i < _len; _i++) { + listener = _listeners[_i]; + _results.push(listener(rateLimitRemaining, rateLimit, method, path, data, options)); + } + return _results; + }; + xhrPromise.then(function(jqXHR) { + var converted, eTag, eTagResponse, i, links, valOptions, _i, _ref; + always(jqXHR); + if (304 === jqXHR.status) { + if (clientOptions.useETags && _cachedETags[path]) { + eTagResponse = _cachedETags[path]; + return resolve(eTagResponse.data, eTagResponse.status, jqXHR); + } else { + return resolve(jqXHR.responseText, status, jqXHR); + } + } else if (204 === jqXHR.status && options.isBoolean) { + return resolve(true, status, jqXHR); + } else { + if (jqXHR.responseText && 'json' === ajaxConfig.dataType) { + data = JSON.parse(jqXHR.responseText); + valOptions = {}; + links = jqXHR.getResponseHeader('Link'); + _.each(links != null ? links.split(',') : void 0, function(part) { + var discard, href, rel, _ref; + _ref = part.match(/<([^>]+)>;\ rel="([^"]+)"/), discard = _ref[0], href = _ref[1], rel = _ref[2]; + return valOptions["" + rel + "Page"] = function() { + return _request('GET', href, null, options); + }; + }); + _.extend(data, valOptions); + } else { + data = jqXHR.responseText; + } + if ('GET' === method && options.isBase64) { + converted = ''; + for (i = _i = 0, _ref = data.length; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) { + converted += String.fromCharCode(data.charCodeAt(i) & 0xff); + } + data = converted; + } + if ('GET' === method && jqXHR.getResponseHeader('ETag') && clientOptions.useETags) { + eTag = jqXHR.getResponseHeader('ETag'); + _cachedETags[path] = new ETagResponse(eTag, data, jqXHR.status); + } + return resolve(data, jqXHR.status, jqXHR); + } + }); + onError = function(jqXHR) { + var err, json; + always(jqXHR); + if (options.isBoolean && 404 === jqXHR.status) { + return resolve(false); + } else { + if (jqXHR.getResponseHeader('Content-Type') !== 'application/json; charset=utf-8') { + err = new Error(jqXHR.responseText); + err['status'] = jqXHR.status; + err['__jqXHR'] = jqXHR; + return reject(err); + } else { + err = new Error("Github error: " + jqXHR.responseText); + if (jqXHR.responseText) { + json = JSON.parse(jqXHR.responseText); + } else { + json = ''; + } + err['error'] = json; + err['status'] = jqXHR.status; + err['__jqXHR'] = jqXHR; + return reject(err); + } + } + }; + return (typeof xhrPromise["catch"] === "function" ? xhrPromise["catch"](onError) : void 0) || xhrPromise.fail(onError); + }); + notifyStart(promise, path); + return promise; + }; + toQueryString = function(options) { + var params; + if (_.isEmpty(options)) { + return ''; + } + params = []; + _.each(_.pairs(options), function(_arg) { + var key, value; + key = _arg[0], value = _arg[1]; + return params.push("" + key + "=" + (encodeURIComponent(value))); + }); + return "?" + (params.join('&')); + }; + this.clearCache = clearCache = function() { + return _cachedETags = {}; + }; + this.getCache = getCache = function() { + return _cachedETags; + }; + this.setCache = setCache = function(cachedETags) { + if (!(cachedETags !== null && typeof cachedETags === 'object')) { + throw new Error('BUG: argument of method "setCache" should be an object'); + } else { + return _cachedETags = cachedETags; + } + }; + this.onRateLimitChanged = function(listener) { + return _listeners.push(listener); + }; + this.getZen = function() { + return _request('GET', '/zen', null, { + raw: true + }); + }; + this.getAllUsers = function(since) { + var options; + if (since == null) { + since = null; + } + options = {}; + if (since) { + options.since = since; + } + return _request('GET', '/users', options); + }; + this.getOrgRepos = function(orgName, type) { + if (type == null) { + type = 'all'; + } + return _request('GET', "/orgs/" + orgName + "/repos?type=" + type + "&per_page=1000&sort=updated&direction=desc", null); + }; + this.getPublicGists = function(since) { + var getDate, options; + if (since == null) { + since = null; + } + options = null; + getDate = function(time) { + if (Date === time.constructor) { + return time.toISOString(); + } + return time; + }; + if (since) { + options = { + since: getDate(since) + }; + } + return _request('GET', '/gists/public', options); + }; + this.getPublicEvents = function() { + return _request('GET', '/events', null); + }; + this.getNotifications = function(options) { + var getDate, queryString; + if (options == null) { + options = {}; + } + getDate = function(time) { + if (Date === time.constructor) { + return time.toISOString(); + } + return time; + }; + if (options.since) { + options.since = getDate(options.since); + } + queryString = toQueryString(options); + return _request('GET', "/notifications" + queryString, null); + }; + User = (function() { + function User(_username) { + var _cachedInfo, _rootPath; + if (_username == null) { + _username = null; + } + if (_username) { + _rootPath = "/users/" + _username; + } else { + _rootPath = "/user"; + } + _cachedInfo = null; + this.getInfo = function(force) { + if (force == null) { + force = false; + } + if (force) { + _cachedInfo = null; + } + if (_cachedInfo) { + return resolvedPromise(_cachedInfo); + } + return _request('GET', "" + _rootPath, null).then(function(info) { + return _cachedInfo = info; + }); + }; + this.getRepos = function(type, sort, direction) { + if (type == null) { + type = 'all'; + } + if (sort == null) { + sort = 'pushed'; + } + if (direction == null) { + direction = 'desc'; + } + return _request('GET', "" + _rootPath + "/repos?type=" + type + "&per_page=1000&sort=" + sort + "&direction=" + direction, null); + }; + this.getOrgs = function() { + return _request('GET', "" + _rootPath + "/orgs", null); + }; + this.getGists = function() { + return _request('GET', "" + _rootPath + "/gists", null); + }; + this.getFollowers = function() { + return _request('GET', "" + _rootPath + "/followers", null); + }; + this.getFollowing = function() { + return _request('GET', "" + _rootPath + "/following", null); + }; + this.isFollowing = function(user) { + return _request('GET', "" + _rootPath + "/following/" + user, null, { + isBoolean: true + }); + }; + this.getPublicKeys = function() { + return _request('GET', "" + _rootPath + "/keys", null); + }; + this.getReceivedEvents = function(onlyPublic) { + var isPublic; + if (!_username) { + throw new Error('BUG: This does not work for authenticated users yet!'); + } + isPublic = ''; + if (onlyPublic) { + isPublic = '/public'; + } + return _request('GET', "/users/" + _username + "/received_events" + isPublic, null); + }; + this.getEvents = function(onlyPublic) { + var isPublic; + if (!_username) { + throw new Error('BUG: This does not work for authenticated users yet!'); + } + isPublic = ''; + if (onlyPublic) { + isPublic = '/public'; + } + return _request('GET', "/users/" + _username + "/events" + isPublic, null); + }; + } + + return User; + + })(); + AuthenticatedUser = (function(_super) { + __extends(AuthenticatedUser, _super); + + function AuthenticatedUser() { + AuthenticatedUser.__super__.constructor.call(this); + this.updateInfo = function(options) { + return _request('PATCH', '/user', options); + }; + this.getGists = function() { + return _request('GET', '/gists', null); + }; + this.follow = function(username) { + return _request('PUT', "/user/following/" + username, null); + }; + this.unfollow = function(username) { + return _request('DELETE', "/user/following/" + username, null); + }; + this.getEmails = function() { + return _request('GET', '/user/emails', null); + }; + this.addEmail = function(emails) { + if (!_.isArray(emails)) { + emails = [emails]; + } + return _request('POST', '/user/emails', emails); + }; + this.addEmail = function(emails) { + if (!_.isArray(emails)) { + emails = [emails]; + } + return _request('DELETE', '/user/emails', emails); + }; + this.getPublicKey = function(id) { + return _request('GET', "/user/keys/" + id, null); + }; + this.addPublicKey = function(title, key) { + return _request('POST', "/user/keys", { + title: title, + key: key + }); + }; + this.updatePublicKey = function(id, options) { + return _request('PATCH', "/user/keys/" + id, options); + }; + this.createRepo = function(name, options) { + if (options == null) { + options = {}; + } + options.name = name; + return _request('POST', "/user/repos", options); + }; + this.getReceivedEvents = function(username, page) { + var currentPage; + if (page == null) { + page = 1; + } + currentPage = '?page=' + page; + return _request('GET', '/users/' + username + '/received_events' + currentPage, null); + }; + } + + return AuthenticatedUser; + + })(User); + Team = (function() { + function Team(id) { + this.id = id; + this.getInfo = function() { + return _request('GET', "/teams/" + this.id, null); + }; + this.updateTeam = function(options) { + return _request('PATCH', "/teams/" + this.id, options); + }; + this.remove = function() { + return _request('DELETE', "/teams/" + this.id); + }; + this.getMembers = function() { + return _request('GET', "/teams/" + this.id + "/members"); + }; + this.isMember = function(user) { + return _request('GET', "/teams/" + this.id + "/members/" + user, null, { + isBoolean: true + }); + }; + this.addMember = function(user) { + return _request('PUT', "/teams/" + this.id + "/members/" + user); + }; + this.removeMember = function(user) { + return _request('DELETE', "/teams/" + this.id + "/members/" + user); + }; + this.getRepos = function() { + return _request('GET', "/teams/" + this.id + "/repos"); + }; + this.addRepo = function(orgName, repoName) { + return _request('PUT', "/teams/" + this.id + "/repos/" + orgName + "/" + repoName); + }; + this.removeRepo = function(orgName, repoName) { + return _request('DELETE', "/teams/" + this.id + "/repos/" + orgName + "/" + repoName); + }; + } + + return Team; + + })(); + Organization = (function() { + function Organization(name) { + this.name = name; + this.getInfo = function() { + return _request('GET', "/orgs/" + this.name, null); + }; + this.updateInfo = function(options) { + return _request('PATCH', "/orgs/" + this.name, options); + }; + this.getTeams = function() { + return _request('GET', "/orgs/" + this.name + "/teams", null); + }; + this.createTeam = function(name, repoNames, permission) { + var options; + if (repoNames == null) { + repoNames = null; + } + if (permission == null) { + permission = 'pull'; + } + options = { + name: name, + permission: permission + }; + if (repoNames) { + options.repo_names = repoNames; + } + return _request('POST', "/orgs/" + this.name + "/teams", options); + }; + this.getMembers = function() { + return _request('GET', "/orgs/" + this.name + "/members", null); + }; + this.isMember = function(user) { + return _request('GET', "/orgs/" + this.name + "/members/" + user, null, { + isBoolean: true + }); + }; + this.removeMember = function(user) { + return _request('DELETE', "/orgs/" + this.name + "/members/" + user, null); + }; + this.createRepo = function(name, options) { + if (options == null) { + options = {}; + } + options.name = name; + return _request('POST', "/orgs/" + this.name + "/repos", options); + }; + this.getRepos = function() { + return _request('GET', "/orgs/" + this.name + "/repos?type=all", null); + }; + } + + return Organization; + + })(); + GitRepo = (function() { + function GitRepo(repoUser, repoName) { + var _repoPath; + this.repoUser = repoUser; + this.repoName = repoName; + _repoPath = "/repos/" + this.repoUser + "/" + this.repoName; + this.deleteRepo = function() { + return _request('DELETE', "" + _repoPath); + }; + this._updateTree = function(branch) { + return this.getRef("heads/" + branch); + }; + this.getRef = function(ref) { + var _this = this; + return _request('GET', "" + _repoPath + "/git/refs/" + ref, null).then(function(res) { + return res.object.sha; + }); + }; + this.createRef = function(options) { + return _request('POST', "" + _repoPath + "/git/refs", options); + }; + this.deleteRef = function(ref) { + return _request('DELETE', "" + _repoPath + "/git/refs/" + ref, this.options); + }; + this.getBranches = function() { + var _this = this; + return _request('GET', "" + _repoPath + "/git/refs/heads", null).then(function(heads) { + return _.map(heads, function(head) { + return _.last(head.ref.split("/")); + }); + }); + }; + this.getBlob = function(sha, isBase64) { + return _request('GET', "" + _repoPath + "/git/blobs/" + sha, null, { + raw: true, + isBase64: isBase64 + }); + }; + this.getSha = function(branch, path) { + var _this = this; + if (path === '') { + return this.getRef("heads/" + branch); + } + return this.getTree(branch, { + recursive: true + }).then(function(tree) { + var file; + file = _.select(tree, function(file) { + return file.path === path; + })[0]; + if (file != null ? file.sha : void 0) { + return file != null ? file.sha : void 0; + } + return rejectedPromise({ + message: 'SHA_NOT_FOUND' + }); + }); + }; + this.getContents = function(path, sha) { + var queryString, + _this = this; + if (sha == null) { + sha = null; + } + queryString = ''; + if (sha !== null) { + queryString = toQueryString({ + ref: sha + }); + } + return _request('GET', "" + _repoPath + "/contents/" + path + queryString, null, { + raw: true + }).then(function(contents) { + return contents; + }); + }; + this.removeFile = function(path, message, sha, branch) { + var params; + params = { + message: message, + sha: sha, + branch: branch + }; + return _request('DELETE', "" + _repoPath + "/contents/" + path, params, null); + }; + this.getTree = function(tree, options) { + var queryString, + _this = this; + if (options == null) { + options = null; + } + queryString = toQueryString(options); + return _request('GET', "" + _repoPath + "/git/trees/" + tree + queryString, null).then(function(res) { + return res.tree; + }); + }; + this.postBlob = function(content, isBase64) { + var _this = this; + if (typeof content === 'string') { + if (isBase64) { + content = base64encode(content); + } + content = { + content: content, + encoding: 'utf-8' + }; + } + if (isBase64) { + content.encoding = 'base64'; + } + return _request('POST', "" + _repoPath + "/git/blobs", content).then(function(res) { + return res.sha; + }); + }; + this.updateTreeMany = function(baseTree, newTree) { + var data, + _this = this; + data = { + base_tree: baseTree, + tree: newTree + }; + return _request('POST', "" + _repoPath + "/git/trees", data).then(function(res) { + return res.sha; + }); + }; + this.postTree = function(tree) { + var _this = this; + return _request('POST', "" + _repoPath + "/git/trees", { + tree: tree + }).then(function(res) { + return res.sha; + }); + }; + this.commit = function(parents, tree, message) { + var data; + if (!_.isArray(parents)) { + parents = [parents]; + } + data = { + message: message, + parents: parents, + tree: tree + }; + return _request('POST', "" + _repoPath + "/git/commits", data).then(function(commit) { + return commit.sha; + }); + }; + this.updateHead = function(head, commit, force) { + var options; + if (force == null) { + force = false; + } + options = { + sha: commit + }; + if (force) { + options.force = true; + } + return _request('PATCH', "" + _repoPath + "/git/refs/heads/" + head, options); + }; + this.getCommit = function(sha) { + return _request('GET', "" + _repoPath + "/commits/" + sha, null); + }; + this.getCommits = function(options) { + var getDate, queryString; + if (options == null) { + options = {}; + } + options = _.extend({}, options); + getDate = function(time) { + if (Date === time.constructor) { + return time.toISOString(); + } + return time; + }; + if (options.since) { + options.since = getDate(options.since); + } + if (options.until) { + options.until = getDate(options.until); + } + queryString = toQueryString(options); + return _request('GET', "" + _repoPath + "/commits" + queryString, null); + }; + } + + return GitRepo; + + })(); + Branch = (function() { + function Branch(git, getRef) { + var _getRef, _git; + _git = git; + _getRef = getRef || function() { + throw new Error('BUG: No way to fetch branch ref!'); + }; + this.getCommit = function(sha) { + return _git.getCommit(sha); + }; + this.getCommits = function(options) { + if (options == null) { + options = {}; + } + options = _.extend({}, options); + return _getRef().then(function(branch) { + options.sha = branch; + return _git.getCommits(options); + }); + }; + this.createBranch = function(newBranchName) { + var _this = this; + return _getRef().then(function(branch) { + return _git.getSha(branch, '').then(function(sha) { + return _git.createRef({ + sha: sha, + ref: "refs/heads/" + newBranchName + }); + }); + }); + }; + this.read = function(path, isBase64) { + var _this = this; + return _getRef().then(function(branch) { + return _git.getSha(branch, path).then(function(sha) { + return _git.getBlob(sha, isBase64).then(function(bytes) { + return { + sha: sha, + content: bytes + }; + }); + }); + }); + }; + this.contents = function(path) { + var _this = this; + return _getRef().then(function(branch) { + return _git.getSha(branch, '').then(function(sha) { + return _git.getContents(path, sha).then(function(contents) { + return contents; + }); + }); + }); + }; + this.remove = function(path, message, sha) { + var _this = this; + if (message == null) { + message = "Removed " + path; + } + if (sha == null) { + sha = null; + } + return _getRef().then(function(branch) { + if (sha) { + return _git.removeFile(path, message, sha, branch); + } else { + return _git.getSha(branch, path).then(function(sha) { + return _git.removeFile(path, message, sha, branch); + }); + } + }); + }; + this.move = function(path, newPath, message) { + var _this = this; + if (message == null) { + message = "Moved " + path; + } + return _getRef().then(function(branch) { + return _git._updateTree(branch).then(function(latestCommit) { + return _git.getTree(latestCommit, { + recursive: true + }).then(function(tree) { + _.each(tree, function(ref) { + if (ref.path === path) { + ref.path = newPath; + } + if (ref.type === 'tree') { + return delete ref.sha; + } + }); + return _git.postTree(tree).then(function(rootTree) { + return _git.commit(latestCommit, rootTree, message).then(function(commit) { + return _git.updateHead(branch, commit).then(function(res) { + return res; + }); + }); + }); + }); + }); + }); + }; + this.write = function(path, content, message, isBase64, parentCommitSha) { + var contents; + if (message == null) { + message = "Changed " + path; + } + if (parentCommitSha == null) { + parentCommitSha = null; + } + contents = {}; + contents[path] = { + content: content, + isBase64: isBase64 + }; + return this.writeMany(contents, message, parentCommitSha); + }; + this.writeMany = function(contents, message, parentCommitShas) { + var _this = this; + if (message == null) { + message = "Changed Multiple"; + } + if (parentCommitShas == null) { + parentCommitShas = null; + } + return _getRef().then(function(branch) { + var afterParentCommitShas; + afterParentCommitShas = function(parentCommitShas) { + var promises; + promises = _.map(_.pairs(contents), function(_arg) { + var content, data, isBase64, path, + _this = this; + path = _arg[0], data = _arg[1]; + content = data.content || data; + isBase64 = data.isBase64 || false; + return _git.postBlob(content, isBase64).then(function(blob) { + return { + path: path, + mode: '100644', + type: 'blob', + sha: blob + }; + }); + }); + return allPromises(promises).then(function(newTrees) { + return _git.updateTreeMany(parentCommitShas, newTrees).then(function(tree) { + return _git.commit(parentCommitShas, tree, message).then(function(commitSha) { + return _git.updateHead(branch, commitSha).then(function(res) { + return res.object; + }); + }); + }); + }); + }; + if (parentCommitShas) { + return afterParentCommitShas(parentCommitShas); + } else { + return _git._updateTree(branch).then(afterParentCommitShas); + } + }); + }; + } + + return Branch; + + })(); + Repository = (function() { + function Repository(options) { + var _repo, _user; + this.options = options; + _user = this.options.user; + _repo = this.options.name; + this.git = new GitRepo(_user, _repo); + this.repoPath = "/repos/" + _user + "/" + _repo; + this.currentTree = { + branch: null, + sha: null + }; + this.updateInfo = function(options) { + return _request('PATCH', this.repoPath, options); + }; + this.getBranches = function() { + return this.git.getBranches(); + }; + this.getBranch = function(branchName) { + var getRef, + _this = this; + if (branchName == null) { + branchName = null; + } + if (branchName) { + getRef = function() { + return resolvedPromise(branchName); + }; + return new Branch(this.git, getRef); + } else { + return this.getDefaultBranch(); + } + }; + this.getDefaultBranch = function() { + var getRef, + _this = this; + getRef = function() { + return _this.getInfo().then(function(info) { + return info.default_branch; + }); + }; + return new Branch(this.git, getRef); + }; + this.setDefaultBranch = function(branchName) { + return this.updateInfo({ + name: _repo, + default_branch: branchName + }); + }; + this.getInfo = function() { + return _request('GET', this.repoPath, null); + }; + this.getContents = function(branch, path) { + return _request('GET', "" + this.repoPath + "/contents?ref=" + branch, { + path: path + }); + }; + this.fork = function(organization) { + if (organization) { + return _request('POST', "" + this.repoPath + "/forks", { + organization: organization + }); + } else { + return _request('POST', "" + this.repoPath + "/forks", null); + } + }; + this.createPullRequest = function(options) { + return _request('POST', "" + this.repoPath + "/pulls", options); + }; + this.getCommits = function(options) { + return this.git.getCommits(options); + }; + this.getEvents = function() { + return _request('GET', "" + this.repoPath + "/events", null); + }; + this.getIssueEvents = function() { + return _request('GET', "" + this.repoPath + "/issues/events", null); + }; + this.getNetworkEvents = function() { + return _request('GET', "/networks/" + _user + "/" + _repo + "/events", null); + }; + this.getNotifications = function(options) { + var getDate, queryString; + if (options == null) { + options = {}; + } + getDate = function(time) { + if (Date === time.constructor) { + return time.toISOString(); + } + return time; + }; + if (options.since) { + options.since = getDate(options.since); + } + queryString = toQueryString(options); + return _request('GET', "" + this.repoPath + "/notifications" + queryString, null); + }; + this.getCollaborators = function() { + return _request('GET', "" + this.repoPath + "/collaborators", null); + }; + this.addCollaborator = function(username) { + if (!username) { + throw new Error('BUG: username is required'); + } + return _request('PUT', "" + this.repoPath + "/collaborators/" + username, null, { + isBoolean: true + }); + }; + this.removeCollaborator = function(username) { + if (!username) { + throw new Error('BUG: username is required'); + } + return _request('DELETE', "" + this.repoPath + "/collaborators/" + username, null, { + isBoolean: true + }); + }; + this.isCollaborator = function(username) { + if (username == null) { + username = null; + } + if (!username) { + throw new Error('BUG: username is required'); + } + return _request('GET', "" + this.repoPath + "/collaborators/" + username, null, { + isBoolean: true + }); + }; + this.canCollaborate = function() { + var _this = this; + if (!(clientOptions.password || clientOptions.token)) { + return resolvedPromise(false); + } + return _client.getLogin().then(function(login) { + if (!login) { + return false; + } else { + return _this.isCollaborator(login); + } + }).then(null, function(err) { + return false; + }); + }; + this.getHooks = function() { + return _request('GET', "" + this.repoPath + "/hooks", null); + }; + this.getHook = function(id) { + return _request('GET', "" + this.repoPath + "/hooks/" + id, null); + }; + this.createHook = function(name, config, events, active) { + var data; + if (events == null) { + events = ['push']; + } + if (active == null) { + active = true; + } + data = { + name: name, + config: config, + events: events, + active: active + }; + return _request('POST', "" + this.repoPath + "/hooks", data); + }; + this.editHook = function(id, config, events, addEvents, removeEvents, active) { + var data; + if (config == null) { + config = null; + } + if (events == null) { + events = null; + } + if (addEvents == null) { + addEvents = null; + } + if (removeEvents == null) { + removeEvents = null; + } + if (active == null) { + active = null; + } + data = {}; + if (config !== null) { + data.config = config; + } + if (events !== null) { + data.events = events; + } + if (addEvents !== null) { + data.add_events = addEvents; + } + if (removeEvents !== null) { + data.remove_events = removeEvents; + } + if (active !== null) { + data.active = active; + } + return _request('PATCH', "" + this.repoPath + "/hooks/" + id, data); + }; + this.testHook = function(id) { + return _request('POST', "" + this.repoPath + "/hooks/" + id + "/tests", null); + }; + this.deleteHook = function(id) { + return _request('DELETE', "" + this.repoPath + "/hooks/" + id, null); + }; + this.getLanguages = function() { + return _request('GET', "" + this.repoPath + "/languages", null); + }; + this.getReleases = function() { + return _request('GET', "" + this.repoPath + "/releases", null); + }; + } + + return Repository; + + })(); + Gist = (function() { + function Gist(options) { + var id, _gistPath; + this.options = options; + id = this.options.id; + _gistPath = "/gists/" + id; + this.read = function() { + return _request('GET', _gistPath, null); + }; + this.create = function(files, isPublic, description) { + if (isPublic == null) { + isPublic = false; + } + if (description == null) { + description = null; + } + options = { + isPublic: isPublic, + files: files + }; + if (description != null) { + options.description = description; + } + return _request('POST', "/gists", options); + }; + this["delete"] = function() { + return _request('DELETE', _gistPath, null); + }; + this.fork = function() { + return _request('POST', "" + _gistPath + "/forks", null); + }; + this.update = function(files, description) { + if (description == null) { + description = null; + } + options = { + files: files + }; + if (description != null) { + options.description = description; + } + return _request('PATCH', _gistPath, options); + }; + this.star = function() { + return _request('PUT', "" + _gistPath + "/star"); + }; + this.unstar = function() { + return _request('DELETE', "" + _gistPath + "/star"); + }; + this.isStarred = function() { + return _request('GET', "" + _gistPath, null, { + isBoolean: true + }); + }; + } + + return Gist; + + })(); + this.getRepo = function(user, repo) { + if (!user) { + throw new Error('BUG! user argument is required'); + } + if (!repo) { + throw new Error('BUG! repo argument is required'); + } + return new Repository({ + user: user, + name: repo + }); + }; + this.getOrg = function(name) { + return new Organization(name); + }; + this.getUser = function(login) { + if (login == null) { + login = null; + } + if (login) { + return new User(login); + } else if (clientOptions.password || clientOptions.token) { + return new AuthenticatedUser(); + } else { + return null; + } + }; + this.getGist = function(id) { + return new Gist({ + id: id + }); + }; + this.getLogin = function() { + if (clientOptions.password || clientOptions.token) { + return new User().getInfo().then(function(info) { + return info.login; + }); + } else { + return resolvedPromise(null); + } + }; + } + + return Octokit; + + })(); + return Octokit; + }; + + if (typeof exports !== "undefined" && exports !== null) { + Promise = this.Promise || require('es6-promise').Promise; + XMLHttpRequest = this.XMLHttpRequest || require('xmlhttprequest').XMLHttpRequest; + newPromise = function(fn) { + return new Promise(fn); + }; + allPromises = function(promises) { + return Promise.all(promises); + }; + encode = this.btoa || function(str) { + var buffer; + buffer = new Buffer(str, 'binary'); + return buffer.toString('base64'); + }; + Octokit = makeOctokit(newPromise, allPromises, XMLHttpRequest, encode, 'octokit'); + exports["new"] = function(options) { + return new Octokit(options); + }; + } else { + createGlobalAndAMD = function(newPromise, allPromises) { + if (_this.define != null) { + return _this.define('octokit', [], function() { + return makeOctokit(newPromise, allPromises, _this.XMLHttpRequest, _this.btoa); + }); + } else { + Octokit = makeOctokit(newPromise, allPromises, _this.XMLHttpRequest, _this.btoa); + _this.Octokit = Octokit; + return _this.Github = Octokit; + } + }; + if (this.Q) { + newPromise = function(fn) { + var deferred, reject, resolve; + deferred = _this.Q.defer(); + resolve = function(val) { + return deferred.resolve(val); + }; + reject = function(err) { + return deferred.reject(err); + }; + fn(resolve, reject); + return deferred.promise; + }; + allPromises = function(promises) { + return this.Q.all(promises); + }; + createGlobalAndAMD(newPromise, allPromises); + } else if (this.angular) { + injector = angular.injector(['ng']); + injector.invoke(function($q) { + newPromise = function(fn) { + var deferred, reject, resolve; + deferred = $q.defer(); + resolve = function(val) { + return deferred.resolve(val); + }; + reject = function(err) { + return deferred.reject(err); + }; + fn(resolve, reject); + return deferred.promise; + }; + allPromises = function(promises) { + return $q.all(promises); + }; + return createGlobalAndAMD(newPromise, allPromises); + }); + } else if ((_ref = this.jQuery) != null ? _ref.Deferred : void 0) { + newPromise = function(fn) { + var promise, reject, resolve; + promise = _this.jQuery.Deferred(); + resolve = function(val) { + return promise.resolve(val); + }; + reject = function(val) { + return promise.reject(val); + }; + fn(resolve, reject); + return promise.promise(); + }; + allPromises = function(promises) { + var _ref1; + return (_ref1 = _this.jQuery).when.apply(_ref1, promises).then(function() { + var promises; + promises = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + return promises; + }); + }; + createGlobalAndAMD(newPromise, allPromises); + } else if (this.Promise) { + newPromise = function(fn) { + return new _this.Promise(function(resolve, reject) { + if (resolve.fulfill) { + return fn(resolve.resolve.bind(resolve), resolve.reject.bind(resolve)); + } else { + return fn.apply(null, arguments); + } + }); + }; + allPromises = this.Promise.all; + createGlobalAndAMD(newPromise, allPromises); + } else { + err = function(msg) { + if (typeof console !== "undefined" && console !== null) { + if (typeof console.error === "function") { + console.error(msg); + } + } + throw new Error(msg); + }; + err('A Promise API was not found. Supported libraries that have Promises are jQuery, angularjs, and https://github.com/jakearchibald/es6-promise'); + } + } + +}).call(this); diff --git a/octokit/promise-1.0.0.js b/octokit/promise-1.0.0.js new file mode 100644 index 0000000..5619cfa --- /dev/null +++ b/octokit/promise-1.0.0.js @@ -0,0 +1,684 @@ +(function() { +var define, requireModule, require, requirejs; + +(function() { + var registry = {}, seen = {}; + + define = function(name, deps, callback) { + registry[name] = { deps: deps, callback: callback }; + }; + + requirejs = require = requireModule = function(name) { + requirejs._eak_seen = registry; + + if (seen[name]) { return seen[name]; } + seen[name] = {}; + + if (!registry[name]) { + throw new Error("Could not find module " + name); + } + + var mod = registry[name], + deps = mod.deps, + callback = mod.callback, + reified = [], + exports; + + for (var i=0, l=deps.length; i<l; i++) { + if (deps[i] === 'exports') { + reified.push(exports = {}); + } else { + reified.push(requireModule(resolve(deps[i]))); + } + } + + var value = callback.apply(this, reified); + return seen[name] = exports || value; + + function resolve(child) { + if (child.charAt(0) !== '.') { return child; } + var parts = child.split("/"); + var parentBase = name.split("/").slice(0, -1); + + for (var i=0, l=parts.length; i<l; i++) { + var part = parts[i]; + + if (part === '..') { parentBase.pop(); } + else if (part === '.') { continue; } + else { parentBase.push(part); } + } + + return parentBase.join("/"); + } + }; +})(); + +define("promise/all", + ["./utils","exports"], + function(__dependency1__, __exports__) { + "use strict"; + /* global toString */ + + var isArray = __dependency1__.isArray; + var isFunction = __dependency1__.isFunction; + + /** + Returns a promise that is fulfilled when all the given promises have been + fulfilled, or rejected if any of them become rejected. The return promise + is fulfilled with an array that gives all the values in the order they were + passed in the `promises` array argument. + + Example: + + ```javascript + var promise1 = RSVP.resolve(1); + var promise2 = RSVP.resolve(2); + var promise3 = RSVP.resolve(3); + var promises = [ promise1, promise2, promise3 ]; + + RSVP.all(promises).then(function(array){ + // The array here would be [ 1, 2, 3 ]; + }); + ``` + + If any of the `promises` given to `RSVP.all` are rejected, the first promise + that is rejected will be given as an argument to the returned promises's + rejection handler. For example: + + Example: + + ```javascript + var promise1 = RSVP.resolve(1); + var promise2 = RSVP.reject(new Error("2")); + var promise3 = RSVP.reject(new Error("3")); + var promises = [ promise1, promise2, promise3 ]; + + RSVP.all(promises).then(function(array){ + // Code here never runs because there are rejected promises! + }, function(error) { + // error.message === "2" + }); + ``` + + @method all + @for RSVP + @param {Array} promises + @param {String} label + @return {Promise} promise that is fulfilled when all `promises` have been + fulfilled, or rejected if any of them become rejected. + */ + function all(promises) { + /*jshint validthis:true */ + var Promise = this; + + if (!isArray(promises)) { + throw new TypeError('You must pass an array to all.'); + } + + return new Promise(function(resolve, reject) { + var results = [], remaining = promises.length, + promise; + + if (remaining === 0) { + resolve([]); + } + + function resolver(index) { + return function(value) { + resolveAll(index, value); + }; + } + + function resolveAll(index, value) { + results[index] = value; + if (--remaining === 0) { + resolve(results); + } + } + + for (var i = 0; i < promises.length; i++) { + promise = promises[i]; + + if (promise && isFunction(promise.then)) { + promise.then(resolver(i), reject); + } else { + resolveAll(i, promise); + } + } + }); + } + + __exports__.all = all; + }); +define("promise/asap", + ["exports"], + function(__exports__) { + "use strict"; + var browserGlobal = (typeof window !== 'undefined') ? window : {}; + var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver; + var local = (typeof global !== 'undefined') ? global : (this === undefined? window:this); + + // node + function useNextTick() { + return function() { + process.nextTick(flush); + }; + } + + function useMutationObserver() { + var iterations = 0; + var observer = new BrowserMutationObserver(flush); + var node = document.createTextNode(''); + observer.observe(node, { characterData: true }); + + return function() { + node.data = (iterations = ++iterations % 2); + }; + } + + function useSetTimeout() { + return function() { + local.setTimeout(flush, 1); + }; + } + + var queue = []; + function flush() { + for (var i = 0; i < queue.length; i++) { + var tuple = queue[i]; + var callback = tuple[0], arg = tuple[1]; + callback(arg); + } + queue = []; + } + + var scheduleFlush; + + // Decide what async method to use to triggering processing of queued callbacks: + if (typeof process !== 'undefined' && {}.toString.call(process) === '[object process]') { + scheduleFlush = useNextTick(); + } else if (BrowserMutationObserver) { + scheduleFlush = useMutationObserver(); + } else { + scheduleFlush = useSetTimeout(); + } + + function asap(callback, arg) { + var length = queue.push([callback, arg]); + if (length === 1) { + // If length is 1, that means that we need to schedule an async flush. + // If additional callbacks are queued before the queue is flushed, they + // will be processed by this flush that we are scheduling. + scheduleFlush(); + } + } + + __exports__.asap = asap; + }); +define("promise/config", + ["exports"], + function(__exports__) { + "use strict"; + var config = { + instrument: false + }; + + function configure(name, value) { + if (arguments.length === 2) { + config[name] = value; + } else { + return config[name]; + } + } + + __exports__.config = config; + __exports__.configure = configure; + }); +define("promise/polyfill", + ["./promise","./utils","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + /*global self*/ + var RSVPPromise = __dependency1__.Promise; + var isFunction = __dependency2__.isFunction; + + function polyfill() { + var local; + + if (typeof global !== 'undefined') { + local = global; + } else if (typeof window !== 'undefined' && window.document) { + local = window; + } else { + local = self; + } + + var es6PromiseSupport = + "Promise" in local && + // Some of these methods are missing from + // Firefox/Chrome experimental implementations + "resolve" in local.Promise && + "reject" in local.Promise && + "all" in local.Promise && + "race" in local.Promise && + // Older version of the spec had a resolver object + // as the arg rather than a function + (function() { + var resolve; + new local.Promise(function(r) { resolve = r; }); + return isFunction(resolve); + }()); + + if (!es6PromiseSupport) { + local.Promise = RSVPPromise; + } + } + + __exports__.polyfill = polyfill; + }); +define("promise/promise", + ["./config","./utils","./all","./race","./resolve","./reject","./asap","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) { + "use strict"; + var config = __dependency1__.config; + var configure = __dependency1__.configure; + var objectOrFunction = __dependency2__.objectOrFunction; + var isFunction = __dependency2__.isFunction; + var now = __dependency2__.now; + var all = __dependency3__.all; + var race = __dependency4__.race; + var staticResolve = __dependency5__.resolve; + var staticReject = __dependency6__.reject; + var asap = __dependency7__.asap; + + var counter = 0; + + config.async = asap; // default async is asap; + + function Promise(resolver) { + if (!isFunction(resolver)) { + throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); + } + + if (!(this instanceof Promise)) { + throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function."); + } + + this._subscribers = []; + + invokeResolver(resolver, this); + } + + function invokeResolver(resolver, promise) { + function resolvePromise(value) { + resolve(promise, value); + } + + function rejectPromise(reason) { + reject(promise, reason); + } + + try { + resolver(resolvePromise, rejectPromise); + } catch(e) { + rejectPromise(e); + } + } + + function invokeCallback(settled, promise, callback, detail) { + var hasCallback = isFunction(callback), + value, error, succeeded, failed; + + if (hasCallback) { + try { + value = callback(detail); + succeeded = true; + } catch(e) { + failed = true; + error = e; + } + } else { + value = detail; + succeeded = true; + } + + if (handleThenable(promise, value)) { + return; + } else if (hasCallback && succeeded) { + resolve(promise, value); + } else if (failed) { + reject(promise, error); + } else if (settled === FULFILLED) { + resolve(promise, value); + } else if (settled === REJECTED) { + reject(promise, value); + } + } + + var PENDING = void 0; + var SEALED = 0; + var FULFILLED = 1; + var REJECTED = 2; + + function subscribe(parent, child, onFulfillment, onRejection) { + var subscribers = parent._subscribers; + var length = subscribers.length; + + subscribers[length] = child; + subscribers[length + FULFILLED] = onFulfillment; + subscribers[length + REJECTED] = onRejection; + } + + function publish(promise, settled) { + var child, callback, subscribers = promise._subscribers, detail = promise._detail; + + for (var i = 0; i < subscribers.length; i += 3) { + child = subscribers[i]; + callback = subscribers[i + settled]; + + invokeCallback(settled, child, callback, detail); + } + + promise._subscribers = null; + } + + Promise.prototype = { + constructor: Promise, + + _state: undefined, + _detail: undefined, + _subscribers: undefined, + + then: function(onFulfillment, onRejection) { + var promise = this; + + var thenPromise = new this.constructor(function() {}); + + if (this._state) { + var callbacks = arguments; + config.async(function invokePromiseCallback() { + invokeCallback(promise._state, thenPromise, callbacks[promise._state - 1], promise._detail); + }); + } else { + subscribe(this, thenPromise, onFulfillment, onRejection); + } + + return thenPromise; + }, + + 'catch': function(onRejection) { + return this.then(null, onRejection); + } + }; + + Promise.all = all; + Promise.race = race; + Promise.resolve = staticResolve; + Promise.reject = staticReject; + + function handleThenable(promise, value) { + var then = null, + resolved; + + try { + if (promise === value) { + throw new TypeError("A promises callback cannot return that same promise."); + } + + if (objectOrFunction(value)) { + then = value.then; + + if (isFunction(then)) { + then.call(value, function(val) { + if (resolved) { return true; } + resolved = true; + + if (value !== val) { + resolve(promise, val); + } else { + fulfill(promise, val); + } + }, function(val) { + if (resolved) { return true; } + resolved = true; + + reject(promise, val); + }); + + return true; + } + } + } catch (error) { + if (resolved) { return true; } + reject(promise, error); + return true; + } + + return false; + } + + function resolve(promise, value) { + if (promise === value) { + fulfill(promise, value); + } else if (!handleThenable(promise, value)) { + fulfill(promise, value); + } + } + + function fulfill(promise, value) { + if (promise._state !== PENDING) { return; } + promise._state = SEALED; + promise._detail = value; + + config.async(publishFulfillment, promise); + } + + function reject(promise, reason) { + if (promise._state !== PENDING) { return; } + promise._state = SEALED; + promise._detail = reason; + + config.async(publishRejection, promise); + } + + function publishFulfillment(promise) { + publish(promise, promise._state = FULFILLED); + } + + function publishRejection(promise) { + publish(promise, promise._state = REJECTED); + } + + __exports__.Promise = Promise; + }); +define("promise/race", + ["./utils","exports"], + function(__dependency1__, __exports__) { + "use strict"; + /* global toString */ + var isArray = __dependency1__.isArray; + + /** + `RSVP.race` allows you to watch a series of promises and act as soon as the + first promise given to the `promises` argument fulfills or rejects. + + Example: + + ```javascript + var promise1 = new RSVP.Promise(function(resolve, reject){ + setTimeout(function(){ + resolve("promise 1"); + }, 200); + }); + + var promise2 = new RSVP.Promise(function(resolve, reject){ + setTimeout(function(){ + resolve("promise 2"); + }, 100); + }); + + RSVP.race([promise1, promise2]).then(function(result){ + // result === "promise 2" because it was resolved before promise1 + // was resolved. + }); + ``` + + `RSVP.race` is deterministic in that only the state of the first completed + promise matters. For example, even if other promises given to the `promises` + array argument are resolved, but the first completed promise has become + rejected before the other promises became fulfilled, the returned promise + will become rejected: + + ```javascript + var promise1 = new RSVP.Promise(function(resolve, reject){ + setTimeout(function(){ + resolve("promise 1"); + }, 200); + }); + + var promise2 = new RSVP.Promise(function(resolve, reject){ + setTimeout(function(){ + reject(new Error("promise 2")); + }, 100); + }); + + RSVP.race([promise1, promise2]).then(function(result){ + // Code here never runs because there are rejected promises! + }, function(reason){ + // reason.message === "promise2" because promise 2 became rejected before + // promise 1 became fulfilled + }); + ``` + + @method race + @for RSVP + @param {Array} promises array of promises to observe + @param {String} label optional string for describing the promise returned. + Useful for tooling. + @return {Promise} a promise that becomes fulfilled with the value the first + completed promises is resolved with if the first completed promise was + fulfilled, or rejected with the reason that the first completed promise + was rejected with. + */ + function race(promises) { + /*jshint validthis:true */ + var Promise = this; + + if (!isArray(promises)) { + throw new TypeError('You must pass an array to race.'); + } + return new Promise(function(resolve, reject) { + var results = [], promise; + + for (var i = 0; i < promises.length; i++) { + promise = promises[i]; + + if (promise && typeof promise.then === 'function') { + promise.then(resolve, reject); + } else { + resolve(promise); + } + } + }); + } + + __exports__.race = race; + }); +define("promise/reject", + ["exports"], + function(__exports__) { + "use strict"; + /** + `RSVP.reject` returns a promise that will become rejected with the passed + `reason`. `RSVP.reject` is essentially shorthand for the following: + + ```javascript + var promise = new RSVP.Promise(function(resolve, reject){ + reject(new Error('WHOOPS')); + }); + + promise.then(function(value){ + // Code here doesn't run because the promise is rejected! + }, function(reason){ + // reason.message === 'WHOOPS' + }); + ``` + + Instead of writing the above, your code now simply becomes the following: + + ```javascript + var promise = RSVP.reject(new Error('WHOOPS')); + + promise.then(function(value){ + // Code here doesn't run because the promise is rejected! + }, function(reason){ + // reason.message === 'WHOOPS' + }); + ``` + + @method reject + @for RSVP + @param {Any} reason value that the returned promise will be rejected with. + @param {String} label optional string for identifying the returned promise. + Useful for tooling. + @return {Promise} a promise that will become rejected with the given + `reason`. + */ + function reject(reason) { + /*jshint validthis:true */ + var Promise = this; + + return new Promise(function (resolve, reject) { + reject(reason); + }); + } + + __exports__.reject = reject; + }); +define("promise/resolve", + ["exports"], + function(__exports__) { + "use strict"; + function resolve(value) { + /*jshint validthis:true */ + if (value && typeof value === 'object' && value.constructor === this) { + return value; + } + + var Promise = this; + + return new Promise(function(resolve) { + resolve(value); + }); + } + + __exports__.resolve = resolve; + }); +define("promise/utils", + ["exports"], + function(__exports__) { + "use strict"; + function objectOrFunction(x) { + return isFunction(x) || (typeof x === "object" && x !== null); + } + + function isFunction(x) { + return typeof x === "function"; + } + + function isArray(x) { + return Object.prototype.toString.call(x) === "[object Array]"; + } + + // Date.now is not available in browsers < IE9 + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now#Compatibility + var now = Date.now || function() { return new Date().getTime(); }; + + + __exports__.objectOrFunction = objectOrFunction; + __exports__.isFunction = isFunction; + __exports__.isArray = isArray; + __exports__.now = now; + }); +requireModule('promise/polyfill').polyfill(); +}());
\ No newline at end of file @@ -16,6 +16,8 @@ <script type="text/javascript" src="xml.js"></script> <script type="text/javascript" src="store.js"></script> <script type="text/javascript" src="locale.js"></script> + <script type="text/javascript" src="octokit/promise-1.0.0.js"></script> + <script type="text/javascript" src="octokit/octokit.js"></script> <script type="text/javascript" src="cloud.js"></script> <script type="text/javascript" src="sha512.js"></script> <script type="text/javascript"> |
