<commit_before>/*
	Copyright 2010 Matthew Riley MacPherson

	Licensed under the Apache License, Version 2.0 (the "License");
	you may not use this file except in compliance with the License.
	You may obtain a copy of the License at

	    http://www.apache.org/licenses/LICENSE-2.0

	Unless required by applicable law or agreed to in writing, software
	distributed under the License is distributed on an "AS IS" BASIS,
	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	See the License for the specific language governing permissions and
	limitations under the License.
*/

//      #   Skarmory -- It's Super Effective!   #
//
//      ##    A JavaScript Twitter Library     ##
//      ##     for Appcelerator Titanium       ##
//
//      ###            Version 0.1            ###

var Skarmory;

// Skarmory constructor
if (Skarmory == null) { Skarmory = function(instance_options) {
	var DEFAULTS = {
		accessTokenURL: 'https://api.twitter.com/oauth/access_token',
		autoAuthorize: false, // Automatically attempt to authorize user if
		                      // no access tokens exist. Can get messy.
		authorizeURL: 'https://api.twitter.com/oauth/authorize',
		requestTokenURL: 'https://api.twitter.com/oauth/request_token',
		responseFormat: 'json',
		restAPI: 'api.twitter.com/1/',
		signatureMethod: 'HMAC-SHA1', // Default to HMAC-SHA1 for Twitter
		transition: Ti.UI.iPhone.AnimationStyle.CURL_DOWN,
		useSSL: false
	},
	METHODS = { // Map of the Twitter REST API methods and their HTTP method types
		// Timeline resources
		'statuses/public_timeline':    'GET',
		'statuses/home_timeline':      'GET',
		'statuses/friends_timeline':   'GET',
		'statuses/user_timeline':      'GET',
		'statuses/mentions':           'GET',
		'statuses/retweeted_by_me':    'GET',
		'statuses/retweeted_to_me':    'GET',
		'statuses/retweets_of_me':     'GET',
		// Tweets resources
		'statuses/show/:id': 'GET',
		'statuses/update': 'POST',
		'statuses/destroy/:id': 'POST',
		'statuses/retweet/:id': 'GET',
		'statuses/retweets/:id': 'GET',
		'statuses/:id/retweeted_by': 'GET',
		'statuses/:id/retweeted_by/ids': 'GET',
		// User resources
		'users/show': 'GET',
		'users/lookup': 'GET',
		'users/search': 'GET',
		'users/suggestions': 'GET',
		'users/suggestions/twitter': 'GET',
		'users/profile_images/twitter': 'GET',
		'statuses/friends': 'GET',
		'statues/followers': 'GET',
		// Trends resources
		'trends': 'GET',
		'trends/current': 'GET',
		'trends/daily': 'GET',
		'trends/weekly': 'GET',
		// Local Trends resources
		'trends/available': 'GET',
		'trends/:woeid': 'GET',
	},
	options = DEFAULTS,
	page_change = 0,
	authorizedCallbacks,
	requestToken = '',
	requestTokenSecret = '',
	queue = [], // Queue of calls to perform -- we back these up if we aren't (yet) authorized
	view,
	webView,
	window;

	// Set each option passed to this instance
	for (var i in instance_options) {
		options[i] = instance_options[i];
	}

	// House our base OAuth properties that we'll use to
	// sign requests.
	var oauth = {
		consumerSecret: options.consumerSecret || DEFAULTS.consumerSecret,
		tokenSecret: ''
	};

	/*
		Authentication Methods

		Use these to determine whether or not this user
		is authorized and to authorize them via OAuth.
	*/

	// Authorize a user and run a callback when authorization succeeds/fails.
	//
	// callbacks: {
	//     onSuccess: function() { // Run when OAuth authorization is successful },
	//     onFailure: function() { // Run when user denies app access },
	// }
	this.authorize = function(callbacks) {
		// show the authorization UI and call back the receive PIN function
		this.showAuthorizationWindow(options.authorizeURL + '?' + this.getRequestToken(options.requestTokenURL, [['oauth_callback', 'oob']]), callbacks);
	};

	// Return true if authorization tokens for this user are available.
	// This pretty much always returns true...
	this.isAuthorized = function() {
		return (options.accessKey && options.accessSecret);
	};

	// Get a request token from Twitter's API and return it.
	this.getRequestToken = function(url) {
		Ti.API.debug('Attempting to get OAuth Request Token from ' + url);

		oauth.tokenSecret = '';

		var request = {
			action: url,
			method: 'POST', // OAuth requests are always POST
			parameters: []
		};
		request.parameters.push(['oauth_consumer_key', options.consumerKey]);
		request.parameters.push(['oauth_signature_method', options.signatureMethod]);

		OAuth.setTimestampAndNonce(request);
		OAuth.SignatureMethod.sign(request, oauth);

		var httpClient = Ti.Network.createHTTPClient();
		httpClient.open('POST', url, false);
		httpClient.send(OAuth.getParameterMap(request.parameters));

		var responseParams = OAuth.getParameterMap(httpClient.responseText);
		requestToken = responseParams.oauth_token;
		requestTokenSecret = responseParams.oauth_token_secret;

		Ti.API.debug('Receieved OAuth Request Token from ' + url + "\nResponse was: " + httpClient.responseText);

		return httpClient.responseText;
	};

	// Display the Twitter OAuth authorization page inside a webView
	// and automatically grab the PIN code from a successful authorization.
	// Also provides an onFailure callback if the user denied access.
	this.showAuthorizationWindow = function(url, callbacks) {
		// Assign the callbacks to instance-available variables
		authorizedCallbacks = callbacks;

		window = Ti.UI.createWindow();
		view = Ti.UI.createView({
			height: 480,
			top: 0,
			transform: Ti.UI.create2DMatrix().scale(0),
			width: 320,
			zIndex: -1
		});
		window.open();

		webView = Ti.UI.createWebView({
			autoDetect: [Ti.UI.AUTODETECT_NONE],
			url: url
		});

		webView.addEventListener('load', _getAccessTokenFromWindow);
		view.add(webView);
		window.add(view);

		var animation = Ti.UI.createAnimation();
		animation.transform = Ti.UI.create2DMatrix();
		animation.duration = 500;
		view.animate(animation);
	};

	/*
		Twitter API Method Wrappers

		Provided to make API requests really JS-like. Provides methods
		that are similar in name to the API calls they relate to.
	*/
	this.timeline = function(method, args) {
		method = 'statuses/' + method;

		this._request(method, args);
	};

	this.tweets = function(method, args) {
		method = 'statuses/' + method;

		this._request(method, args);
	};

	this.user = function(method, args) {
		method = (_valueInArray(method, ['friends', 'followers'])) ? 'statuses/' + method : 'users/' + method;

		this._request(method, args);
	};

	/*
		Private API/methods

		Used internally by Starmory. Don't rely on these; they could change.
	*/
	this._request = function(method, args) {
		if (options.autoAuthorize && _valueInArray(null, [options.accessKey, options.accessSecret])) {
			Ti.API.debug("No access token found -- requesting one and queuing " + http_method + ' ' + url);

			_addToQueue(method, args);

			return;
		}

		var httpMethod = METHODS[method] || 'GET', // If not available in our known methods use GET
		params = args.params,
		// Do token replacement for the URL string
		url = ((options.useSSL) ? 'https://' : 'http://') + options.restAPI;

		// Do token replacement for any params found in the URL
		// i.e. /users/show/:id will replace :id and remove it
		// from the params.
		if (params) {
			for (var i in params) {
				Ti.API.debug(i + ' | ' + params[i]);
				if (method.match(':' + params[i][0])) {
					method = method.replace(':' + params[i][0], params[i][1]);
					delete params[i];
				}
			}
		}

		// Finish up the URL. Response format defaults to JSON;
		// it's fast to parse and the least data over the wire.
		url += method + '.' + options.responseFormat;

		Ti.API.info(httpMethod + ' ' + url);

		// Format our request according to what the OAuth library wants
		var request = {
			action: url,
			method: httpMethod,
			parameters: []
		};
		request.parameters.push(['oauth_consumer_key', options.consumerKey]);
		request.parameters.push(['oauth_signature_method', options.signatureMethod]);
		request.parameters.push(['oauth_token', options.accessKey]);

		for (var i in params) {
			request.parameters.push(params[i]);
		}

		OAuth.setTimestampAndNonce(request);
		OAuth.SignatureMethod.sign(request, oauth);

		var httpClient = Ti.Network.createHTTPClient(),
		parameterMap = OAuth.getParameterMap(request.parameters);

		// We have to add our params to the GET string if this is
		// a GET request; otherwise we just stick them into the
		// body of the POST.
		if (httpMethod == 'GET') {
			url += '?';
			if (parameterMap) {
				for (var i in parameterMap) {
					url += i + '=' + parameterMap[i] + '&';
				}
			}

			url = url.slice(0, -1);
			httpClient.open(httpMethod, url, false);
			httpClient.send();
		} else {
			httpClient.open(httpMethod, url, false);
			httpClient.send(parameterMap);
		}

		// If we got an OK (200) status then we run our success callback;
		// otherwise we run our failure one.
		if (httpClient.status == 200) {
			args.onSuccess(JSON.parse(httpClient.responseText));
		} else {
			args.onFailure(httpClient);
		}

		// If automatic authorization is enabled, we'll start open a
		// webView window and begin the OAuth access token nonsense.
		if (options.autoAuthorize && !this.isAuthorized()) {
			this.authorize({
				onSuccess: function() {
					alert('it worked!');
				},
				onFailure: function() {
					alert('it failed!');
				}
			});
		}
	};

	// Enable queuing of requests. Use this method to add
	// to this instance's internal queue.
	var _addToQueue = function(method, args) {
		queue.push([method, args]);
	};

	// Runs after a request token is obtained from the webView
	// launched on authorize(). Gets the access token for a
	// user and runs callbacks.
	var _getAccessTokenFromWindow = function(page) {
		var doc = Ti.XML.parseString(page.source.html),
		nodeList = doc.getElementsByTagName('div'),
		success = false;

		// Iterating down is usually faster, but I'm not sure what
		// Titatium outputs. TODO: See if this is a performance hit/gain.
		for (var i = nodeList.length - 1; i > 0; i--) {
			var node = nodeList.item(i),
			id = node.attributes.getNamedItem('id');

			if (id && id.nodeValue == 'oauth_pin') {
				Ti.API.debug('Attempting to get OAuth Access Token from ' + options.accessTokenURL);

				// Use the PIN from the page to get our request token.
				var pin = node.text;
				oauth.tokenSecret = requestTokenSecret;

				var request = {
					action: options.accessTokenURL,
					method: 'POST', // OAuth requests are always POST
					parameters: []
				};
				request.parameters.push(['oauth_token', requestToken]);
				request.parameters.push(['oauth_verifier', pin]);

				OAuth.setTimestampAndNonce(request);
				OAuth.SignatureMethod.sign(request, oauth);

				var parameterMap = OAuth.getParameterMap(request.parameters);
				for (var p in parameterMap) {
					Ti.API.debug(p + ': ' + parameterMap[p]);
				}

				var httpClient = Ti.Network.createHTTPClient();
				httpClient.open('POST', options.accessTokenURL, false);
				httpClient.send(parameterMap);

				var responseParams = OAuth.getParameterMap(httpClient.responseText);
				options.accessKey = responseParams.oauth_token;
				options.accessSecret = responseParams.oauth_token_secret;

				success = true;
				authorizedCallbacks.onSuccess();

				Ti.API.debug('Receieved OAuth Access Token from ' + options.accessTokenURL + "\nResponse was: " + httpClient.responseText);

				id = null;
				node = null;

				break;
			}
		}

		// Used to disable page state change on first request, so
		// we don't close the window as soon as it loads.
		if (page_change) {
			// Either the PIN was obtained or the user denied the
			// request; either way we close this window.
			page_change = 0;
			_closeAuthorizationWindow();
			if (!success) {
				authorizedCallbacks.onFailure();
			}
		} else {
			page_change = 1;
		}
	};

	// Remove the authorization window and callbacks/event
	// listeners associated with it.
	var _closeAuthorizationWindow = function() {
		try {
			webView.removeEventListener('load', _getAccessTokenFromWindow);
			window.hide();
		} catch(e) {
			Ti.API.debug('Authorization window not found or could not be removed. Maybe it was already removed? Ignoring...');
		}
	};

	// Iterate through this instance's internal queue and run each request.
	var _processQueue = function() {
		while ((q = queue.shift()) != null) {
			Ti.API.debug('Processing item in queue: ' + q[0]);
			this._request(q[0], q[1]);
		}

		Ti.API.debug('Queue processed.');
	};

	// Utility function to emulate an in_array() check.
	var _valueInArray = function(value, array) {
		var i = array.length;

		while (i--) {
			if (array[i] === value) {
				return true;
			}
		}

		return false;
	};
}};

