////////////////////////////// // Browser ////////////////////////////// var XHR = window.XMLHttpRequest || function () { try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e1) {} try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e2) {} try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e3) {} throw new Error("This browser does not support XMLHttpRequest."); }; /** * @private * @name Browser * * @description * Constructor for the object exposed as $browser service. * * This object has two goals: * * - hide all the global state in the browser caused by the window object * - abstract away all the browser specific features and inconsistencies * * @param {object} window The global window object. * @param {object} document jQuery wrapped document. * @param {object} body jQuery wrapped document.body. * @param {function()} XHR XMLHttpRequest constructor. * @param {object} $log console.log or an object with the same interface. */ function Browser(window, document, body, XHR, $log) { var self = this, location = window.location, setTimeout = window.setTimeout; self.isMock = false; ////////////////////////////////////////////////////////////// // XHR API ////////////////////////////////////////////////////////////// var idCounter = 0; var outstandingRequestCount = 0; var outstandingRequestCallbacks = []; /** * Executes the `fn` function (supports currying) and decrements the `outstandingRequestCallbacks` * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed. */ function completeOutstandingRequest(fn) { try { fn.apply(null, slice.call(arguments, 1)); } finally { outstandingRequestCount--; if (outstandingRequestCount === 0) { while(outstandingRequestCallbacks.length) { try { outstandingRequestCallbacks.pop()(); } catch (e) { $log.error(e); } } } } } /** * @workInProgress * @ngdoc method * @name angular.service.$browser#xhr * @methodOf angular.service.$browser * * @param {string} method Requested method (get|post|put|delete|head|json) * @param {string} url Requested url * @param {string=} post Post data to send * @param {function(number, string)} callback Function that will be called on response * * @description * Send ajax request */ self.xhr = function(method, url, post, callback) { if (isFunction(post)) { callback = post; post = _null; } outstandingRequestCount ++; if (lowercase(method) == 'json') { var callbackId = "angular_" + Math.random() + '_' + (idCounter++); callbackId = callbackId.replace(/\d\./, ''); var script = document[0].createElement('script'); script.type = 'text/javascript'; script.src = url.replace('JSON_CALLBACK', callbackId); window[callbackId] = function(data){ window[callbackId] = _undefined; completeOutstandingRequest(callback, 200, data); }; body.append(script); } else { var xhr = new XHR(); xhr.open(method, url, true); xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xhr.setRequestHeader("Accept", "application/json, text/plain, */*"); xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); xhr.onreadystatechange = function() { if (xhr.readyState == 4) { completeOutstandingRequest(callback, xhr.status || 200, xhr.responseText); } }; xhr.send(post || ''); } }; /** * @workInProgress * @ngdoc method * @name angular.service.$browser#notifyWhenNoOutstandingRequests * @methodOf angular.service.$browser * * @param {function()} callback Function that will be called when no outstanding request */ self.notifyWhenNoOutstandingRequests = function(callback) { if (outstandingRequestCount === 0) { callback(); } else { outstandingRequestCallbacks.push(callback); } }; ////////////////////////////////////////////////////////////// // Poll Watcher API ////////////////////////////////////////////////////////////// var pollFns = []; /** * @workInProgress * @ngdoc method * @name angular.service.$browser#poll * @methodOf angular.service.$browser */ self.poll = function() { forEach(pollFns, function(pollFn){ pollFn(); }); }; /** * @workInProgress * @ngdoc method * @name angular.service.$browser#addPollFn * @methodOf angular.service.$browser * * @param {function()} fn Poll function to add * * @description * Adds a function to the list of functions that poller periodically executes * * @returns {function()} the added function */ self.addPollFn = function(fn) { pollFns.push(fn); return fn; }; /** * @workInProgress * @ngdoc method * @name angular.service.$browser#startPoller * @methodOf angular.service.$browser * * @param {number} interval How often should browser call poll functions (ms) * @param {function()} setTimeout Reference to a real or fake `setTimeout` function. * * @description * Configures the poller to run in the specified intervals, using the specified * setTimeout fn and kicks it off. */ self.startPoller = function(interval, setTimeout) { (function check(){ self.poll(); setTimeout(check, interval); })(); }; ////////////////////////////////////////////////////////////// // URL API ////////////////////////////////////////////////////////////// /** * @workInProgress * @ngdoc method * @name angular.service.$browser#setUrl * @methodOf angular.service.$browser * * @param {string} url New url * * @description * Sets browser's url */ self.setUrl = function(url) { var existingURL = location.href; if (!existingURL.match(/#/)) existingURL += '#'; if (!url.match(/#/)) url += '#'; location.href = url; }; /** * @workInProgress * @ngdoc method * @name angular.service.$browser#getUrl * @methodOf angular.service.$browser * * @description * Get current browser's url * * @returns {string} Browser's url */ self.getUrl = function() { return location.href; }; /** * @workInProgress * @ngdoc method * @name angular.service.$browser#onHashChange * @methodOf angular.service.$browser * * @description * Detects if browser support onhashchange events and register a listener otherwise registers * $browser poller. The `listener` will then get called when the hash changes. * * The listener gets called with either HashChangeEvent object or simple object that also contains * `oldURL` and `newURL` properties. * * NOTE: this api is intended for use only by the $location service. Please use the * {@link angular.service.$location $location service} to monitor hash changes in angular apps. * * @param {function(event)} listener Listener function to be called when url hash changes. * @return {function()} Returns the registered listener fn - handy if the fn is anonymous. */ self.onHashChange = function(listener) { if ('onhashchange' in window) { jqLite(window).bind('hashchange', listener); } else { var lastBrowserUrl = self.getUrl(); self.addPollFn(function() { if (lastBrowserUrl != self.getUrl()) { listener(); lastBrowserUrl = self.getUrl(); } }); } return listener; }; ////////////////////////////////////////////////////////////// // Cookies API ////////////////////////////////////////////////////////////// var rawDocument = document[0]; var lastCookies = {}; var lastCookieString = ''; /** * @workInProgress * @ngdoc method * @name angular.service.$browser#cookies * @methodOf angular.service.$browser * * @param {string=} name Cookie name * @param {string=} value Cokkie value * * @description * The cookies method provides a 'private' low level access to browser cookies. * It is not meant to be used directly, use the $cookie service instead. * * The return values vary depending on the arguments that the method was called with as follows: *