Path: blob/trunk/third_party/closure/goog/net/cookies.js
2868 views
// Copyright 2006 The Closure Library Authors. All Rights Reserved.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS-IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314/**15* @fileoverview Functions for setting, getting and deleting cookies.16*17* @author [email protected] (Erik Arvidsson)18*/192021goog.provide('goog.net.Cookies');22goog.provide('goog.net.cookies');2324goog.require('goog.string');25262728/**29* A class for handling browser cookies.30* @param {?Document} context The context document to get/set cookies on.31* @constructor32* @final33*/34goog.net.Cookies = function(context) {35/**36* The context document to get/set cookies on. If no document context is37* passed, use a fake one with only the "cookie" attribute. This allows38* this class to be instantiated safely in web worker environments.39* @private {{cookie: string}}40*/41this.document_ = context || {cookie: ''};42};434445/**46* Static constant for the size of cookies. Per the spec, there's a 4K limit47* to the size of a cookie. To make sure users can't break this limit, we48* should truncate long cookies at 3950 bytes, to be extra careful with dumb49* browsers/proxies that interpret 4K as 4000 rather than 4096.50* @type {number}51*/52goog.net.Cookies.MAX_COOKIE_LENGTH = 3950;535455/**56* Returns true if cookies are enabled.57* @return {boolean} True if cookies are enabled.58*/59goog.net.Cookies.prototype.isEnabled = function() {60return navigator.cookieEnabled;61};626364/**65* We do not allow '=', ';', or white space in the name.66*67* NOTE: The following are allowed by this method, but should be avoided for68* cookies handled by the server.69* - any name starting with '$'70* - 'Comment'71* - 'Domain'72* - 'Expires'73* - 'Max-Age'74* - 'Path'75* - 'Secure'76* - 'Version'77*78* @param {string} name Cookie name.79* @return {boolean} Whether name is valid.80*81* @see <a href="http://tools.ietf.org/html/rfc2109">RFC 2109</a>82* @see <a href="http://tools.ietf.org/html/rfc2965">RFC 2965</a>83*/84goog.net.Cookies.prototype.isValidName = function(name) {85return !(/[;=\s]/.test(name));86};878889/**90* We do not allow ';' or line break in the value.91*92* Spec does not mention any illegal characters, but in practice semi-colons93* break parsing and line breaks truncate the name.94*95* @param {string} value Cookie value.96* @return {boolean} Whether value is valid.97*98* @see <a href="http://tools.ietf.org/html/rfc2109">RFC 2109</a>99* @see <a href="http://tools.ietf.org/html/rfc2965">RFC 2965</a>100*/101goog.net.Cookies.prototype.isValidValue = function(value) {102return !(/[;\r\n]/.test(value));103};104105106/**107* Sets a cookie. The max_age can be -1 to set a session cookie. To remove and108* expire cookies, use remove() instead.109*110* Neither the {@code name} nor the {@code value} are encoded in any way. It is111* up to the callers of {@code get} and {@code set} (as well as all the other112* methods) to handle any possible encoding and decoding.113*114* @throws {!Error} If the {@code name} fails #goog.net.cookies.isValidName.115* @throws {!Error} If the {@code value} fails #goog.net.cookies.isValidValue.116*117* @param {string} name The cookie name.118* @param {string} value The cookie value.119* @param {number=} opt_maxAge The max age in seconds (from now). Use -1 to120* set a session cookie. If not provided, the default is -1121* (i.e. set a session cookie).122* @param {?string=} opt_path The path of the cookie. If not present then this123* uses the full request path.124* @param {?string=} opt_domain The domain of the cookie, or null to not125* specify a domain attribute (browser will use the full request host name).126* If not provided, the default is null (i.e. let browser use full request127* host name).128* @param {boolean=} opt_secure Whether the cookie should only be sent over129* a secure channel.130*/131goog.net.Cookies.prototype.set = function(132name, value, opt_maxAge, opt_path, opt_domain, opt_secure) {133if (!this.isValidName(name)) {134throw Error('Invalid cookie name "' + name + '"');135}136if (!this.isValidValue(value)) {137throw Error('Invalid cookie value "' + value + '"');138}139140if (!goog.isDef(opt_maxAge)) {141opt_maxAge = -1;142}143144var domainStr = opt_domain ? ';domain=' + opt_domain : '';145var pathStr = opt_path ? ';path=' + opt_path : '';146var secureStr = opt_secure ? ';secure' : '';147148var expiresStr;149150// Case 1: Set a session cookie.151if (opt_maxAge < 0) {152expiresStr = '';153154// Case 2: Remove the cookie.155// Note: We don't tell people about this option in the function doc because156// we prefer people to use remove() to remove cookies.157} else if (opt_maxAge == 0) {158// Note: Don't use Jan 1, 1970 for date because NS 4.76 will try to convert159// it to local time, and if the local time is before Jan 1, 1970, then the160// browser will ignore the Expires attribute altogether.161var pastDate = new Date(1970, 1 /*Feb*/, 1); // Feb 1, 1970162expiresStr = ';expires=' + pastDate.toUTCString();163164// Case 3: Set a persistent cookie.165} else {166var futureDate = new Date(goog.now() + opt_maxAge * 1000);167expiresStr = ';expires=' + futureDate.toUTCString();168}169170this.setCookie_(171name + '=' + value + domainStr + pathStr + expiresStr + secureStr);172};173174175/**176* Returns the value for the first cookie with the given name.177* @param {string} name The name of the cookie to get.178* @param {string=} opt_default If not found this is returned instead.179* @return {string|undefined} The value of the cookie. If no cookie is set this180* returns opt_default or undefined if opt_default is not provided.181*/182goog.net.Cookies.prototype.get = function(name, opt_default) {183var nameEq = name + '=';184var parts = this.getParts_();185for (var i = 0, part; i < parts.length; i++) {186part = goog.string.trim(parts[i]);187// startsWith188if (part.lastIndexOf(nameEq, 0) == 0) {189return part.substr(nameEq.length);190}191if (part == name) {192return '';193}194}195return opt_default;196};197198199/**200* Removes and expires a cookie.201* @param {string} name The cookie name.202* @param {string=} opt_path The path of the cookie, or null to expire a cookie203* set at the full request path. If not provided, the default is '/'204* (i.e. path=/).205* @param {string=} opt_domain The domain of the cookie, or null to expire a206* cookie set at the full request host name. If not provided, the default is207* null (i.e. cookie at full request host name).208* @return {boolean} Whether the cookie existed before it was removed.209*/210goog.net.Cookies.prototype.remove = function(name, opt_path, opt_domain) {211var rv = this.containsKey(name);212this.set(name, '', 0, opt_path, opt_domain);213return rv;214};215216217/**218* Gets the names for all the cookies.219* @return {Array<string>} An array with the names of the cookies.220*/221goog.net.Cookies.prototype.getKeys = function() {222return this.getKeyValues_().keys;223};224225226/**227* Gets the values for all the cookies.228* @return {Array<string>} An array with the values of the cookies.229*/230goog.net.Cookies.prototype.getValues = function() {231return this.getKeyValues_().values;232};233234235/**236* @return {boolean} Whether there are any cookies for this document.237*/238goog.net.Cookies.prototype.isEmpty = function() {239return !this.getCookie_();240};241242243/**244* @return {number} The number of cookies for this document.245*/246goog.net.Cookies.prototype.getCount = function() {247var cookie = this.getCookie_();248if (!cookie) {249return 0;250}251return this.getParts_().length;252};253254255/**256* Returns whether there is a cookie with the given name.257* @param {string} key The name of the cookie to test for.258* @return {boolean} Whether there is a cookie by that name.259*/260goog.net.Cookies.prototype.containsKey = function(key) {261// substring will return empty string if the key is not found, so the get262// function will only return undefined263return goog.isDef(this.get(key));264};265266267/**268* Returns whether there is a cookie with the given value. (This is an O(n)269* operation.)270* @param {string} value The value to check for.271* @return {boolean} Whether there is a cookie with that value.272*/273goog.net.Cookies.prototype.containsValue = function(value) {274// this O(n) in any case so lets do the trivial thing.275var values = this.getKeyValues_().values;276for (var i = 0; i < values.length; i++) {277if (values[i] == value) {278return true;279}280}281return false;282};283284285/**286* Removes all cookies for this document. Note that this will only remove287* cookies from the current path and domain. If there are cookies set using a288* subpath and/or another domain these will still be there.289*/290goog.net.Cookies.prototype.clear = function() {291var keys = this.getKeyValues_().keys;292for (var i = keys.length - 1; i >= 0; i--) {293this.remove(keys[i]);294}295};296297298/**299* Private helper function to allow testing cookies without depending on the300* browser.301* @param {string} s The cookie string to set.302* @private303*/304goog.net.Cookies.prototype.setCookie_ = function(s) {305this.document_.cookie = s;306};307308309/**310* Private helper function to allow testing cookies without depending on the311* browser. IE6 can return null here.312* @return {string} Returns the {@code document.cookie}.313* @private314*/315goog.net.Cookies.prototype.getCookie_ = function() {316return this.document_.cookie;317};318319320/**321* @return {!Array<string>} The cookie split on semi colons.322* @private323*/324goog.net.Cookies.prototype.getParts_ = function() {325return (this.getCookie_() || '').split(';');326};327328329/**330* Gets the names and values for all the cookies.331* @return {!{keys:!Array<string>, values:!Array<string>}} An object with keys332* and values.333* @private334*/335goog.net.Cookies.prototype.getKeyValues_ = function() {336var parts = this.getParts_();337var keys = [], values = [], index, part;338for (var i = 0; i < parts.length; i++) {339part = goog.string.trim(parts[i]);340index = part.indexOf('=');341342if (index == -1) { // empty name343keys.push('');344values.push(part);345} else {346keys.push(part.substring(0, index));347values.push(part.substring(index + 1));348}349}350return {keys: keys, values: values};351};352353354// TODO(closure-team): This should be a singleton getter instead of a static355// instance.356/**357* A static default instance.358* @const {!goog.net.Cookies}359*/360goog.net.cookies =361new goog.net.Cookies(typeof document == 'undefined' ? null : document);362363364/**365* Define the constant on the instance in order not to break many references to366* it.367* @type {number}368* @deprecated Use goog.net.Cookies.MAX_COOKIE_LENGTH instead.369*/370goog.net.cookies.MAX_COOKIE_LENGTH = goog.net.Cookies.MAX_COOKIE_LENGTH;371372373