// Utility function for making a single request to
// the REST API. Requires valid OAuth credentials
// to be passed into args.options.
Skarmory.request = function(method, args) {
	var options = (args.options) ? args.options : {},
	instance = new Skarmory(options);

	delete args.options;

	instance._request(method, args);
};

// Skarmory Static Public Methods
Skarmory.setDefaults = function(options) {
	for (var i in options) {
		this.DEFAULTS = options.i
	}
};

// Skarmory.demo = function(tweet_text) {
// 	var s = new Skarmory({
// 		consumerKey: FALCON_CONFIG['consumer_key'],
// 		consumerSecret: FALCON_CONFIG['consumer_secret']
// 	});
// 	// s._request('statuses/update', [['status', "Hello worlds"]], {
// 	//	onSuccess: function(response) {
// 	//		alert(JSON.parse(response.responseText).message);
// 	//	}
// 	// });
// 	s._request('statuses/show/:id', {
// 		params: [['id', 26202194952]],
// 		onSuccess: function(response) {
// 			// alert(JSON.parse(response.responseText).text);
// 		}
// 	});
// }
<commit_msg>Add transition options for webViews<commit_after>/*
	Copyright 2010 Matthew Riley MacPherson

	Licensed under the Apache License, Version 2.0 (the "License");
	you may not use this file except in compliance with the License.
	You may obtain a copy of the License at

	    http://www.apache.org/licenses/LICENSE-2.0

	Unless required by applicable law or agreed to in writing, software
	distributed under the License is distributed on an "AS IS" BASIS,
	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	See the License for the specific language governing permissions and
	limitations under the License.
*/

//      #   Skarmory -- It's Super Effective!   #
//
//      ##    A JavaScript Twitter Library     ##
//      ##     for Appcelerator Titanium       ##
//
//      ###            Version 0.1            ###

var Skarmory;

// Skarmory constructor
if (Skarmory == null) { Skarmory = function(instance_options) {
	var DEFAULTS = {
		accessTokenURL: 'https://api.twitter.com/oauth/access_token',
		autoAuthorize: false, // Automatically attempt to authorize user if
		                      // no access tokens exist. Can get messy.
		authorizeURL: 'https://api.twitter.com/oauth/authorize',
		closeTransition: Ti.UI.iPhone.AnimationStyle.CURL_UP,
		openTransition: Ti.UI.iPhone.AnimationStyle.CURL_DOWN,
		requestTokenURL: 'https://api.twitter.com/oauth/request_token',
		responseFormat: 'json',
		restAPI: 'api.twitter.com/1/',
		signatureMethod: 'HMAC-SHA1', // Default to HMAC-SHA1 for Twitter
		useSSL: false
	},
	METHODS = { // Map of the Twitter REST API methods and their HTTP method types
		// Timeline resources
		'statuses/public_timeline':    'GET',
		'statuses/home_timeline':      'GET',
		'statuses/friends_timeline':   'GET',
		'statuses/user_timeline':      'GET',
		'statuses/mentions':           'GET',
		'statuses/retweeted_by_me':    'GET',
		'statuses/retweeted_to_me':    'GET',
		'statuses/retweets_of_me':     'GET',
		// Tweets resources
		'statuses/show/:id': 'GET',
		'statuses/update': 'POST',
		'statuses/destroy/:id': 'POST',
		'statuses/retweet/:id': 'GET',
		'statuses/retweets/:id': 'GET',
		'statuses/:id/retweeted_by': 'GET',
		'statuses/:id/retweeted_by/ids': 'GET',
		// User resources
		'users/show': 'GET',
		'users/lookup': 'GET',
		'users/search': 'GET',
		'users/suggestions': 'GET',
		'users/suggestions/twitter': 'GET',
		'users/profile_images/twitter': 'GET',
		'statuses/friends': 'GET',
		'statues/followers': 'GET',
		// Trends resources
		'trends': 'GET',
		'trends/current': 'GET',
		'trends/daily': 'GET',
		'trends/weekly': 'GET',
		// Local Trends resources
		'trends/available': 'GET',
		'trends/:woeid': 'GET',
	},
	options = DEFAULTS,
	page_change = 0,
	authorizedCallbacks,
	requestToken = '',
	requestTokenSecret = '',
	queue = [], // Queue of calls to perform -- we back these up if we aren't (yet) authorized
	view,
	webView,
	window;

	// Set each option passed to this instance
	for (var i in instance_options) {
		options[i] = instance_options[i];
	}

	// House our base OAuth properties that we'll use to
	// sign requests.
	var oauth = {
		consumerSecret: options.consumerSecret || DEFAULTS.consumerSecret,
		tokenSecret: ''
	};

	/*
		Authentication Methods

		Use these to determine whether or not this user
		is authorized and to authorize them via OAuth.
	*/

	// Authorize a user and run a callback when authorization succeeds/fails.
	//
	// callbacks: {
	//     onSuccess: function() { // Run when OAuth authorization is successful },
	//     onFailure: function() { // Run when user denies app access },
	// }
	this.authorize = function(callbacks) {
		// show the authorization UI and call back the receive PIN function
		this.showAuthorizationWindow(options.authorizeURL + '?' + this.getRequestToken(options.requestTokenURL, [['oauth_callback', 'oob']]), callbacks);
	};

	// Return true if authorization tokens for this user are available.
	// This pretty much always returns true...
	this.isAuthorized = function() {
		return (options.accessKey && options.accessSecret);
	};

	// Get a request token from Twitter's API and return it.
	this.getRequestToken = function(url) {
		Ti.API.debug('Attempting to get OAuth Request Token from ' + url);

		oauth.tokenSecret = '';

		var request = {
			action: url,
			method: 'POST', // OAuth requests are always POST
			parameters: []
		};
		request.parameters.push(['oauth_consumer_key', options.consumerKey]);
		request.parameters.push(['oauth_signature_method', options.signatureMethod]);

		OAuth.setTimestampAndNonce(request);
		OAuth.SignatureMethod.sign(request, oauth);

		var httpClient = Ti.Network.createHTTPClient();
		httpClient.open('POST', url, false);
		httpClient.send(OAuth.getParameterMap(request.parameters));

		var responseParams = OAuth.getParameterMap(httpClient.responseText);
		requestToken = responseParams.oauth_token;
		requestTokenSecret = responseParams.oauth_token_secret;

		Ti.API.debug('Receieved OAuth Request Token from ' + url + "\nResponse was: " + httpClient.responseText);

		return httpClient.responseText;
	};

	// Display the Twitter OAuth authorization page inside a webView
	// and automatically grab the PIN code from a successful authorization.
	// Also provides an onFailure callback if the user denied access.
	this.showAuthorizationWindow = function(url, callbacks) {
		// Assign the callbacks to instance-available variables
		authorizedCallbacks = callbacks;

		window = Ti.UI.createWindow();
		view = Ti.UI.createView({
			height: 480,
			top: 0,
			transform: Ti.UI.create2DMatrix().scale(0),
			width: 320,
			zIndex: -1
		});
		window.open({transition: options.openTransition});

		webView = Ti.UI.createWebView({
			autoDetect: [Ti.UI.AUTODETECT_NONE],
			url: url
		});

		webView.addEventListener('load', _getAccessTokenFromWindow);
		view.add(webView);
		window.add(view);

		var animation = Ti.UI.createAnimation();
		animation.transform = Ti.UI.create2DMatrix();
		animation.duration = 500;
		view.animate(animation);
	};

	/*
		Twitter API Method Wrappers

		Provided to make API requests really JS-like. Provides methods
		that are similar in name to the API calls they relate to.
	*/
	this.timeline = function(method, args) {
		method = 'statuses/' + method;

		this._request(method, args);
	};

	this.tweets = function(method, args) {
		method = 'statuses/' + method;

		this._request(method, args);
	};

	this.user = function(method, args) {
		method = (_valueInArray(method, ['friends', 'followers'])) ? 'statuses/' + method : 'users/' + method;

		this._request(method, args);
	};

	/*
		Private API/methods

		Used internally by Starmory. Don't rely on these; they could change.
	*/
	this._request = function(method, args) {
		if (options.autoAuthorize && _valueInArray(null, [options.accessKey, options.accessSecret])) {
			Ti.API.debug("No access token found -- requesting one and queuing " + http_method + ' ' + url);

			_addToQueue(method, args);

			return;
		}

		var httpMethod = METHODS[method] || 'GET', // If not available in our known methods use GET
		params = args.params,
		// Do token replacement for the URL string
		url = ((options.useSSL) ? 'https://' : 'http://') + options.restAPI;

		// Do token replacement for any params found in the URL
		// i.e. /users/show/:id will replace :id and remove it
		// from the params.
		if (params) {
			for (var i in params) {
				Ti.API.debug(i + ' | ' + params[i]);
				if (method.match(':' + params[i][0])) {
					method = method.replace(':' + params[i][0], params[i][1]);
					delete params[i];
				}
			}
		}

		// Finish up the URL. Response format defaults to JSON;
		// it's fast to parse and the least data over the wire.
		url += method + '.' + options.responseFormat;

		Ti.API.info(httpMethod + ' ' + url);

		// Format our request according to what the OAuth library wants
		var request = {
			action: url,
			method: httpMethod,
			parameters: []
		};
		request.parameters.push(['oauth_consumer_key', options.consumerKey]);
		request.parameters.push(['oauth_signature_method', options.signatureMethod]);
		request.parameters.push(['oauth_token', options.accessKey]);

		for (var i in params) {
			request.parameters.push(params[i]);
		}

		OAuth.setTimestampAndNonce(request);
		OAuth.SignatureMethod.sign(request, oauth);

		var httpClient = Ti.Network.createHTTPClient(),
		parameterMap = OAuth.getParameterMap(request.parameters);

		// We have to add our params to the GET string if this is
		// a GET request; otherwise we just stick them into the
		// body of the POST.
		if (httpMethod == 'GET') {
			url += '?';
			if (parameterMap) {
				for (var i in parameterMap) {
					url += i + '=' + parameterMap[i] + '&';
				}
			}

			url = url.slice(0, -1);
			httpClient.open(httpMethod, url, false);
			httpClient.send();
		} else {
			httpClient.open(httpMethod, url, false);
			httpClient.send(parameterMap);
		}

		// If we got an OK (200) status then we run our success callback;
		// otherwise we run our failure one.
		if (httpClient.status == 200) {
			args.onSuccess(JSON.parse(httpClient.responseText));
		} else {
			args.onFailure(httpClient);
		}

		// If automatic authorization is enabled, we'll start open a
		// webView window and begin the OAuth access token nonsense.
		if (options.autoAuthorize && !this.isAuthorized()) {
			this.authorize({
				onSuccess: function() {
					alert('it worked!');
				},
				onFailure: function() {
					alert('it failed!');
				}
			});
		}
	};

	// Enable queuing of requests. Use this method to add
	// to this instance's internal queue.
	var _addToQueue = function(method, args) {
		queue.push([method, args]);
	};

	// Runs after a request token is obtained from the webView
	// launched on authorize(). Gets the access token for a
	// user and runs callbacks.
	var _getAccessTokenFromWindow = function(page) {
		var doc = Ti.XML.parseString(page.source.html),
		nodeList = doc.getElementsByTagName('div'),
		success = false;

		// Iterating down is usually faster, but I'm not sure what
		// Titatium outputs. TODO: See if this is a performance hit/gain.
		for (var i = nodeList.length - 1; i > 0; i--) {
			var node = nodeList.item(i),
			id = node.attributes.getNamedItem('id');

			if (id && id.nodeValue == 'oauth_pin') {
				Ti.API.debug('Attempting to get OAuth Access Token from ' + options.accessTokenURL);

				// Use the PIN from the page to get our request token.
				var pin = node.text;
				oauth.tokenSecret = requestTokenSecret;

				var request = {
					action: options.accessTokenURL,
					method: 'POST', // OAuth requests are always POST
					parameters: []
				};
				request.parameters.push(['oauth_token', requestToken]);
				request.parameters.push(['oauth_verifier', pin]);

				OAuth.setTimestampAndNonce(request);
				OAuth.SignatureMethod.sign(request, oauth);

				var parameterMap = OAuth.getParameterMap(request.parameters);
				for (var p in parameterMap) {
					Ti.API.debug(p + ': ' + parameterMap[p]);
				}

				var httpClient = Ti.Network.createHTTPClient();
				httpClient.open('POST', options.accessTokenURL, false);
				httpClient.send(parameterMap);

				var responseParams = OAuth.getParameterMap(httpClient.responseText);
				options.accessKey = responseParams.oauth_token;
				options.accessSecret = responseParams.oauth_token_secret;

				success = true;
				authorizedCallbacks.onSuccess();

				Ti.API.debug('Receieved OAuth Access Token from ' + options.accessTokenURL + "\nResponse was: " + httpClient.responseText);

				id = null;
				node = null;

				break;
			}
		}

		// Used to disable page state change on first request, so
		// we don't close the window as soon as it loads.
		if (page_change) {
			// Either the PIN was obtained or the user denied the
			// request; either way we close this window.
			page_change = 0;
			_closeAuthorizationWindow();
			if (!success) {
				authorizedCallbacks.onFailure();
			}
		} else {
			page_change = 1;
		}
	};

	// Remove the authorization window and callbacks/event
	// listeners associated with it.
	var _closeAuthorizationWindow = function() {
		try {
			webView.removeEventListener('load', _getAccessTokenFromWindow);
			window.close({transition: options.closeTransition});
		} catch(e) {
			Ti.API.debug('Authorization window not found or could not be removed. Maybe it was already removed? Ignoring...');
		}
	};

	// Iterate through this instance's internal queue and run each request.
	var _processQueue = function() {
		while ((q = queue.shift()) != null) {
			Ti.API.debug('Processing item in queue: ' + q[0]);
			this._request(q[0], q[1]);
		}

		Ti.API.debug('Queue processed.');
	};

	// Utility function to emulate an in_array() check.
	var _valueInArray = function(value, array) {
		var i = array.length;

		while (i--) {
			if (array[i] === value) {
				return true;
			}
		}

		return false;
	};
}};

