UNPKG

ares-ide

Version:

A browser-based code editor and UI designer for Enyo 2 projects

633 lines (577 loc) 23.6 kB
if typeof XMLHttpRequest isnt 'undefined' and (typeof window isnt 'undefined' or typeof self isnt 'undefined') and typeof navigator isnt 'undefined' and typeof navigator.userAgent is 'string' # Browser or Web Worker. if typeof XDomainRequest isnt 'undefined' and not ('withCredentials' of new XMLHttpRequest()) DropboxXhrRequest = XDomainRequest DropboxXhrIeMode = true # IE's XDR doesn't allow setting requests' Content-Type to anything other # than text/plain, so it can't send _any_ forms. DropboxXhrCanSendForms = false else DropboxXhrRequest = XMLHttpRequest DropboxXhrIeMode = false # Web Workers don't support FormData at all. # Also, Firefox doesn't support adding named files to FormData. # https://bugzilla.mozilla.org/show_bug.cgi?id=690659 DropboxXhrCanSendForms = typeof FormData isnt 'undefined' and navigator.userAgent.indexOf('Firefox') is -1 DropboxXhrDoesPreflight = true else # Node.js. DropboxXhrRequest = require('xhr2') # Need an XHR emulation. DropboxXhrIeMode = false # Node.js doesn't have FormData. We wouldn't want to bother putting together # upload forms in node.js anyway, because it doesn't do CORS preflight # checks, so we can use PUT requests without a performance hit. DropboxXhrCanSendForms = false # Our XHR emulation skips CORS checks, which don't make sense for a server. DropboxXhrDoesPreflight = false if typeof Uint8Array is 'undefined' # IE <= 9 DropboxXhrArrayBufferView = null DropboxXhrWrapBlob = false DropboxXhrSendArrayBufferView = false else # Old browsers don't expose the ArrayBufferView constructor. if Object.getPrototypeOf DropboxXhrArrayBufferView = Object.getPrototypeOf( Object.getPrototypeOf(new Uint8Array(0))).constructor else if Object.__proto__ DropboxXhrArrayBufferView = (new Uint8Array(0)).__proto__.__proto__.constructor if typeof Blob is 'undefined' DropboxXhrWrapBlob = false DropboxXhrSendArrayBufferView = true else try if (new Blob [new Uint8Array(2)]).size is 2 DropboxXhrWrapBlob = true DropboxXhrSendArrayBufferView = true else DropboxXhrSendArrayBufferView = false DropboxXhrWrapBlob = (new Blob [new ArrayBuffer(2)]).size is 2 catch blobError DropboxXhrSendArrayBufferView = false DropboxXhrWrapBlob = false if typeof WebKitBlobBuilder isnt 'undefined' # Android's WebView doesn't support adding named files to FormData. if navigator.userAgent.indexOf('Android') isnt -1 DropboxXhrCanSendForms = false if DropboxXhrArrayBufferView is Object # Browsers that haven't implemented XHR#send(ArrayBufferView) also don't # have a real ArrayBufferView prototype. (Safari, Firefox) DropboxXhrSendArrayBufferView = false # Dispatches low-level AJAX calls (XMLHttpRequests). class Dropbox.Xhr # The object used to perform AJAX requests (XMLHttpRequest). @Request = DropboxXhrRequest # Set to true when using the XDomainRequest API. @ieXdr = DropboxXhrIeMode # Set to true if the platform has proper support for FormData. @canSendForms = DropboxXhrCanSendForms # Set to true if the platform performs CORS preflight checks. @doesPreflight = DropboxXhrDoesPreflight # Superclass for all ArrayBufferView objects. @ArrayBufferView = DropboxXhrArrayBufferView # True if we think we can send ArrayBufferView objects via XHR. @sendArrayBufferView = DropboxXhrSendArrayBufferView # True if ArrayBuffer and ArrayBufferView instances get wrapped in Blobs # before sending via XHR. @wrapBlob = DropboxXhrWrapBlob # Sets up an AJAX request. # # @param {String} method the HTTP method used to make the request ('GET', # 'POST', 'PUT', etc.) # @param {String} baseUrl the URL that receives the request; this URL might # be modified, e.g. by appending parameters for GET requests constructor: (@method, baseUrl) -> @isGet = @method is 'GET' @url = baseUrl @wantHeaders = false @headers = {} @params = null @body = null @preflight = not (@isGet or (@method is 'POST')) @signed = false @completed = false @responseType = null @callback = null @xhr = null @onError = null # @property {?XMLHttpRequest} the raw XMLHttpRequest object used to make the # request; null until Dropbox.Xhr#prepare is called xhr: null # @property {?function(Dropbox.ApiError, function(Dropbox.ApiError))} if the # XHR fails and this is non-null, it will be called with the error as its # first argument; the function is responsible for calling its 2nd argument # and passing it the ApiError, otherwise the Xhr's callback will not be # called onError: null # Sets the parameters (form field values) that will be sent with the request. # # @param {?Object} params an associative array (hash) containing the HTTP # request parameters # @return {Dropbox.Xhr} this, for easy call chaining setParams: (params) -> if @signed throw new Error 'setParams called after addOauthParams or addOauthHeader' if @params throw new Error 'setParams cannot be called twice' @params = params @ # Sets the function called when the XHR completes. # # This function can also be set when calling Dropbox.Xhr#send. # # @param {function(?Dropbox.ApiError, ?Object, ?Object)} callback called when # the XHR completes; if an error occurs, the first parameter will be a # Dropbox.ApiError instance; otherwise, the second parameter will be an # instance of the required response type (e.g., String, Blob), and the # third parameter will be the JSON-parsed 'x-dropbox-metadata' header # @return {Dropbox.Xhr} this, for easy call chaining setCallback: (@callback) -> @ # Ammends the request parameters to include an OAuth signature. # # The OAuth signature will become invalid if the parameters are changed after # the signing process. # # This method automatically decides the best way to add the OAuth signature # to the current request. Modifying the request in any way (e.g., by adding # headers) might result in a valid signature that is applied in a sub-optimal # fashion. For best results, call this right before Dropbox.Xhr#prepare. # # @param {Dropbox.Oauth} oauth OAuth instance whose key and secret will be # used to sign the request # @param {Boolean} cacheFriendly if true, the signing process choice will be # biased towards allowing the HTTP cache to work; by default, the choice # attempts to avoid the CORS preflight request whenever possible # @return {Dropbox.Xhr} this, for easy call chaining signWithOauth: (oauth, cacheFriendly) -> if Dropbox.Xhr.ieXdr @addOauthParams oauth else if @preflight or !Dropbox.Xhr.doesPreflight @addOauthHeader oauth else if @isGet and cacheFriendly @addOauthHeader oauth else @addOauthParams oauth # Ammends the request parameters to include an OAuth signature. # # The OAuth signature will become invalid if the parameters are changed after # the signing process. # # @param {Dropbox.Oauth} oauth OAuth instance whose key and secret will be # used to sign the request # @return {Dropbox.Xhr} this, for easy call chaining addOauthParams: (oauth) -> if @signed throw new Error 'Request already has an OAuth signature' @params or= {} oauth.addAuthParams @method, @url, @params @signed = true @ # Adds an Authorize header containing an OAuth signature. # # The OAuth signature will become invalid if the parameters are changed after # the signing process. # # @param {Dropbox.Oauth} oauth OAuth instance whose key and secret will be # used to sign the request # @return {Dropbox.Xhr} this, for easy call chaining addOauthHeader: (oauth) -> if @signed throw new Error 'Request already has an OAuth signature' @params or= {} @signed = true @setHeader 'Authorization', oauth.authHeader(@method, @url, @params) # Sets the body (piece of data) that will be sent with the request. # # @param {String, Blob, ArrayBuffer} body the body to be sent in a request; # GET requests cannot have a body # @return {Dropbox.Xhr} this, for easy call chaining setBody: (body) -> if @isGet throw new Error 'setBody cannot be called on GET requests' if @body isnt null throw new Error 'Request already has a body' if typeof body is 'string' # Content-Type will be set automatically. else if (typeof FormData isnt 'undefined') and (body instanceof FormData) # Content-Type will be set automatically. else @headers['Content-Type'] = 'application/octet-stream' @preflight = true @body = body @ # Sends off an AJAX request and requests a custom response type. # # This method requires XHR Level 2 support, which is not available in IE # versions <= 9. If these browsers must be supported, it is recommended to # check if typeof Uint8Array !== 'undefined' # # @param {String} responseType the value that will be assigned to the XHR's # responseType property # @return {Dropbox.Xhr} this, for easy call chaining setResponseType: (@responseType) -> @ # Sets the value of a custom HTTP header. # # Custom HTTP headers require a CORS preflight in browsers, so requests that # use them will take more time to complete, especially on high-latency mobile # connections. # # @param {String} headerName the name of the HTTP header # @param {String} value the value that the header will be set to # @return {Dropbox.Xhr} this, for easy call chaining setHeader: (headerName, value) -> if @headers[headerName] oldValue = @headers[headerName] throw new Error "HTTP header #{headerName} already set to #{oldValue}" if headerName is 'Content-Type' throw new Error 'Content-Type is automatically computed based on setBody' @preflight = true @headers[headerName] = value @ # Requests that the response headers be reported to the callback. # # Response headers are not returned by default because the parsing is # non-trivial and produces many intermediate strings. # # Response headers are not available on Internet Explorer 9 and below. # # @return {Dropbox.Xhr} this, for easy call chaining reportResponseHeaders: -> @wantHeaders = true # Simulates having an <input type="file"> being sent with the request. # # @param {String} fieldName the name of the form field / parameter (not of # the uploaded file) # @param {String} fileName the name of the uploaded file (not the name of the # form field / parameter) # @param {String, Blob, File} fileData contents of the file to be uploaded # @param {?String} contentType the MIME type of the file to be uploaded; if # fileData is a Blob or File, its MIME type is used instead setFileField: (fieldName, fileName, fileData, contentType) -> if @body isnt null throw new Error 'Request already has a body' if @isGet throw new Error 'setFileField cannot be called on GET requests' if typeof(fileData) is 'object' if typeof ArrayBuffer isnt 'undefined' if fileData instanceof ArrayBuffer # Convert ArrayBuffer -> ArrayBufferView on standard-compliant # browsers, to avoid warnings from the Blob constructor. if Dropbox.Xhr.sendArrayBufferView fileData = new Uint8Array fileData else # Convert ArrayBufferView -> ArrayBuffer on older browsers, to avoid # having a Blob that contains "[object Uint8Array]" instead of the # actual data. if !Dropbox.Xhr.sendArrayBufferView and fileData.byteOffset is 0 and fileData.buffer instanceof ArrayBuffer fileData = fileData.buffer contentType or= 'application/octet-stream' try fileData = new Blob [fileData], type: contentType catch blobError # Stock Android / iPhone browsers don't implement the Blob contructor. # This code is only used on iPhone Safari / WebView (Cordova), because # Android's browser has a bug in sending Blobs. if window.WebKitBlobBuilder builder = new WebKitBlobBuilder builder.append fileData if blob = builder.getBlob contentType fileData = blob # Workaround for http://crbug.com/165095 if typeof File isnt 'undefined' and fileData instanceof File fileData = new Blob [fileData], type: fileData.type useFormData = fileData instanceof Blob else useFormData = false if useFormData @body = new FormData() @body.append fieldName, fileData, fileName else contentType or= 'application/octet-stream' boundary = @multipartBoundary() @headers['Content-Type'] = "multipart/form-data; boundary=#{boundary}" @body = ['--', boundary, "\r\n", 'Content-Disposition: form-data; name="', fieldName, '"; filename="', fileName, "\"\r\n", 'Content-Type: ', contentType, "\r\n", "Content-Transfer-Encoding: binary\r\n\r\n", fileData, "\r\n", '--', boundary, '--', "\r\n"].join '' # @private # @return {String} a nonce suitable for use as a part boundary in a multipart # MIME message multipartBoundary: -> [Date.now().toString(36), Math.random().toString(36)].join '----' # Moves this request's parameters to its URL. # # @private # @return {Dropbox.Xhr} this, for easy call chaining paramsToUrl: -> if @params queryString = Dropbox.Xhr.urlEncode @params if queryString.length isnt 0 @url = [@url, '?', queryString].join '' @params = null @ # Moves this request's parameters to its body. # # @private # @return {Dropbox.Xhr} this, for easy call chaining paramsToBody: -> if @params if @body isnt null throw new Error 'Request already has a body' if @isGet throw new Error 'paramsToBody cannot be called on GET requests' @headers['Content-Type'] = 'application/x-www-form-urlencoded' @body = Dropbox.Xhr.urlEncode @params @params = null @ # Sets up an XHR request. # # This method completely sets up a native XHR object and stops short of # calling its send() method, so the API client has a chance of customizing # the XHR. After customizing the XHR, Dropbox.Xhr#send should be called. # # # @return {Dropbox.Xhr} this, for easy call chaining prepare: -> ieXdr = Dropbox.Xhr.ieXdr if @isGet or @body isnt null or ieXdr @paramsToUrl() if @body isnt null and typeof @body is 'string' @headers['Content-Type'] = 'text/plain; charset=utf8' else @paramsToBody() @xhr = new Dropbox.Xhr.Request() if ieXdr @xhr.onload = => @onXdrLoad() @xhr.onerror = => @onXdrError() @xhr.ontimeout = => @onXdrError() # NOTE: there are reports that XHR somtimes fails if onprogress doesn't # have any handler @xhr.onprogress = -> else @xhr.onreadystatechange = => @onReadyStateChange() @xhr.open @method, @url, true unless ieXdr for own header, value of @headers @xhr.setRequestHeader header, value if @responseType if @responseType is 'b' if @xhr.overrideMimeType @xhr.overrideMimeType 'text/plain; charset=x-user-defined' else @xhr.responseType = @responseType @ # Fires off the prepared XHR request. # # Dropbox.Xhr#prepare should be called exactly once before this method. # # @param {function(?Dropbox.ApiError, ?Object, ?Object)} callback called when # the XHR completes; if an error occurs, the first parameter will be a # Dropbox.ApiError instance; otherwise, the second parameter will be an # instance of the required response type (e.g., String, Blob), and the # third parameter will be the JSON-parsed 'x-dropbox-metadata' header # @return {Dropbox.Xhr} this, for easy call chaining send: (callback) -> @callback = callback or @callback if @body isnt null body = @body if Dropbox.Xhr.sendArrayBufferView # Standards-compliant browsers don't like to send() naked ArrayBuffers if body instanceof ArrayBuffer body = new Uint8Array body else # Convert ArrayBufferView -> ArrayBuffer on older browsers, because # they will send "[object Uint8Array]" instead of the actual data. if body.byteOffset is 0 and body.buffer instanceof ArrayBuffer body = body.buffer try @xhr.send body catch xhrError # Node.js doesn't implement Blob. if !Dropbox.Xhr.sendArrayBufferView and Dropbox.Xhr.wrapBlob # Firefox doesn't support sending ArrayBufferViews. body = new Blob [body], type: 'application/octet-stream' @xhr.send body else throw xhrError else @xhr.send() @ # Encodes an associative array (hash) into a x-www-form-urlencoded String. # # For consistency, the keys are sorted in alphabetical order in the encoded # output. # # @param {Object} object the JavaScript object whose keys will be encoded # @return {String} the object's keys and values, encoded using # x-www-form-urlencoded @urlEncode: (object) -> chunks = [] for key, value of object chunks.push @urlEncodeValue(key) + '=' + @urlEncodeValue(value) chunks.sort().join '&' # Encodes an object into a x-www-form-urlencoded key or value. # # @param {Object} object the object to be encoded; the encoding calls # toString() on the object to obtain its string representation # @return {String} encoded string, suitable for use as a key or value in an # x-www-form-urlencoded string @urlEncodeValue: (object) -> encodeURIComponent(object.toString()).replace(/\!/g, '%21'). replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29'). replace(/\*/g, '%2A') # Decodes an x-www-form-urlencoded String into an associative array (hash). # # @param {String} string the x-www-form-urlencoded String to be decoded # @return {Object} an associative array whose keys and values are all strings @urlDecode: (string) -> result = {} for token in string.split '&' kvp = token.split '=' result[decodeURIComponent(kvp[0])] = decodeURIComponent kvp[1] result # Handles the XHR readystate event. onReadyStateChange: -> return true if @xhr.readyState isnt 4 # XMLHttpRequest.DONE is 4 # WebKit might fire this multiple times. # http://crbug.com/159827 return true if @completed @completed = true if @xhr.status < 200 or @xhr.status >= 300 apiError = new Dropbox.ApiError @xhr, @method, @url if @onError @onError apiError, @callback else @callback apiError return true if @wantHeaders allHeaders = @xhr.getAllResponseHeaders() if allHeaders headers = Dropbox.Xhr.parseResponseHeaders allHeaders else # Work around https://bugzilla.mozilla.org/show_bug.cgi?id=608735 headers = @guessResponseHeaders() metadataJson = headers['x-dropbox-metadata'] else headers = undefined metadataJson = @xhr.getResponseHeader 'x-dropbox-metadata' if metadataJson?.length try metadata = JSON.parse metadataJson catch jsonError # Make sure the app doesn't crash if the server goes crazy. metadata = undefined else metadata = undefined if @responseType if @responseType is 'b' dirtyText = if @xhr.responseText? @xhr.responseText else @xhr.response bytes = [] for i in [0...dirtyText.length] bytes.push String.fromCharCode(dirtyText.charCodeAt(i) & 0xFF) text = bytes.join '' @callback null, text, metadata, headers else @callback null, @xhr.response, metadata, headers return true text = if @xhr.responseText? then @xhr.responseText else @xhr.response switch @xhr.getResponseHeader('Content-Type') when 'application/x-www-form-urlencoded' @callback null, Dropbox.Xhr.urlDecode(text), metadata, headers when 'application/json', 'text/javascript' @callback null, JSON.parse(text), metadata, headers else @callback null, text, metadata, headers true # Parses a block of raw HTTP headers. # # @private # Called by XHR's response processing code. # # @param {String} allHeaders the return value of an getAllResponseHeaders() # call on a XMLHttpRequest object # @return {Object<String, String>} object whose keys are the lowercased HTTP # header names, and whose values are the corresponding HTTP header values @parseResponseHeaders: (allHeaders) -> headers = {} headerLines = allHeaders.split "\n" for line in headerLines # NOTE: IE8 doesn't support trim(); we don't implement a fallback because # XDR (used on IE < 10) doesn't support headers, so this won't get # called anyway colonIndex = line.indexOf ':' name = line.substring(0, colonIndex).trim().toLowerCase() value = line.substring(colonIndex + 1).trim() headers[name] = value headers # Emulates getAllResponseHeaders()+parseResponseHeaders() on buggy browsers. # # @private # Called by XHR's response processing code. # # @return {Object<String, String>} object whose keys are the lowercased HTTP # header names, and whose values are the corresponding HTTP header values guessResponseHeaders: -> # TODO(pwnall): investigate removing this when Firefox 21 gets released. headers = {} # Using ther header names listed at # http://www.w3.org/TR/cors/#simple-response-header # and the names used by the Dropbox API server in # access-control-expose-headers. for name in ['cache-control', 'content-language', 'content-range', 'content-type', 'expires', 'last-modified', 'pragma', 'x-dropbox-metadata'] value = @xhr.getResponseHeader name headers[name] = value if value headers # Handles the XDomainRequest onload event. (IE 8, 9) onXdrLoad: -> # WebKit fires onreadystatechange multiple times, might as well include the # same fix in IE-specific code. return true if @completed @completed = true text = @xhr.responseText if @wantHeaders headers = 'content-type': @xhr.contentType else headers = undefined metadata = undefined if @responseType @callback null, text, metadata, headers return true switch @xhr.contentType when 'application/x-www-form-urlencoded' @callback null, Dropbox.Xhr.urlDecode(text), metadata, headers when 'application/json', 'text/javascript' @callback null, JSON.parse(text), metadata, headers else @callback null, text, metadata, headers true # Handles the XDomainRequest onload event. (IE 8, 9) onXdrError: -> # WebKit fires onreadystatechange multiple times, might as well include the # same fix in IE-specific code. return true if @completed @completed = true apiError = new Dropbox.ApiError @xhr, @method, @url if @onError @onError apiError, @callback else @callback apiError return true