Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial commit

  • Loading branch information...
commit 170bd520e0de87115f0ba9d4ab9f423853ff745f 0 parents
@ebryn authored
Showing with 3,953 additions and 0 deletions.
  1. +4 −0 .gitignore
  2. +23 −0 .project
  3. +3 −0  .settings/com.appcelerator.titanium.mobile.prefs
  4. +45 −0 README.md
  5. BIN  Resources/android/appicon.png
  6. BIN  Resources/android/default.png
  7. BIN  Resources/android/images/res-long-land-hdpi/default.png
  8. BIN  Resources/android/images/res-long-land-ldpi/default.png
  9. BIN  Resources/android/images/res-long-port-hdpi/default.png
  10. BIN  Resources/android/images/res-long-port-ldpi/default.png
  11. BIN  Resources/android/images/res-notlong-land-hdpi/default.png
  12. BIN  Resources/android/images/res-notlong-land-ldpi/default.png
  13. BIN  Resources/android/images/res-notlong-land-mdpi/default.png
  14. BIN  Resources/android/images/res-notlong-port-hdpi/default.png
  15. BIN  Resources/android/images/res-notlong-port-ldpi/default.png
  16. BIN  Resources/android/images/res-notlong-port-mdpi/default.png
  17. +38 −0 Resources/app.js
  18. BIN  Resources/iphone/Default-Landscape.png
  19. BIN  Resources/iphone/Default-Portrait.png
  20. BIN  Resources/iphone/Default.png
  21. BIN  Resources/iphone/Default@2x.png
  22. BIN  Resources/iphone/appicon.png
  23. +994 −0 Resources/jsOAuth-1.3.1.js
  24. +2,421 −0 Resources/tests/lib/jasmine-1.0.2.js
  25. +104 −0 Resources/tests/lib/jasmine-titanium.js
  26. +9 −0 Resources/tests/tests.js
  27. +40 −0 Resources/tests/twitter.js
  28. +223 −0 Resources/twitter.js
  29. +8 −0 manifest
  30. +41 −0 tiapp.xml