// Utility function for making a single request to
// the REST API. Requires valid OAuth credentials
// to be passed into args.options.
Skarmory.request = function(method, args) {
	var options = (args.options) ? args.options : {},
	instance = new Skarmory(options);

	delete args.options;

	instance._request(method, args);
};

// Skarmory Static Public Methods
Skarmory.setDefaults = function(options) {
	for (var i in options) {
		this.DEFAULTS = options.i
	}
};

// Skarmory.demo = function(tweet_text) {
// 	var s = new Skarmory({
// 		consumerKey: FALCON_CONFIG['consumer_key'],
// 		consumerSecret: FALCON_CONFIG['consumer_secret']
// 	});
// 	// s._request('statuses/update', [['status', "Hello worlds"]], {
// 	//	onSuccess: function(response) {
// 	//		alert(JSON.parse(response.responseText).message);
// 	//	}
// 	// });
// 	s._request('statuses/show/:id', {
// 		params: [['id', 26202194952]],
// 		onSuccess: function(response) {
// 			// alert(JSON.parse(response.responseText).text);
// 		}
// 	});
// }
<|endoftext|>


<commit_before>// Copyright 2013 The Obvious Corporation.

/**
 * @fileoverview Helpers made available via require('slimerjs') once package is
 * installed.
 */

var fs = require('fs')
var path = require('path')
var which = require('which')

/**
 * Where the slimer binary can be found.
 * @type {string}
 */
try {
  exports.path = path.resolve(__dirname, require('./location').location)
} catch(e) {
  // Must be running inside install script.
  exports.path = null
}

/**
 * The version of slimerjs installed by this package.
 * @type {number}
 */
exports.version = require('./../package.json').version.split('-')[0]

/**
 * Returns a clean path that helps avoid `which` finding bin files installed
 * by NPM for this repo.
 * @param {string} path
 * @return {string}
 */
exports.cleanPath = function (path) {
  return path
      .replace(/:[^:]*node_modules[^:]*/g, '')
      .replace(/(^|:)\.\/bin(\:|$)/g, ':')
      .replace(/^:+/, '')
      .replace(/:+$/, '')
}

// Make sure the binary is executable.  For some reason doing this inside
// install does not work correctly, likely due to some NPM step.
if (exports.path) {
  try {
    // avoid touching the binary if it's already got the correct permissions
    var st = fs.statSync(exports.path);
    var mode = st.mode | 0555;
    if (mode !== st.mode) {
      fs.chmodSync(exports.path, mode);
    }
  } catch (e) {
    // Just ignore error if we don't have permission.
    // We did our best. Likely because slimerjs was already installed.
  }
}
<commit_msg>npm adds dashes, simpler to hard code version<commit_after>// Copyright 2013 The Obvious Corporation.

/**
 * @fileoverview Helpers made available via require('slimerjs') once package is
 * installed.
 */

var fs = require('fs')
var path = require('path')
var which = require('which')

/**
 * Where the slimer binary can be found.
 * @type {string}
 */
try {
  exports.path = path.resolve(__dirname, require('./location').location)
} catch(e) {
  // Must be running inside install script.
  exports.path = null
}

/**
 * The version of slimerjs installed by this package.
 * @type {number}
 */
exports.version = '0.10.0pre'

/**
 * Returns a clean path that helps avoid `which` finding bin files installed
 * by NPM for this repo.
 * @param {string} path
 * @return {string}
 */
exports.cleanPath = function (path) {
  return path
      .replace(/:[^:]*node_modules[^:]*/g, '')
      .replace(/(^|:)\.\/bin(\:|$)/g, ':')
      .replace(/^:+/, '')
      .replace(/:+$/, '')
}

// Make sure the binary is executable.  For some reason doing this inside
// install does not work correctly, likely due to some NPM step.
if (exports.path) {
  try {
    // avoid touching the binary if it's already got the correct permissions
    var st = fs.statSync(exports.path);
    var mode = st.mode | 0555;
    if (mode !== st.mode) {
      fs.chmodSync(exports.path, mode);
    }
  } catch (e) {
    // Just ignore error if we don't have permission.
    // We did our best. Likely because slimerjs was already installed.
  }
}
<|endoftext|>


<commit_before>'use strict';

var reqRegEx = /^\s*(?:\{(.*)\})?\s*(?:(\$?[^\s]+))?\s*(?:\((.*)\))?\s*(?:-?\s*([^<$]*))?\s*(?:<?\s*(.*)\s*>)?$/;

var utils = require('../../utils');
var logger = require('../../log');

