var exports = exports || this; exports.Twitter = (function(global) { var K = function(){}, isAndroid = Ti.Platform.osname === "android", jsOAuth = require('jsOAuth-1.3.1'); /** * Twitter constructor function * * var client = Twitter({ * consumerKey: "INSERT YOUR KEY HERE", * consumerSecret: "INSERT YOUR SECRET HERE" * }); * * Can be used with or without `new` keyword. * * @constructor * @requires jsOAuth: http://github.com/bytespider/jsOAuth * @param options {Object} Configuration object * @param options.consumerKey {String} Application consumer key * @param options.consumerSecret {String} Application consumer secret * @param options.accessTokenKey {String} (optional) The user's access token key * @param options.accessTokenSecret {String} (optional) The user's access token secret * @param [options.windowTitle="Twitter Authorization"] {String} (optional) The title to display in the authentication window */ var Twitter = function(options) { var self; if (this instanceof Twitter) { self = this; } else { self = new K(); } if (!options) { options = {}; } self.windowTitle = options.windowTitle || "Twitter Authorization"; self.consumerKey = options.consumerKey; self.consumerSecret = options.consumerSecret; self.authorizeUrl = "https://api.twitter.com/oauth/authorize"; self.accessTokenKey = options.accessTokenKey; self.accessTokenSecret = options.accessTokenSecret; self.authorized = false; self.listeners = {}; if (self.accessTokenKey && self.accessTokenSecret) { self.authorized = true; } options.requestTokenUrl = options.requestTokenUrl || "https://api.twitter.com/oauth/request_token"; self.oauthClient = jsOAuth.OAuth(options); return self; }; K.prototype = Twitter.prototype; function createAuthWindow() { var self = this, oauth = this.oauthClient, webViewWindow = Ti.UI.createWindow({title: this.windowTitle}), webView = Ti.UI.createWebView(), loadingOverlay = Ti.UI.createView({ backgroundColor: 'black', opacity: 0.7, zIndex: 1 }), actInd = Titanium.UI.createActivityIndicator({ height: 50, width: 10, message: 'Loading...', color: 'white' }), closeButton = Ti.UI.createButton({ title: "Close" }), backButton = Ti.UI.createButton({ title: "Back" }); this.webView = webView; webViewWindow.leftNavButton = closeButton; actInd.show(); loadingOverlay.add(actInd); webViewWindow.add(loadingOverlay); webViewWindow.open({modal: true}); webViewWindow.add(webView); closeButton.addEventListener('click', function(e) { webViewWindow.close(); self.fireEvent('cancel', { success: false, error: "The user cancelled.", result: null }); }); backButton.addEventListener('click', function(e) { webView.goBack(); }); webView.addEventListener('beforeload', function(e) { if (!isAndroid) { webViewWindow.add(loadingOverlay); } actInd.show(); }); webView.addEventListener('load', function(event) { // If we're not on the Twitter authorize page if (event.url.indexOf(self.authorizeUrl) === -1) { webViewWindow.remove(loadingOverlay); actInd.hide(); // Required for Android // Switch out close button for back button if (webViewWindow.leftNavButton !== backButton) { webViewWindow.leftNavButton = backButton; } } else { // Switch out back button for close button if (webViewWindow.leftNavButton !== closeButton) { webViewWindow.leftNavButton = closeButton; } // Grab the PIN code out of the DOM var pin = event.source.evalJS("document.getElementById('oauth_pin').getElementsByTagName('code')[0].innerText"); if (!pin) { // We're here when: // - "No thanks" button clicked // - Bad username/password webViewWindow.remove(loadingOverlay); actInd.hide(); } else { if (!isAndroid) { // on iOS we can close the modal window right away webViewWindow.close(); } oauth.accessTokenUrl = "https://api.twitter.com/oauth/access_token?oauth_verifier=" + pin; oauth.fetchAccessToken(function(data) { var returnedParams = oauth.parseTokenRequest(data.text); self.fireEvent('login', { success: true, error: false, accessTokenKey: returnedParams.oauth_token, accessTokenSecret: returnedParams.oauth_token_secret }); if (isAndroid) { // we have to wait until now to close the modal window on Android: http://developer.appcelerator.com/question/91261/android-probelm-with-httpclient webViewWindow.close(); } }, function(data) { self.fireEvent('login', { success: false, error: "Failure to fetch access token, please try again.", result: data }); }); } } }); } /* * Requests the user to authorize via Twitter through a modal WebView. */ Twitter.prototype.authorize = function() { var self = this; if (this.authorized) { // TODO: verify access tokens are still valid? // We're putting this fireEvent call inside setTimeout to allow // a user to add an event listener below the call to authorize. // Not totally sure if the timeout should be greater than 1. It // seems to do the trick on iOS/Android. setTimeout(function() { self.fireEvent('login', { success: true, error: false, accessTokenKey: self.accessTokenKey, accessTokenSecret: self.accessTokenSecret }); }, 1); } else { createAuthWindow.call(this); this.oauthClient.fetchRequestToken( function(requestParams) { var authorizeUrl = self.authorizeUrl + requestParams; self.webView.url = authorizeUrl; }, function(data) { self.fireEvent('login', { success: false, error: "Failure to fetch access token, please try again.", result: data }); } ); } }; /* * Make an authenticated Twitter API request. * * @param {String} path the Twitter API path without leading forward slash. For example: `1/statuses/home_timeline.json` * @param {Object} params the parameters to send along with the API call * @param {String} [httpVerb="GET"] the HTTP verb to use * @param {Function} callback */ Twitter.prototype.request = function(path, params, httpVerb, callback) { var self = this, oauth = this.oauthClient, url = "https://api.twitter.com/" + path; oauth.request({ method: httpVerb, url: url, data: params, success: function(data) { callback.call(self, { success: true, error: false, result: data }); }, failure: function(data) { callback.call(self, { success: false, error: "Request failed", result: data }); } }); }; /* * Add an event listener */ Twitter.prototype.addEventListener = function(eventName, callback) { this.listeners = this.listeners || {}; this.listeners[eventName] = this.listeners[eventName] || []; this.listeners[eventName].push(callback); }; /* * Fire an event */ Twitter.prototype.fireEvent = function(eventName, data) { var eventListeners = this.listeners[eventName] || []; for (var i = 0; i < eventListeners.length; i++) { eventListeners[i].call(this, data); } }; return Twitter; })(this);