├── .gitignore
├── Gruntfile.js
├── README.md
├── coffee
├── config.coffee
├── lib
│ ├── api.coffee
│ ├── core.coffee
│ ├── oauth.coffee
│ ├── providers.coffee
│ ├── request.coffee
│ └── user.coffee
├── main.coffee
└── tools
│ ├── cache.coffee
│ ├── location_operations.coffee
│ ├── lstorage.coffee
│ ├── sha1.coffee
│ └── url.coffee
├── compile-jquery.sh
├── dist
├── oauth.js
└── oauth.min.js
├── example
├── index.html
├── jquery-2.1.1.min.js
└── jquery-2.1.1.min.map
├── js
└── tools
│ └── jquery-lite.js
├── package.json
└── plugin.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | coverage
3 | /js/*
4 | !/js/tools
5 | /js/tools/*
6 | !/js/tools/jquery-lite.js
7 | *sublime*
8 | .idea
9 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var package_info = require('./package.json');
4 | var fs = require('fs');
5 |
6 |
7 | module.exports = function(grunt) {
8 | // Project configuration.
9 | var gruntConf = {
10 | watch: {
11 | options: {
12 | nospawn: true
13 | },
14 | default: {
15 | files: ['./**/*.coffee'],
16 | tasks: ['coffee', 'browserify']
17 | }
18 | },
19 | coffee: {
20 | default: {
21 | expand: true,
22 | cwd: 'coffee',
23 | src: ['**/*.coffee'],
24 | dest: 'js',
25 | ext: '.js',
26 | options: {
27 | bare: true
28 | }
29 | }
30 | },
31 | concurrent: {
32 | server: {
33 | options: {
34 | logConcurrentOutput: true
35 | }
36 | }
37 | },
38 | browserify: {
39 | dist: {
40 | files: {
41 | './dist/oauth.js': ['js/main.js']
42 | },
43 | options: {
44 | transform: [
45 | [
46 | 'envify', {
47 | oauthd_url: 'https://oauth.io',
48 | api_url: 'https://oauth.io/api',
49 | sdk_version: "phonegap-" + package_info.version
50 | }
51 | ]
52 | ],
53 | browserifyOptions: {
54 | standalone: 'oauthio-phonegap'
55 | }
56 | }
57 | }
58 | },
59 | uglify: {
60 | my_target: {
61 | files: {
62 | './dist/oauth.min.js': ['dist/oauth.js']
63 | }
64 | }
65 | },
66 | jasmine_node: {
67 |
68 | options: {
69 | forceExit: true,
70 | match: '.',
71 | matchall: false,
72 | extensions: 'js',
73 | specNameMatcher: 'spec'
74 | },
75 | all: ['tests/unit/spec/']
76 | },
77 |
78 | taskDefault: ['coffee', 'browserify', 'uglify']
79 | };
80 |
81 | grunt.initConfig(gruntConf);
82 |
83 | // These plugins provide necessary tasks.
84 | grunt.loadNpmTasks('grunt-contrib-watch');
85 | grunt.loadNpmTasks('grunt-contrib-coffee');
86 | grunt.loadNpmTasks('grunt-concurrent');
87 | grunt.loadNpmTasks('grunt-browserify');
88 | grunt.loadNpmTasks('grunt-contrib-uglify');
89 | grunt.loadNpmTasks('grunt-jasmine-node');
90 |
91 | grunt.registerTask('coverage', 'Creates a tests coverage report', function() {
92 | var exec = require('child_process').exec;
93 | var done = this.async();
94 | exec('npm test', function(error, stdout, stderr) {
95 | console.log("Coverage report should be generated in ./coverage/lcov-report/index.html");
96 | done();
97 | });
98 | });
99 |
100 | // Default task.
101 | grunt.registerTask('default', gruntConf.taskDefault);
102 |
103 | };
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | OAuth.io Cordova/Phonegap SDK
2 | =======================
3 |
4 | This is the Cordova/Phonegap SDK for [OAuth.io](https://oauth.io). OAuth.io allows you to integrate **100+ providers** really easily in your web app, without worrying about each provider's OAuth specific implementation.
5 |
6 | Installation
7 | ============
8 |
9 | * This plugin is supported on PhoneGap (Cordova) v3.0.0 and above.
10 |
11 | OAuth.io Requirements and Set-Up
12 | --------------------------------
13 |
14 | To use this plugin you will need to make sure you've registered your OAuth.io app and have a public key (please check https://oauth.io/docs).
15 |
16 | Getting the SDK
17 | ---------------
18 |
19 | Run the following command in your project directory. If you are using Phonegap, use the `phonegap` command. If you are using Cordova, use the `cordova` command.
20 |
21 | ```sh
22 | $ cordova plugin add https://github.com/oauth-io/oauth-phonegap
23 | ```
24 |
25 | Whitelisting API URLs
26 | ---------------------
27 |
28 | OAuth.io will try to access various URLs, and their domains must be whitelisted in your `config.xml` in the `` tag's `origin` attribute :
29 |
30 | ```xml
31 |
32 |
33 |
34 |
35 |
36 |
37 | ```
38 |
39 |
40 | You can either add each domain separately, depending on the providers you use:
41 |
42 | * graph.facebook.com
43 | * api.twitter.com
44 | * github.com
45 | * ...
46 |
47 | Or you can allow all domains with `*` like this :
48 |
49 | ```xml
50 |
51 | ```
52 |
53 |
54 | Integrating in your project
55 | ---------------------------
56 |
57 | The `OAuth` object is automatically added to `window` when you include the plugin, so you don't need to add it yourself.
58 |
59 | In your JavaScript, add this line to initialize OAuth.js. You can get the public key of your app from your [OAuth.io dashboard](https://oauth.io/dashboard/apps).
60 |
61 | ```javascript
62 | OAuth.initialize('your_app_public_key');
63 | ```
64 |
65 | Usage
66 | =====
67 |
68 | To connect your user using facebook (as an example):
69 |
70 | ```javascript
71 | //Using popup (option 1)
72 | OAuth.popup('facebook')
73 | .done(function(result) {
74 | //use result.access_token in your API request
75 | //or use result.get|post|put|del|patch|me methods (see below)
76 | })
77 | .fail(function (err) {
78 | //handle error with err
79 | });
80 | ```
81 |
82 | Using the cache
83 | ---------------
84 |
85 | As of version `0.2.0`, you can use the cache feature. This prevents the user from having to log in to the provider through a popup everytime he wants to access the app.
86 |
87 | To use the cache, pass an options object to your popup method like this :
88 |
89 | ```javascript
90 | OAuth.popup('facebook', {
91 | cache: true
92 | })
93 | .done(function(result) {
94 | //use result.access_token in your API request
95 | //or use result.get|post|put|del|patch|me methods (see below)
96 | })
97 | .fail(function (err) {
98 | //handle error with err
99 | });
100 | ```
101 |
102 | That way, your user will have to login to the provider only if the request token has not been retrieved yet or has expired.
103 |
104 | Making requests
105 | ---------------
106 |
107 | You can make requests to the provider's API manually with the access token you got from the `popup` or `callback` methods, or use the request methods stored in the `result` object.
108 |
109 | **GET Request**
110 |
111 | To make a GET request, you have to call the `result.get` method like this :
112 |
113 | ```javascript
114 | //Let's say the /me endpoint on the provider API returns a JSON object
115 | //with the field "name" containing the name "John Doe"
116 | OAuth.popup(provider)
117 | .done(function(result) {
118 | result.get('/me')
119 | .done(function (response) {
120 | //this will display "John Doe" in the console
121 | console.log(response.name);
122 | })
123 | .fail(function (err) {
124 | //handle error with err
125 | });
126 | })
127 | .fail(function (err) {
128 | //handle error with err
129 | });
130 | ```
131 |
132 | **POST Request**
133 |
134 | To make a POST request, you have to call the `result.post` method like this :
135 |
136 | ```javascript
137 | //Let's say the /message endpoint on the provider waits for
138 | //a POST request containing the fields "user_id" and "content"
139 | //and returns the field "id" containing the id of the sent message
140 | OAuth.popup(provider)
141 | .done(function(result) {
142 | result.post('/message', {
143 | data: {
144 | user_id: 93,
145 | content: 'Hello Mr. 93 !'
146 | }
147 | })
148 | .done(function (response) {
149 | //this will display the id of the message in the console
150 | console.log(response.id);
151 | })
152 | .fail(function (err) {
153 | //handle error with err
154 | });
155 | })
156 | .fail(function (err) {
157 | //handle error with err
158 | });
159 | ```
160 |
161 | **PUT Request**
162 |
163 | To make a PUT request, you have to call the `result.post` method like this :
164 |
165 | ```javascript
166 | //Let's say the /profile endpoint on the provider waits for
167 | //a PUT request to update the authenticated user's profile
168 | //containing the field "name" and returns the field "name"
169 | //containing the new name
170 | OAuth.popup(provider)
171 | .done(function(result) {
172 | result.put('/profile', {
173 | data: {
174 | name: "John Williams Doe III"
175 | }
176 | })
177 | .done(function (response) {
178 | //this will display the new name in the console
179 | console.log(response.name);
180 | })
181 | .fail(function (err) {
182 | //handle error with err
183 | });
184 | })
185 | .fail(function (err) {
186 | //handle error with err
187 | });
188 | ```
189 |
190 | **PATCH Request**
191 |
192 | To make a PATCH request, you have to call the `result.patch` method like this :
193 |
194 | ```javascript
195 | //Let's say the /profile endpoint on the provider waits for
196 | //a PATCH request to update the authenticated user's profile
197 | //containing the field "name" and returns the field "name"
198 | //containing the new name
199 | OAuth.popup(provider)
200 | .done(function(result) {
201 | result.patch('/profile', {
202 | data: {
203 | name: "John Williams Doe III"
204 | }
205 | })
206 | .done(function (response) {
207 | //this will display the new name in the console
208 | console.log(response.name);
209 | })
210 | .fail(function (err) {
211 | //handle error with err
212 | });
213 | })
214 | .fail(function (err) {
215 | //handle error with err
216 | });
217 | ```
218 |
219 | **DELETE Request**
220 |
221 | To make a DELETE request, you have to call the `result.del` method like this :
222 |
223 | ```javascript
224 | //Let's say the /picture?id=picture_id endpoint on the provider waits for
225 | //a DELETE request to delete a picture with the id "84"
226 | //and returns true or false depending on the user's rights on the picture
227 | OAuth.popup(provider)
228 | .done(function(result) {
229 | result.del('/picture?id=84')
230 | .done(function (response) {
231 | //this will display true if the user was authorized to delete
232 | //the picture
233 | console.log(response);
234 | })
235 | .fail(function (err) {
236 | //handle error with err
237 | });
238 | })
239 | .fail(function (err) {
240 | //handle error with err
241 | });
242 | ```
243 |
244 | **Me() Request**
245 |
246 | The `me()` request is an OAuth.io feature that allows you, when the provider is supported, to retrieve a unified object describing the authenticated user. That can be very useful when you need to login a user via several providers, but don't want to handle a different response each time.
247 |
248 | To use the `me()` feature, do like the following (the example works for Facebook, Github, Twitter and many other providers in this case) :
249 |
250 | ```javascript
251 | //provider can be 'facebook', 'twitter', 'github', or any supported
252 | //provider that contain the fields 'firstname' and 'lastname'
253 | //or an equivalent (e.g. "FirstName" or "first-name")
254 | var provider = 'facebook';
255 |
256 | OAuth.popup(provider)
257 | .done(function(result) {
258 | result.me()
259 | .done(function (response) {
260 | console.log('Firstname: ', response.firstname);
261 | console.log('Lastname: ', response.lastname);
262 | })
263 | .fail(function (err) {
264 | //handle error with err
265 | });
266 | })
267 | .fail(function (err) {
268 | //handle error with err
269 | });
270 | ```
271 |
272 | *Filtering the results*
273 |
274 | You can filter the results of the `me()` method by passing an array of fields you need :
275 |
276 | ```javascript
277 | //...
278 | result.me(['firstname', 'lastname', 'email'/*, ...*/])
279 | //...
280 | ```
281 |
282 |
283 | Contributing
284 | ============
285 |
286 | **Issues**
287 |
288 | Feel free to post issues if you have problems while using this SDK.
289 |
290 | **Pull requests**
291 |
292 | You are welcome to fork and make pull requests. We appreciate the time you spend working on this project and we will be happy to review your code and merge it if it brings nice improvements :)
293 |
294 | If you want to do a pull request, please mind these simple rules :
295 |
296 | - *One feature per pull request*
297 | - *Write lear commit messages*
298 | - *Unit test your feature* : if it's a bug fix for example, write a test that proves the bug exists and that your fix resolves it.
299 | - *Write a clear description of the pull request*
300 |
301 | If you do so, we'll be able to merge your pull request more quickly :)
302 |
303 | Testing the SDK
304 | ===============
305 |
306 | Unit Testing
307 | ------------
308 |
309 | To test the SDK, you first need to install the npm modules `jasmine-node` and `istanbul` (to get the tests coverage) :
310 |
311 | ```sh
312 | $ sudo npm install -g jasmine-node@2.0.0 istanbul
313 | ```
314 |
315 | Then you can run the testsuite from the SDK www directory :
316 |
317 | ```sh
318 | $ jasmine-node --verbose tests/unit/spec
319 | ```
320 |
321 | Once you've installed `istanbul`, you can run the following command to get coverage information :
322 |
323 | ```sh
324 | $ npm test
325 | ```
326 |
327 | The coverage report is generated in the `coverage` folder. You can have a nice HTML render of the report in `coverage/lcof-report/index.html`
328 |
329 | Running the included samples
330 | ------------------------
331 |
332 | **Create a new project as described in the [PhoneGap documentation](http://docs.phonegap.com/en/edge/guide_cli_index.md.html#The%20Command-line%20Interface). For example:**
333 |
334 | ```sh
335 | $ phonegap create oauthio-test com.example.oauthio-test OAuthioTest
336 | $ cd oauthio-test
337 | $ phonegap install android
338 | ```
339 |
340 | **Install OAuth.io plugin into the project**
341 |
342 | ```sh
343 | $ phonegap local plugin add https://github.com/oauth-io/oauth-phonegap
344 | ```
345 |
346 | **Replace the generated example `index.html` with the one included in the example folder, and copy jquery.**
347 |
348 | A valid key is provided, but you can do your own app on [OAuth.io](https://oauth.io/). Also, please check that your `config.xml` file contains `` or accept oauth.io and the provider's domain (e.g. graph.facebook.com).
349 |
350 | **Plug your phone & run it ! (or add --emulate)**
351 |
352 | ```sh
353 | $ phonegap run android
354 | ```
355 |
356 | License
357 | =======
358 |
359 | This SDK is published under the Apache2 License.
360 |
361 |
362 |
363 | More information in [oauth.io documentation](http://oauth.io/#/docs)
364 |
--------------------------------------------------------------------------------
/coffee/config.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | oauthd_url: process.env.oauthd_url
3 | oauthd_api: process.env.api_url
4 | version: process.env.sdk_version
5 | options: {}
--------------------------------------------------------------------------------
/coffee/lib/api.coffee:
--------------------------------------------------------------------------------
1 | "use strict"
2 |
3 | module.exports = (Materia) ->
4 | $ = Materia.getJquery()
5 | apiCall = (type, url, params) =>
6 | defer = $.Deferred()
7 | base = Materia.getOAuthdURL()
8 | $.ajax(
9 | url: base + url
10 | type: type
11 | data: params
12 | ).then(
13 | ((data) => defer.resolve data),
14 | ((err) => defer.reject err && err.responseJSON)
15 | )
16 | return defer.promise()
17 |
18 | return {
19 | get: (url, params) => apiCall 'get', url, params
20 | post: (url, params) => apiCall 'post', url, params
21 | put: (url, params) => apiCall 'put', url, params
22 | del: (url, params) => apiCall 'delete', url, params
23 | }
24 |
--------------------------------------------------------------------------------
/coffee/lib/core.coffee:
--------------------------------------------------------------------------------
1 | "use strict"
2 |
3 | config = require('../config')
4 | Url = require("../tools/url")
5 | Location = require('../tools/location_operations')
6 | lstorage = require("../tools/lstorage")
7 | cache = require("../tools/cache")
8 |
9 | module.exports = (window, document, jquery, navigator) ->
10 | Url = Url(document)
11 | location_operations = Location document
12 |
13 | cache.init lstorage, config
14 |
15 | Materia =
16 | initialize: (public_key, options) ->
17 | config.key = public_key
18 | if options
19 | for i of options
20 | config.options[i] = options[i]
21 | return
22 |
23 | setOAuthdURL: (url) ->
24 | config.oauthd_url = url
25 | config.oauthd_base = Url.getAbsUrl(config.oauthd_url).match(/^.{2,5}:\/\/[^/]+/)[0]
26 | return
27 |
28 | getOAuthdURL: () -> return config.oauthd_url
29 | getVersion: () -> return config.version
30 |
31 | extend: (name, module) ->
32 | @[name] = module @
33 |
34 | # private
35 | getConfig: () -> return config
36 | getWindow: () -> return window
37 | getDocument: () -> return document
38 | getNavigator: () -> return navigator
39 | getJquery: () -> return jquery
40 | getUrl: () -> return Url
41 | getCache: () -> return cache
42 | getStorage: () -> return lstorage
43 | getLocationOperations: () -> return location_operations
44 |
45 | return Materia
46 |
--------------------------------------------------------------------------------
/coffee/lib/oauth.coffee:
--------------------------------------------------------------------------------
1 | "use strict"
2 |
3 | cookies = require("../tools/cookies")
4 | oauthio_requests = require("./request")
5 | sha1 = require("../tools/sha1")
6 |
7 | module.exports = (Materia) ->
8 | Url = Materia.getUrl()
9 | config = Materia.getConfig()
10 | document = Materia.getDocument()
11 | window = Materia.getWindow()
12 | $ = Materia.getJquery()
13 | cache = Materia.getCache()
14 |
15 | providers_api = require('./providers') Materia
16 |
17 | config.oauthd_base = Url.getAbsUrl(config.oauthd_url).match(/^.{2,5}:\/\/[^/]+/)[0]
18 |
19 | client_states = []
20 | location_operations = Materia.getLocationOperations()
21 | oauthio = request: oauthio_requests(Materia, client_states, providers_api)
22 |
23 | oauth = {
24 | initialize: (public_key, options) -> return Materia.initialize public_key, options
25 | setOAuthdURL: (url) ->
26 | config.oauthd_url = url
27 | config.oauthd_base = Url.getAbsUrl(config.oauthd_url).match(/^.{2,5}:\/\/[^/]+/)[0]
28 | return
29 | create: (provider, tokens, request) ->
30 | return cache.tryCache(oauth, provider, true) unless tokens
31 | providers_api.fetchDescription provider if typeof request isnt "object"
32 | make_res = (method) ->
33 | oauthio.request.mkHttp provider, tokens, request, method
34 |
35 | make_res_endpoint = (method, url) ->
36 | oauthio.request.mkHttpEndpoint provider, tokens, request, method, url
37 |
38 | res = {}
39 | for i of tokens
40 | res[i] = tokens[i]
41 |
42 | res.toJson = ->
43 | a = {}
44 | a.access_token = res.access_token if res.access_token?
45 | a.oauth_token = res.oauth_token if res.oauth_token?
46 | a.oauth_token_secret = res.oauth_token_secret if res.oauth_token_secret?
47 | a.expires_in = res.expires_in if res.expires_in?
48 | a.token_type = res.token_type if res.token_type?
49 | a.id_token = res.id_token if res.id_token?
50 | a.provider = res.provider if res.provider?
51 | a.email = res.email if res.email?
52 | return a
53 |
54 | res.get = make_res("GET")
55 | res.post = make_res("POST")
56 | res.put = make_res("PUT")
57 | res.patch = make_res("PATCH")
58 | res.del = make_res("DELETE")
59 | res.me = oauthio.request.mkHttpMe provider, tokens, request, "GET"
60 |
61 | res
62 |
63 | popup: (provider, opts, callback) ->
64 | gotmessage = false
65 | getMessage = (e) ->
66 | console.log("going in callback")
67 | console.log(JSON.stringify(e))
68 | if not gotmessage
69 | return if e.origin isnt config.oauthd_base
70 | gotmessage = true
71 | try
72 | wnd.close()
73 | opts.data = e.data
74 | oauthio.request.sendCallback opts, defer
75 | wnd = undefined
76 | frm = undefined
77 | wndTimeout = undefined
78 | defer = $.Deferred()
79 | opts = opts or {}
80 | unless config.key
81 | defer?.reject new Error("OAuth object must be initialized")
82 | if not callback?
83 | return defer.promise()
84 | else
85 | return callback(new Error("OAuth object must be initialized"))
86 | if arguments.length is 2 and typeof opts == 'function'
87 | callback = opts
88 | opts = {}
89 | if cache.cacheEnabled(opts.cache)
90 | res = cache.tryCache(oauth, provider, opts.cache)
91 | if res
92 | defer?.resolve res
93 | if callback
94 | return callback(null, res)
95 | else
96 | return defer.promise()
97 | unless opts.state
98 | opts.state = sha1.create_hash()
99 | opts.state_type = "client"
100 | client_states.push opts.state
101 | url = config.oauthd_url + "/auth/" + provider + "?k=" + config.key
102 | url += '&redirect_uri=http%3A%2F%2Flocalhost'
103 | url += "&opts=" + encodeURIComponent(JSON.stringify(opts)) if opts
104 |
105 | opts.provider = provider
106 | opts.cache = opts.cache
107 |
108 | wndTimeout = setTimeout(->
109 | defer?.reject new Error("Authorization timed out")
110 | if opts.callback and typeof opts.callback == "function"
111 | opts.callback new Error("Authorization timed out")
112 | try
113 | wnd.close()
114 | return
115 | , 1200 * 1000)
116 |
117 | wnd = window.open(url, "_blank", 'toolbar=yes,closebuttoncaption=Back,presentationstyle=formsheet,toolbarposition=top,clearsessioncache=yes,clearcache=yes')
118 |
119 | wnd.addEventListener "loadstart", (ev) ->
120 | return if ev.url.substr(0, 17) isnt "http://localhost/"
121 | clearTimeout wndTimeout if wndTimeout
122 | results = /[\\#&]oauthio=([^&]*)/.exec(ev.url)
123 | gotmessage = true
124 | wnd.close()
125 | if results and results[1]
126 | opts.data = decodeURIComponent(results[1].replace(/\+/g, " "))
127 | opts.callback = callback
128 | opts.provider = provider
129 | oauthio.request.sendCallback opts, defer
130 | else
131 | if opts.callback and typeof opts.callback == "function"
132 | opts.callback new Error("unable to receive token")
133 | defer?.reject new Error("unable to receive token")
134 | return
135 | wnd.addEventListener "exit", () ->
136 | if not gotmessage
137 | defer?.reject new Error("The popup was closed")
138 | opts.callback new Error("The popup was closed") if opts.callback and typeof opts.callback == "function"
139 |
140 | return defer?.promise()
141 |
142 | clearCache: (provider) ->
143 | cache.clearCache provider
144 |
145 | http_me: (opts) ->
146 | oauthio.request.http_me opts if oauthio.request.http_me
147 | return
148 |
149 | http: (opts) ->
150 | oauthio.request.http opts if oauthio.request.http
151 | return
152 | getVersion: () ->
153 | Materia.getVersion.apply this
154 | }
155 | return oauth
156 |
--------------------------------------------------------------------------------
/coffee/lib/providers.coffee:
--------------------------------------------------------------------------------
1 | "use strict"
2 |
3 | config = require("../config")
4 |
5 | module.exports = (Materia) ->
6 | $ = Materia.getJquery()
7 |
8 | providers_desc = {}
9 | providers_cb = {}
10 | providers_api =
11 | execProvidersCb: (provider, e, r) ->
12 | if providers_cb[provider]
13 | cbs = providers_cb[provider]
14 | delete providers_cb[provider]
15 |
16 | for i of cbs
17 | cbs[i] e, r
18 | return
19 |
20 | fetchDescription: (provider) ->
21 | return if providers_desc[provider]
22 | providers_desc[provider] = true
23 | $.ajax(
24 | url: config.oauthd_api + "/providers/" + provider
25 | data:
26 | extend: true
27 |
28 | dataType: "json"
29 | ).done((data) ->
30 | providers_desc[provider] = data.data
31 | providers_api.execProvidersCb provider, null, data.data
32 | return
33 | ).always ->
34 | if typeof providers_desc[provider] isnt "object"
35 | delete providers_desc[provider]
36 |
37 | providers_api.execProvidersCb provider, new Error("Unable to fetch request description")
38 | return
39 |
40 | return
41 |
42 | getDescription: (provider, opts, callback) ->
43 | opts = opts or {}
44 | return callback(null, providers_desc[provider]) if typeof providers_desc[provider] is "object"
45 | providers_api.fetchDescription provider unless providers_desc[provider]
46 | return callback(null, {}) unless opts.wait
47 | providers_cb[provider] = providers_cb[provider] or []
48 | providers_cb[provider].push callback
49 | return
50 |
51 | return providers_api
52 |
--------------------------------------------------------------------------------
/coffee/lib/request.coffee:
--------------------------------------------------------------------------------
1 | "use strict"
2 |
3 | Url = require('../tools/url')()
4 |
5 | module.exports = (Materia, client_states, providers_api) ->
6 | $ = Materia.getJquery()
7 | config = Materia.getConfig()
8 | cache = Materia.getCache()
9 | extended_methods = []
10 |
11 | fetched_methods = false
12 | retrieveMethods: () ->
13 | defer = $.Deferred()
14 | if not fetched_methods
15 | $.ajax(config.oauthd_url + '/api/extended-endpoints')
16 | .then (data) ->
17 | extended_methods = data.data
18 | fetched_methods = true
19 | defer.resolve()
20 | .fail (e) ->
21 | fetched_methods = true
22 | defer.reject(e)
23 | else
24 | defer.resolve extended_methods
25 | return defer.promise()
26 |
27 | generateMethods: (request_object, tokens, provider) ->
28 | if extended_methods?
29 | for v, k in extended_methods
30 | # v is a method to add
31 | name_array = v.name.split '.'
32 | pt = request_object
33 | for vv, kk in name_array
34 | if kk < name_array.length - 1
35 | if not pt[vv]?
36 | pt[vv] = {}
37 | pt = pt[vv]
38 | else
39 | pt[vv] = @mkHttpAll provider, tokens, v, arguments
40 |
41 | http: (opts) ->
42 | doRequest = ->
43 | request = options.oauthio.request or {}
44 | unless request.cors
45 | options.url = encodeURIComponent(options.url)
46 | options.url = "/" + options.url unless options.url[0] is "/"
47 | options.url = config.oauthd_url + "/request/" + options.oauthio.provider + options.url
48 | options.headers = options.headers or {}
49 | options.headers.oauthio = "k=" + config.key
50 | options.headers.oauthio += "&oauthv=1" if options.oauthio.tokens.oauth_token and options.oauthio.tokens.oauth_token_secret # make sure to use oauth 1
51 | for k of options.oauthio.tokens
52 | options.headers.oauthio += "&" + encodeURIComponent(k) + "=" + encodeURIComponent(options.oauthio.tokens[k])
53 | delete options.oauthio
54 |
55 | return $.ajax(options)
56 | if options.oauthio.tokens
57 | #Fetching the url if a common endpoint is called
58 | options.oauthio.tokens.token = options.oauthio.tokens.access_token if options.oauthio.tokens.access_token
59 | unless options.url.match(/^[a-z]{2,16}:\/\//)
60 | options.url = "/" + options.url if options.url[0] isnt "/"
61 | options.url = request.url + options.url
62 | options.url = Url.replaceParam(options.url, options.oauthio.tokens, request.parameters)
63 | if request.query
64 | qs = []
65 | for i of request.query
66 | qs.push encodeURIComponent(i) + "=" + encodeURIComponent(Url.replaceParam(request.query[i], options.oauthio.tokens, request.parameters))
67 | if "?" in options.url
68 | options.url += "&" + qs
69 | else
70 | options.url += "?" + qs
71 | if request.headers
72 | options.headers = options.headers or {}
73 | for i of request.headers
74 | options.headers[i] = Url.replaceParam(request.headers[i], options.oauthio.tokens, request.parameters)
75 | delete options.oauthio
76 | $.ajax options
77 | options = {}
78 | i = undefined
79 | for i of opts
80 | options[i] = opts[i]
81 | if not options.oauthio.request or options.oauthio.request is true
82 | desc_opts = wait: !!options.oauthio.request
83 | defer = $.Deferred()
84 | providers_api.getDescription options.oauthio.provider, desc_opts, (e, desc) ->
85 | return defer.reject(e) if e
86 | if options.oauthio.tokens.oauth_token and options.oauthio.tokens.oauth_token_secret
87 | options.oauthio.request = desc.oauth1 and desc.oauth1.request
88 | else
89 | options.oauthio.request = desc.oauth2 and desc.oauth2.request
90 | defer.resolve()
91 | return
92 |
93 | return defer.then(doRequest)
94 | else
95 | return doRequest()
96 | return
97 |
98 | http_me: (opts) ->
99 | doRequest = ->
100 | defer = $.Deferred()
101 | request = options.oauthio.request or {}
102 | options.url = config.oauthd_url + "/auth/" + options.oauthio.provider + "/me"
103 | options.headers = options.headers or {}
104 | options.headers.oauthio = "k=" + config.key
105 | options.headers.oauthio += "&oauthv=1" if options.oauthio.tokens.oauth_token and options.oauthio.tokens.oauth_token_secret # make sure to use oauth 1
106 | for k of options.oauthio.tokens
107 | options.headers.oauthio += "&" + encodeURIComponent(k) + "=" + encodeURIComponent(options.oauthio.tokens[k])
108 | delete options.oauthio
109 |
110 | promise = $.ajax(options)
111 | $.when(promise).done((data) ->
112 | defer.resolve data.data
113 | return
114 | ).fail (data) ->
115 | if data.responseJSON
116 | defer.reject data.responseJSON.data
117 | else
118 | defer.reject new Error("An error occured while trying to access the resource")
119 | return
120 |
121 | defer.promise()
122 | options = {}
123 | for k of opts
124 | options[k] = opts[k]
125 | if not options.oauthio.request or options.oauthio.request is true
126 | desc_opts = wait: !!options.oauthio.request
127 | defer = $.Deferred()
128 | providers_api.getDescription options.oauthio.provider, desc_opts, (e, desc) ->
129 | return defer.reject(e) if e
130 | if options.oauthio.tokens.oauth_token and options.oauthio.tokens.oauth_token_secret
131 | options.oauthio.request = desc.oauth1 and desc.oauth1.request
132 | else
133 | options.oauthio.request = desc.oauth2 and desc.oauth2.request
134 | defer.resolve()
135 | return
136 |
137 | return defer.then(doRequest)
138 | else
139 | return doRequest()
140 | return
141 |
142 | http_all: (options, endpoint_descriptor, parameters) ->
143 | doRequest = ->
144 | defer = $.Deferred()
145 | request = options.oauthio.request or {}
146 | options.headers = options.headers or {}
147 | options.headers.oauthio = "k=" + config.key
148 | options.headers.oauthio += "&oauthv=1" if options.oauthio.tokens.oauth_token and options.oauthio.tokens.oauth_token_secret # make sure to use oauth 1
149 | for k of options.oauthio.tokens
150 | options.headers.oauthio += "&" + encodeURIComponent(k) + "=" + encodeURIComponent(options.oauthio.tokens[k])
151 | delete options.oauthio
152 |
153 |
154 | promise = $.ajax(options)
155 | $.when(promise).done((data) ->
156 |
157 | if typeof data.data == 'string'
158 | try
159 | data.data = JSON.parse data.data
160 | catch error
161 | data.data = data.data
162 | finally
163 | defer.resolve data.data
164 | return
165 | ).fail (data) ->
166 | if data.responseJSON
167 | defer.reject data.responseJSON.data
168 | else
169 | defer.reject new Error("An error occured while trying to access the resource")
170 | return
171 |
172 | defer.promise()
173 |
174 | return doRequest()
175 |
176 | mkHttp: (provider, tokens, request, method) ->
177 | base = this
178 | (opts, opts2) ->
179 | options = {}
180 | if typeof opts is "string"
181 | if typeof opts2 is "object"
182 | for i of opts2
183 | options[i] = opts2[i]
184 | options.url = opts
185 | else if typeof opts is "object"
186 | for i of opts
187 | options[i] = opts[i]
188 | options.type = options.type or method
189 | options.oauthio =
190 | provider: provider
191 | tokens: tokens
192 | request: request
193 |
194 | base.http options
195 |
196 | mkHttpMe: (provider, tokens, request, method) ->
197 | base = this
198 | (filter) ->
199 | options = {}
200 | options.type = options.type or method
201 | options.oauthio =
202 | provider: provider
203 | tokens: tokens
204 | request: request
205 |
206 | options.data = options.data or {}
207 | options.data.filter = filter.join(",") if filter
208 | base.http_me options
209 |
210 | mkHttpAll: (provider, tokens, endpoint_descriptor) ->
211 | base = this
212 | () ->
213 | options = {}
214 | options.type = endpoint_descriptor.method
215 | options.url = config.oauthd_url + endpoint_descriptor.endpoint.replace ':provider', provider
216 | options.oauthio =
217 | provider: provider
218 | tokens: tokens
219 | options.data = {}
220 | for k, v of arguments
221 | th_param = endpoint_descriptor.params[k]
222 | if th_param?
223 | options.data[th_param.name] = v
224 |
225 | options.data = options.data or {}
226 | base.http_all options, endpoint_descriptor, arguments
227 |
228 | sendCallback: (opts, defer) ->
229 | base = this
230 | data = undefined
231 | err = undefined
232 | try
233 | data = JSON.parse(opts.data)
234 | catch e
235 | defer.reject new Error("Error while parsing result")
236 | return opts.callback(new Error("Error while parsing result"))
237 | return if not data or not data.provider
238 | if opts.provider and data.provider.toLowerCase() isnt opts.provider.toLowerCase()
239 | err = new Error("Returned provider name does not match asked provider")
240 | defer.reject err
241 | if opts.callback and typeof opts.callback == "function"
242 | return opts.callback(err)
243 | else
244 | return
245 | if data.status is "error" or data.status is "fail"
246 | err = new Error(data.message)
247 | err.body = data.data
248 | defer.reject err
249 | if opts.callback and typeof opts.callback == "function"
250 | return opts.callback(err)
251 | else
252 | return
253 | if data.status isnt "success" or not data.data
254 | err = new Error()
255 | err.body = data.data
256 | defer.reject err
257 | if opts.callback and typeof opts.callback == "function"
258 | return opts.callback(err)
259 | else
260 | return
261 |
262 | #checking if state is known
263 | data.state = data.state.replace(/\s+/g,"")
264 | i = 0
265 | while i < client_states.length
266 | client_states[i] = client_states[i].replace(/\s+/g,"")
267 | i++
268 |
269 | if not data.state or client_states.indexOf(data.state) == -1
270 | defer.reject new Error("State is not matching")
271 | if opts.callback and typeof opts.callback == "function"
272 | return opts.callback(new Error("State is not matching"))
273 | else
274 | return
275 | data.data.provider = data.provider unless opts.provider
276 | res = data.data
277 | res.provider = data.provider.toLowerCase()
278 | if cache.cacheEnabled(opts.cache) and res
279 | if opts.expires && ! res.expires_in
280 | res.expires_in = opts.expires
281 | cache.storeCache data.provider, res
282 | request = res.request
283 | delete res.request
284 |
285 | tokens = undefined
286 | if res.access_token
287 | tokens = access_token: res.access_token
288 | else if res.oauth_token and res.oauth_token_secret
289 | tokens =
290 | oauth_token: res.oauth_token
291 | oauth_token_secret: res.oauth_token_secret
292 | unless request
293 | defer.resolve res
294 | if opts.callback and typeof opts.callback == "function"
295 | return opts.callback(null, res)
296 | else
297 | return
298 | if request.required
299 | for i of request.required
300 | tokens[request.required[i]] = res[request.required[i]]
301 | make_res = (method) ->
302 | base.mkHttp data.provider, tokens, request, method
303 |
304 | res.toJson = ->
305 | a = {}
306 | a.access_token = res.access_token if res.access_token?
307 | a.oauth_token = res.oauth_token if res.oauth_token?
308 | a.oauth_token_secret = res.oauth_token_secret if res.oauth_token_secret?
309 | a.expires_in = res.expires_in if res.expires_in?
310 | a.token_type = res.token_type if res.token_type?
311 | a.id_token = res.id_token if res.id_token?
312 | a.provider = res.provider if res.provider?
313 | a.email = res.email if res.email?
314 | return a
315 |
316 | res.get = make_res("GET")
317 | res.post = make_res("POST")
318 | res.put = make_res("PUT")
319 | res.patch = make_res("PATCH")
320 | res.del = make_res("DELETE")
321 | res.me = base.mkHttpMe(data.provider, tokens, request, "GET")
322 |
323 | @retrieveMethods()
324 | .then () =>
325 | @generateMethods res, tokens, data.provider
326 | defer.resolve res
327 | if opts.callback and typeof opts.callback == "function"
328 | opts.callback null, res
329 | else
330 | return
331 | .fail (e) =>
332 | console.log 'Could not retrieve methods', e
333 | defer.resolve res
334 | if opts.callback and typeof opts.callback == "function"
335 | opts.callback null, res
336 | else
337 | return
338 |
--------------------------------------------------------------------------------
/coffee/lib/user.coffee:
--------------------------------------------------------------------------------
1 | "use strict"
2 |
3 | module.exports = (Materia) ->
4 | $ = Materia.getJquery()
5 | config = Materia.getConfig()
6 | storage = Materia.getStorage()
7 |
8 | lastSave = null
9 |
10 | class UserObject
11 | constructor: (data) ->
12 | @token = data.token
13 | @data = data.user
14 | @providers = data.providers
15 | lastSave = @getEditableData()
16 |
17 | getEditableData: () ->
18 | data = []
19 | for key of @data
20 | if ['id', 'email'].indexOf(key) == -1
21 | data.push
22 | key: key
23 | value: @data[key]
24 | return data
25 |
26 | save: () ->
27 | #call to save on stormpath
28 |
29 | dataToSave = {}
30 | for d in lastSave
31 | dataToSave[d.key] = @data[d.key] if @data[d.key] != d.value
32 | delete @data[d.key] if @data[d.key] == null
33 | keyIsInLastSave = (key) ->
34 | for o in lastSave
35 | return true if o.key == key
36 | return false
37 |
38 | for d in @getEditableData()
39 | if !keyIsInLastSave d.key
40 | dataToSave[d.key] = @data[d.key]
41 | @saveLocal()
42 | return Materia.API.put '/api/usermanagement/user?k=' + config.key + '&token=' + @token, dataToSave
43 |
44 | ## todo select(provider)
45 | select: (provider) ->
46 | OAuthResult = null
47 | return OAuthResult
48 |
49 | saveLocal: () ->
50 | copy = token: @token, user: @data, providers: @providers
51 | storage.erase 'oio_auth'
52 | storage.create 'oio_auth', JSON.stringify(copy), 21600
53 |
54 | hasProvider: (provider) ->
55 | return @providers?.indexOf(provider) != -1
56 |
57 | getProviders: () ->
58 | defer = $.Deferred()
59 | Materia.API.get '/api/usermanagement/user/providers?k=' + config.key + '&token=' + @token
60 | .done (providers) =>
61 | @providers = providers.data
62 | @saveLocal()
63 | defer.resolve @providers
64 | .fail (err) ->
65 | defer.reject err
66 | return defer.promise()
67 |
68 | addProvider: (oauthRes) ->
69 | defer = $.Deferred()
70 | oauthRes = oauthRes.toJson() if typeof oauthRes.toJson == 'function'
71 | oauthRes.email = @data.email
72 | @providers.push oauthRes.provider
73 | Materia.API.post '/api/usermanagement/user/providers?k=' + config.key + '&token=' + @token, oauthRes
74 | .done (res) =>
75 | @data = res.data
76 | @saveLocal()
77 | defer.resolve()
78 | .fail (err) =>
79 | @providers.splice @providers.indexOf(oauthRes.provider), 1
80 | defer.reject err
81 | return defer.promise()
82 |
83 | removeProvider: (provider) ->
84 | defer = $.Deferred()
85 | @providers.splice @providers.indexOf(provider), 1
86 | Materia.API.del '/api/usermanagement/user/providers/' + provider + '?k=' + config.key + '&token=' + @token
87 | .done (res) =>
88 | @saveLocal()
89 | defer.resolve res
90 | .fail (err) =>
91 | @providers.push provider
92 | defer.reject err
93 | return defer.promise()
94 |
95 | # todo - not working
96 | changePassword: (oldPassword, newPassword) ->
97 | return Materia.API.post '/api/usermanagement/user/password?k=' + config.key + '&token=' + @token,
98 | password: newPassword
99 | #oldPassword ?
100 |
101 | #### 0.5.0 => remove this method
102 | isLoggued: () ->
103 | return Materia.User.isLogged()
104 | ###########
105 |
106 | isLogged: () ->
107 | return Materia.User.isLogged()
108 |
109 | logout: () ->
110 | defer = $.Deferred()
111 | storage.erase 'oio_auth'
112 | Materia.API.post('/api/usermanagement/user/logout?k=' + config.key + '&token=' + @token)
113 | .done ->
114 | defer.resolve()
115 | .fail (err)->
116 | defer.reject err
117 |
118 | return defer.promise()
119 | return {
120 | initialize: (public_key, options) -> return Materia.initialize public_key, options
121 | setOAuthdURL: (url) -> return Materia.setOAuthdURL url
122 | signup: (data) ->
123 | defer = $.Deferred()
124 | data = data.toJson() if typeof data.toJson == 'function'
125 | Materia.API.post '/api/usermanagement/signup?k=' + config.key, data
126 | .done (res) ->
127 | storage.create 'oio_auth', JSON.stringify(res.data), res.data.expires_in || 21600
128 | defer.resolve new UserObject(res.data)
129 | .fail (err) ->
130 | defer.reject err
131 |
132 | return defer.promise()
133 |
134 | signin: (email, password) ->
135 | defer = $.Deferred()
136 | if typeof email != "string" and not password
137 | # signin(OAuthRes)
138 | signinData = email
139 | signinData = signinData.toJson() if typeof signinData.toJson == 'function'
140 | Materia.API.post '/api/usermanagement/signin?k=' + config.key, signinData
141 | .done (res) ->
142 | storage.create 'oio_auth', JSON.stringify(res.data), res.data.expires_in || 21600
143 | defer.resolve new UserObject(res.data)
144 | .fail (err) ->
145 | defer.reject err
146 | else
147 | # signin(email, password)
148 | Materia.API.post('/api/usermanagement/signin?k=' + config.key,
149 | email: email
150 | password: password
151 | ).done((res) ->
152 | storage.create 'oio_auth', JSON.stringify(res.data), res.data.expires_in || 21600
153 | defer.resolve new UserObject(res.data)
154 | ).fail (err) ->
155 | defer.reject err
156 | return defer.promise()
157 |
158 | confirmResetPassword: (newPassword, sptoken) ->
159 | return Materia.API.post '/api/usermanagement/user/password?k=' + config.key,
160 | password: newPassword
161 | token: sptoken
162 |
163 | resetPassword: (email, callback) ->
164 | Materia.API.post '/api/usermanagement/user/password/reset?k=' + config.key, email: email
165 |
166 | refreshIdentity: () ->
167 | defer = $.Deferred()
168 | Materia.API.get('/api/usermanagement/user?k=' + config.key + '&token=' + JSON.parse(storage.read('oio_auth')).token)
169 | .done (res) ->
170 | defer.resolve new UserObject(res.data)
171 | .fail (err) ->
172 | defer.reject err
173 | return defer.promise()
174 |
175 | getIdentity: () ->
176 | user = storage.read 'oio_auth'
177 | return null if not user
178 | return new UserObject(JSON.parse(user))
179 |
180 | isLogged: () ->
181 | a = storage.read 'oio_auth'
182 | return true if a
183 | return false
184 | }
185 |
--------------------------------------------------------------------------------
/coffee/main.coffee:
--------------------------------------------------------------------------------
1 | do ->
2 | jquery = require('./tools/jquery-lite.js')
3 |
4 | Materia = require('./lib/core') window, document, jquery, navigator
5 | Materia.extend 'OAuth', require('./lib/oauth')
6 | Materia.extend 'API', require('./lib/api')
7 | Materia.extend 'User', require('./lib/user')
8 |
9 | if angular?
10 | angular.module 'oauthio', []
11 | .factory 'Materia', [() ->
12 | return Materia
13 | ]
14 | .factory 'OAuth', [() ->
15 | return Materia.OAuth
16 | ]
17 | .factory 'User', [() ->
18 | return Materia.User
19 | ]
20 |
21 | window.Materia = exports.Materia = Materia
22 | window.User = exports.User = exports.Materia.User
23 | window.OAuth = exports.OAuth = exports.Materia.OAuth
24 |
25 | if (typeof define == 'function' && define.amd)
26 | define -> exports
27 | if (module?.exports)
28 | module.exports = exports
29 |
30 | return exports
--------------------------------------------------------------------------------
/coffee/tools/cache.coffee:
--------------------------------------------------------------------------------
1 | "use strict"
2 |
3 | module.exports =
4 | init: (storage, config) ->
5 | @config = config
6 | @storage = storage
7 | tryCache: (OAuth, provider, cache) ->
8 | if @cacheEnabled(cache)
9 | cache = @storage.read("oauthio_provider_" + provider)
10 | return false unless cache
11 | cache = decodeURIComponent(cache)
12 | if typeof cache is "string"
13 | try cache = JSON.parse(cache)
14 | catch e
15 | return false
16 | if typeof cache is "object"
17 | res = {}
18 | for i of cache
19 | res[i] = cache[i] if i isnt "request" and typeof cache[i] isnt "function"
20 | return OAuth.create(provider, res, cache.request)
21 | false
22 |
23 | storeCache: (provider, cache) ->
24 | expires = 3600
25 | if cache.expires_in
26 | expires = cache.expires_in
27 | else if @config.options.expires || @config.options.expires == false
28 | expires = @config.options.expires
29 |
30 | @storage.create "oauthio_provider_" + provider, encodeURIComponent(JSON.stringify(cache)), expires
31 | return
32 |
33 | cacheEnabled: (cache) ->
34 | return @config.options.cache if typeof cache is "undefined"
35 | cache
36 |
37 | clearCache: (provider) ->
38 | if provider
39 | @storage.erase "oauthio_provider_" + provider
40 | else
41 | @storage.eraseFrom "oauthio_provider_"
42 | return
43 |
--------------------------------------------------------------------------------
/coffee/tools/location_operations.coffee:
--------------------------------------------------------------------------------
1 | "use strict"
2 |
3 | module.exports = (document) ->
4 | return {
5 | reload: ->
6 | document.location.reload()
7 | getHash: ->
8 | return document.location.hash
9 | setHash: (newHash) ->
10 | document.location.hash = newHash
11 | changeHref: (newLocation) ->
12 | document.location.href = newLocation
13 | }
--------------------------------------------------------------------------------
/coffee/tools/lstorage.coffee:
--------------------------------------------------------------------------------
1 | "use strict"
2 |
3 | useCache = (callback) ->
4 | cacheobj = localStorage.getItem('oauthio_cache')
5 | if cacheobj
6 | cacheobj = JSON.parse(cacheobj)
7 | else
8 | cacheobj = {}
9 | return callback cacheobj, ->
10 | localStorage.setItem('oauthio_cache', JSON.stringify(cacheobj))
11 |
12 |
13 | module.exports =
14 | init: (config, document) ->
15 | @config = config
16 | @document = document
17 |
18 | active: -> localStorage?
19 |
20 | create: (name, value, expires) ->
21 | @erase name
22 | date = new Date()
23 | localStorage.setItem(name, value)
24 | useCache (cacheobj, cacheupdate) ->
25 | cacheobj[name] = expires ? date.getTime() + (expires or 1200) * 1000 : false
26 | cacheupdate()
27 | return
28 |
29 | read: (name) ->
30 | return useCache (cacheobj, cacheupdate) ->
31 | if ! cacheobj[name]?
32 | return null
33 | if cacheobj[name] == false
34 | return localStorage.getItem(name)
35 | else if (new Date()).getTime() < cacheobj[name]
36 | localStorage.removeItem(name)
37 | delete cacheobj[name]
38 | cacheupdate()
39 | return null
40 | else
41 | return localStorage.getItem(name)
42 |
43 | erase: (name) ->
44 | useCache (cacheobj, cacheupdate) ->
45 | localStorage.removeItem(name)
46 | delete cacheobj[name]
47 | cacheupdate()
48 |
49 | eraseFrom: (prefix) ->
50 | useCache (cacheobj, cacheupdate) ->
51 | cachenames = Object.keys(cacheobj)
52 | for name in cachenames
53 | if name.substr(0, prefix.length) == prefix
54 | localStorage.removeItem(name)
55 | delete cacheobj[name]
56 | cacheupdate()
57 | return
--------------------------------------------------------------------------------
/coffee/tools/sha1.coffee:
--------------------------------------------------------------------------------
1 |
2 | #
3 | # * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
4 | # * in FIPS 180-1
5 | # * Version 2.2 Copyright Paul Johnston 2000 - 2009.
6 | # * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
7 | # * Distributed under the BSD License
8 | # * See http://pajhome.org.uk/crypt/md5 for details.
9 | #
10 |
11 | #
12 | # * Configurable variables. You may need to tweak these to be compatible with
13 | # * the server-side, but the defaults work in most cases.
14 | #
15 | hexcase = 0 # hex output format. 0 - lowercase; 1 - uppercase
16 | b64pad = "" # base-64 pad character. "=" for strict RFC compliance
17 |
18 | #
19 | # * These are the functions you'll usually want to call
20 | # * They take string arguments and return either hex or base-64 encoded strings
21 | #
22 | ### istanbul ignore next ###
23 | module.exports =
24 | hex_sha1: (s) ->
25 | @rstr2hex @rstr_sha1(@str2rstr_utf8(s))
26 |
27 | b64_sha1: (s) ->
28 | @rstr2b64 @rstr_sha1(@str2rstr_utf8(s))
29 |
30 | any_sha1: (s, e) ->
31 | @rstr2any @rstr_sha1(@str2rstr_utf8(s)), e
32 |
33 | hex_hmac_sha1: (k, d) ->
34 | @rstr2hex @rstr_hmac_sha1(@str2rstr_utf8(k), @str2rstr_utf8(d))
35 |
36 | b64_hmac_sha1: (k, d) ->
37 | @rstr2b64 @rstr_hmac_sha1(@str2rstr_utf8(k), @str2rstr_utf8(d))
38 |
39 | any_hmac_sha1: (k, d, e) ->
40 | @rstr2any @rstr_hmac_sha1(@str2rstr_utf8(k), @str2rstr_utf8(d)), e
41 |
42 |
43 | #
44 | # * Perform a simple self-test to see if the VM is working
45 | #
46 | sha1_vm_test: ->
47 | thishex_sha1("abc").toLowerCase() is "a9993e364706816aba3e25717850c26c9cd0d89d"
48 |
49 |
50 | #
51 | # * Calculate the SHA1 of a raw string
52 | #
53 | rstr_sha1: (s) ->
54 | @binb2rstr @binb_sha1(@rstr2binb(s), s.length * 8)
55 |
56 |
57 | #
58 | # * Calculate the HMAC-SHA1 of a key and some data (raw strings)
59 | #
60 | rstr_hmac_sha1: (key, data) ->
61 | bkey = @rstr2binb(key)
62 | bkey = @binb_sha1(bkey, key.length * 8) if bkey.length > 16
63 | ipad = Array(16)
64 | opad = Array(16)
65 | i = 0
66 |
67 | while i < 16
68 | ipad[i] = bkey[i] ^ 0x36363636
69 | opad[i] = bkey[i] ^ 0x5C5C5C5C
70 | i++
71 | hash = @binb_sha1(ipad.concat(@rstr2binb(data)), 512 + data.length * 8)
72 | @binb2rstr @binb_sha1(opad.concat(hash), 512 + 160)
73 |
74 |
75 | #
76 | # * Convert a raw string to a hex string
77 | #
78 | rstr2hex: (input) ->
79 | try
80 | hexcase
81 | catch e
82 | hexcase = 0
83 | hex_tab = (if hexcase then "0123456789ABCDEF" else "0123456789abcdef")
84 | output = ""
85 | x = undefined
86 | i = 0
87 |
88 | while i < input.length
89 | x = input.charCodeAt(i)
90 | output += hex_tab.charAt((x >>> 4) & 0x0F) + hex_tab.charAt(x & 0x0F)
91 | i++
92 | output
93 |
94 |
95 | #
96 | # * Convert a raw string to a base-64 string
97 | #
98 | rstr2b64: (input) ->
99 | try
100 | b64pad
101 | catch e
102 | b64pad = ""
103 | tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
104 | output = ""
105 | len = input.length
106 | i = 0
107 |
108 | while i < len
109 | triplet = (input.charCodeAt(i) << 16) | ((if i + 1 < len then input.charCodeAt(i + 1) << 8 else 0)) | ((if i + 2 < len then input.charCodeAt(i + 2) else 0))
110 | j = 0
111 |
112 | while j < 4
113 | if i * 8 + j * 6 > input.length * 8
114 | output += b64pad
115 | else
116 | output += tab.charAt((triplet >>> 6 * (3 - j)) & 0x3F)
117 | j++
118 | i += 3
119 | output
120 |
121 |
122 | #
123 | # * Convert a raw string to an arbitrary string encoding
124 | #
125 | rstr2any: (input, encoding) ->
126 | divisor = encoding.length
127 | remainders = Array()
128 | i = undefined
129 | q = undefined
130 | x = undefined
131 | quotient = undefined
132 |
133 | # Convert to an array of 16-bit big-endian values, forming the dividend
134 | dividend = Array(Math.ceil(input.length / 2))
135 | i = 0
136 | while i < dividend.length
137 | dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1)
138 | i++
139 |
140 | #
141 | # * Repeatedly perform a long division. The binary array forms the dividend,
142 | # * the length of the encoding is the divisor. Once computed, the quotient
143 | # * forms the dividend for the next step. We stop when the dividend is zero.
144 | # * All remainders are stored for later use.
145 | #
146 | while dividend.length > 0
147 | quotient = Array()
148 | x = 0
149 | i = 0
150 | while i < dividend.length
151 | x = (x << 16) + dividend[i]
152 | q = Math.floor(x / divisor)
153 | x -= q * divisor
154 | quotient[quotient.length] = q if quotient.length > 0 or q > 0
155 | i++
156 | remainders[remainders.length] = x
157 | dividend = quotient
158 |
159 | # Convert the remainders to the output string
160 | output = ""
161 | i = remainders.length - 1
162 | while i >= 0
163 | output += encoding.charAt(remainders[i])
164 | i--
165 |
166 | # Append leading zero equivalents
167 | full_length = Math.ceil(input.length * 8 / (Math.log(encoding.length) / Math.log(2)))
168 | i = output.length
169 | while i < full_length
170 | output = encoding[0] + output
171 | i++
172 | output
173 |
174 |
175 | #
176 | # * Encode a string as utf-8.
177 | # * For efficiency, this assumes the input is valid utf-16.
178 | #
179 | str2rstr_utf8: (input) ->
180 | output = ""
181 | i = -1
182 | x = undefined
183 | y = undefined
184 | while ++i < input.length
185 |
186 | # Decode utf-16 surrogate pairs
187 | x = input.charCodeAt(i)
188 | y = (if i + 1 < input.length then input.charCodeAt(i + 1) else 0)
189 | if 0xD800 <= x and x <= 0xDBFF and 0xDC00 <= y and y <= 0xDFFF
190 | x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF)
191 | i++
192 |
193 | # Encode output as utf-8
194 | if x <= 0x7F
195 | output += String.fromCharCode(x)
196 | else if x <= 0x7FF
197 | output += String.fromCharCode(0xC0 | ((x >>> 6) & 0x1F), 0x80 | (x & 0x3F))
198 | else if x <= 0xFFFF
199 | output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F), 0x80 | ((x >>> 6) & 0x3F), 0x80 | (x & 0x3F))
200 | else output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07), 0x80 | ((x >>> 12) & 0x3F), 0x80 | ((x >>> 6) & 0x3F), 0x80 | (x & 0x3F)) if x <= 0x1FFFFF
201 | output
202 |
203 |
204 | #
205 | # * Encode a string as utf-16
206 | #
207 | str2rstr_utf16le: (input) ->
208 | output = ""
209 | i = 0
210 |
211 | while i < input.length
212 | output += String.fromCharCode(input.charCodeAt(i) & 0xFF, (input.charCodeAt(i) >>> 8) & 0xFF)
213 | i++
214 | output
215 |
216 | str2rstr_utf16be: (input) ->
217 | output = ""
218 | i = 0
219 |
220 | while i < input.length
221 | output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF, input.charCodeAt(i) & 0xFF)
222 | i++
223 | output
224 |
225 |
226 | #
227 | # * Convert a raw string to an array of big-endian words
228 | # * Characters >255 have their high-byte silently ignored.
229 | #
230 | rstr2binb: (input) ->
231 | output = Array(input.length >> 2)
232 | i = 0
233 |
234 | while i < output.length
235 | output[i] = 0
236 | i++
237 | i = 0
238 |
239 | while i < input.length * 8
240 | output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32)
241 | i += 8
242 | output
243 |
244 |
245 | #
246 | # * Convert an array of big-endian words to a string
247 | #
248 | binb2rstr: (input) ->
249 | output = ""
250 | i = 0
251 |
252 | while i < input.length * 32
253 | output += String.fromCharCode((input[i >> 5] >>> (24 - i % 32)) & 0xFF)
254 | i += 8
255 | output
256 |
257 |
258 | #
259 | # * Calculate the SHA-1 of an array of big-endian words, and a bit length
260 | #
261 | binb_sha1: (x, len) ->
262 |
263 | # append padding
264 | x[len >> 5] |= 0x80 << (24 - len % 32)
265 | x[((len + 64 >> 9) << 4) + 15] = len
266 | w = Array(80)
267 | a = 1732584193
268 | b = -271733879
269 | c = -1732584194
270 | d = 271733878
271 | e = -1009589776
272 | i = 0
273 |
274 | while i < x.length
275 | olda = a
276 | oldb = b
277 | oldc = c
278 | oldd = d
279 | olde = e
280 | j = 0
281 |
282 | while j < 80
283 | if j < 16
284 | w[j] = x[i + j]
285 | else
286 | w[j] = @bit_rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1)
287 | t = @safe_add(@safe_add(@bit_rol(a, 5), @sha1_ft(j, b, c, d)), @safe_add(@safe_add(e, w[j]), @sha1_kt(j)))
288 | e = d
289 | d = c
290 | c = @bit_rol(b, 30)
291 | b = a
292 | a = t
293 | j++
294 | a = @safe_add(a, olda)
295 | b = @safe_add(b, oldb)
296 | c = @safe_add(c, oldc)
297 | d = @safe_add(d, oldd)
298 | e = @safe_add(e, olde)
299 | i += 16
300 | Array a, b, c, d, e
301 |
302 |
303 | #
304 | # * Perform the appropriate triplet combination function for the current
305 | # * iteration
306 | #
307 | sha1_ft: (t, b, c, d) ->
308 | return (b & c) | ((~b) & d) if t < 20
309 | return b ^ c ^ d if t < 40
310 | return (b & c) | (b & d) | (c & d) if t < 60
311 | b ^ c ^ d
312 |
313 |
314 | #
315 | # * Determine the appropriate additive constant for the current iteration
316 | #
317 | sha1_kt: (t) ->
318 | (if (t < 20) then 1518500249 else (if (t < 40) then 1859775393 else (if (t < 60) then -1894007588 else -899497514)))
319 |
320 |
321 | #
322 | # * Add integers, wrapping at 2^32. This uses 16-bit operations internally
323 | # * to work around bugs in some JS interpreters.
324 | #
325 | safe_add: (x, y) ->
326 | lsw = (x & 0xFFFF) + (y & 0xFFFF)
327 | msw = (x >> 16) + (y >> 16) + (lsw >> 16)
328 | (msw << 16) | (lsw & 0xFFFF)
329 |
330 |
331 | #
332 | # * Bitwise rotate a 32-bit number to the left.
333 | #
334 | bit_rol: (num, cnt) ->
335 | (num << cnt) | (num >>> (32 - cnt))
336 |
337 | create_hash: ->
338 | hash = @b64_sha1((new Date()).getTime() + ":" + Math.floor(Math.random() * 9999999))
339 | hash.replace(/\+/g, "-").replace(/\//g, "_").replace /\=+$/, ""
340 |
--------------------------------------------------------------------------------
/coffee/tools/url.coffee:
--------------------------------------------------------------------------------
1 | module.exports = (document) ->
2 | getAbsUrl: (url) ->
3 | return url if url.match(/^.{2,5}:\/\//)
4 | return document.location.protocol + "//" + document.location.host + url if url[0] is "/"
5 | base_url = document.location.protocol + "//" + document.location.host + document.location.pathname
6 | return base_url + "/" + url if base_url[base_url.length - 1] isnt "/" and url[0] isnt "#"
7 | base_url + url
8 |
9 | replaceParam: (param, rep, rep2) ->
10 | param = param.replace(/\{\{(.*?)\}\}/g, (m, v) ->
11 | rep[v] or ""
12 | )
13 | if rep2
14 | param = param.replace(/\{(.*?)\}/g, (m, v) ->
15 | rep2[v] or ""
16 | )
17 | param
18 |
--------------------------------------------------------------------------------
/compile-jquery.sh:
--------------------------------------------------------------------------------
1 | #! /bin/sh
2 | # In the jquery sources folder, you have to:
3 | #
4 | # git clone git@github.com:jquery/jquery.git
5 | # git checkout 2.1.1
6 | # npm install
7 | #
8 | # Make sure to have grunt-cli installed with
9 | # npm install -g grunt-cli
10 |
11 | OAUTHJS_PATH=`pwd`
12 | JQUERY_PATH="../jquery"
13 |
14 | cd $JQUERY_PATH
15 | grunt custom:-attributes,-attributes/attr,-attributes/classes,-attributes/prop,-attributes/support,-attributes/val,-attributes,-css/addGetHookIf,-css/curCSS,-css/defaultDisplay,-css/hiddenVisibleSelectors,-css/support,-css/swap,-css/var,-css/var/cssExpand,-css/var/getStyles,-css/var/isHidden,-css/var/rmargin,-css/var/rnumnonpx,-css,-data/var/data_user,-deprecated,-dimensions,-effects,-effects/animatedSelector,-effects/Tween,-event/alias,-event/support,-intro,-manipulation/_evalUrl,-manipulation/support,-manipulation/var,-manipulation/var/rcheckableType,-manipulation,-offset,-outro,-queue,-queue/delay,-selector-native,-selector-sizzle,-sizzle,-sizzle/dist,-sizzle/dist/sizzle,-sizzle/test,-traversing,-traversing/findFilter,-traversing/var,-traversing/var/rneedsContext,-wrap,-exports,-exports/global,-exports/amd
16 | cat ./dist/jquery.js | head -n $((`cat ./dist/jquery.js | wc -l`-2)) > ./dist/jquery-lite.js
17 | LASTLINES=`cat ./dist/jquery.js | tail -n 1`
18 | echo 'return jQuery;' >> ./dist/jquery-lite.js
19 | echo $LASTLINES >> ./dist/jquery-lite.js
20 |
21 | cd $OAUTHJS_PATH
22 | cp $JQUERY_PATH/dist/jquery-lite.js ./js/tools/jquery-lite.js && echo "copied jquery to oauth-js"
23 | grunt && echo "compiled oauth-js"
24 |
--------------------------------------------------------------------------------
/dist/oauth.min.js:
--------------------------------------------------------------------------------
1 | !function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;b="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,b.oauthioPhonegap=a()}}(function(){var a;return function b(a,c,d){function e(g,h){if(!c[g]){if(!a[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};a[g][0].call(k.exports,function(b){var c=a[g][1][b];return e(c?c:b)},k,k.exports,b,a,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;gb;b++)if(b in this&&this[b]===a)return b;return-1};d=a("../tools/url")(),b.exports=function(a,b,c){var f,g,h,i,j;return f=a.getJquery(),h=a.getConfig(),g=a.getCache(),i=[],j=!1,{retrieveMethods:function(){var a;return a=f.Deferred(),j?a.resolve(i):f.ajax(h.oauthd_url+"/api/extended-endpoints").then(function(b){return i=b.data,j=!0,a.resolve()}).fail(function(b){return j=!0,a.reject(b)}),a.promise()},generateMethods:function(a,b,c){var d,e,f,g,h,j,k,l,m;if(null!=i){for(k=[],e=d=0,g=i.length;g>d;e=++d)l=i[e],h=l.name.split("."),j=a,k.push(function(){var a,d,e;for(e=[],f=a=0,d=h.length;d>a;f=++a)m=h[f],f=0?k.url+="&"+c:k.url+="?"+c}if(g.headers){k.headers=k.headers||{};for(a in g.headers)k.headers[a]=d.replaceParam(g.headers[a],k.oauthio.tokens,g.parameters)}return delete k.oauthio,f.ajax(k)}},k={},j=void 0;for(j in a)k[j]=a[j];return k.oauthio.request&&k.oauthio.request!==!0?i():(g={wait:!!k.oauthio.request},b=f.Deferred(),c.getDescription(k.oauthio.provider,g,function(a,c){return a?b.reject(a):(k.oauthio.tokens.oauth_token&&k.oauthio.tokens.oauth_token_secret?k.oauthio.request=c.oauth1&&c.oauth1.request:k.oauthio.request=c.oauth2&&c.oauth2.request,void b.resolve())}),b.then(i))},http_me:function(a){var b,d,e,g,i;e=function(){var a,b,c,d;a=f.Deferred(),d=i.oauthio.request||{},i.url=h.oauthd_url+"/auth/"+i.oauthio.provider+"/me",i.headers=i.headers||{},i.headers.oauthio="k="+h.key,i.oauthio.tokens.oauth_token&&i.oauthio.tokens.oauth_token_secret&&(i.headers.oauthio+="&oauthv=1");for(b in i.oauthio.tokens)i.headers.oauthio+="&"+encodeURIComponent(b)+"="+encodeURIComponent(i.oauthio.tokens[b]);return delete i.oauthio,c=f.ajax(i),f.when(c).done(function(b){a.resolve(b.data)}).fail(function(b){b.responseJSON?a.reject(b.responseJSON.data):a.reject(new Error("An error occured while trying to access the resource"))}),a.promise()},i={};for(g in a)i[g]=a[g];return i.oauthio.request&&i.oauthio.request!==!0?e():(d={wait:!!i.oauthio.request},b=f.Deferred(),c.getDescription(i.oauthio.provider,d,function(a,c){return a?b.reject(a):(i.oauthio.tokens.oauth_token&&i.oauthio.tokens.oauth_token_secret?i.oauthio.request=c.oauth1&&c.oauth1.request:i.oauthio.request=c.oauth2&&c.oauth2.request,void b.resolve())}),b.then(e))},http_all:function(a,b,c){var d;return(d=function(){var b,c,d,e;b=f.Deferred(),e=a.oauthio.request||{},a.headers=a.headers||{},a.headers.oauthio="k="+h.key,a.oauthio.tokens.oauth_token&&a.oauthio.tokens.oauth_token_secret&&(a.headers.oauthio+="&oauthv=1");for(c in a.oauthio.tokens)a.headers.oauthio+="&"+encodeURIComponent(c)+"="+encodeURIComponent(a.oauthio.tokens[c]);return delete a.oauthio,d=f.ajax(a),f.when(d).done(function(a){var c;if("string"==typeof a.data)try{a.data=JSON.parse(a.data)}catch(d){c=d,a.data=a.data}finally{b.resolve(a.data)}}).fail(function(a){a.responseJSON?b.reject(a.responseJSON.data):b.reject(new Error("An error occured while trying to access the resource"))}),b.promise()})()},mkHttp:function(a,b,c,d){var e;return e=this,function(f,g){var h,i;if(i={},"string"==typeof f){if("object"==typeof g)for(h in g)i[h]=g[h];i.url=f}else if("object"==typeof f)for(h in f)i[h]=f[h];return i.type=i.type||d,i.oauthio={provider:a,tokens:b,request:c},e.http(i)}},mkHttpMe:function(a,b,c,d){var e;return e=this,function(f){var g;return g={},g.type=g.type||d,g.oauthio={provider:a,tokens:b,request:c},g.data=g.data||{},f&&(g.data.filter=f.join(",")),e.http_me(g)}},mkHttpAll:function(a,b,c){var d;return d=this,function(){var e,f,g,i;f={},f.type=c.method,f.url=h.oauthd_url+c.endpoint.replace(":provider",a),f.oauthio={provider:a,tokens:b},f.data={};for(e in arguments)i=arguments[e],g=c.params[e],null!=g&&(f.data[g.name]=i);return f.data=f.data||{},d.http_all(f,c,arguments)}},sendCallback:function(a,c){var d,e,f,h,i,j,k,l,m;d=this,e=void 0,h=void 0;try{e=JSON.parse(a.data)}catch(n){return f=n,c.reject(new Error("Error while parsing result")),a.callback(new Error("Error while parsing result"))}if(e&&e.provider){if(a.provider&&e.provider.toLowerCase()!==a.provider.toLowerCase())return h=new Error("Returned provider name does not match asked provider"),c.reject(h),a.callback&&"function"==typeof a.callback?a.callback(h):void 0;if("error"===e.status||"fail"===e.status)return h=new Error(e.message),h.body=e.data,c.reject(h),a.callback&&"function"==typeof a.callback?a.callback(h):void 0;if("success"!==e.status||!e.data)return h=new Error,h.body=e.data,c.reject(h),a.callback&&"function"==typeof a.callback?a.callback(h):void 0;for(e.state=e.state.replace(/\s+/g,""),i=0;if;f++)b=e[f],this.data[b.key]!==b.value&&(c[b.key]=this.data[b.key]),null===this.data[b.key]&&delete this.data[b.key];for(h=function(a){var b,c,d;for(b=0,c=e.length;c>b;b++)if(d=e[b],d.key===a)return!0;return!1},k=this.getEditableData(),g=0,j=k.length;j>g;g++)b=k[g],h(b.key)||(c[b.key]=this.data[b.key]);return this.saveLocal(),a.API.put("/api/usermanagement/user?k="+d.key+"&token="+this.token,c)},c.prototype.select=function(a){var b;return b=null},c.prototype.saveLocal=function(){var a;return a={token:this.token,user:this.data,providers:this.providers},f.erase("oio_auth"),f.create("oio_auth",JSON.stringify(a),21600)},c.prototype.hasProvider=function(a){var b;return-1!==(null!=(b=this.providers)?b.indexOf(a):void 0)},c.prototype.getProviders=function(){var c;return c=b.Deferred(),a.API.get("/api/usermanagement/user/providers?k="+d.key+"&token="+this.token).done(function(a){return function(b){return a.providers=b.data,a.saveLocal(),c.resolve(a.providers)}}(this)).fail(function(a){return c.reject(a)}),c.promise()},c.prototype.addProvider=function(c){var e;return e=b.Deferred(),"function"==typeof c.toJson&&(c=c.toJson()),c.email=this.data.email,this.providers.push(c.provider),a.API.post("/api/usermanagement/user/providers?k="+d.key+"&token="+this.token,c).done(function(a){return function(b){return a.data=b.data,a.saveLocal(),e.resolve()}}(this)).fail(function(a){return function(b){return a.providers.splice(a.providers.indexOf(c.provider),1),e.reject(b)}}(this)),e.promise()},c.prototype.removeProvider=function(c){var e;return e=b.Deferred(),this.providers.splice(this.providers.indexOf(c),1),a.API.del("/api/usermanagement/user/providers/"+c+"?k="+d.key+"&token="+this.token).done(function(a){return function(b){return a.saveLocal(),e.resolve(b)}}(this)).fail(function(a){return function(b){return a.providers.push(c),e.reject(b)}}(this)),e.promise()},c.prototype.changePassword=function(b,c){return a.API.post("/api/usermanagement/user/password?k="+d.key+"&token="+this.token,{password:c})},c.prototype.isLoggued=function(){return a.User.isLogged()},c.prototype.isLogged=function(){return a.User.isLogged()},c.prototype.logout=function(){var c;return c=b.Deferred(),f.erase("oio_auth"),a.API.post("/api/usermanagement/user/logout?k="+d.key+"&token="+this.token).done(function(){return c.resolve()}).fail(function(a){return c.reject(a)}),c.promise()},c}(),{initialize:function(b,c){return a.initialize(b,c)},setOAuthdURL:function(b){return a.setOAuthdURL(b)},signup:function(e){var g;return g=b.Deferred(),"function"==typeof e.toJson&&(e=e.toJson()),a.API.post("/api/usermanagement/signup?k="+d.key,e).done(function(a){return f.create("oio_auth",JSON.stringify(a.data),a.data.expires_in||21600),g.resolve(new c(a.data))}).fail(function(a){return g.reject(a)}),g.promise()},signin:function(e,g){var h,i;return h=b.Deferred(),"string"==typeof e||g?a.API.post("/api/usermanagement/signin?k="+d.key,{email:e,password:g}).done(function(a){return f.create("oio_auth",JSON.stringify(a.data),a.data.expires_in||21600),h.resolve(new c(a.data))}).fail(function(a){return h.reject(a)}):(i=e,"function"==typeof i.toJson&&(i=i.toJson()),a.API.post("/api/usermanagement/signin?k="+d.key,i).done(function(a){return f.create("oio_auth",JSON.stringify(a.data),a.data.expires_in||21600),h.resolve(new c(a.data))}).fail(function(a){return h.reject(a)})),h.promise()},confirmResetPassword:function(b,c){return a.API.post("/api/usermanagement/user/password?k="+d.key,{password:b,token:c})},resetPassword:function(b,c){return a.API.post("/api/usermanagement/user/password/reset?k="+d.key,{email:b})},refreshIdentity:function(){var e;return e=b.Deferred(),a.API.get("/api/usermanagement/user?k="+d.key+"&token="+JSON.parse(f.read("oio_auth")).token).done(function(a){return e.resolve(new c(a.data))}).fail(function(a){return e.reject(a)}),e.promise()},getIdentity:function(){var a;return a=f.read("oio_auth"),a?new c(JSON.parse(a)):null},isLogged:function(){var a;return a=f.read("oio_auth"),a?!0:!1}}}},{}],8:[function(b,c,d){!function(){var e,f;return f=b("./tools/jquery-lite.js"),e=b("./lib/core")(window,document,f,navigator),e.extend("OAuth",b("./lib/oauth")),e.extend("API",b("./lib/api")),e.extend("User",b("./lib/user")),"undefined"!=typeof angular&&null!==angular&&angular.module("oauthio",[]).factory("Materia",[function(){return e}]).factory("OAuth",[function(){return e.OAuth}]).factory("User",[function(){return e.User}]),window.Materia=d.Materia=e,window.User=d.User=d.Materia.User,window.OAuth=d.OAuth=d.Materia.OAuth,"function"==typeof a&&a.amd&&a(function(){return d}),("undefined"!=typeof c&&null!==c?c.exports:void 0)&&(c.exports=d),d}()},{"./lib/api":2,"./lib/core":3,"./lib/oauth":4,"./lib/user":7,"./tools/jquery-lite.js":11}],9:[function(a,b,c){"use strict";b.exports={init:function(a,b){return this.config=b,this.storage=a},tryCache:function(a,b,c){var d,e,f;if(this.cacheEnabled(c)){if(c=this.storage.read("oauthio_provider_"+b),!c)return!1;c=decodeURIComponent(c)}if("string"==typeof c)try{c=JSON.parse(c)}catch(g){return d=g,!1}if("object"==typeof c){f={};for(e in c)"request"!==e&&"function"!=typeof c[e]&&(f[e]=c[e]);return a.create(b,f,c.request)}return!1},storeCache:function(a,b){var c;c=3600,b.expires_in?c=b.expires_in:(this.config.options.expires||this.config.options.expires===!1)&&(c=this.config.options.expires),this.storage.create("oauthio_provider_"+a,encodeURIComponent(JSON.stringify(b)),c)},cacheEnabled:function(a){return"undefined"==typeof a?this.config.options.cache:a},clearCache:function(a){a?this.storage.erase("oauthio_provider_"+a):this.storage.eraseFrom("oauthio_provider_")}}},{}],10:[function(a,b,c){"use strict";b.exports={init:function(a,b){return this.config=a,this.document=b},create:function(a,b,c){var d;this.erase(a),d=new Date,c?d.setTime(d.getTime()+1e3*(c||1200)):d.setFullYear(d.getFullYear()+3),c="; expires="+d.toGMTString(),this.document.cookie=a+"="+b+c+"; path=/"},read:function(a){var b,c,d,e;for(e=a+"=",c=this.document.cookie.split(";"),d=0;de;e++)c=d[e],b=c.split("=")[0].trim(),b.substr(0,a.length)===a&&this.erase(b)}}},{}],11:[function(a,b,c){!function(a,c){"object"==typeof b&&"object"==typeof b.exports?b.exports=a.document?c(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return c(a)}:c(a)}("undefined"!=typeof window?window:this,function(a,b){function c(a){var b=a.length,c=B.type(a);return"function"===c||B.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}function d(a){var b=L[a]={};return B.each(a.match(K)||[],function(a,c){b[c]=!0}),b}function e(){z.removeEventListener("DOMContentLoaded",e,!1),a.removeEventListener("load",e,!1),B.ready()}function f(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=B.expando+Math.random()}function g(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(Q,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:P.test(c)?B.parseJSON(c):c}catch(e){}data_user.set(a,b,c)}else c=void 0;return c}function h(){return!0}function i(){return!1}function j(){try{return z.activeElement}catch(a){}}function k(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(K)||[];if(B.isFunction(c))for(;d=f[e++];)"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function l(a,b,c,d){function e(h){var i;return f[h]=!0,B.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||g||f[j]?g?!(i=j):void 0:(b.dataTypes.unshift(j),e(j),!1)}),i}var f={},g=a===ga;return e(b.dataTypes[0])||!f["*"]&&e("*")}function m(a,b){var c,d,e=B.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&B.extend(!0,a,d),a}function n(a,b,c){for(var d,e,f,g,h=a.contents,i=a.dataTypes;"*"===i[0];)i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}return f?(f!==i[0]&&i.unshift(f),c[f]):void 0}function o(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];for(f=k.shift();f;)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}function p(a,b,c,d){var e;if(B.isArray(b))B.each(b,function(b,e){c||ka.test(a)?d(a,e):p(a+"["+("object"==typeof e?b:"")+"]",e,c,d)});else if(c||"object"!==B.type(b))d(a,b);else for(e in b)p(a+"["+e+"]",b[e],c,d)}var q=[],r=q.slice,s=q.concat,t=q.push,u=q.indexOf,v={},w=v.toString,x=v.hasOwnProperty,y={},z=a.document,A="2.1.1 -attributes,-attributes/attr,-attributes/classes,-attributes/prop,-attributes/support,-attributes/val,-css/addGetHookIf,-css/curCSS,-css/defaultDisplay,-css/hiddenVisibleSelectors,-css/support,-css/swap,-css/var,-css/var/cssExpand,-css/var/getStyles,-css/var/isHidden,-css/var/rmargin,-css/var/rnumnonpx,-css,-effects,-effects/Tween,-effects/animatedSelector,-dimensions,-offset,-data/var/data_user,-deprecated,-event/alias,-event/support,-intro,-manipulation/_evalUrl,-manipulation/support,-manipulation/var,-manipulation/var/rcheckableType,-manipulation,-outro,-queue,-queue/delay,-selector-native,-selector-sizzle,-sizzle/dist,-sizzle/dist/sizzle,-sizzle/dist/min,-sizzle/test,-sizzle/test/jquery,-traversing,-traversing/findFilter,-traversing/var/rneedsContext,-traversing/var,-wrap,-exports,-exports/amd",B=function(a,b){return new B.fn.init(a,b)},C=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,D=/^-ms-/,E=/-([\da-z])/gi,F=function(a,b){return b.toUpperCase()};B.fn=B.prototype={jquery:A,constructor:B,selector:"",length:0,toArray:function(){return r.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:r.call(this)},pushStack:function(a){var b=B.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return B.each(this,a,b)},map:function(a){return this.pushStack(B.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(r.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:t,sort:q.sort,splice:q.splice},B.extend=B.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||B.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(B.isPlainObject(d)||(e=B.isArray(d)))?(e?(e=!1,f=c&&B.isArray(c)?c:[]):f=c&&B.isPlainObject(c)?c:{},g[b]=B.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},B.extend({expando:"jQuery"+(A+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===B.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){return!B.isArray(a)&&a-parseFloat(a)>=0},isPlainObject:function(a){return"object"!==B.type(a)||a.nodeType||B.isWindow(a)?!1:a.constructor&&!x.call(a.constructor.prototype,"isPrototypeOf")?!1:!0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?v[w.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=B.trim(a),a&&(1===a.indexOf("use strict")?(b=z.createElement("script"),b.text=a,z.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(D,"ms-").replace(E,F)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,d){var e,f=0,g=a.length,h=c(a);if(d){if(h)for(;g>f&&(e=b.apply(a[f],d),e!==!1);f++);else for(f in a)if(e=b.apply(a[f],d),e===!1)break}else if(h)for(;g>f&&(e=b.call(a[f],f,a[f]),e!==!1);f++);else for(f in a)if(e=b.call(a[f],f,a[f]),e===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(C,"")},makeArray:function(a,b){var d=b||[];return null!=a&&(c(Object(a))?B.merge(d,"string"==typeof a?[a]:a):t.call(d,a)),d},inArray:function(a,b,c){return null==b?-1:u.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,d){var e,f=0,g=a.length,h=c(a),i=[];if(h)for(;g>f;f++)e=b(a[f],f,d),null!=e&&i.push(e);else for(f in a)e=b(a[f],f,d),null!=e&&i.push(e);return s.apply([],i)},guid:1,proxy:function(a,b){var c,d,e;return"string"==typeof b&&(c=a[b],b=a,a=c),B.isFunction(a)?(d=r.call(arguments,2),e=function(){return a.apply(b||this,d.concat(r.call(arguments)))},e.guid=a.guid=a.guid||B.guid++,e):void 0},now:Date.now,support:y}),B.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){v["[object "+b+"]"]=b.toLowerCase()});var G,H=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,I=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,J=B.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:I.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||G).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof B?b[0]:b,B.merge(this,B.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:z,!0)),H.test(c[1])&&B.isPlainObject(b))for(c in b)B.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}return d=z.getElementById(c[2]),d&&d.parentNode&&(this.length=1,this[0]=d),this.context=z,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):B.isFunction(a)?"undefined"!=typeof G.ready?G.ready(a):a(B):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),B.makeArray(a,this))};J.prototype=B.fn,G=B(z);var K=/\S+/g,L={};B.Callbacks=function(a){a="string"==typeof a?L[a]||d(a):B.extend({},a);var b,c,e,f,g,h,i=[],j=!a.once&&[],k=function(d){for(b=a.memory&&d,c=!0,h=f||0,f=0,g=i.length,e=!0;i&&g>h;h++)if(i[h].apply(d[0],d[1])===!1&&a.stopOnFalse){b=!1;break}e=!1,i&&(j?j.length&&k(j.shift()):b?i=[]:l.disable())},l={add:function(){if(i){var c=i.length;!function d(b){B.each(b,function(b,c){var e=B.type(c);"function"===e?a.unique&&l.has(c)||i.push(c):c&&c.length&&"string"!==e&&d(c)})}(arguments),e?g=i.length:b&&(f=c,k(b))}return this},remove:function(){return i&&B.each(arguments,function(a,b){for(var c;(c=B.inArray(b,i,c))>-1;)i.splice(c,1),e&&(g>=c&&g--,h>=c&&h--)}),this},has:function(a){return a?B.inArray(a,i)>-1:!(!i||!i.length)},empty:function(){return i=[],g=0,this},disable:function(){return i=j=b=void 0,this},disabled:function(){return!i},lock:function(){return j=void 0,b||l.disable(),this},locked:function(){return!j},fireWith:function(a,b){return!i||c&&!j||(b=b||[],b=[a,b.slice?b.slice():b],e?j.push(b):k(b)),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!c}};return l},B.extend({Deferred:function(a){var b=[["resolve","done",B.Callbacks("once memory"),"resolved"],["reject","fail",B.Callbacks("once memory"),"rejected"],["notify","progress",B.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return B.Deferred(function(c){B.each(b,function(b,f){var g=B.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&B.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?B.extend(a,d):d}},e={};return d.pipe=d.then,B.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b,c,d,e=0,f=r.call(arguments),g=f.length,h=1!==g||a&&B.isFunction(a.promise)?g:0,i=1===h?a:B.Deferred(),j=function(a,c,d){return function(e){c[a]=this,d[a]=arguments.length>1?r.call(arguments):e,d===b?i.notifyWith(c,d):--h||i.resolveWith(c,d)}};if(g>1)for(b=new Array(g),c=new Array(g),d=new Array(g);g>e;e++)f[e]&&B.isFunction(f[e].promise)?f[e].promise().done(j(e,d,f)).fail(i.reject).progress(j(e,c,b)):--h;return h||i.resolveWith(d,f),i.promise()}});var M;B.fn.ready=function(a){return B.ready.promise().done(a),this},B.extend({isReady:!1,
2 | readyWait:1,holdReady:function(a){a?B.readyWait++:B.ready(!0)},ready:function(a){(a===!0?--B.readyWait:B.isReady)||(B.isReady=!0,a!==!0&&--B.readyWait>0||(M.resolveWith(z,[B]),B.fn.triggerHandler&&(B(z).triggerHandler("ready"),B(z).off("ready"))))}}),B.ready.promise=function(b){return M||(M=B.Deferred(),"complete"===z.readyState?setTimeout(B.ready):(z.addEventListener("DOMContentLoaded",e,!1),a.addEventListener("load",e,!1))),M.promise(b)},B.ready.promise();var N=B.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===B.type(c)){e=!0;for(h in c)B.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,B.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(B(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f};B.acceptData=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType},f.uid=1,f.accepts=B.acceptData,f.prototype={key:function(a){if(!f.accepts(a))return 0;var b={},c=a[this.expando];if(!c){c=f.uid++;try{b[this.expando]={value:c},Object.defineProperties(a,b)}catch(d){b[this.expando]=c,B.extend(a,b)}}return this.cache[c]||(this.cache[c]={}),c},set:function(a,b,c){var d,e=this.key(a),f=this.cache[e];if("string"==typeof b)f[b]=c;else if(B.isEmptyObject(f))B.extend(this.cache[e],b);else for(d in b)f[d]=b[d];return f},get:function(a,b){var c=this.cache[this.key(a)];return void 0===b?c:c[b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,B.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=this.key(a),g=this.cache[f];if(void 0===b)this.cache[f]={};else{B.isArray(b)?d=b.concat(b.map(B.camelCase)):(e=B.camelCase(b),b in g?d=[b,e]:(d=e,d=d in g?[d]:d.match(K)||[])),c=d.length;for(;c--;)delete g[d[c]]}},hasData:function(a){return!B.isEmptyObject(this.cache[a[this.expando]]||{})},discard:function(a){a[this.expando]&&delete this.cache[a[this.expando]]}};var O=new f,P=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Q=/([A-Z])/g;B.extend({hasData:function(a){return data_user.hasData(a)||O.hasData(a)},data:function(a,b,c){return data_user.access(a,b,c)},removeData:function(a,b){data_user.remove(a,b)},_data:function(a,b,c){return O.access(a,b,c)},_removeData:function(a,b){O.remove(a,b)}}),B.fn.extend({data:function(a,b){var c,d,e,f=this[0],h=f&&f.attributes;if(void 0===a){if(this.length&&(e=data_user.get(f),1===f.nodeType&&!O.get(f,"hasDataAttrs"))){for(c=h.length;c--;)h[c]&&(d=h[c].name,0===d.indexOf("data-")&&(d=B.camelCase(d.slice(5)),g(f,d,e[d])));O.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){data_user.set(this,a)}):N(this,function(b){var c,d=B.camelCase(a);if(f&&void 0===b){if(c=data_user.get(f,a),void 0!==c)return c;if(c=data_user.get(f,d),void 0!==c)return c;if(c=g(f,d,void 0),void 0!==c)return c}else this.each(function(){var c=data_user.get(this,d);data_user.set(this,d,b),-1!==a.indexOf("-")&&void 0!==c&&data_user.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){data_user.remove(this,a)})}});var R=(/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,"undefined"),S=/^key/,T=/^(?:mouse|pointer|contextmenu)|click/,U=/^(?:focusinfocus|focusoutblur)$/,V=/^([^.]*)(?:\.(.+)|)$/;B.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=O.get(a);if(q)for(c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=B.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return typeof B!==R&&B.event.triggered!==b.type?B.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(K)||[""],j=b.length;j--;)h=V.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=B.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=B.event.special[n]||{},k=B.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&B.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g,!1)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),B.event.global[n]=!0)},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=O.hasData(a)&&O.get(a);if(q&&(i=q.events)){for(b=(b||"").match(K)||[""],j=b.length;j--;)if(h=V.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){for(l=B.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;f--;)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||B.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)B.event.remove(a,n+b[j],c,d,!0);B.isEmptyObject(i)&&(delete q.handle,O.remove(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,j,k,l,m=[d||z],n=x.call(b,"type")?b.type:b,o=x.call(b,"namespace")?b.namespace.split("."):[];if(g=h=d=d||z,3!==d.nodeType&&8!==d.nodeType&&!U.test(n+B.event.triggered)&&(n.indexOf(".")>=0&&(o=n.split("."),n=o.shift(),o.sort()),j=n.indexOf(":")<0&&"on"+n,b=b[B.expando]?b:new B.Event(n,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=o.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:B.makeArray(c,[b]),l=B.event.special[n]||{},e||!l.trigger||l.trigger.apply(d,c)!==!1)){if(!e&&!l.noBubble&&!B.isWindow(d)){for(i=l.delegateType||n,U.test(i+n)||(g=g.parentNode);g;g=g.parentNode)m.push(g),h=g;h===(d.ownerDocument||z)&&m.push(h.defaultView||h.parentWindow||a)}for(f=0;(g=m[f++])&&!b.isPropagationStopped();)b.type=f>1?i:l.bindType||n,k=(O.get(g,"events")||{})[b.type]&&O.get(g,"handle"),k&&k.apply(g,c),k=j&&g[j],k&&k.apply&&B.acceptData(g)&&(b.result=k.apply(g,c),b.result===!1&&b.preventDefault());return b.type=n,e||b.isDefaultPrevented()||l._default&&l._default.apply(m.pop(),c)!==!1||!B.acceptData(d)||j&&B.isFunction(d[n])&&!B.isWindow(d)&&(h=d[j],h&&(d[j]=null),B.event.triggered=n,d[n](),B.event.triggered=void 0,h&&(d[j]=h)),b.result}},dispatch:function(a){a=B.event.fix(a);var b,c,d,e,f,g=[],h=r.call(arguments),i=(O.get(this,"events")||{})[a.type]||[],j=B.event.special[a.type]||{};if(h[0]=a,a.delegateTarget=this,!j.preDispatch||j.preDispatch.call(this,a)!==!1){for(g=B.event.handlers.call(this,a,i),b=0;(e=g[b++])&&!a.isPropagationStopped();)for(a.currentTarget=e.elem,c=0;(f=e.handlers[c++])&&!a.isImmediatePropagationStopped();)(!a.namespace_re||a.namespace_re.test(f.namespace))&&(a.handleObj=f,a.data=f.data,d=((B.event.special[f.origType]||{}).handle||f.handler).apply(e.elem,h),void 0!==d&&(a.result=d)===!1&&(a.preventDefault(),a.stopPropagation()));return j.postDispatch&&j.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!==this;i=i.parentNode||this)if(i.disabled!==!0||"click"!==a.type){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?B(e,this).index(i)>=0:B.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h0?4:0,i=a>=200&&300>a||304===a,c&&(u=n(m,y,c)),u=o(m,u,y,i),i?(m.ifModified&&(v=y.getResponseHeader("Last-Modified"),v&&(B.lastModified[e]=v),v=y.getResponseHeader("etag"),v&&(B.etag[e]=v)),204===a||"HEAD"===m.type?x="nocontent":304===a?x="notmodified":(x=u.state,k=u.data,l=u.error,i=!l)):(l=x,(a||!x)&&(x="error",0>a&&(a=0))),y.status=a,y.statusText=(b||x)+"",i?r.resolveWith(p,[k,x,y]):r.rejectWith(p,[y,x,l]),y.statusCode(t),t=void 0,j&&q.trigger(i?"ajaxSuccess":"ajaxError",[y,m,i?k:l]),s.fireWith(p,[y,x]),j&&(q.trigger("ajaxComplete",[y,m]),--B.active||B.event.trigger("ajaxStop")))}"object"==typeof a&&(b=a,a=void 0),b=b||{};var d,e,f,g,h,i,j,k,m=B.ajaxSetup({},b),p=m.context||m,q=m.context&&(p.nodeType||p.jquery)?B(p):B.event,r=B.Deferred(),s=B.Callbacks("once memory"),t=m.statusCode||{},u={},v={},w=0,x="canceled",y={readyState:0,getResponseHeader:function(a){var b;if(2===w){if(!g)for(g={};b=aa.exec(f);)g[b[1].toLowerCase()]=b[2];b=g[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return 2===w?f:null},setRequestHeader:function(a,b){var c=a.toLowerCase();return w||(a=v[c]=v[c]||a,u[a]=b),this},overrideMimeType:function(a){return w||(m.mimeType=a),this},statusCode:function(a){var b;if(a)if(2>w)for(b in a)t[b]=[t[b],a[b]];else y.always(a[y.status]);return this},abort:function(a){var b=a||x;return d&&d.abort(b),c(0,b),this}};if(r.promise(y).complete=s.add,y.success=y.done,y.error=y.fail,m.url=((a||m.url||Z)+"").replace($,"").replace(da,Y[1]+"//"),m.type=b.method||b.type||m.method||m.type,m.dataTypes=B.trim(m.dataType||"*").toLowerCase().match(K)||[""],null==m.crossDomain&&(i=ea.exec(m.url.toLowerCase()),m.crossDomain=!(!i||i[1]===Y[1]&&i[2]===Y[2]&&(i[3]||("http:"===i[1]?"80":"443"))===(Y[3]||("http:"===Y[1]?"80":"443")))),m.data&&m.processData&&"string"!=typeof m.data&&(m.data=B.param(m.data,m.traditional)),l(fa,m,b,y),2===w)return y;j=m.global,j&&0===B.active++&&B.event.trigger("ajaxStart"),m.type=m.type.toUpperCase(),m.hasContent=!ca.test(m.type),e=m.url,m.hasContent||(m.data&&(e=m.url+=(X.test(e)?"&":"?")+m.data,delete m.data),m.cache===!1&&(m.url=_.test(e)?e.replace(_,"$1_="+W++):e+(X.test(e)?"&":"?")+"_="+W++)),m.ifModified&&(B.lastModified[e]&&y.setRequestHeader("If-Modified-Since",B.lastModified[e]),B.etag[e]&&y.setRequestHeader("If-None-Match",B.etag[e])),(m.data&&m.hasContent&&m.contentType!==!1||b.contentType)&&y.setRequestHeader("Content-Type",m.contentType),y.setRequestHeader("Accept",m.dataTypes[0]&&m.accepts[m.dataTypes[0]]?m.accepts[m.dataTypes[0]]+("*"!==m.dataTypes[0]?", "+ha+"; q=0.01":""):m.accepts["*"]);for(k in m.headers)y.setRequestHeader(k,m.headers[k]);if(m.beforeSend&&(m.beforeSend.call(p,y,m)===!1||2===w))return y.abort();x="abort";for(k in{success:1,error:1,complete:1})y[k](m[k]);if(d=l(ga,m,b,y)){y.readyState=1,j&&q.trigger("ajaxSend",[y,m]),m.async&&m.timeout>0&&(h=setTimeout(function(){y.abort("timeout")},m.timeout));try{w=1,d.send(u,c)}catch(z){if(!(2>w))throw z;c(-1,z)}}else c(-1,"No Transport");return y},getJSON:function(a,b,c){return B.get(a,b,c,"json")},getScript:function(a,b){return B.get(a,void 0,b,"script")}}),B.each(["get","post"],function(a,b){B[b]=function(a,c,d,e){return B.isFunction(c)&&(e=e||d,d=c,c=void 0),B.ajax({url:a,type:b,dataType:e,data:c,success:d})}}),B.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){B.fn[b]=function(a){return this.on(b,a)}});var ja=/%20/g,ka=/\[\]$/,la=/\r?\n/g,ma=/^(?:submit|button|image|reset|file)$/i,na=/^(?:input|select|textarea|keygen)/i;B.param=function(a,b){var c,d=[],e=function(a,b){b=B.isFunction(b)?b():null==b?"":b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};if(void 0===b&&(b=B.ajaxSettings&&B.ajaxSettings.traditional),B.isArray(a)||a.jquery&&!B.isPlainObject(a))B.each(a,function(){e(this.name,this.value)});else for(c in a)p(c,a[c],b,e);return d.join("&").replace(ja,"+")},B.fn.extend({serialize:function(){return B.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=B.prop(this,"elements");return a?B.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!B(this).is(":disabled")&&na.test(this.nodeName)&&!ma.test(a)&&(this.checked||!rcheckableType.test(a))}).map(function(a,b){var c=B(this).val();return null==c?null:B.isArray(c)?B.map(c,function(a){return{name:b.name,value:a.replace(la,"\r\n")}}):{name:b.name,value:c.replace(la,"\r\n")}}).get()}}),B.ajaxSettings.xhr=function(){try{return new XMLHttpRequest}catch(a){}};var oa=0,pa={},qa={0:200,1223:204},ra=B.ajaxSettings.xhr();a.ActiveXObject&&B(a).on("unload",function(){for(var a in pa)pa[a]()}),y.cors=!!ra&&"withCredentials"in ra,y.ajax=ra=!!ra,B.ajaxTransport(function(a){var b;return y.cors||ra&&!a.crossDomain?{send:function(c,d){var e,f=a.xhr(),g=++oa;if(f.open(a.type,a.url,a.async,a.username,a.password),a.xhrFields)for(e in a.xhrFields)f[e]=a.xhrFields[e];a.mimeType&&f.overrideMimeType&&f.overrideMimeType(a.mimeType),a.crossDomain||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest");for(e in c)f.setRequestHeader(e,c[e]);b=function(a){return function(){b&&(delete pa[g],b=f.onload=f.onerror=null,"abort"===a?f.abort():"error"===a?d(f.status,f.statusText):d(qa[f.status]||f.status,f.statusText,"string"==typeof f.responseText?{text:f.responseText}:void 0,f.getAllResponseHeaders()))}},f.onload=b(),f.onerror=b("error"),b=pa[g]=b("abort");try{f.send(a.hasContent&&a.data||null)}catch(h){if(b)throw h}},abort:function(){b&&b()}}:void 0}),B.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(a){return B.globalEval(a),a}}}),B.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),B.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(d,e){b=B("
21 |
22 |
23 |
24 |
68 |