module.exports = {

  parse: function (text) {
    var match = reqRegEx.exec(text.trim());

    var obj = {
      type: match[1] || 'function',
      name: match[2]
    };

    obj.external = utils.splitNamespace(obj.name).length > 1;

    if (obj.name.indexOf('$') === 0) {
      obj.type = 'variable';
      obj.name = obj.name.slice(1);
    }

    if (match[4]) {
      obj.description = match[4].trim();
    }

    if (match[5]) {
      obj.url = match[5];
    }

    return obj;
  },

  resolve: function (byTypeAndName) {

    utils.eachItem(byTypeAndName, function (item) {
      if (utils.isset(item.requires)) {
        item.requires = item.requires.map(function (req) {
          if (req.external === true) {
            return req;
          }

          if (utils.isset(byTypeAndName[req.type]) &&
              utils.isset(byTypeAndName[req.type][req.name])) {

            var reqItem = byTypeAndName[req.type][req.name];

            if (!Array.isArray(reqItem.usedBy)) {
              reqItem.usedBy = [];
            }
            reqItem.usedBy.push(item);
            req.item = reqItem;

          } else {
            logger.log('Item `' + item.context.name +
              '` requires `' + req.name + '` from type `' + req.type +
              '` but this item doesn\'t exist.');
          }

          return req;
        });
      }
    });

  }
};
<commit_msg>Fix for #167<commit_after>'use strict';

var reqRegEx = /^\s*(?:\{(.*)\})?\s*(?:(\$?[^\s]+))?\s*(?:\((.*)\))?\s*(?:-?\s*([^<$]*))?\s*(?:<?\s*(.*)\s*>)?$/;

var utils = require('../../utils');
var logger = require('../../log');

module.exports = {

  parse: function (text) {
    var match = reqRegEx.exec(text.trim());

    var obj = {
      type: match[1] || 'function',
      name: match[2]
    };

    obj.external = utils.splitNamespace(obj.name).length > 1;

    if (obj.name.indexOf('$') === 0) {
      obj.type = 'variable';
      obj.name = obj.name.slice(1);
    }

    if (match[4]) {
      obj.description = match[4].trim();
    }

    if (match[5]) {
      obj.url = match[5];
    }

    return obj;
  },

  resolve: function (byTypeAndName) {

    utils.eachItem(byTypeAndName, function (item) {
      if (utils.isset(item.requires)) {
        item.requires = item.requires.map(function (req) {
          if (req.external === true) {
            return req;
          }

          if (utils.isset(byTypeAndName[req.type]) &&
              utils.isset(byTypeAndName[req.type][req.name])) {

            var reqItem = byTypeAndName[req.type][req.name];

            if (!Array.isArray(reqItem.usedBy)) {
              reqItem.usedBy = [];
            }
            reqItem.usedBy.push(item);
            req.item = reqItem;

            return reqItem;
          } else {
            logger.log('Item `' + item.context.name +
              '` requires `' + req.name + '` from type `' + req.type +
              '` but this item doesn\'t exist.');
          }

          return req;
        });
      }
    });

  }
};
<|endoftext|>


<commit_before>var React = require('react');

var PlayButton = require('./PlayButton');
var PlayerTexture = require('./PlayerTexture');
var PlayerGradient = require('./PlayerGradient');
var PlayInlineStatus = require('./PlayInlineStatus');
var PlayVolume = require('./PlayVolume');
var PlayMenu = require('./PlayMenu');
var PlayList = require('./PlayList');
var PlayProgress = require('./PlayProgress');
var utils= require('../utilities/helpers');

var classNames = require('classnames');

var StandardUIPlayer = React.createClass({

  getInitialState: function() {
    return {
      grabbing: false
    };
  },
  componentWillMount: function() {
  },
  componentDidMount: function(){
  },
  componentWillUnmount: function(){
  },
  handleMouseDown: function(e){
    this.setState({grabbing:true});
  },
  handleMouseUp: function(e){
    this.setState({
      grabbing: false
    });
  },
  handleClick: function(e){
    this.props.sound.togglePause();
  },
  render: function(){
    var playing = (this.props.sound.playState == 0) ? false : !this.props.sound.paused;
    var classes = classNames('sm2-bar-ui',
    {'full-width': this.props.fullWidth },
    {playing: playing },
    {grabbing: this.state.grabbing },
    {buffering: this.props.sound.isBuffering },
    {paused: !playing })
    return (
      /*jshint ignore:start */

          <PlayerTexture sound={this.props.sound} update={this.update} />
          <PlayerGradient sound={this.props.sound} update={this.update}  />
          <PlayButton handleClick={this.handleClick} sound={this.props.sound} />
          <PlayInlineStatus grabbingOn={this.handleMouseDown}
                            grabbingOff={this.handleMouseUp}
                            songName={this.props.songName}
                            author={this.props.author}
                            sound={this.props.sound}
                            position={this.props.sound.position}
                            duration={this.props.sound.durationEstimate} />
          <PlayVolume sound={this.props.sound} update={this.update} />
          <PlayMenu sound={this.props.sound} update={this.update} />

      /*jshint ignore:end */
    )
  }
});

module.exports = StandardUIPlayer;
<commit_msg>remove unused update prop<commit_after>var React = require('react');

var PlayButton = require('./PlayButton');
var PlayerTexture = require('./PlayerTexture');
var PlayerGradient = require('./PlayerGradient');
var PlayInlineStatus = require('./PlayInlineStatus');
var PlayVolume = require('./PlayVolume');
var PlayMenu = require('./PlayMenu');
var PlayList = require('./PlayList');
var PlayProgress = require('./PlayProgress');
var utils= require('../utilities/helpers');

var classNames = require('classnames');

var StandardUIPlayer = React.createClass({

  getInitialState: function() {
    return {
      grabbing: false
    };
  },
  componentWillMount: function() {
  },
  componentDidMount: function(){
  },
  componentWillUnmount: function(){
  },
  handleMouseDown: function(e){
    this.setState({grabbing:true});
  },
  handleMouseUp: function(e){
    this.setState({
      grabbing: false
    });
  },
  handleClick: function(e){
    this.props.sound.togglePause();
  },
  render: function(){
    var playing = (this.props.sound.playState == 0) ? false : !this.props.sound.paused;
    var classes = classNames('sm2-bar-ui',
    {'full-width': this.props.fullWidth },
    {playing: playing },
    {grabbing: this.state.grabbing },
    {buffering: this.props.sound.isBuffering },
    {paused: !playing })
    return (
      /*jshint ignore:start */

          <PlayerTexture sound={this.props.sound} />
          <PlayerGradient sound={this.props.sound} />
          <PlayButton handleClick={this.handleClick} sound={this.props.sound} />
          <PlayInlineStatus grabbingOn={this.handleMouseDown}
                            grabbingOff={this.handleMouseUp}
                            songName={this.props.songName}
                            author={this.props.author}
                            sound={this.props.sound}
                            position={this.props.sound.position}
                            duration={this.props.sound.durationEstimate} />
          <PlayVolume sound={this.props.sound} />
          <PlayMenu sound={this.props.sound} />

      /*jshint ignore:end */
    )
  }
});

module.exports = StandardUIPlayer;
<|endoftext|>


