UNPKG

12 kBtext/coffeescriptView Raw
1#
2# batman.nodep.coffee
3# batman.js
4#
5# Created by Nick Small
6# Copyright 2011, Shopify
7#
8`
9/*!
10 * Reqwest! A x-browser general purpose XHR connection manager
11 * copyright Dustin Diaz 2011
12 * https://github.com/ded/reqwest
13 * license MIT
14 */
15!
16function(context, win) {
17
18 var twoHundo = /^20\d$/,
19 doc = document,
20 byTag = 'getElementsByTagName',
21 contentType = 'Content-Type',
22 head = doc[byTag]('head')[0],
23 uniqid = 0,
24 lastValue // data stored by the most recent JSONP callback
25 , xhr = ('XMLHttpRequest' in win) ?
26 function() {
27 return new XMLHttpRequest()
28 } : function() {
29 return new ActiveXObject('Microsoft.XMLHTTP')
30 }
31
32 function readyState(o, success, error) {
33 return function() {
34 if (o && o.readyState == 4) {
35 if (twoHundo.test(o.status)) {
36 success(o)
37 } else {
38 error(o)
39 }
40 }
41 }
42 }
43
44 function setHeaders(http, o) {
45 var headers = o.headers || {}
46 headers.Accept = headers.Accept || 'text/javascript, text/html, application/xml, text/xml, */*'
47
48 // breaks cross-origin requests with legacy browsers
49 if (!o.crossOrigin) {
50 headers['X-Requested-With'] = headers['X-Requested-With'] || 'XMLHttpRequest'
51 }
52
53 if (o.data) {
54 headers[contentType] = headers[contentType] || 'application/x-www-form-urlencoded'
55 }
56 for (var h in headers) {
57 headers.hasOwnProperty(h) && http.setRequestHeader(h, headers[h], false)
58 }
59 }
60
61 function getCallbackName(o) {
62 var callbackVar = o.jsonpCallback || "callback"
63 if (o.url.slice(-(callbackVar.length + 2)) == (callbackVar + "=?")) {
64 // Generate a guaranteed unique callback name
65 var callbackName = "reqwest_" + uniqid++
66
67 // Replace the ? in the URL with the generated name
68 o.url = o.url.substr(0, o.url.length - 1) + callbackName
69 return callbackName
70 } else {
71 // Find the supplied callback name
72 var regex = new RegExp(callbackVar + "=([\\w]+)")
73 return o.url.match(regex)[1]
74 }
75 }
76
77 // Store the data returned by the most recent callback
78
79
80 function generalCallback(data) {
81 lastValue = data
82 }
83
84 function getRequest(o, fn, err) {
85 function onload() {
86 // Call the user callback with the last value stored
87 // and clean up values and scripts.
88 o.success && o.success(lastValue)
89 lastValue = undefined
90 head.removeChild(script);
91 }
92 if (o.type == 'jsonp') {
93 var script = doc.createElement('script')
94
95 // Add the global callback
96 win[getCallbackName(o)] = generalCallback;
97
98 // Setup our script element
99 script.type = 'text/javascript'
100 script.src = o.url
101 script.async = true
102
103 script.onload = onload
104 // onload for IE
105 script.onreadystatechange = function() {
106 /^loaded|complete$/.test(script.readyState) && onload()
107 }
108
109 // Add the script to the DOM head
110 head.appendChild(script)
111 } else {
112 var http = xhr()
113 http.open(o.method || 'GET', typeof o == 'string' ? o : o.url, true)
114 setHeaders(http, o)
115 http.onreadystatechange = readyState(http, fn, err)
116 o.before && o.before(http)
117 http.send(o.data || null)
118 return http
119 }
120 }
121
122 function Reqwest(o, fn) {
123 this.o = o
124 this.fn = fn
125 init.apply(this, arguments)
126 }
127
128 function setType(url) {
129 if (/\.json$/.test(url)) {
130 return 'json'
131 }
132 if (/\.jsonp$/.test(url)) {
133 return 'jsonp'
134 }
135 if (/\.js$/.test(url)) {
136 return 'js'
137 }
138 if (/\.html?$/.test(url)) {
139 return 'html'
140 }
141 if (/\.xml$/.test(url)) {
142 return 'xml'
143 }
144 return 'js'
145 }
146
147 function init(o, fn) {
148 this.url = typeof o == 'string' ? o : o.url
149 this.timeout = null
150 var type = o.type || setType(this.url),
151 self = this
152 fn = fn ||
153 function() {}
154
155 if (o.timeout) {
156 this.timeout = setTimeout(function() {
157 self.abort()
158 error()
159 }, o.timeout)
160 }
161
162 function complete(resp) {
163 o.complete && o.complete(resp)
164 }
165
166 function success(resp) {
167 o.timeout && clearTimeout(self.timeout) && (self.timeout = null)
168 var r = resp.responseText
169
170 if (r) {
171 switch (type) {
172 case 'json':
173 resp = win.JSON ? win.JSON.parse(r) : eval('(' + r + ')')
174 break;
175 case 'js':
176 resp = eval(r)
177 break;
178 case 'html':
179 resp = r
180 break;
181 }
182 }
183
184 fn(resp)
185 o.success && o.success(resp)
186 complete(resp)
187 }
188
189 function error(resp) {
190 o.error && o.error(resp)
191 complete(resp)
192 }
193
194 this.request = getRequest(o, success, error)
195 }
196
197 Reqwest.prototype = {
198 abort: function() {
199 this.request.abort()
200 }
201
202 ,
203 retry: function() {
204 init.call(this, this.o, this.fn)
205 }
206 }
207
208 function reqwest(o, fn) {
209 return new Reqwest(o, fn)
210 }
211
212 function enc(v) {
213 return encodeURIComponent(v)
214 }
215
216 function serial(el) {
217 var n = el.name
218 // don't serialize elements that are disabled or without a name
219 if (el.disabled || !n) {
220 return ''
221 }
222 n = enc(n)
223 switch (el.tagName.toLowerCase()) {
224 case 'input':
225 switch (el.type) {
226 // silly wabbit
227 case 'reset':
228 case 'button':
229 case 'image':
230 case 'file':
231 return ''
232 case 'checkbox':
233 case 'radio':
234 return el.checked ? n + '=' + (el.value ? enc(el.value) : true) + '&' : ''
235 default:
236 // text hidden password submit
237 return n + '=' + (el.value ? enc(el.value) : '') + '&'
238 }
239 break;
240 case 'textarea':
241 return n + '=' + enc(el.value) + '&'
242 case 'select':
243 // @todo refactor beyond basic single selected value case
244 return n + '=' + enc(el.options[el.selectedIndex].value) + '&'
245 }
246 return ''
247 }
248
249 reqwest.serialize = function(form) {
250 var fields = [form[byTag]('input'), form[byTag]('select'), form[byTag]('textarea')],
251 serialized = []
252
253 for (var i = 0, l = fields.length; i < l; ++i) {
254 for (var j = 0, l2 = fields[i].length; j < l2; ++j) {
255 serialized.push(serial(fields[i][j]))
256 }
257 }
258 return serialized.join('').replace(/&$/, '')
259 }
260
261 reqwest.serializeArray = function(f) {
262 for (var pairs = this.serialize(f).split('&'), i = 0, l = pairs.length, r = [], o; i < l; i++) {
263 pairs[i] && (o = pairs[i].split('=')) && r.push({
264 name: o[0],
265 value: o[1]
266 })
267 }
268 return r
269 }
270
271 var old = context.reqwest
272 reqwest.noConflict = function() {
273 context.reqwest = old
274 return this
275 }
276
277 // defined as extern for Closure Compilation
278 if (typeof module !== 'undefined') module.exports = reqwest
279 context['reqwest'] = reqwest
280
281}(this, window)
282
283`
284(exports ? this).reqwest = if window? then window.reqwest else reqwest
285
286# `param` and `buildParams` stolen from jQuery
287#
288# jQuery JavaScript Library v1.6.1
289# http://jquery.com/
290#
291# Copyright 2011, John Resig
292# Dual licensed under the MIT or GPL Version 2 licenses.
293# http://jquery.org/license
294rbracket = /\[\]$/
295r20 = /%20/g
296param = (a) ->
297 s = []
298 add = (key, value) ->
299 value = (if Batman.typeOf(value) is 'function' then value() else value)
300 s[s.length] = encodeURIComponent(key) + "=" + encodeURIComponent(value)
301
302 if Batman.typeOf(a) is 'Array'
303 for value, name of a
304 add name, value
305 else
306 for k, v of a
307 buildParams k, v, add
308 s.join("&").replace r20, "+"
309
310buildParams = (prefix, obj, add) ->
311 if Batman.typeOf(obj) is 'Array'
312 for v, i in obj
313 if rbracket.test(prefix)
314 add prefix, v
315 else
316 buildParams prefix + "[" + (if typeof v == "object" or Batman.typeOf(v) is 'Array' then i else "") + "]", v, add
317 else if obj? and typeof obj == "object"
318 for name of obj
319 buildParams prefix + "[" + name + "]", obj[name], add
320 else
321 add prefix, obj
322
323Batman.Request::send = (data) ->
324 @fire 'loading'
325
326 options =
327 url: @get 'url'
328 method: @get 'method'
329 type: @get 'type'
330 headers: {}
331
332 success: (response) =>
333 @set 'response', response
334 @set 'status', xhr.status
335 @fire 'success', response
336
337 error: (xhr) =>
338 @set 'response', xhr.responseText || xhr.content
339 @set 'status', xhr.status
340 xhr.request = @
341 @fire 'error', xhr
342
343 complete: =>
344 @fire 'loaded'
345
346 if options.method in ['PUT', 'POST']
347 unless @get('formData')
348 options.headers['Content-type'] = @get 'contentType'
349 data = data || @get('data')
350 if options.method is 'GET'
351 options.url += param(data)
352 else
353 options.data = param(data)
354 else
355 options.data = @constructor.objectToFormData(options.data)
356
357 # Fires the request. Grab a reference to the xhr object so we can get the status code elsewhere.
358 xhr = (reqwest options).request
359
360prefixes = ['Webkit', 'Moz', 'O', 'ms', '']
361Batman.mixins.animation =
362 initialize: ->
363 for prefix in prefixes
364 @style["#{prefix}Transform"] = 'scale(0, 0)'
365 @style.opacity = 0
366
367 @style["#{prefix}TransitionProperty"] = "#{if prefix then '-' + prefix.toLowerCase() + '-' else ''}transform, opacity"
368 @style["#{prefix}TransitionDuration"] = "0.8s, 0.55s"
369 @style["#{prefix}TransformOrigin"] = "left top"
370 @
371 show: (addToParent) ->
372 show = =>
373 @style.opacity = 1
374 for prefix in prefixes
375 @style["#{prefix}Transform"] = 'scale(1, 1)'
376 @
377
378 if addToParent
379 addToParent.append?.appendChild @
380 addToParent.before?.parentNode.insertBefore @, addToParent.before
381
382 setTimeout show, 0
383 else
384 show()
385 @
386 hide: (shouldRemove) ->
387 @style.opacity = 0
388 for prefix in prefixes
389 @style["#{prefix}Transform"] = 'scale(0, 0)'
390
391 setTimeout((=> @parentNode?.removeChild @), 600) if shouldRemove
392 @