4 .gitignore
@@ -0,0 +1,4 @@
+tmp
+.DS_Store
+build/*
+*.log
23 .project
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>TwitterOauth</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>com.appcelerator.titanium.core.builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.aptana.ide.core.unifiedBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>com.appcelerator.titanium.mobile.nature</nature>
+ <nature>com.aptana.projects.webnature</nature>
+ </natures>
+</projectDescription>
3  .settings/com.appcelerator.titanium.mobile.prefs
@@ -0,0 +1,3 @@
+#Sun Oct 09 19:32:12 PDT 2011
+MOBILE_PROJECT_SDK_VERSION=1.7.2
+eclipse.preferences.version=1
45 README.md
@@ -0,0 +1,45 @@
+# twitter-titanium
+
+twitter-titanium is a client-side Twitter library for Titanium Mobile. It simplifies the task of authenticating a user via Twitter. A backend is not required.
+It's designed to emulate the API of the Facebook module included in Titanium Mobile.
+
+It presents a very simple and straightforward API. You provide your OAuth configuration and simply call `authorize()`.
+The user is prompted with a modal WebView to login to Twitter. After the user has logged in, the WebView disappears and a `login` event is fired.
+Requests to Twitter's API endpoints are done with the `request()` function. We intentionally are not wrapping Twitter's API calls, this can
+become a maintainence issue if Twitter updates it's API.
+
+## How to use
+
+- There is an example app included in this repository. See `app.js`
+
+```
+var client = Twitter({
+ consumerKey: "INSERT YOUR KEY HERE",
+ consumerSecret: "INSERT YOUR SECRET HERE"
+});
+
+client.authorize(); // Pops up a modal WebView
+
+client.addEventListener('login', function(e) {
+ // Your app code goes here... you'll likely want to save the access tokens passed in the event.
+
+ // Here's an example API call:
+ client.request("1/statuses/home_timeline.json", {count: 100}, 'GET', function(data) {
+ // `data` is the response text
+ });
+});
+```
+
+
+## Nice features
+
+- Android compatible and tested!
+- On iOS, a back button is displayed if the user does any navigation within the WebView. For example, if the user clicks the forgot password link.
+
+## Thanks
+
+twitter-titanium uses the jsOAuth library by @bytespider
+
+## Contact
+
+twitter-titanium was written by Erik Bryn. You can find him on Twitter at @ebryn.
BIN  Resources/android/appicon.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  Resources/android/default.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  Resources/android/images/res-long-land-hdpi/default.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  Resources/android/images/res-long-land-ldpi/default.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  Resources/android/images/res-long-port-hdpi/default.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  Resources/android/images/res-long-port-ldpi/default.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  Resources/android/images/res-notlong-land-hdpi/default.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  Resources/android/images/res-notlong-land-ldpi/default.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  Resources/android/images/res-notlong-land-mdpi/default.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  Resources/android/images/res-notlong-port-hdpi/default.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  Resources/android/images/res-notlong-port-ldpi/default.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  Resources/android/images/res-notlong-port-mdpi/default.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 Resources/app.js
@@ -0,0 +1,38 @@
+(function() {
+ var RUN_TESTS = false;
+
+ if (RUN_TESTS) {
+ Ti.include('tests/tests.js');
+ } else {
+ var Twitter = require('twitter').Twitter;
+
+ var client = Twitter({
+ consumerKey: "INSERT KEY HERE"
+ consumerSecret: "INSERT SECRET HERE",
+ accessTokenKey: Ti.App.Properties.getString('twitterAccessTokenKey'),
+ accessTokenSecret: Ti.App.Properties.getString('twitterAccessTokenSecret')
+ });
+
+ var win = Ti.UI.createWindow({backgroundColor: 'white'}),
+ tableView = Ti.UI.createTableView();
+
+ win.add(tableView);
+ win.open();
+
+ client.addEventListener('login', function(e) {
+ Ti.App.Properties.setString('twitterAccessTokenKey', data.accessTokenKey);
+ Ti.App.Properties.setString('twitterAccessTokenSecret', data.accessTokenSecret);
+
+ client.request("1/statuses/home_timeline.json", {count: 100}, 'GET', function(data) {
+ var json = JSON.parse(data.text),
+ tweets = json.map(function(tweet) {
+ return {title: tweet.text};
+ });
+
+ tableView.setData(tweets);
+ });
+ });
+
+ client.authorize();
+ }
+})(this);
BIN  Resources/iphone/Default-Landscape.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  Resources/iphone/Default-Portrait.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  Resources/iphone/Default.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  Resources/iphone/Default@2x.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  Resources/iphone/appicon.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
994 Resources/jsOAuth-1.3.1.js
@@ -0,0 +1,994 @@
+/**
+ * @license
+ * jsOAuth version 1.3.1
+ * Copyright (c) 2010, 2011 Rob Griffiths (http://bytespider.eu)
+ * jsOAuth is freely distributable under the terms of an MIT-style license.
+ */
+var exports = exports || this;
+exports.OAuth = (function (global) {
+
+ /** signed.applets.codebase_principal_support to enable support in Firefox **/
+
+ function Collection(obj) {
+ var args = arguments, args_callee = args.callee, args_length = args.length,
+ i, collection = this;
+
+ if (!(this instanceof args_callee)) {
+ return new args_callee(obj);
+ }
+
+ for(i in obj) {
+ if (obj.hasOwnProperty(i)) {
+ collection[i] = obj[i];
+ }
+ }
+
+ return collection;
+ };
+
+ function Hash() {}
+ Hash.prototype = {
+ join: function(string){
+ string = string || '';
+ return this.values().join(string);
+ },
+ keys: function(){
+ var i, arr = [], self = this;
+ for (i in self) {
+ if (self.hasOwnProperty(i)) {
+ arr.push(i);
+ }
+ }
+
+ return arr;
+ },
+ values: function(){
+ var i, arr = [], self = this;
+ for (i in self) {
+ if (self.hasOwnProperty(i)) {
+ arr.push(self[i]);
+ }
+ }
+
+ return arr;
+ },
+ shift: function(){throw 'not implimented'},
+ unshift: function(){throw 'not implimented'},
+ push: function(){throw 'not implimented'},
+ pop: function(){throw 'not implimented'},
+ sort: function(){throw 'not implimented'},
+
+ ksort: function(func){
+ var self = this, keys = self.keys(), i, value, key;
+
+ if (func == undefined) {
+ keys.sort();
+ } else {
+ keys.sort(func);
+ }
+
+ for (i = 0; i < keys.length; i++) {
+ key = keys[i];
+ value = self[key];
+ delete self[key];
+ self[key] = value;
+ }
+
+ return self;
+ },
+ toObject: function () {
+ var obj = {}, i, self = this;;
+ for (i in self) {
+ if (self.hasOwnProperty(i)) {
+ obj[i] = self[i];
+ }
+ }
+
+ return obj;
+ }
+ };
+ Collection.prototype = new Hash; /**
+ * Url
+ *
+ * @constructor
+ * @param {String} url
+ */
+ function URI(url) {
+ var args = arguments, args_callee = args.callee,
+ parsed_uri, scheme, host, port, path, query, anchor,
+ parser = /^([^:\/?#]+?:\/\/)*([^\/:?#]*)?(:[^\/?#]*)*([^?#]*)(\?[^#]*)?(#(.*))*/
+ uri = this;
+
+ if (!(this instanceof args_callee)) {
+ return new args_callee(url);
+ }
+
+ uri.scheme = '';
+ uri.host = '';
+ uri.port = '';
+ uri.path = '';
+ uri.query = new QueryString();
+ uri.anchor = '';
+
+ if (url !== null) {
+ parsed_uri = url.match(parser);
+
+ scheme = parsed_uri[1];
+ host = parsed_uri[2];
+ port = parsed_uri[3];
+ path = parsed_uri[4];
+ query = parsed_uri[5];
+ anchor = parsed_uri[6];
+
+ scheme = (scheme !== undefined) ? scheme.replace('://', '').toLowerCase() : 'http';
+ port = (port ? port.replace(':', '') : (scheme === 'https' ? '443' : '80'));
+ // correct the scheme based on port number
+ scheme = (scheme == 'http' && port === '443' ? 'https' : scheme);
+ query = query ? query.replace('?', '') : '';
+ anchor = anchor ? anchor.replace('#', '') : '';
+
+
+ // Fix the host name to include port if non-standard ports were given
+ if ((scheme === 'https' && port !== '443') || (scheme === 'http' && port !== '80')) {
+ host = host + ':' + port;
+ }
+
+ uri.scheme = scheme;
+ uri.host = host;
+ uri.port = port;
+ uri.path = path || '/';
+ uri.query.setQueryParams(query);
+ uri.anchor = anchor || '';
+ }
+ }
+
+ URI.prototype = {
+ scheme: '',
+ host: '',
+ port: '',
+ path: '',
+ query: '',
+ anchor: '',
+ toString: function () {
+ var self = this, query = self.query + '';
+ return self.scheme + '://' + self.host + self.path + (query != '' ? '?' + query : '') + (self.anchor !== '' ? '#' + self.anchor : '');
+ }
+ };
+
+ /**
+ * Create and manage a query string
+ *
+ * @param {Object} obj
+ */
+ function QueryString(obj){
+ var args = arguments, args_callee = args.callee, args_length = args.length,
+ i, querystring = this;
+
+ if (!(this instanceof args_callee)) {
+ return new args_callee(obj);
+ }
+
+ if (obj != undefined) {
+ for (i in obj) {
+ if (obj.hasOwnProperty(i)) {
+ querystring[i] = obj[i];
+ }
+ }
+ }
+
+ return querystring;
+ }
+ // QueryString is a type of collection So inherit
+ QueryString.prototype = new Collection();
+
+ QueryString.prototype.toString = function () {
+ var i, self = this, q_arr = [], ret = '',
+ val = '', encode = OAuth.urlEncode;
+ self.ksort(); // lexicographical byte value ordering of the keys
+
+ for (i in self) {
+ if (self.hasOwnProperty(i)) {
+ if (i != undefined && self[i] != undefined) {
+ val = encode(i) + '=' + encode(self[i]);
+ q_arr.push(val);
+ }
+ }
+ }
+
+ if (q_arr.length > 0) {
+ ret = q_arr.join('&');
+ }
+
+ return ret;
+ };
+
+ /**
+ *
+ * @param {Object} query
+ */
+ QueryString.prototype.setQueryParams = function (query) {
+ var args = arguments, args_length = args.length, i, query_array,
+ query_array_length, querystring = this, key_value;
+
+ if (args_length == 1) {
+ if (typeof query === 'object') {
+ // iterate
+ for (i in query) {
+ if (query.hasOwnProperty(i)) {
+ querystring[i] = query[i];
+ }
+ }
+ } else if (typeof query === 'string') {
+ // split string on '&'
+ query_array = query.split('&');
+ // iterate over each of the array items
+ for (i = 0, query_array_length = query_array.length; i < query_array_length; i++) {
+ // split on '=' to get key, value
+ key_value = query_array[i].split('=');
+ querystring[key_value[0]] = key_value[1];
+ }
+ }
+ } else {
+ for (i = 0; i < arg_length; i += 2) {
+ // treat each arg as key, then value
+ querystring[args[i]] = args[i+1];
+ }
+ }
+ };
+ /** @const */ var OAUTH_VERSION_1_0 = '1.0';
+
+ /**
+ * OAuth
+ *
+ * @constructor
+ */
+ function OAuth(options) {
+ if (!(this instanceof OAuth)) {
+ return new OAuth(options);
+ }
+
+ return this.init(options);
+ }
+
+ OAuth.prototype = {
+ realm: '',
+ requestTokenUrl: '',
+ authorizationUrl: '',
+ accessTokenUrl: '',
+
+ init: function (options) {
+ var empty = '';
+ var oauth = {
+ enablePrivilege: options.enablePrivilege || false,
+
+ callbackUrl: options.callbackUrl || 'oob',
+
+ consumerKey: options.consumerKey,
+ consumerSecret: options.consumerSecret,
+ accessTokenKey: options.accessTokenKey || empty,
+ accessTokenSecret: options.accessTokenSecret || empty,
+ verifier: empty,
+
+ signatureMethod: options.signatureMethod || 'HMAC-SHA1'
+ };
+
+ this.realm = options.realm || empty;
+ this.requestTokenUrl = options.requestTokenUrl || empty;
+ this.authorizationUrl = options.authorizationUrl || empty;
+ this.accessTokenUrl = options.accessTokenUrl || empty;
+
+ this.getAccessToken = function () {
+ return [oauth.accessTokenKey, oauth.accessTokenSecret];
+ };
+
+ this.getAccessTokenKey = function () {
+ return oauth.accessTokenKey;
+ };
+
+ this.getAccessTokenSecret = function () {
+ return oauth.accessTokenSecret;
+ };
+
+ this.setAccessToken = function (tokenArray, tokenSecret) {
+ if (tokenSecret) {
+ tokenArray = [tokenArray, tokenSecret];
+ }
+ oauth.accessTokenKey = tokenArray[0];
+ oauth.accessTokenSecret = tokenArray[1];
+ };
+
+ this.getVerifier = function () {
+ return oauth.verifier;
+ };
+
+ this.setVerifier = function (verifier) {
+ oauth.verifier = verifier;
+ };
+
+ this.setCallbackUrl = function (url) {
+ oauth.callbackUrl = url;
+ };
+
+ /**
+ * Makes an authenticated http request
+ *
+ * @param options {object}
+ * method {string} ['GET', 'POST', 'PUT', ...]
+ * url {string} A valid http(s) url
+ * data {object} A key value paired object of data
+ * example: {'q':'foobar'}
+ * for GET this will append a query string
+ * headers {object} A key value paired object of additional headers
+ * success {function} callback for a sucessful request
+ * failure {function} callback for a failed request
+ */
+ this.request = function (options) {
+ var method, url, data, headers, success, failure, xhr, i,
+ headerParams, signatureMethod, signatureString, signature,
+ query = [], appendQueryString, signatureData = {}, params, withFile;
+
+ method = options.method || 'GET';
+ url = URI(options.url);
+ data = options.data || {};
+ headers = options.headers || {};
+ success = options.success || function () {};
+ failure = options.failure || function () {};
+
+ // According to the spec
+ withFile = (function(){
+ var hasFile = false;
+ for(var name in data) {
+ // Thanks to the FileAPI any file entry
+ // has a fileName property
+ if(typeof data[name].fileName != 'undefined') hasFile = true;
+ }
+
+ return hasFile;
+ })();
+
+ appendQueryString = options.appendQueryString ? options.appendQueryString : false;
+
+ if (oauth.enablePrivilege) {
+ netscape.security.PrivilegeManager
+ .enablePrivilege('UniversalBrowserRead UniversalBrowserWrite');
+ }
+
+ xhr = Request();
+ xhr.onreadystatechange = function () {
+ if (xhr.readyState === 4) {
+ var regex = /^(.*?):\s*(.*?)\r?$/mg,
+ requestHeaders = headers,
+ responseHeaders = {},
+ responseHeadersString = '',
+ match;
+
+ if (!!xhr.getAllResponseHeaders) {
+ responseHeadersString = xhr.getAllResponseHeaders();
+ while((match = regex.exec(responseHeadersString))) {
+ responseHeaders[match[1]] = match[2];
+ }
+ } else if(!!xhr.getResponseHeaders) {
+ responseHeadersString = xhr.getResponseHeaders();
+ for (var i = 0, len = responseHeadersString.length; i < len; ++i) {
+ responseHeaders[responseHeadersString[i][0]] = responseHeadersString[i][1];
+ }
+ }
+
+ var responseObject = {text: xhr.responseText, requestHeaders: requestHeaders, responseHeaders: responseHeaders};
+
+ // we are powerless against 3xx redirects
+ if((xhr.status >= 200 && xhr.status <= 226) || xhr.status == 304 || xhr.status === 0) {
+ success(responseObject);
+ // everything what is 400 and above is a failure code
+ } else if(xhr.status >= 400 && xhr.status !== 0) {
+ failure(responseObject);
+ }
+ }
+ };
+
+ headerParams = {
+ 'oauth_callback': oauth.callbackUrl,
+ 'oauth_consumer_key': oauth.consumerKey,
+ 'oauth_token': oauth.accessTokenKey,
+ 'oauth_signature_method': oauth.signatureMethod,
+ 'oauth_timestamp': getTimestamp(),
+ 'oauth_nonce': getNonce(),
+ 'oauth_verifier': oauth.verifier,
+ 'oauth_version': OAUTH_VERSION_1_0
+ };
+
+ signatureMethod = oauth.signatureMethod;
+
+ // According to the OAuth spec
+ // if data is transfered using
+ // multipart the POST data doesn't
+ // have to be signed:
+ // http://www.mail-archive.com/oauth@googlegroups.com/msg01556.html
+ if((!('Content-Type' in headers) || headers['Content-Type'] == 'application/x-www-form-urlencoded') && !withFile) {
+ params = url.query.toObject();
+ for (i in params) {
+ signatureData[i] = params[i];
+ }
+ for (i in data) {
+ signatureData[i] = data[i];
+ }
+ }
+
+ urlString = url.scheme + '://' + url.host + url.path;
+ signatureString = toSignatureBaseString(method, urlString, headerParams, signatureData);
+
+ signature = OAuth.signatureMethod[signatureMethod](oauth.consumerSecret, oauth.accessTokenSecret, signatureString);
+
+ headerParams.oauth_signature = signature;
+
+ if (this.realm)
+ {
+ headerParams['realm'] = this.realm;
+ }
+
+ if(appendQueryString || method == 'GET') {
+ url.query.setQueryParams(data);
+ query = null;
+ } else if(!withFile){
+ if (typeof data == 'string') {
+ query = data;
+ if (!('Content-Type' in headers)) {
+ headers['Content-Type'] = 'text/plain';
+ }
+ } else {
+ for(i in data) {
+ query.push(OAuth.urlEncode(i) + '=' + OAuth.urlEncode(data[i] + ''));
+ }
+ query = query.sort().join('&');
+ if (!('Content-Type' in headers)) {
+ headers['Content-Type'] = 'application/x-www-form-urlencoded';
+ }
+ }
+
+ } else if(withFile) {
+ // When using FormData multipart content type
+ // is used by default and required header
+ // is set to multipart/form-data etc
+ query = new FormData();
+ for(i in data) {
+ query.append(i, data[i]);
+ }
+ }
+
+ xhr.open(method, url+'', true);
+
+ xhr.setRequestHeader('Authorization', 'OAuth ' + toHeaderString(headerParams));
+ xhr.setRequestHeader('X-Requested-With','XMLHttpRequest');
+ for (i in headers) {
+ xhr.setRequestHeader(i, headers[i]);
+ }
+
+ xhr.send(query);
+ };
+
+ return this;
+ },
+
+ /**
+ * Wrapper for GET OAuth.request
+ *
+ * @param url {string} vaild http(s) url
+ * @param success {function} callback for a successful request
+ * @param failure {function} callback for a failed request
+ */
+ get: function (url, success, failure) {
+ this.request({'url': url, 'success': success, 'failure': failure});
+ },
+
+ /**
+ * Wrapper for POST OAuth.request
+ *
+ * @param url {string} vaild http(s) url
+ * @param data {object} A key value paired object of data
+ * example: {'q':'foobar'}
+ * for GET this will append a query string
+ * @param success {function} callback for a successful request
+ * @param failure {function} callback for a failed request
+ */
+ post: function (url, data, success, failure) {
+ this.request({'method': 'POST', 'url': url, 'data': data, 'success': success, 'failure': failure});
+ },
+
+ /**
+ * Wrapper to parse a JSON string and pass it to the callback
+ *
+ * @param url {string} vaild http(s) url
+ * @param success {function} callback for a successful request
+ * @param failure {function} callback for a failed request
+ */
+ getJSON: function (url, success, failure) {
+ this.get(url, function (data) {
+ success(JSON.parse(data.text));
+ }, failure);
+ },
+
+ /**
+ * Wrapper to parse a JSON string and pass it to the callback
+ *
+ * @param url {string} vaild http(s) url
+ * @param success {function} callback for a successful request
+ * @param failure {function} callback for a failed request
+ */
+ postJSON: function (url, data, success, failure) {
+ this.post(url, JSON.stringify(data), function (data) {
+ success(JSON.parse(data.text));
+ } , failure);
+ },
+
+ parseTokenRequest: function (tokenRequestString) {
+ var i = 0, arr = tokenRequestString.split('&'), len = arr.length, obj = {};
+ for (; i < len; ++i) {
+ var pair = arr[i].split('=');
+ obj[OAuth.urlDecode(pair[0])] = OAuth.urlDecode(pair[1]);
+ }
+
+ return obj;
+ },
+
+ fetchRequestToken: function (success, failure) {
+ var oauth = this;
+ oauth.setAccessToken('', '');
+
+ var url = oauth.authorizationUrl;
+ this.get(this.requestTokenUrl, function (data) {
+ var token = oauth.parseTokenRequest(data.text);
+ oauth.setAccessToken([token.oauth_token, token.oauth_token_secret]);
+ success(url + '?' + data.text);
+ }, failure);
+ },
+
+ fetchAccessToken: function (success, failure) {
+ var oauth = this;
+ this.get(this.accessTokenUrl, function (data) {
+ var token = oauth.parseTokenRequest(data.text);
+ oauth.setAccessToken([token.oauth_token, token.oauth_token_secret]);
+
+ // clean up a few un-needed things
+ oauth.setVerifier('');
+
+ success(data);
+ }, failure);
+ }
+ };
+
+ OAuth.signatureMethod = {
+ /**
+ * Sign the request
+ *
+ * @param consumer_secret {string} the consumer secret
+ * @param token_secret {string} the token secret
+ * @param signature_base {string} the signature base string
+ */
+ 'HMAC-SHA1': function (consumer_secret, token_secret, signature_base) {
+ var passphrase, signature, encode = OAuth.urlEncode;
+
+ consumer_secret = encode(consumer_secret);
+ token_secret = encode(token_secret || '');
+
+ passphrase = consumer_secret + '&' + token_secret;
+ signature = HMAC(SHA1.prototype, passphrase, signature_base);
+
+ return global.btoa(signature);
+ }
+ };
+
+ /**
+ * Get a string of the parameters for the OAuth Authorization header
+ *
+ * @param params {object} A key value paired object of data
+ * example: {'q':'foobar'}
+ * for GET this will append a query string
+ */
+ function toHeaderString(params) {
+ var arr = [], i, realm;
+
+ for (i in params) {
+ if (params[i] && params[i] !== undefined && params[i] !== '') {
+ if (i === 'realm') {
+ realm = i + '="' + params[i] + '"';
+ } else {
+ arr.push(i + '="' + OAuth.urlEncode(params[i]+'') + '"');
+ }
+ }
+ }
+
+ arr.sort();
+ if (realm) {
+ arr.unshift(realm);
+ }
+
+ return arr.join(', ');
+ }
+
+ /**
+ * Generate a signature base string for the request
+ *
+ * @param method {string} ['GET', 'POST', 'PUT', ...]
+ * @param url {string} A valid http(s) url
+ * @param header_params A key value paired object of additional headers
+ * @param query_params {object} A key value paired object of data
+ * example: {'q':'foobar'}
+ * for GET this will append a query string
+ */
+ function toSignatureBaseString(method, url, header_params, query_params) {
+ var arr = [], i, encode = OAuth.urlEncode;
+
+ for (i in header_params) {
+ if (header_params[i] !== undefined && header_params[i] !== '') {
+ arr.push(OAuth.urlEncode(i) + '=' + OAuth.urlEncode(header_params[i]+''));
+ }
+ }
+
+ for (i in query_params) {
+ if (query_params[i] !== undefined && query_params[i] !== '') {
+ if (!header_params[i]) {
+ arr.push(encode(i) + '=' + encode(query_params[i] + ''));
+ }
+ }
+ }
+
+ return [
+ method,
+ encode(url),
+ encode(arr.sort().join('&'))
+ ].join('&');
+ }
+
+ /**
+ * Generate a timestamp for the request
+ */
+ function getTimestamp() {
+ return parseInt(+new Date() / 1000, 10); // use short form of getting a timestamp
+ }
+
+ /**
+ * Generate a nonce for the request
+ *
+ * @param key_length {number} Optional nonce length
+ */
+ function getNonce(key_length) {
+ function rand() {
+ return Math.floor(Math.random() * chars.length);
+ }
+
+ key_length = key_length || 64;
+
+ var key_bytes = key_length / 8, value = '', key_iter = key_bytes / 4,
+ key_remainder = key_bytes % 4, i,
+ chars = ['20', '21', '22', '23', '24', '25', '26', '27', '28', '29',
+ '2A', '2B', '2C', '2D', '2E', '2F', '30', '31', '32', '33',
+ '34', '35', '36', '37', '38', '39', '3A', '3B', '3C', '3D',
+ '3E', '3F', '40', '41', '42', '43', '44', '45', '46', '47',
+ '48', '49', '4A', '4B', '4C', '4D', '4E', '4F', '50', '51',
+ '52', '53', '54', '55', '56', '57', '58', '59', '5A', '5B',
+ '5C', '5D', '5E', '5F', '60', '61', '62', '63', '64', '65',
+ '66', '67', '68', '69', '6A', '6B', '6C', '6D', '6E', '6F',
+ '70', '71', '72', '73', '74', '75', '76', '77', '78', '79',
+ '7A', '7B', '7C', '7D', '7E'];
+
+ for (i = 0; i < key_iter; i++) {
+ value += chars[rand()] + chars[rand()] + chars[rand()]+ chars[rand()];
+ }
+
+ // handle remaing bytes
+ for (i = 0; i < key_remainder; i++) {
+ value += chars[rand()];
+ }
+
+ return value;
+ }
+
+ /**
+ * rfc3986 compatable encode of a string
+ *
+ * @param {String} string
+ */
+ OAuth.urlEncode = function (string) {
+ function hex(code) {
+ var hex = code.toString(16).toUpperCase();
+ if (hex.length < 2) {
+ hex = 0 + hex;
+ }
+ return '%' + hex;
+ }
+
+ if (!string) {
+ return '';
+ }
+
+ string = string + '';
+ var reserved_chars = /[ \r\n!*"'();:@&=+$,\/?%#\[\]<>{}|`^\\\u0080-\uffff]/,
+ str_len = string.length, i, string_arr = string.split(''), c;
+
+ for (i = 0; i < str_len; i++) {
+ if (c = string_arr[i].match(reserved_chars)) {
+ c = c[0].charCodeAt(0);
+
+ if (c < 128) {
+ string_arr[i] = hex(c);
+ } else if (c < 2048) {
+ string_arr[i] = hex(192+(c>>6)) + hex(128+(c&63));
+ } else if (c < 65536) {
+ string_arr[i] = hex(224+(c>>12)) + hex(128+((c>>6)&63)) + hex(128+(c&63));
+ } else if (c < 2097152) {
+ string_arr[i] = hex(240+(c>>18)) + hex(128+((c>>12)&63)) + hex(128+((c>>6)&63)) + hex(128+(c&63));
+ }
+ }
+ }
+
+ return string_arr.join('');
+ };
+
+ /**
+ * rfc3986 compatable decode of a string
+ *
+ * @param {String} string
+ */
+ OAuth.urlDecode = function (string){
+ if (!string) {
+ return '';
+ }
+
+ return string.replace(/%[a-fA-F0-9]{2}/ig, function (match) {
+ return String.fromCharCode(parseInt(match.replace('%', ''), 16));
+ });
+ };
+ /**
+ * Factory object for XMLHttpRequest
+ */
+ function Request() {
+ var XHR;
+
+
+ if (typeof global.Titanium !== 'undefined' && typeof global.Titanium.Network.createHTTPClient != 'undefined') {
+ XHR = global.Titanium.Network.createHTTPClient();
+ } else if (typeof require !== 'undefined') {
+ // CommonJS require
+ XHR = new require("xhr").XMLHttpRequest();
+ } else {
+ // W3C
+ XHR = new global.XMLHttpRequest();
+ }
+
+ return XHR;
+ }
+ function zeroPad(length) {
+ var arr = new Array(++length);
+ return arr.join(0).split('');
+ }
+
+ function stringToByteArray(str) {
+ var bytes = [], code, i;
+
+ for(i = 0; i < str.length; i++) {
+ code = str.charCodeAt(i);
+
+ if (code < 128) {
+ bytes.push(code);
+ } else if (code < 2048) {
+ bytes.push(192+(code>>6), 128+(code&63));
+ } else if (code < 65536) {
+ bytes.push(224+(code>>12), 128+((code>>6)&63), 128+(code&63));
+ } else if (code < 2097152) {
+ bytes.push(240+(code>>18), 128+((code>>12)&63), 128+((code>>6)&63), 128+(code&63));
+ }
+ }
+
+ return bytes;
+ }
+
+ function wordsToByteArray(words) {
+ var bytes = [], i;
+ for (i = 0; i < words.length * 32; i += 8) {
+ bytes.push((words[i >>> 5] >>> (24 - i % 32)) & 255);
+ }
+ return bytes;
+ }
+
+ function byteArrayToHex(byteArray) {
+ var hex = [], l = byteArray.length, i;
+ for (i = 0; i < l; i++) {
+ hex.push((byteArray[i] >>> 4).toString(16));
+ hex.push((byteArray[i] & 0xF).toString(16));
+ }
+ return hex.join('');
+ }
+
+ function byteArrayToString(byteArray) {
+ var string = '', l = byteArray.length, i;
+ for (i = 0; i < l; i++) {
+ string += String.fromCharCode(byteArray[i]);
+ }
+ return string;
+ }
+
+ function leftrotate(value, shift) {
+ return (value << shift) | (value >>> (32 - shift));
+ }
+
+ function SHA1(message) {
+ if (message !== undefined) {
+ var m = message, crypto, digest;
+ if (m.constructor === String) {
+ m = stringToByteArray(m);
+ }
+
+ if (!(this instanceof SHA1)) {
+ crypto = new SHA1(message);
+ } else {
+ crypto = this;
+ }
+ digest = crypto.hash(m);
+
+ return byteArrayToHex(digest);
+ } else {
+ if (!(this instanceof SHA1)) {
+ return new SHA1();
+ }
+ }
+
+ return this;
+ }
+
+ SHA1.prototype = new SHA1();
+ SHA1.prototype.blocksize = 64;
+ SHA1.prototype.hash = function (m) {
+ var H = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0],
+ K = [0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6],
+ lb, hb,
+ l, pad, ml, blocks, b, block, bl, w, i, A, B, C, D, E, t, n, TEMP;
+
+ function fn(t, B, C, D) {
+ switch (t) {
+ case 0:
+ return (B & C) | ((~B) & D);
+ case 1:
+ case 3:
+ return B ^ C ^ D;
+ case 2:
+ return (B & C) | (B & D) | (C & D);
+ }
+
+ return -1;
+ }
+
+
+ if (m.constructor === String) {
+ m = stringToByteArray(m.encodeUTF8());
+ }
+
+ l = m.length;
+
+ pad = (Math.ceil((l + 9) / this.blocksize) * this.blocksize) - (l + 9);
+
+ hb = (Math.floor(l / 4294967296));
+ lb = (Math.floor(l % 4294967296));
+
+ ml = [
+ ((hb * 8) >> 24) & 255,
+ ((hb * 8) >> 16) & 255,
+ ((hb * 8) >> 8) & 255,
+ (hb * 8) & 255,
+ ((lb * 8) >> 24) & 255,
+ ((lb * 8) >> 16) & 255,
+ ((lb * 8) >> 8) & 255,
+ (lb * 8) & 255
+ ];
+
+ m = m.concat([0x80], zeroPad(pad), ml);
+
+ blocks = Math.ceil(m.length / this.blocksize);
+
+ for (b = 0; b < blocks; b++) {
+ block = m.slice(b * this.blocksize, (b+1) * this.blocksize);
+ bl = block.length;
+
+ w = [];
+
+ for (i = 0; i < bl; i++) {
+ w[i >>> 2] |= block[i] << (24 - (i - ((i >> 2) * 4)) * 8);
+ }
+
+ A = H[0];
+ B = H[1];
+ C = H[2];
+ D = H[3];
+ E = H[4];
+
+ for (t=0; t < 80; t++) {
+ if (t >= 16) {
+ w[t] = leftrotate(w[t-3] ^ w[t-8] ^ w[t-14] ^ w[t-16], 1);
+ }
+
+ n = Math.floor(t / 20);
+ TEMP = leftrotate(A, 5) + fn(n, B, C, D) + E + K[n] + w[t];
+
+ E = D;
+ D = C;
+ C = leftrotate(B, 30);
+ B = A;
+ A = TEMP;
+ }
+
+ H[0] += A;
+ H[1] += B;
+ H[2] += C;
+ H[3] += D;
+ H[4] += E;
+ }
+
+ return wordsToByteArray(H);
+ };
+
+ function HMAC(fn, key, message, toHex){
+ var k = stringToByteArray(key), m = stringToByteArray(message),
+ l = k.length, byteArray, oPad, iPad, i;
+
+ if (l > fn.blocksize) {
+ k = fn.hash(k);
+ l = k.length;
+ }
+
+ k = k.concat(zeroPad(fn.blocksize - l));
+
+ oPad = k.slice(0); // copy
+ iPad = k.slice(0); // copy
+
+ for (i = 0; i < fn.blocksize; i++) {
+ oPad[i] ^= 0x5C;
+ iPad[i] ^= 0x36;
+ }
+
+ byteArray = fn.hash(oPad.concat(fn.hash(iPad.concat(m))));
+
+ if (toHex) {
+ return byteArrayToHex(byteArray);
+ }
+ return byteArrayToString(byteArray);
+ }
+
+ return OAuth;
+})(this);
+(function (global) {
+ var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
+
+ /**
+ * Base64 encode a string
+ * @param string {string} the string to be base64 encoded
+ */
+ if (global.btoa) { Ti.API.debug('btoa defined!') }
+ global.btoa = global.btoa || function (string) {
+ var i = 0, length = string.length, ascii, index, output = '';
+
+ for (; i < length; i+=3) {
+ ascii = [
+ string.charCodeAt(i),
+ string.charCodeAt(i+1),
+ string.charCodeAt(i+2)
+ ];
+
+ index = [
+ ascii[0] >> 2,
+ ((ascii[0] & 3) << 4) | ascii[1] >> 4,
+ ((ascii[1] & 15) << 2) | ascii[2] >> 6,
+ ascii[2] & 63
+ ];
+
+ if (isNaN(ascii[1])) {
+ index[2] = 64;
+ }
+ if (isNaN(ascii[2])) {
+ index[3] = 64;
+ }
+
+ output += b64.charAt(index[0]) + b64.charAt(index[1]) + b64.charAt(index[2]) + b64.charAt(index[3]);
+ }
+
+ return output;
+ };
+})(this);
2,421 Resources/tests/lib/jasmine-1.0.2.js
@@ -0,0 +1,2421 @@
+/**
+ * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework.
+ *
+ * @namespace
+ */
+var jasmine = {};
+
+/**
+ * @private
+ */
+jasmine.unimplementedMethod_ = function() {
+ throw new Error("unimplemented method");
+};
+
+/**
+ * Use <code>jasmine.undefined</code> instead of <code>undefined</code>, since <code>undefined</code> is just
+ * a plain old variable and may be redefined by somebody else.
+ *
+ * @private
+ */
+jasmine.undefined = jasmine.___undefined___;
+
+/**
+ * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed.
+ *
+ */
+jasmine.DEFAULT_UPDATE_INTERVAL = 250;
+
+/**
+ * Default timeout interval in milliseconds for waitsFor() blocks.
+ */
+jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;
+
+jasmine.getGlobal = function() {
+ function getGlobal() {
+ return this;
+ }
+
+ return getGlobal();
+};
+
+/**
+ * Allows for bound functions to be compared. Internal use only.
+ *
+ * @ignore
+ * @private
+ * @param base {Object} bound 'this' for the function
+ * @param name {Function} function to find
+ */
+jasmine.bindOriginal_ = function(base, name) {
+ var original = base[name];
+ if (original.apply) {
+ return function() {
+ return original.apply(base, arguments);
+ };
+ } else {
+ // IE support
+ return jasmine.getGlobal()[name];
+ }
+};
+
+jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout');
+jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout');
+jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval');
+jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval');
+
+jasmine.MessageResult = function(values) {
+ this.type = 'log';
+ this.values = values;
+ this.trace = new Error(); // todo: test better
+};
+
+jasmine.MessageResult.prototype.toString = function() {
+ var text = "";
+ for(var i = 0; i < this.values.length; i++) {
+ if (i > 0) text += " ";
+ if (jasmine.isString_(this.values[i])) {
+ text += this.values[i];
+ } else {
+ text += jasmine.pp(this.values[i]);
+ }
+ }
+ return text;
+};
+
+jasmine.ExpectationResult = function(params) {
+ this.type = 'expect';
+ this.matcherName = params.matcherName;
+ this.passed_ = params.passed;
+ this.expected = params.expected;
+ this.actual = params.actual;
+
+ this.message = this.passed_ ? 'Passed.' : params.message;
+ this.trace = this.passed_ ? '' : new Error(this.message);
+};
+
+jasmine.ExpectationResult.prototype.toString = function () {
+ return this.message;
+};
+
+jasmine.ExpectationResult.prototype.passed = function () {
+ return this.passed_;
+};
+
+/**
+ * Getter for the Jasmine environment. Ensures one gets created
+ */
+jasmine.getEnv = function() {
+ return jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env();
+};
+
+/**
+ * @ignore
+ * @private
+ * @param value
+ * @returns {Boolean}
+ */
+jasmine.isArray_ = function(value) {
+ return jasmine.isA_("Array", value);
+};
+
+/**
+ * @ignore
+ * @private
+ * @param value
+ * @returns {Boolean}
+ */
+jasmine.isString_ = function(value) {
+ return jasmine.isA_("String", value);
+};
+
+/**
+ * @ignore
+ * @private
+ * @param value
+ * @returns {Boolean}
+ */
+jasmine.isNumber_ = function(value) {
+ return jasmine.isA_("Number", value);
+};
+
+/**
+ * @ignore
+ * @private
+ * @param {String} typeName
+ * @param value
+ * @returns {Boolean}
+ */
+jasmine.isA_ = function(typeName, value) {
+ return Object.prototype.toString.apply(value) === '[object ' + typeName + ']';
+};
+
+/**
+ * Pretty printer for expecations. Takes any object and turns it into a human-readable string.
+ *
+ * @param value {Object} an object to be outputted
+ * @returns {String}
+ */
+jasmine.pp = function(value) {
+ var stringPrettyPrinter = new jasmine.StringPrettyPrinter();
+ stringPrettyPrinter.format(value);
+ return stringPrettyPrinter.string;
+};
+
+/**
+ * Returns true if the object is a DOM Node.
+ *
+ * @param {Object} obj object to check
+ * @returns {Boolean}
+ */
+jasmine.isDomNode = function(obj) {
+ return obj['nodeType'] > 0;
+};
+
+/**
+ * Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter.
+ *
+ * @example
+ * // don't care about which function is passed in, as long as it's a function
+ * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function));
+ *
+ * @param {Class} clazz
+ * @returns matchable object of the type clazz
+ */
+jasmine.any = function(clazz) {
+ return new jasmine.Matchers.Any(clazz);
+};
+
+/**
+ * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks.
+ *
+ * Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine
+ * expectation syntax. Spies can be checked if they were called or not and what the calling params were.
+ *
+ * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs).
+ *
+ * Spies are torn down at the end of every spec.
+ *
+ * Note: Do <b>not</b> call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj.
+ *
+ * @example
+ * // a stub
+ * var myStub = jasmine.createSpy('myStub'); // can be used anywhere
+ *
+ * // spy example
+ * var foo = {
+ * not: function(bool) { return !bool; }
+ * }
+ *
+ * // actual foo.not will not be called, execution stops
+ * spyOn(foo, 'not');
+
+ // foo.not spied upon, execution will continue to implementation
+ * spyOn(foo, 'not').andCallThrough();
+ *
+ * // fake example
+ * var foo = {
+ * not: function(bool) { return !bool; }
+ * }
+ *
+ * // foo.not(val) will return val
+ * spyOn(foo, 'not').andCallFake(function(value) {return value;});
+ *
+ * // mock example
+ * foo.not(7 == 7);
+ * expect(foo.not).toHaveBeenCalled();
+ * expect(foo.not).toHaveBeenCalledWith(true);
+ *
+ * @constructor
+ * @see spyOn, jasmine.createSpy, jasmine.createSpyObj
+ * @param {String} name
+ */
+jasmine.Spy = function(name) {
+ /**
+ * The name of the spy, if provided.
+ */
+ this.identity = name || 'unknown';
+ /**
+ * Is this Object a spy?
+ */
+ this.isSpy = true;
+ /**
+ * The actual function this spy stubs.
+ */
+ this.plan = function() {
+ };
+ /**
+ * Tracking of the most recent call to the spy.
+ * @example
+ * var mySpy = jasmine.createSpy('foo');
+ * mySpy(1, 2);
+ * mySpy.mostRecentCall.args = [1, 2];
+ */
+ this.mostRecentCall = {};
+
+ /**
+ * Holds arguments for each call to the spy, indexed by call count
+ * @example
+ * var mySpy = jasmine.createSpy('foo');
+ * mySpy(1, 2);
+ * mySpy(7, 8);
+ * mySpy.mostRecentCall.args = [7, 8];
+ * mySpy.argsForCall[0] = [1, 2];
+ * mySpy.argsForCall[1] = [7, 8];
+ */
+ this.argsForCall = [];
+ this.calls = [];
+};
+
+/**
+ * Tells a spy to call through to the actual implemenatation.
+ *
+ * @example
+ * var foo = {
+ * bar: function() { // do some stuff }
+ * }
+ *
+ * // defining a spy on an existing property: foo.bar
+ * spyOn(foo, 'bar').andCallThrough();
+ */
+jasmine.Spy.prototype.andCallThrough = function() {
+ this.plan = this.originalValue;
+ return this;
+};
+
+/**
+ * For setting the return value of a spy.
+ *
+ * @example
+ * // defining a spy from scratch: foo() returns 'baz'
+ * var foo = jasmine.createSpy('spy on foo').andReturn('baz');
+ *
+ * // defining a spy on an existing property: foo.bar() returns 'baz'
+ * spyOn(foo, 'bar').andReturn('baz');
+ *
+ * @param {Object} value
+ */
+jasmine.Spy.prototype.andReturn = function(value) {
+ this.plan = function() {
+ return value;
+ };
+ return this;
+};
+
+/**
+ * For throwing an exception when a spy is called.
+ *
+ * @example
+ * // defining a spy from scratch: foo() throws an exception w/ message 'ouch'
+ * var foo = jasmine.createSpy('spy on foo').andThrow('baz');
+ *
+ * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch'
+ * spyOn(foo, 'bar').andThrow('baz');
+ *
+ * @param {String} exceptionMsg
+ */
+jasmine.Spy.prototype.andThrow = function(exceptionMsg) {
+ this.plan = function() {
+ throw exceptionMsg;
+ };
+ return this;
+};
+
+/**
+ * Calls an alternate implementation when a spy is called.
+ *
+ * @example
+ * var baz = function() {
+ * // do some stuff, return something
+ * }
+ * // defining a spy from scratch: foo() calls the function baz
+ * var foo = jasmine.createSpy('spy on foo').andCall(baz);
+ *
+ * // defining a spy on an existing property: foo.bar() calls an anonymnous function
+ * spyOn(foo, 'bar').andCall(function() { return 'baz';} );
+ *
+ * @param {Function} fakeFunc
+ */
+jasmine.Spy.prototype.andCallFake = function(fakeFunc) {
+ this.plan = fakeFunc;
+ return this;
+};
+
+/**
+ * Resets all of a spy's the tracking variables so that it can be used again.
+ *
+ * @example
+ * spyOn(foo, 'bar');
+ *
+ * foo.bar();
+ *
+ * expect(foo.bar.callCount).toEqual(1);
+ *
+ * foo.bar.reset();
+ *
+ * expect(foo.bar.callCount).toEqual(0);
+ */
+jasmine.Spy.prototype.reset = function() {
+ this.wasCalled = false;
+ this.callCount = 0;
+ this.argsForCall = [];
+ this.calls = [];
+ this.mostRecentCall = {};
+};
+
+jasmine.createSpy = function(name) {
+
+ var spyObj = function() {
+ spyObj.wasCalled = true;
+ spyObj.callCount++;
+ var args = jasmine.util.argsToArray(arguments);
+ spyObj.mostRecentCall.object = this;
+ spyObj.mostRecentCall.args = args;
+ spyObj.argsForCall.push(args);
+ spyObj.calls.push({object: this, args: args});
+ return spyObj.plan.apply(this, arguments);
+ };
+
+ var spy = new jasmine.Spy(name);
+
+ for (var prop in spy) {
+ spyObj[prop] = spy[prop];
+ }
+
+ spyObj.reset();
+
+ return spyObj;
+};
+
+/**
+ * Determines whether an object is a spy.
+ *
+ * @param {jasmine.Spy|Object} putativeSpy
+ * @returns {Boolean}
+ */
+jasmine.isSpy = function(putativeSpy) {
+ return putativeSpy && putativeSpy.isSpy;
+};
+
+/**
+ * Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something
+ * large in one call.
+ *
+ * @param {String} baseName name of spy class
+ * @param {Array} methodNames array of names of methods to make spies
+ */
+jasmine.createSpyObj = function(baseName, methodNames) {
+ if (!jasmine.isArray_(methodNames) || methodNames.length == 0) {
+ throw new Error('createSpyObj requires a non-empty array of method names to create spies for');
+ }
+ var obj = {};
+ for (var i = 0; i < methodNames.length; i++) {
+ obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]);
+ }
+ return obj;
+};
+
+/**
+ * All parameters are pretty-printed and concatenated together, then written to the current spec's output.
+ *
+ * Be careful not to leave calls to <code>jasmine.log</code> in production code.
+ */
+jasmine.log = function() {
+ var spec = jasmine.getEnv().currentSpec;
+ spec.log.apply(spec, arguments);
+};
+
+/**
+ * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy.
+ *
+ * @example
+ * // spy example
+ * var foo = {
+ * not: function(bool) { return !bool; }
+ * }
+ * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops
+ *
+ * @see jasmine.createSpy
+ * @param obj
+ * @param methodName
+ * @returns a Jasmine spy that can be chained with all spy methods
+ */
+var spyOn = function(obj, methodName) {
+ return jasmine.getEnv().currentSpec.spyOn(obj, methodName);
+};
+
+/**
+ * Creates a Jasmine spec that will be added to the current suite.
+ *
+ * // TODO: pending tests
+ *
+ * @example
+ * it('should be true', function() {
+ * expect(true).toEqual(true);
+ * });
+ *
+ * @param {String} desc description of this specification
+ * @param {Function} func defines the preconditions and expectations of the spec
+ */
+var it = function(desc, func) {
+ return jasmine.getEnv().it(desc, func);
+};
+
+/**
+ * Creates a <em>disabled</em> Jasmine spec.
+ *
+ * A convenience method that allows existing specs to be disabled temporarily during development.
+ *
+ * @param {String} desc description of this specification
+ * @param {Function} func defines the preconditions and expectations of the spec
+ */
+var xit = function(desc, func) {
+ return jasmine.getEnv().xit(desc, func);
+};
+
+/**
+ * Starts a chain for a Jasmine expectation.
+ *
+ * It is passed an Object that is the actual value and should chain to one of the many
+ * jasmine.Matchers functions.
+ *
+ * @param {Object} actual Actual value to test against and expected value
+ */
+var expect = function(actual) {
+ return jasmine.getEnv().currentSpec.expect(actual);
+};
+
+/**
+ * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs.
+ *
+ * @param {Function} func Function that defines part of a jasmine spec.
+ */
+var runs = function(func) {
+ jasmine.getEnv().currentSpec.runs(func);
+};
+
+/**
+ * Waits a fixed time period before moving to the next block.
+ *
+ * @deprecated Use waitsFor() instead
+ * @param {Number} timeout milliseconds to wait
+ */
+var waits = function(timeout) {
+ jasmine.getEnv().currentSpec.waits(timeout);
+};
+
+/**
+ * Waits for the latchFunction to return true before proceeding to the next block.
+ *
+ * @param {Function} latchFunction
+ * @param {String} optional_timeoutMessage
+ * @param {Number} optional_timeout
+ */
+var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
+ jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments);
+};
+
+/**
+ * A function that is called before each spec in a suite.
+ *
+ * Used for spec setup, including validating assumptions.
+ *
+ * @param {Function} beforeEachFunction
+ */
+var beforeEach = function(beforeEachFunction) {
+ jasmine.getEnv().beforeEach(beforeEachFunction);
+};
+
+/**
+ * A function that is called after each spec in a suite.
+ *
+ * Used for restoring any state that is hijacked during spec execution.
+ *
+ * @param {Function} afterEachFunction
+ */
+var afterEach = function(afterEachFunction) {
+ jasmine.getEnv().afterEach(afterEachFunction);
+};
+
+/**
+ * Defines a suite of specifications.
+ *
+ * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared
+ * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization
+ * of setup in some tests.
+ *
+ * @example
+ * // TODO: a simple suite
+ *
+ * // TODO: a simple suite with a nested describe block
+ *
+ * @param {String} description A string, usually the class under test.
+ * @param {Function} specDefinitions function that defines several specs.
+ */
+var describe = function(description, specDefinitions) {
+ return jasmine.getEnv().describe(description, specDefinitions);
+};
+
+/**
+ * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development.
+ *
+ * @param {String} description A string, usually the class under test.
+ * @param {Function} specDefinitions function that defines several specs.
+ */
+var xdescribe = function(description, specDefinitions) {
+ return jasmine.getEnv().xdescribe(description, specDefinitions);
+};
+
+
+// Provide the XMLHttpRequest class for IE 5.x-6.x:
+jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() {
+ try {
+ return new ActiveXObject("Msxml2.XMLHTTP.6.0");
+ } catch(e) {
+ }
+ try {
+ return new ActiveXObject("Msxml2.XMLHTTP.3.0");
+ } catch(e) {
+ }
+ try {
+ return new ActiveXObject("Msxml2.XMLHTTP");
+ } catch(e) {
+ }
+ try {
+ return new ActiveXObject("Microsoft.XMLHTTP");
+ } catch(e) {
+ }
+ throw new Error("This browser does not support XMLHttpRequest.");
+} : XMLHttpRequest;
+/**
+ * @namespace
+ */
+jasmine.util = {};
+
+/**
+ * Declare that a child class inherit it's prototype from the parent class.
+ *
+ * @private
+ * @param {Function} childClass
+ * @param {Function} parentClass
+ */
+jasmine.util.inherit = function(childClass, parentClass) {
+ /**
+ * @private
+ */
+ var subclass = function() {
+ };
+ subclass.prototype = parentClass.prototype;
+ childClass.prototype = new subclass;
+};
+
+jasmine.util.formatException = function(e) {
+ var lineNumber;
+ if (e.line) {
+ lineNumber = e.line;
+ }
+ else if (e.lineNumber) {
+ lineNumber = e.lineNumber;
+ }
+
+ var file;
+
+ if (e.sourceURL) {
+ file = e.sourceURL;
+ }
+ else if (e.fileName) {
+ file = e.fileName;
+ }
+
+ var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString();
+
+ if (file && lineNumber) {
+ message += ' in ' + file + ' (line ' + lineNumber + ')';
+ }
+
+ return message;
+};
+
+jasmine.util.htmlEscape = function(str) {
+ if (!str) return str;
+ return str.replace(/&/g, '&amp;')
+ .replace(/</g, '&lt;')
+ .replace(/>/g, '&gt;');
+};
+
+jasmine.util.argsToArray = function(args) {
+ var arrayOfArgs = [];
+ for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]);
+ return arrayOfArgs;
+};
+
+jasmine.util.extend = function(destination, source) {
+ for (var property in source) destination[property] = source[property];
+ return destination;
+};
+
+/**
+ * Environment for Jasmine
+ *
+ * @constructor
+ */
+jasmine.Env = function() {
+ this.currentSpec = null;
+ this.currentSuite = null;
+ this.currentRunner_ = new jasmine.Runner(this);
+
+ this.reporter = new jasmine.MultiReporter();
+
+ this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL;
+ this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL;
+ this.lastUpdate = 0;
+ this.specFilter = function() {
+ return true;
+ };
+
+ this.nextSpecId_ = 0;
+ this.nextSuiteId_ = 0;
+ this.equalityTesters_ = [];
+
+ // wrap matchers
+ this.matchersClass = function() {
+ jasmine.Matchers.apply(this, arguments);
+ };
+ jasmine.util.inherit(this.matchersClass, jasmine.Matchers);
+
+ jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass);
+};
+
+
+jasmine.Env.prototype.setTimeout = jasmine.setTimeout;
+jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout;
+jasmine.Env.prototype.setInterval = jasmine.setInterval;
+jasmine.Env.prototype.clearInterval = jasmine.clearInterval;
+
+/**
+ * @returns an object containing jasmine version build info, if set.
+ */
+jasmine.Env.prototype.version = function () {
+ if (jasmine.version_) {
+ return jasmine.version_;
+ } else {
+ throw new Error('Version not set');
+ }
+};
+
+/**
+ * @returns string containing jasmine version build info, if set.
+ */
+jasmine.Env.prototype.versionString = function() {
+ if (jasmine.version_) {
+ var version = this.version();
+ return version.major + "." + version.minor + "." + version.build + " revision " + version.revision;
+ } else {
+ return "version unknown";
+ }
+};
+
+/**
+ * @returns a sequential integer starting at 0
+ */
+jasmine.Env.prototype.nextSpecId = function () {
+ return this.nextSpecId_++;
+};
+
+/**
+ * @returns a sequential integer starting at 0
+ */
+jasmine.Env.prototype.nextSuiteId = function () {
+ return this.nextSuiteId_++;
+};
+
+/**
+ * Register a reporter to receive status updates from Jasmine.
+ * @param {jasmine.Reporter} reporter An object which will receive status updates.
+ */
+jasmine.Env.prototype.addReporter = function(reporter) {
+ this.reporter.addReporter(reporter);
+};
+
+jasmine.Env.prototype.execute = function() {
+ this.currentRunner_.execute();
+};
+
+jasmine.Env.prototype.describe = function(description, specDefinitions) {
+ var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite);
+
+ var parentSuite = this.currentSuite;
+ if (parentSuite) {
+ parentSuite.add(suite);
+ } else {
+ this.currentRunner_.add(suite);
+ }
+
+ this.currentSuite = suite;
+
+ var declarationError = null;
+ try {
+ specDefinitions.call(suite);
+ } catch(e) {
+ declarationError = e;
+ }
+
+ this.currentSuite = parentSuite;
+
+ if (declarationError) {
+ this.it("encountered a declaration exception", function() {
+ throw declarationError;
+ });
+ }
+
+ return suite;
+};
+
+jasmine.Env.prototype.beforeEach = function(beforeEachFunction) {
+ if (this.currentSuite) {
+ this.currentSuite.beforeEach(beforeEachFunction);
+ } else {
+ this.currentRunner_.beforeEach(beforeEachFunction);
+ }
+};
+
+jasmine.Env.prototype.currentRunner = function () {
+ return this.currentRunner_;
+};
+
+jasmine.Env.prototype.afterEach = function(afterEachFunction) {
+ if (this.currentSuite) {
+ this.currentSuite.afterEach(afterEachFunction);
+ } else {
+ this.currentRunner_.afterEach(afterEachFunction);
+ }
+
+};
+
+jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) {
+ return {
+ execute: function() {
+ }
+ };
+};
+
+jasmine.Env.prototype.it = function(description, func) {
+ var spec = new jasmine.Spec(this, this.currentSuite, description);
+ this.currentSuite.add(spec);
+ this.currentSpec = spec;
+
+ if (func) {
+ spec.runs(func);
+ }
+
+ return spec;
+};
+
+jasmine.Env.prototype.xit = function(desc, func) {
+ return {
+ id: this.nextSpecId(),
+ runs: function() {
+ }
+ };
+};
+
+jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) {
+ if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) {
+ return true;
+ }
+
+ a.__Jasmine_been_here_before__ = b;
+ b.__Jasmine_been_here_before__ = a;
+
+ var hasKey = function(obj, keyName) {
+ return obj != null && obj[keyName] !== jasmine.undefined;
+ };
+
+ for (var property in b) {
+ if (!hasKey(a, property) && hasKey(b, property)) {
+ mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
+ }
+ }
+ for (property in a) {
+ if (!hasKey(b, property) && hasKey(a, property)) {
+ mismatchKeys.push("expected missing key '" + property + "', but present in actual.");
+ }
+ }
+ for (property in b) {
+ if (property == '__Jasmine_been_here_before__') continue;
+ if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) {
+ mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual.");
+ }
+ }
+
+ if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) {
+ mismatchValues.push("arrays were not the same length");
+ }
+
+ delete a.__Jasmine_been_here_before__;
+ delete b.__Jasmine_been_here_before__;
+ return (mismatchKeys.length == 0 && mismatchValues.length == 0);
+};
+
+jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) {
+ mismatchKeys = mismatchKeys || [];
+ mismatchValues = mismatchValues || [];
+
+ for (var i = 0; i < this.equalityTesters_.length; i++) {
+ var equalityTester = this.equalityTesters_[i];
+ var result = equalityTester(a, b, this, mismatchKeys, mismatchValues);
+ if (result !== jasmine.undefined) return result;
+ }
+
+ if (a === b) return true;
+
+ if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) {
+ return (a == jasmine.undefined && b == jasmine.undefined);
+ }
+
+ if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) {
+ return a === b;
+ }
+
+ if (a instanceof Date && b instanceof Date) {
+ return a.getTime() == b.getTime();
+ }
+
+ if (a instanceof jasmine.Matchers.Any) {
+ return a.matches(b);
+ }
+
+ if (b instanceof jasmine.Matchers.Any) {
+ return b.matches(a);
+ }
+
+ if (jasmine.isString_(a) && jasmine.isString_(b)) {
+ return (a == b);
+ }
+
+ if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) {
+ return (a == b);
+ }
+
+ if (typeof a === "object" && typeof b === "object") {
+ return this.compareObjects_(a, b, mismatchKeys, mismatchValues);
+ }
+
+ //Straight check
+ return (a === b);
+};
+
+jasmine.Env.prototype.contains_ = function(haystack, needle) {
+ if (jasmine.isArray_(haystack)) {
+ for (var i = 0; i < haystack.length; i++) {
+ if (this.equals_(haystack[i], needle)) return true;
+ }
+ return false;
+ }
+ return haystack.indexOf(needle) >= 0;
+};
+
+jasmine.Env.prototype.addEqualityTester = function(equalityTester) {
+ this.equalityTesters_.push(equalityTester);
+};
+/** No-op base class for Jasmine reporters.
+ *
+ * @constructor
+ */
+jasmine.Reporter = function() {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.reportRunnerStarting = function(runner) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.reportRunnerResults = function(runner) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.reportSuiteResults = function(suite) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.reportSpecStarting = function(spec) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.reportSpecResults = function(spec) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.log = function(str) {
+};
+
+/**
+ * Blocks are functions with executable code that make up a spec.
+ *
+ * @constructor
+ * @param {jasmine.Env} env
+ * @param {Function} func
+ * @param {jasmine.Spec} spec
+ */
+jasmine.Block = function(env, func, spec) {
+ this.env = env;
+ this.func = func;
+ this.spec = spec;
+};
+
+jasmine.Block.prototype.execute = function(onComplete) {
+ try {
+ this.func.apply(this.spec);
+ } catch (e) {
+ this.spec.fail(e);
+ }
+ onComplete();
+};
+/** JavaScript API reporter.
+ *
+ * @constructor
+ */
+jasmine.JsApiReporter = function() {
+ this.started = false;
+ this.finished = false;
+ this.suites_ = [];
+ this.results_ = {};
+};
+
+jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) {
+ this.started = true;
+ var suites = runner.topLevelSuites();
+ for (var i = 0; i < suites.length; i++) {
+ var suite = suites[i];
+ this.suites_.push(this.summarize_(suite));
+ }
+};
+
+jasmine.JsApiReporter.prototype.suites = function() {
+ return this.suites_;
+};
+
+jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) {
+ var isSuite = suiteOrSpec instanceof jasmine.Suite;
+ var summary = {
+ id: suiteOrSpec.id,
+ name: suiteOrSpec.description,
+ type: isSuite ? 'suite' : 'spec',
+ children: []
+ };
+
+ if (isSuite) {
+ var children = suiteOrSpec.children();
+ for (var i = 0; i < children.length; i++) {
+ summary.children.push(this.summarize_(children[i]));
+ }
+ }
+ return summary;
+};
+
+jasmine.JsApiReporter.prototype.results = function() {
+ return this.results_;
+};
+
+jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) {
+ return this.results_[specId];
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) {
+ this.finished = true;
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) {
+ this.results_[spec.id] = {
+ messages: spec.results().getItems(),
+ result: spec.results().failedCount > 0 ? "failed" : "passed"
+ };
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.JsApiReporter.prototype.log = function(str) {
+};
+
+jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){
+ var results = {};
+ for (var i = 0; i < specIds.length; i++) {
+ var specId = specIds[i];
+ results[specId] = this.summarizeResult_(this.results_[specId]);
+ }
+ return results;
+};
+
+jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){
+ var summaryMessages = [];
+ var messagesLength = result.messages.length;
+ for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) {
+ var resultMessage = result.messages[messageIndex];
+ summaryMessages.push({
+ text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined,
+ passed: resultMessage.passed ? resultMessage.passed() : true,
+ type: resultMessage.type,
+ message: resultMessage.message,
+ trace: {
+ stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined
+ }
+ });
+ }
+
+ return {
+ result : result.result,
+ messages : summaryMessages
+ };
+};
+
+/**
+ * @constructor
+ * @param {jasmine.Env} env
+ * @param actual
+ * @param {jasmine.Spec} spec
+ */
+jasmine.Matchers = function(env, actual, spec, opt_isNot) {
+ this.env = env;
+ this.actual = actual;
+ this.spec = spec;
+ this.isNot = opt_isNot || false;
+ this.reportWasCalled_ = false;
+};
+
+// todo: @deprecated as of Jasmine 0.11, remove soon [xw]
+jasmine.Matchers.pp = function(str) {
+ throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!");
+};
+
+// todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw]
+jasmine.Matchers.prototype.report = function(result, failing_message, details) {
+ throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs");
+};
+
+jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) {
+ for (var methodName in prototype) {
+ if (methodName == 'report') continue;
+ var orig = prototype[methodName];
+ matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig);
+ }
+};
+
+jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) {
+ return function() {
+ var matcherArgs = jasmine.util.argsToArray(arguments);
+ var result = matcherFunction.apply(this, arguments);
+
+ if (this.isNot) {
+ result = !result;
+ }
+
+ if (this.reportWasCalled_) return result;
+
+ var message;
+ if (!result) {
+ if (this.message) {
+ message = this.message.apply(this, arguments);
+ if (jasmine.isArray_(message)) {
+ message = message[this.isNot ? 1 : 0];
+ }
+ } else {
+ var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); });
+ message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate;
+ if (matcherArgs.length > 0) {
+ for (var i = 0; i < matcherArgs.length; i++) {
+ if (i > 0) message += ",";
+ message += " " + jasmine.pp(matcherArgs[i]);
+ }
+ }
+ message += ".";
+ }
+ }
+ var expectationResult = new jasmine.ExpectationResult({
+ matcherName: matcherName,
+ passed: result,
+ expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0],
+ actual: this.actual,
+ message: message
+ });
+ this.spec.addMatcherResult(expectationResult);
+ return jasmine.undefined;
+ };
+};
+
+
+
+
+/**
+ * toBe: compares the actual to the expected using ===
+ * @param expected
+ */
+jasmine.Matchers.prototype.toBe = function(expected) {
+ return this.actual === expected;
+};
+
+/**
+ * toNotBe: compares the actual to the expected using !==
+ * @param expected
+ * @deprecated as of 1.0. Use not.toBe() instead.
+ */
+jasmine.Matchers.prototype.toNotBe = function(expected) {
+ return this.actual !== expected;
+};
+
+/**
+ * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc.
+ *
+ * @param expected
+ */
+jasmine.Matchers.prototype.toEqual = function(expected) {
+ return this.env.equals_(this.actual, expected);
+};
+
+/**
+ * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual
+ * @param expected
+ * @deprecated as of 1.0. Use not.toNotEqual() instead.
+ */
+jasmine.Matchers.prototype.toNotEqual = function(expected) {
+ return !this.env.equals_(this.actual, expected);
+};
+
+/**
+ * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes
+ * a pattern or a String.
+ *
+ * @param expected
+ */
+jasmine.Matchers.prototype.toMatch = function(expected) {
+ return new RegExp(expected).test(this.actual);
+};
+
+/**
+ * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch
+ * @param expected
+ * @deprecated as of 1.0. Use not.toMatch() instead.
+ */
+jasmine.Matchers.prototype.toNotMatch = function(expected) {
+ return !(new RegExp(expected).test(this.actual));
+};
+
+/**
+ * Matcher that compares the actual to jasmine.undefined.
+ */
+jasmine.Matchers.prototype.toBeDefined = function() {
+ return (this.actual !== jasmine.undefined);
+};
+