<commit_before>function file_get_contents(url, flags, context, offset, maxLen) {
  //  discuss at: http://phpjs.org/functions/file_get_contents/
  // original by: Legaev Andrey
  //    input by: Jani Hartikainen
  //    input by: Raphael (Ao) RUDLER
  // improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
  // improved by: Brett Zamir (http://brett-zamir.me)
  // bugfixed by: Brett Zamir (http://brett-zamir.me)
  //        note: This function uses XmlHttpRequest and cannot retrieve resource from different domain without modifications.
  //        note: Synchronous by default (as in PHP) so may lock up browser. Can
  //        note: get async by setting a custom "phpjs.async" property to true and "notification" for an
  //        note: optional callback (both as context params, with responseText, and other JS-specific
  //        note: request properties available via 'this'). Note that file_get_contents() will not return the text
  //        note: in such a case (use this.responseText within the callback). Or, consider using
  //        note: jQuery's: $('#divId').load('http://url') instead.
  //        note: The context argument is only implemented for http, and only partially (see below for
  //        note: "Presently unimplemented HTTP context options"); also the arguments passed to
  //        note: notification are incomplete
  //        test: skip
  //   example 1: var buf file_get_contents('http://google.com');
  //   example 1: buf.indexOf('Google') !== -1
  //   returns 1: true

  var tmp, headers = [],
    newTmp = [],
    k = 0,
    i = 0,
    href = '',
    pathPos = -1,
    flagNames = 0,
    content = null,
    http_stream = false;
  var func = function(value) {
    return value.substring(1) !== '';
  };

  // BEGIN REDUNDANT
  this.php_js = this.php_js || {};
  this.php_js.ini = this.php_js.ini || {};
  // END REDUNDANT
  var ini = this.php_js.ini;
  context = context || this.php_js.default_streams_context || null;

  if (!flags) {
    flags = 0;
  }
  var OPTS = {
    FILE_USE_INCLUDE_PATH: 1,
    FILE_TEXT: 32,
    FILE_BINARY: 64
  };
  if (typeof flags === 'number') { // Allow for a single string or an array of string flags
    flagNames = flags;
  } else {
    flags = [].concat(flags);
    for (i = 0; i < flags.length; i++) {
      if (OPTS[flags[i]]) {
        flagNames = flagNames | OPTS[flags[i]];
      }
    }
  }

  if (flagNames & OPTS.FILE_BINARY && (flagNames & OPTS.FILE_TEXT)) { // These flags shouldn't be together
    throw 'You cannot pass both FILE_BINARY and FILE_TEXT to file_get_contents()';
  }

  if ((flagNames & OPTS.FILE_USE_INCLUDE_PATH) && ini.include_path && ini.include_path.local_value) {
    var slash = ini.include_path.local_value.indexOf('/') !== -1 ? '/' : '\\';
    url = ini.include_path.local_value + slash + url;
  } else if (!/^(https?|file):/.test(url)) { // Allow references within or below the same directory (should fix to allow other relative references or root reference; could make dependent on parse_url())
    href = this.window.location.href;
    pathPos = url.indexOf('/') === 0 ? href.indexOf('/', 8) - 1 : href.lastIndexOf('/');
    url = href.slice(0, pathPos + 1) + url;
  }

  var http_options;
  if (context) {
    http_options = context.stream_options && context.stream_options.http;
    http_stream = !! http_options;
  }

  if (!context || http_stream) {
    var req = this.window.ActiveXObject ? new ActiveXObject('Microsoft.XMLHTTP') : new XMLHttpRequest();
    if (!req) {
      throw new Error('XMLHttpRequest not supported');
    }

    var method = http_stream ? http_options.method : 'GET';
    var async = !! (context && context.stream_params && context.stream_params['phpjs.async']);

    if (ini['phpjs.ajaxBypassCache'] && ini['phpjs.ajaxBypassCache'].local_value) {
      url += (url.match(/\?/) == null ? '?' : '&') + (new Date())
        .getTime(); // Give optional means of forcing bypass of cache
    }

    req.open(method, url, async);
    if (async) {
      var notification = context.stream_params.notification;
      if (typeof notification === 'function') {
        // Fix: make work with req.addEventListener if available: https://developer.mozilla.org/En/Using_XMLHttpRequest
        if (0 && req.addEventListener) { // Unimplemented so don't allow to get here
          /*
          req.addEventListener('progress', updateProgress, false);
          req.addEventListener('load', transferComplete, false);
          req.addEventListener('error', transferFailed, false);
          req.addEventListener('abort', transferCanceled, false);
          */
        } else {
          req.onreadystatechange = function(aEvt) { // aEvt has stopPropagation(), preventDefault(); see https://developer.mozilla.org/en/NsIDOMEvent
            // Other XMLHttpRequest properties: multipart, responseXML, status, statusText, upload, withCredentials
            /*
  PHP Constants:
  STREAM_NOTIFY_RESOLVE   1       A remote address required for this stream has been resolved, or the resolution failed. See severity  for an indication of which happened.
  STREAM_NOTIFY_CONNECT   2     A connection with an external resource has been established.
  STREAM_NOTIFY_AUTH_REQUIRED 3     Additional authorization is required to access the specified resource. Typical issued with severity level of STREAM_NOTIFY_SEVERITY_ERR.
  STREAM_NOTIFY_MIME_TYPE_IS  4     The mime-type of resource has been identified, refer to message for a description of the discovered type.
  STREAM_NOTIFY_FILE_SIZE_IS  5     The size of the resource has been discovered.
  STREAM_NOTIFY_REDIRECTED    6     The external resource has redirected the stream to an alternate location. Refer to message .
  STREAM_NOTIFY_PROGRESS  7     Indicates current progress of the stream transfer in bytes_transferred and possibly bytes_max as well.
  STREAM_NOTIFY_COMPLETED 8     There is no more data available on the stream.
  STREAM_NOTIFY_FAILURE   9     A generic error occurred on the stream, consult message and message_code for details.
  STREAM_NOTIFY_AUTH_RESULT   10     Authorization has been completed (with or without success).

  STREAM_NOTIFY_SEVERITY_INFO 0     Normal, non-error related, notification.
  STREAM_NOTIFY_SEVERITY_WARN 1     Non critical error condition. Processing may continue.
  STREAM_NOTIFY_SEVERITY_ERR  2     A critical error occurred. Processing cannot continue.
  */
            var objContext = {
              responseText: req.responseText,
              responseXML: req.responseXML,
              status: req.status,
              statusText: req.statusText,
              readyState: req.readyState,
              evt: aEvt
            }; // properties are not available in PHP, but offered on notification via 'this' for convenience
            // notification args: notification_code, severity, message, message_code, bytes_transferred, bytes_max (all int's except string 'message')
            // Need to add message, etc.
            var bytes_transferred;
            switch (req.readyState) {
              case 0:
                //     UNINITIALIZED     open() has not been called yet.
                notification.call(objContext, 0, 0, '', 0, 0, 0);
                break;
              case 1:
                //     LOADING     send() has not been called yet.
                notification.call(objContext, 0, 0, '', 0, 0, 0);
                break;
              case 2:
                //     LOADED     send() has been called, and headers and status are available.
                notification.call(objContext, 0, 0, '', 0, 0, 0);
                break;
              case 3:
                //     INTERACTIVE     Downloading; responseText holds partial data.
                bytes_transferred = req.responseText.length * 2; // One character is two bytes
                notification.call(objContext, 7, 0, '', 0, bytes_transferred, 0);
                break;
              case 4:
                //     COMPLETED     The operation is complete.
                if (req.status >= 200 && req.status < 400) {
                  bytes_transferred = req.responseText.length * 2; // One character is two bytes
                  notification.call(objContext, 8, 0, '', req.status, bytes_transferred, 0);
                } else if (req.status === 403) { // Fix: These two are finished except for message
                  notification.call(objContext, 10, 2, '', req.status, 0, 0);
                } else { // Errors
                  notification.call(objContext, 9, 2, '', req.status, 0, 0);
                }
                break;
              default:
                throw 'Unrecognized ready state for file_get_contents()';
            }
          };
        }
      }
    }

    if (http_stream) {
      var sendHeaders = http_options.header && http_options.header.split(/\r?\n/);
      var userAgentSent = false;
      for (i = 0; i < sendHeaders.length; i++) {
        var sendHeader = sendHeaders[i];
        var breakPos = sendHeader.search(/:\s*/);
        var sendHeaderName = sendHeader.substring(0, breakPos);
        req.setRequestHeader(sendHeaderName, sendHeader.substring(breakPos + 1));
        if (sendHeaderName === 'User-Agent') {
          userAgentSent = true;
        }
      }
      if (!userAgentSent) {
        var user_agent = http_options.user_agent || (ini.user_agent && ini.user_agent.local_value);
        if (user_agent) {
          req.setRequestHeader('User-Agent', user_agent);
        }
      }
      content = http_options.content || null;
      /*
      // Presently unimplemented HTTP context options
      var request_fulluri = http_options.request_fulluri || false; // When set to TRUE, the entire URI will be used when constructing the request. (i.e. GET http://www.example.com/path/to/file.html HTTP/1.0). While this is a non-standard request format, some proxy servers require it.
      var max_redirects = http_options.max_redirects || 20; // The max number of redirects to follow. Value 1 or less means that no redirects are followed.
      var protocol_version = http_options.protocol_version || 1.0; // HTTP protocol version
      var timeout = http_options.timeout || (ini.default_socket_timeout && ini.default_socket_timeout.local_value); // Read timeout in seconds, specified by a float
      var ignore_errors = http_options.ignore_errors || false; // Fetch the content even on failure status codes.
      */
    }

    if (flagNames & OPTS.FILE_TEXT) { // Overrides how encoding is treated (regardless of what is returned from the server)
      var content_type = 'text/html';
      if (http_options && http_options['phpjs.override']) { // Fix: Could allow for non-HTTP as well
        content_type = http_options['phpjs.override']; // We use this, e.g., in gettext-related functions if character set
        //   overridden earlier by bind_textdomain_codeset()
      } else {
        var encoding = (ini['unicode.stream_encoding'] && ini['unicode.stream_encoding'].local_value) ||
          'UTF-8';
        if (http_options && http_options.header && (/^content-type:/im)
          .test(http_options.header)) { // We'll assume a content-type expects its own specified encoding if present
          content_type = http_options.header.match(/^content-type:\s*(.*)$/im)[1]; // We let any header encoding stand
        }
        if (!(/;\s*charset=/)
          .test(content_type)) { // If no encoding
          content_type += '; charset=' + encoding;
        }
      }
      req.overrideMimeType(content_type);
    }
    // Default is FILE_BINARY, but for binary, we apparently deviate from PHP in requiring the flag, since many if not
    //     most people will also want a way to have it be auto-converted into native JavaScript text instead
    else if (flagNames & OPTS.FILE_BINARY) { // Trick at https://developer.mozilla.org/En/Using_XMLHttpRequest to get binary
      req.overrideMimeType('text/plain; charset=x-user-defined');
      // Getting an individual byte then requires:
      // responseText.charCodeAt(x) & 0xFF; // throw away high-order byte (f7) where x is 0 to responseText.length-1 (see notes in our substr())
    }

    try {
      if (http_options && http_options['phpjs.sendAsBinary']) { // For content sent in a POST or PUT request (use with file_put_contents()?)
        req.sendAsBinary(content); // In Firefox, only available FF3+
      } else {
        req.send(content);
      }
    } catch (e) {
      // catches exception reported in issue #66
      return false;
    }

    tmp = req.getAllResponseHeaders();
    if (tmp) {
      tmp = tmp.split('\n');
      for (k = 0; k < tmp.length; k++) {
        if (func(tmp[k])) {
          newTmp.push(tmp[k]);
        }
      }
      tmp = newTmp;
      for (i = 0; i < tmp.length; i++) {
        headers[i] = tmp[i];
      }
      this.$http_response_header = headers; // see http://php.net/manual/en/reserved.variables.httpresponseheader.php
    }

    if (offset || maxLen) {
      if (maxLen) {
        return req.responseText.substr(offset || 0, maxLen);
      }
      return req.responseText.substr(offset);
    }
    return req.responseText;
  }
  return false;
}<commit_msg>Fix async case when no headers are supplied<commit_after>function file_get_contents(url, flags, context, offset, maxLen) {
  //  discuss at: http://phpjs.org/functions/file_get_contents/
  // original by: Legaev Andrey
  //    input by: Jani Hartikainen
  //    input by: Raphael (Ao) RUDLER
  // improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
  // improved by: Brett Zamir (http://brett-zamir.me)
  // bugfixed by: Brett Zamir (http://brett-zamir.me)
  //        note: This function uses XmlHttpRequest and cannot retrieve resource from different domain without modifications.
  //        note: Synchronous by default (as in PHP) so may lock up browser. Can
  //        note: get async by setting a custom "phpjs.async" property to true and "notification" for an
  //        note: optional callback (both as context params, with responseText, and other JS-specific
  //        note: request properties available via 'this'). Note that file_get_contents() will not return the text
  //        note: in such a case (use this.responseText within the callback). Or, consider using
  //        note: jQuery's: $('#divId').load('http://url') instead.
  //        note: The context argument is only implemented for http, and only partially (see below for
  //        note: "Presently unimplemented HTTP context options"); also the arguments passed to
  //        note: notification are incomplete
  //        test: skip
  //   example 1: var buf file_get_contents('http://google.com');
  //   example 1: buf.indexOf('Google') !== -1
  //   returns 1: true

  var tmp, headers = [],
    newTmp = [],
    k = 0,
    i = 0,
    href = '',
    pathPos = -1,
    flagNames = 0,
    content = null,
    http_stream = false;
  var func = function(value) {
    return value.substring(1) !== '';
  };

  // BEGIN REDUNDANT
  this.php_js = this.php_js || {};
  this.php_js.ini = this.php_js.ini || {};
  // END REDUNDANT
  var ini = this.php_js.ini;
  context = context || this.php_js.default_streams_context || null;

  if (!flags) {
    flags = 0;
  }
  var OPTS = {
    FILE_USE_INCLUDE_PATH: 1,
    FILE_TEXT: 32,
    FILE_BINARY: 64
  };
  if (typeof flags === 'number') { // Allow for a single string or an array of string flags
    flagNames = flags;
  } else {
    flags = [].concat(flags);
    for (i = 0; i < flags.length; i++) {
      if (OPTS[flags[i]]) {
        flagNames = flagNames | OPTS[flags[i]];
      }
    }
  }

  if (flagNames & OPTS.FILE_BINARY && (flagNames & OPTS.FILE_TEXT)) { // These flags shouldn't be together
    throw 'You cannot pass both FILE_BINARY and FILE_TEXT to file_get_contents()';
  }

  if ((flagNames & OPTS.FILE_USE_INCLUDE_PATH) && ini.include_path && ini.include_path.local_value) {
    var slash = ini.include_path.local_value.indexOf('/') !== -1 ? '/' : '\\';
    url = ini.include_path.local_value + slash + url;
  } else if (!/^(https?|file):/.test(url)) { // Allow references within or below the same directory (should fix to allow other relative references or root reference; could make dependent on parse_url())
    href = this.window.location.href;
    pathPos = url.indexOf('/') === 0 ? href.indexOf('/', 8) - 1 : href.lastIndexOf('/');
    url = href.slice(0, pathPos + 1) + url;
  }

  var http_options;
  if (context) {
    http_options = context.stream_options && context.stream_options.http;
    http_stream = !!http_options;
  }

  if (!context || http_stream) {
    var req = this.window.ActiveXObject ? new ActiveXObject('Microsoft.XMLHTTP') : new XMLHttpRequest();
    if (!req) {
      throw new Error('XMLHttpRequest not supported');
    }

    var method = http_stream ? http_options.method : 'GET';
    var async = !! (context && context.stream_params && context.stream_params['phpjs.async']);

    if (ini['phpjs.ajaxBypassCache'] && ini['phpjs.ajaxBypassCache'].local_value) {
      url += (url.match(/\?/) == null ? '?' : '&') + (new Date())
        .getTime(); // Give optional means of forcing bypass of cache
    }

    req.open(method, url, async);
    if (async) {
      var notification = context.stream_params.notification;
      if (typeof notification === 'function') {
        // Fix: make work with req.addEventListener if available: https://developer.mozilla.org/En/Using_XMLHttpRequest
        if (0 && req.addEventListener) { // Unimplemented so don't allow to get here
          /*
          req.addEventListener('progress', updateProgress, false);
          req.addEventListener('load', transferComplete, false);
          req.addEventListener('error', transferFailed, false);
          req.addEventListener('abort', transferCanceled, false);
          */
        } else {
          req.onreadystatechange = function(aEvt) { // aEvt has stopPropagation(), preventDefault(); see https://developer.mozilla.org/en/NsIDOMEvent
            // Other XMLHttpRequest properties: multipart, responseXML, status, statusText, upload, withCredentials
            /*
  PHP Constants:
  STREAM_NOTIFY_RESOLVE   1       A remote address required for this stream has been resolved, or the resolution failed. See severity  for an indication of which happened.
  STREAM_NOTIFY_CONNECT   2     A connection with an external resource has been established.
  STREAM_NOTIFY_AUTH_REQUIRED 3     Additional authorization is required to access the specified resource. Typical issued with severity level of STREAM_NOTIFY_SEVERITY_ERR.
  STREAM_NOTIFY_MIME_TYPE_IS  4     The mime-type of resource has been identified, refer to message for a description of the discovered type.
  STREAM_NOTIFY_FILE_SIZE_IS  5     The size of the resource has been discovered.
  STREAM_NOTIFY_REDIRECTED    6     The external resource has redirected the stream to an alternate location. Refer to message .
  STREAM_NOTIFY_PROGRESS  7     Indicates current progress of the stream transfer in bytes_transferred and possibly bytes_max as well.
  STREAM_NOTIFY_COMPLETED 8     There is no more data available on the stream.
  STREAM_NOTIFY_FAILURE   9     A generic error occurred on the stream, consult message and message_code for details.
  STREAM_NOTIFY_AUTH_RESULT   10     Authorization has been completed (with or without success).

  STREAM_NOTIFY_SEVERITY_INFO 0     Normal, non-error related, notification.
  STREAM_NOTIFY_SEVERITY_WARN 1     Non critical error condition. Processing may continue.
  STREAM_NOTIFY_SEVERITY_ERR  2     A critical error occurred. Processing cannot continue.
  */
            var objContext = {
              responseText: req.responseText,
              responseXML: req.responseXML,
              status: req.status,
              statusText: req.statusText,
              readyState: req.readyState,
              evt: aEvt
            }; // properties are not available in PHP, but offered on notification via 'this' for convenience
            // notification args: notification_code, severity, message, message_code, bytes_transferred, bytes_max (all int's except string 'message')
            // Need to add message, etc.
            var bytes_transferred;
            switch (req.readyState) {
              case 0:
                //     UNINITIALIZED     open() has not been called yet.
                notification.call(objContext, 0, 0, '', 0, 0, 0);
                break;
              case 1:
                //     LOADING     send() has not been called yet.
                notification.call(objContext, 0, 0, '', 0, 0, 0);
                break;
              case 2:
                //     LOADED     send() has been called, and headers and status are available.
                notification.call(objContext, 0, 0, '', 0, 0, 0);
                break;
              case 3:
                //     INTERACTIVE     Downloading; responseText holds partial data.
                bytes_transferred = req.responseText.length * 2; // One character is two bytes
                notification.call(objContext, 7, 0, '', 0, bytes_transferred, 0);
                break;
              case 4:
                //     COMPLETED     The operation is complete.
                if (req.status >= 200 && req.status < 400) {
                  bytes_transferred = req.responseText.length * 2; // One character is two bytes
                  notification.call(objContext, 8, 0, '', req.status, bytes_transferred, 0);
                } else if (req.status === 403) { // Fix: These two are finished except for message
                  notification.call(objContext, 10, 2, '', req.status, 0, 0);
                } else { // Errors
                  notification.call(objContext, 9, 2, '', req.status, 0, 0);
                }
                break;
              default:
                throw 'Unrecognized ready state for file_get_contents()';
            }
          };
        }
      }
    }

    if (http_stream) {
      var sendHeaders = (http_options.header && http_options.header.split(/\r?\n/)) || [];
      var userAgentSent = false;
      for (i = 0; i < sendHeaders.length; i++) {
        var sendHeader = sendHeaders[i];
        var breakPos = sendHeader.search(/:\s*/);
        var sendHeaderName = sendHeader.substring(0, breakPos);
        req.setRequestHeader(sendHeaderName, sendHeader.substring(breakPos + 1));
        if (sendHeaderName === 'User-Agent') {
          userAgentSent = true;
        }
      }
      if (!userAgentSent) {
        var user_agent = http_options.user_agent || (ini.user_agent && ini.user_agent.local_value);
        if (user_agent) {
          req.setRequestHeader('User-Agent', user_agent);
        }
      }
      content = http_options.content || null;
      /*
      // Presently unimplemented HTTP context options
      var request_fulluri = http_options.request_fulluri || false; // When set to TRUE, the entire URI will be used when constructing the request. (i.e. GET http://www.example.com/path/to/file.html HTTP/1.0). While this is a non-standard request format, some proxy servers require it.
      var max_redirects = http_options.max_redirects || 20; // The max number of redirects to follow. Value 1 or less means that no redirects are followed.
      var protocol_version = http_options.protocol_version || 1.0; // HTTP protocol version
      var timeout = http_options.timeout || (ini.default_socket_timeout && ini.default_socket_timeout.local_value); // Read timeout in seconds, specified by a float
      var ignore_errors = http_options.ignore_errors || false; // Fetch the content even on failure status codes.
      */
    }

    if (flagNames & OPTS.FILE_TEXT) { // Overrides how encoding is treated (regardless of what is returned from the server)
      var content_type = 'text/html';
      if (http_options && http_options['phpjs.override']) { // Fix: Could allow for non-HTTP as well
        content_type = http_options['phpjs.override']; // We use this, e.g., in gettext-related functions if character set
        //   overridden earlier by bind_textdomain_codeset()
      } else {
        var encoding = (ini['unicode.stream_encoding'] && ini['unicode.stream_encoding'].local_value) ||
          'UTF-8';
        if (http_options && http_options.header && (/^content-type:/im)
          .test(http_options.header)) { // We'll assume a content-type expects its own specified encoding if present
          content_type = http_options.header.match(/^content-type:\s*(.*)$/im)[1]; // We let any header encoding stand
        }
        if (!(/;\s*charset=/)
          .test(content_type)) { // If no encoding
          content_type += '; charset=' + encoding;
        }
      }
      req.overrideMimeType(content_type);
    }
    // Default is FILE_BINARY, but for binary, we apparently deviate from PHP in requiring the flag, since many if not
    //     most people will also want a way to have it be auto-converted into native JavaScript text instead
    else if (flagNames & OPTS.FILE_BINARY) { // Trick at https://developer.mozilla.org/En/Using_XMLHttpRequest to get binary
      req.overrideMimeType('text/plain; charset=x-user-defined');
      // Getting an individual byte then requires:
      // responseText.charCodeAt(x) & 0xFF; // throw away high-order byte (f7) where x is 0 to responseText.length-1 (see notes in our substr())
    }

    try {
      if (http_options && http_options['phpjs.sendAsBinary']) { // For content sent in a POST or PUT request (use with file_put_contents()?)
        req.sendAsBinary(content); // In Firefox, only available FF3+
      } else {
        req.send(content);
      }
    } catch (e) {
      // catches exception reported in issue #66
      return false;
    }

    tmp = req.getAllResponseHeaders();
    if (tmp) {
      tmp = tmp.split('\n');
      for (k = 0; k < tmp.length; k++) {
        if (func(tmp[k])) {
          newTmp.push(tmp[k]);
        }
      }
      tmp = newTmp;
      for (i = 0; i < tmp.length; i++) {
        headers[i] = tmp[i];
      }
      this.$http_response_header = headers; // see http://php.net/manual/en/reserved.variables.httpresponseheader.php
    }

    if (offset || maxLen) {
      if (maxLen) {
        return req.responseText.substr(offset || 0, maxLen);
      }
      return req.responseText.substr(offset);
    }
    return req.responseText;
  }
  return false;
}<|endoftext|>


<commit_before>/*
 * File: jetpack.Package.js
 */

/*
 * Javascript Package/PackageRevision representation
 */

var Package = new Class({
	// this Class should be always extended
	Implements: [Options],
	options: {
		// data
			// package specific
				// id_number: '',
				// full_name: '',
				// name: '',
				// description: '',
				// type: '', // 'a'/'l'
				// package_author: '',
				// url: '',
				// license: '',
				// package_version_name: '',
				// version_url: '', // link to the current version revision
				// latest_utl: '', // link to the latest revision
			// revision specific data
				// revision_verion_name: '',
				// revision_number: '',
				// message: '', // commit message
				// dependecies: [], // list of names and urls
				// origin_url: '', // link to a revision used to created this one
				// revision_author: '',
				// modules: [], // a list of module filename, author pairs
		readonly: false,
		package_info_el: 'package-info',
		test_el: 'try_in_browser'
	},
	modules: {},
	initialize: function(options) {
		this.setOptions(options);
		this.instantiate_modules();
		$('revisions_list').addEvent('click', this.show_revision_list);

		// testing
		this.boundTestAddon = this.testAddon.bind(this);
		if (this.isAddon()) {
			this.test_url = $(this.options.test_el).get('href');
			$(this.options.test_el).addEvent('click', this.boundTestAddon)
		}
	},
	testAddon: function(e){
		if (e) e.stop();
		if (fd.alertIfNoAddOn()) {
			new Request.JSON({
				url: this.test_url,
				data: this.data || {},
				onSuccess: fd.testXPI.bind(fd)
			}).send();
		}
	},
	isAddon: function() {
		return (this.options.type == 'a');
	},
	instantiate_modules: function() {
		// iterate by modules and instantiate Module
		var main_module;
		this.options.modules.each(function(module) {
			module.readonly = this.options.readonly;
			if (!main_module) {
				module.main = true;
				main_module = module;
			}
			this.modules[module.filename] = new Module(module);
		}, this);
	},
	show_revision_list: function(e) {
		if (e) e.stop();
		new Request({
			url: settings.revisions_list_html_url,
			onSuccess: function(html) {
				fd.displayModal(html);
			}
		}).send();
	}
});

var Module = new Class({
	Implements: [Options, Events],
	options: {
		// data
			// filename: '',
			// code: '',
			// author: '',
		// DOM
			code_trigger_suffix: '_switch', // id of an element which is used to switch editors
			code_editor_suffix: '_textarea', // id of the textarea
		readonly: false,
		main: false,
		executable: false,
		active: false,
		type: 'js',
		append: false
	},
	initialize: function(options) {
		this.setOptions(options);
		if (this.options.append) {
			this.append();
		}
		// connect trigger with editor
		if ($(this.get_trigger_id()) && $(this.get_editor_id())) {
			this.textarea = $(this.get_editor_id());
			this.trigger = $(this.get_trigger_id());
			this.editor = new FDEditor({
				element: this.get_editor_id(),
				activate: this.options.main || this.options.executable,
				type: this.options.type,
				readonly: this.options.readonly
			});
			// connect trigger
			this.trigger.addEvent('click', function(e) {
				if (e) e.preventDefault();
				// placeholder for switching editors
				fd.switchBespinEditor(this.get_editor_id(), this.options.type);
			}.bind(this));
			if (this.options.main || this.options.executable) {
				this.trigger.getParent('li').switch_mode_on();
			}
			if (this.options.active) {
				fd.switchBespinEditor(this.get_editor_id(), this.options.type);
				var li = this.trigger.getParent('li')
				fd.assignModeSwitch(li);
				li.switch_mode_on();
			}
			if (!this.options.readonly) {
				// here special functionality for edit page
			}
		}
	},
	get_editor_id: function() {
		if (!this._editor_id)
			this._editor_id = this.options.filename + this.options.code_editor_suffix;
		return this._editor_id;
	},
	get_trigger_id: function() {
		if (!this._trigger_id)
			this._trigger_id = this.options.filename + this.options.code_trigger_suffix;
		return this._trigger_id;
	},
	append: function() {
		var html = ''+
						'{filename}'+
						''+
					'';
		var li = new Element('li',{
			'class': 'UI_File_normal',
			'html': html.substitute(this.options)
		}).inject($('add_module_div').getPrevious('ul'));

		var textarea = new Element('textarea', {
			'id': this.options.filename + '_textarea',
			'class': 'UI_Editor_Area',
			'name': this.options.filename + '_textarea',
			'html': this.options.code
		}).inject('editor-wrapper');
	}
})

Package.View = new Class({
	Extends: Package,
	Implements: [Options, Events],
	options: {
		readonly: true,
		copy_el: 'package-copy',
		// copy_url: '',
	},
	initialize: function(options) {
		this.setOptions(options);
		this.parent(options);
		$(this.options.package_info_el).addEvent('click', this.showInfo.bind(this));
		this.copy_el = $(this.options.copy_el)
		if (this.copy_el) {
			this.copy_el.addEvent('click', this.copyPackage.bind(this));
		}
	},
	/*
	 * Method: showInfo
	   display a window with info about current Package
	 */
	showInfo: function(e) {
		e.stop();
		fd.displayModal(this.options.package_info);
	},
	/*
	 * Method: copyPackage
	 * create a new Package with the same name for the current user
	 */
	copyPackage: function(e) {
		e.stop();
		if (!settings.user) {
			fd.alertNotAuthenticated();
			return;
		}
		new Request.JSON({
			url: this.options.copy_url,
			onSuccess: function(response) {
				window.location.href = response.edit_url;
			}
		}).send();
	}
});

Package.Edit = new Class({
	Extends: Package,
	Implements: [Options, Events],
	options: {
		// DOM elements
			save_el: 'package-save',
			menu_el: 'UI_Editor_Menu',
			assign_library_el: 'assign_library_action',
			assign_library_input: 'library_id_number',
			add_module_el: 'add_module_action',
			add_module_input: 'add_module',

		// urls
			// save_url: '',
			// delete_url: '',
			// add_module_url: '',
			// assign_library_url: '',
		package_info_form_elements: [
			'full_name', 'version_name', 'package_description', 'revision_message'
			]
	},
	initialize: function(options) {
		this.setOptions(options);
		// this.data is a temporary holder of the data for the submit
		this.data = {};
		this.parent(options);
		$(this.options.package_info_el).addEvent('click', this.editInfo.bind(this));
		$(this.options.save_el).addEvent('click', this.save.bind(this));
		this.boundSubmitInfo = this.submitInfo.bind(this);
		this.boundAssignLibraryAction = this.assignLibraryAction.bind(this);
		this.boundAddModuleAction = this.addModuleAction.bind(this);
		$(this.options.add_module_el).addEvent('click',
			this.boundAddModuleAction);
		$(this.options.assign_library_el).addEvent('click',
			this.boundAssignLibraryAction);
		this.autocomplete = new FlightDeck.Autocomplete({
			'url': settings.library_autocomplete_url
		});
	},
	addModuleAction: function(e) {
		e.stop();
		// get data
		var filename = $(this.options.add_module_input).value;
		if (!filename) {
			fd.error.alert('Filename can\'t be empty', 'Please provide the name of the module');
			return;
		}
		if (this.options.modules.contains(filename)) {
			fd.error.alert('Filename has to be unique', 'You already have the module with that name');
			return;
		}
		this.addModule(filename);
	},
	addModule: function(filename) {
		new Request.JSON({
			url: this.add_module_url || this.options.add_module_url,
			data: {'filename': filename},
			onSuccess: function(response) {
				// set the redirect data to edit_url of the new revision
				fd.setURIRedirect(response.edit_url);
				// set data changed by save
				this.setUrls(response);
				fd.message.alert(response.message_title, response.message);
				// initiate new Module
				var mod = new Module({
					append: true,
					active: true,
					filename: response.filename,
					author: response.author,
					code: response.code
				});
				this.modules[response.filename] = mod;
			}.bind(this)
		}).send();
	},
	assignLibraryAction: function(e) {
		// get data
		library_id = $(this.options.assign_library_input).get('value');
		// assign Library by giving filename
		this.assignLibrary(library_id);
	},
	assignLibrary: function(library_id) {
		new Request.JSON({
			url: this.assign_library_url || this.options.assign_library_url,
			data: {'id_number': library_id},
			onSuccess: function(response) {
				// set the redirect data to edit_url of the new revision
				fd.setURIRedirect(response.edit_url);
				// set data changed by save
				this.setUrls(response);
				fd.message.alert('Library assigned', response.message);
				this.appendLibrary(response);
			}.bind(this)
		}).send();
	},
	appendLibrary: function(lib) {
		var html='{full_name}';
		new Element('li', {
			'class': 'UI_File_Normal',
			'html': html.substitute(lib)
		}).inject($('assign_library_div').getPrevious('ul'));
	},
	editInfo: function(e) {
		e.stop();
		this.savenow = false;
		fd.editPackageInfoModal = fd.displayModal(settings.edit_package_info_template.substitute(this.data || this.options));
		$('package-info_form').addEvent('submit', this.boundSubmitInfo);
		$('savenow').addEvent('click', function() {
			this.savenow = true;
		}.bind(this));
		// XXX: hack to get the right data in the form
		$each(this.data, function(value, key) {
			if ($(key)) $(key).value = value;
		})
	},
	submitInfo: function(e) {
		e.stop();
		// collect data from the Modal
		this.options.package_info_form_elements.each(function(key) {
			this.data[key] = $(key).value;
		}, this);
		// check if save should be called
		if (this.savenow) {
			return this.save();
		}
		fd.editPackageInfoModal.destroy();
	},
	collectData: function() {
		fd.saveCurrentEditor();
		$each(this.modules, function(module, filename) {
			this.data[filename] = fd.editor_contents[filename + module.options.code_editor_suffix]
		}, this);
	},
	testAddon: function(e){
		this.collectData();
		this.data.live_data_testing = true;
		this.parent(e);
	},
	save: function(e) {
		if (e) e.stop();
		this.collectData();
		new Request.JSON({
			url: this.save_url || this.options.save_url,
			data: this.data,
			onSuccess: function(response) {
				// set the redirect data to edit_url of the new revision
				if (response.reload) {
				 	window.location.href = response.edit_url;
				}
				fd.setURIRedirect(response.edit_url);
				// set data changed by save
				this.setUrls(response);
				fd.message.alert(response.message_title, response.message);
				// clean data leaving package_info data
				this.data = {};
				this.options.package_info_form_elements.each(function(key) {
					if ($defined(response[key])) {
						this.data[key] = response[key]
					}
				}, this);
				if (fd.editPackageInfoModal) fd.editPackageInfoModal.destroy();
			}.bind(this)
		}).send();
	},
	setUrls: function(urls) {
		this.save_url = urls.save_url;
		this.test_url = urls.test_url;
		this.add_module_url = urls.add_module_url;
		this.assign_library_url = this.assign_library_url;
	}
});
<commit_msg>assigning library does not display a message<commit_after>/*
 * File: jetpack.Package.js
 */

/*
 * Javascript Package/PackageRevision representation
 */

var Package = new Class({
	// this Class should be always extended
	Implements: [Options],
	options: {
		// data
			// package specific
				// id_number: '',
				// full_name: '',
				// name: '',
				// description: '',
				// type: '', // 'a'/'l'
				// package_author: '',
				// url: '',
				// license: '',
				// package_version_name: '',
				// version_url: '', // link to the current version revision
				// latest_utl: '', // link to the latest revision
			// revision specific data
				// revision_verion_name: '',
				// revision_number: '',
				// message: '', // commit message
				// dependecies: [], // list of names and urls
				// origin_url: '', // link to a revision used to created this one
				// revision_author: '',
				// modules: [], // a list of module filename, author pairs
		readonly: false,
		package_info_el: 'package-info',
		test_el: 'try_in_browser'
	},
	modules: {},
	initialize: function(options) {
		this.setOptions(options);
		this.instantiate_modules();
		$('revisions_list').addEvent('click', this.show_revision_list);

		// testing
		this.boundTestAddon = this.testAddon.bind(this);
		if (this.isAddon()) {
			this.test_url = $(this.options.test_el).get('href');
			$(this.options.test_el).addEvent('click', this.boundTestAddon)
		}
	},
	testAddon: function(e){
		if (e) e.stop();
		if (fd.alertIfNoAddOn()) {
			new Request.JSON({
				url: this.test_url,
				data: this.data || {},
				onSuccess: fd.testXPI.bind(fd)
			}).send();
		}
	},
	isAddon: function() {
		return (this.options.type == 'a');
	},
	instantiate_modules: function() {
		// iterate by modules and instantiate Module
		var main_module;
		this.options.modules.each(function(module) {
			module.readonly = this.options.readonly;
			if (!main_module) {
				module.main = true;
				main_module = module;
			}
			this.modules[module.filename] = new Module(module);
		}, this);
	},
	show_revision_list: function(e) {
		if (e) e.stop();
		new Request({
			url: settings.revisions_list_html_url,
			onSuccess: function(html) {
				fd.displayModal(html);
			}
		}).send();
	}
});

var Module = new Class({
	Implements: [Options, Events],
	options: {
		// data
			// filename: '',
			// code: '',
			// author: '',
		// DOM
			code_trigger_suffix: '_switch', // id of an element which is used to switch editors
			code_editor_suffix: '_textarea', // id of the textarea
		readonly: false,
		main: false,
		executable: false,
		active: false,
		type: 'js',
		append: false
	},
	initialize: function(options) {
		this.setOptions(options);
		if (this.options.append) {
			this.append();
		}
		// connect trigger with editor
		if ($(this.get_trigger_id()) && $(this.get_editor_id())) {
			this.textarea = $(this.get_editor_id());
			this.trigger = $(this.get_trigger_id());
			this.editor = new FDEditor({
				element: this.get_editor_id(),
				activate: this.options.main || this.options.executable,
				type: this.options.type,
				readonly: this.options.readonly
			});
			// connect trigger
			this.trigger.addEvent('click', function(e) {
				if (e) e.preventDefault();
				// placeholder for switching editors
				fd.switchBespinEditor(this.get_editor_id(), this.options.type);
			}.bind(this));
			if (this.options.main || this.options.executable) {
				this.trigger.getParent('li').switch_mode_on();
			}
			if (this.options.active) {
				fd.switchBespinEditor(this.get_editor_id(), this.options.type);
				var li = this.trigger.getParent('li')
				fd.assignModeSwitch(li);
				li.switch_mode_on();
			}
			if (!this.options.readonly) {
				// here special functionality for edit page
			}
		}
	},
	get_editor_id: function() {
		if (!this._editor_id)
			this._editor_id = this.options.filename + this.options.code_editor_suffix;
		return this._editor_id;
	},
	get_trigger_id: function() {
		if (!this._trigger_id)
			this._trigger_id = this.options.filename + this.options.code_trigger_suffix;
		return this._trigger_id;
	},
	append: function() {
		var html = ''+
						'{filename}'+
						''+
					'';
		var li = new Element('li',{
			'class': 'UI_File_normal',
			'html': html.substitute(this.options)
		}).inject($('add_module_div').getPrevious('ul'));

		var textarea = new Element('textarea', {
			'id': this.options.filename + '_textarea',
			'class': 'UI_Editor_Area',
			'name': this.options.filename + '_textarea',
			'html': this.options.code
		}).inject('editor-wrapper');
	}
})

Package.View = new Class({
	Extends: Package,
	Implements: [Options, Events],
	options: {
		readonly: true,
		copy_el: 'package-copy',
		// copy_url: '',
	},
	initialize: function(options) {
		this.setOptions(options);
		this.parent(options);
		$(this.options.package_info_el).addEvent('click', this.showInfo.bind(this));
		this.copy_el = $(this.options.copy_el)
		if (this.copy_el) {
			this.copy_el.addEvent('click', this.copyPackage.bind(this));
		}
	},
	/*
	 * Method: showInfo
	   display a window with info about current Package
	 */
	showInfo: function(e) {
		e.stop();
		fd.displayModal(this.options.package_info);
	},
	/*
	 * Method: copyPackage
	 * create a new Package with the same name for the current user
	 */
	copyPackage: function(e) {
		e.stop();
		if (!settings.user) {
			fd.alertNotAuthenticated();
			return;
		}
		new Request.JSON({
			url: this.options.copy_url,
			onSuccess: function(response) {
				window.location.href = response.edit_url;
			}
		}).send();
	}
});

Package.Edit = new Class({
	Extends: Package,
	Implements: [Options, Events],
	options: {
		// DOM elements
			save_el: 'package-save',
			menu_el: 'UI_Editor_Menu',
			assign_library_el: 'assign_library_action',
			assign_library_input: 'library_id_number',
			add_module_el: 'add_module_action',
			add_module_input: 'add_module',

		// urls
			// save_url: '',
			// delete_url: '',
			// add_module_url: '',
			// assign_library_url: '',
		package_info_form_elements: [
			'full_name', 'version_name', 'package_description', 'revision_message'
			]
	},
	initialize: function(options) {
		this.setOptions(options);
		// this.data is a temporary holder of the data for the submit
		this.data = {};
		this.parent(options);
		$(this.options.package_info_el).addEvent('click', this.editInfo.bind(this));
		$(this.options.save_el).addEvent('click', this.save.bind(this));
		this.boundSubmitInfo = this.submitInfo.bind(this);
		this.boundAssignLibraryAction = this.assignLibraryAction.bind(this);
		this.boundAddModuleAction = this.addModuleAction.bind(this);
		$(this.options.add_module_el).addEvent('click',
			this.boundAddModuleAction);
		$(this.options.assign_library_el).addEvent('click',
			this.boundAssignLibraryAction);
		this.autocomplete = new FlightDeck.Autocomplete({
			'url': settings.library_autocomplete_url
		});
	},
	addModuleAction: function(e) {
		e.stop();
		// get data
		var filename = $(this.options.add_module_input).value;
		if (!filename) {
			fd.error.alert('Filename can\'t be empty', 'Please provide the name of the module');
			return;
		}
		if (this.options.modules.contains(filename)) {
			fd.error.alert('Filename has to be unique', 'You already have the module with that name');
			return;
		}
		this.addModule(filename);
	},
	addModule: function(filename) {
		new Request.JSON({
			url: this.add_module_url || this.options.add_module_url,
			data: {'filename': filename},
			onSuccess: function(response) {
				// set the redirect data to edit_url of the new revision
				fd.setURIRedirect(response.edit_url);
				// set data changed by save
				this.setUrls(response);
				fd.message.alert(response.message_title, response.message);
				// initiate new Module
				var mod = new Module({
					append: true,
					active: true,
					filename: response.filename,
					author: response.author,
					code: response.code
				});
				this.modules[response.filename] = mod;
			}.bind(this)
		}).send();
	},
	assignLibraryAction: function(e) {
		// get data
		library_id = $(this.options.assign_library_input).get('value');
		// assign Library by giving filename
		this.assignLibrary(library_id);
	},
	assignLibrary: function(library_id) {
		new Request.JSON({
			url: this.assign_library_url || this.options.assign_library_url,
			data: {'id_number': library_id},
			onSuccess: function(response) {
				// set the redirect data to edit_url of the new revision
				fd.setURIRedirect(response.edit_url);
				// set data changed by save
				this.setUrls(response);
				//fd.message.alert('Library assigned', response.message);
				this.appendLibrary(response);
			}.bind(this)
		}).send();
	},
	appendLibrary: function(lib) {
		var html='{full_name}';
		new Element('li', {
			'class': 'UI_File_Normal',
			'html': html.substitute(lib)
		}).inject($('assign_library_div').getPrevious('ul'));
	},
	editInfo: function(e) {
		e.stop();
		this.savenow = false;
		fd.editPackageInfoModal = fd.displayModal(settings.edit_package_info_template.substitute(this.data || this.options));
		$('package-info_form').addEvent('submit', this.boundSubmitInfo);
		$('savenow').addEvent('click', function() {
			this.savenow = true;
		}.bind(this));
		// XXX: hack to get the right data in the form
		$each(this.data, function(value, key) {
			if ($(key)) $(key).value = value;
		})
	},
	submitInfo: function(e) {
		e.stop();
		// collect data from the Modal
		this.options.package_info_form_elements.each(function(key) {
			this.data[key] = $(key).value;
		}, this);
		// check if save should be called
		if (this.savenow) {
			return this.save();
		}
		fd.editPackageInfoModal.destroy();
	},
	collectData: function() {
		fd.saveCurrentEditor();
		$each(this.modules, function(module, filename) {
			this.data[filename] = fd.editor_contents[filename + module.options.code_editor_suffix]
		}, this);
	},
	testAddon: function(e){
		this.collectData();
		this.data.live_data_testing = true;
		this.parent(e);
	},
	save: function(e) {
		if (e) e.stop();
		this.collectData();
		new Request.JSON({
			url: this.save_url || this.options.save_url,
			data: this.data,
			onSuccess: function(response) {
				// set the redirect data to edit_url of the new revision
				if (response.reload) {
				 	window.location.href = response.edit_url;
				}
				fd.setURIRedirect(response.edit_url);
				// set data changed by save
				this.setUrls(response);
				fd.message.alert(response.message_title, response.message);
				// clean data leaving package_info data
				this.data = {};
				this.options.package_info_form_elements.each(function(key) {
					if ($defined(response[key])) {
						this.data[key] = response[key]
					}
				}, this);
				if (fd.editPackageInfoModal) fd.editPackageInfoModal.destroy();
			}.bind(this)
		}).send();
	},
	setUrls: function(urls) {
		this.save_url = urls.save_url;
		this.test_url = urls.test_url;
		this.add_module_url = urls.add_module_url;
		this.assign_library_url = this.assign_library_url;
	}
});
<|endoftext|>