Index: lib/src/firebase/firebase-debug.js |
diff --git a/lib/src/firebase/firebase-debug.js b/lib/src/firebase/firebase-debug.js |
index 91dd2138ad2394f7fd842ab4e4b4da8bbcbe9f23..3b4589a15f9c745912b3717fd19e80fcadd3d705 100644 |
--- a/lib/src/firebase/firebase-debug.js |
+++ b/lib/src/firebase/firebase-debug.js |
@@ -1,4 +1,4 @@ |
-/*! @license Firebase v2.2.9 |
+/*! @license Firebase v2.3.1 |
License: https://www.firebase.com/terms/terms-of-service.html */ |
(function(ns) { |
ns.wrapper = function(goog, fb) { |
@@ -6,7 +6,7 @@ |
var CLOSURE_NO_DEPS = true; |
// Sets CLIENT_VERSION manually, since we can't use a closure --define with WHITESPACE_ONLY compilation. |
- var CLIENT_VERSION = '2.2.9'; |
+ var CLIENT_VERSION = '2.3.1'; |
var COMPILED = false; |
var goog = goog || {}; |
goog.global = this; |
@@ -4132,161 +4132,6 @@ fb.core.util.ServerValues.resolveDeferredValueSnapshot = function(node, serverVa |
return newNode; |
} |
}; |
-goog.provide("fb.core.util.Path"); |
-goog.provide("fb.core.util.ValidationPath"); |
-fb.core.util.Path = goog.defineClass(null, {constructor:function(pathOrString, opt_pieceNum) { |
- if (arguments.length == 1) { |
- this.pieces_ = pathOrString.split("/"); |
- var copyTo = 0; |
- for (var i = 0;i < this.pieces_.length;i++) { |
- if (this.pieces_[i].length > 0) { |
- this.pieces_[copyTo] = this.pieces_[i]; |
- copyTo++; |
- } |
- } |
- this.pieces_.length = copyTo; |
- this.pieceNum_ = 0; |
- } else { |
- this.pieces_ = pathOrString; |
- this.pieceNum_ = opt_pieceNum; |
- } |
-}, getFront:function() { |
- if (this.pieceNum_ >= this.pieces_.length) { |
- return null; |
- } |
- return this.pieces_[this.pieceNum_]; |
-}, getLength:function() { |
- return this.pieces_.length - this.pieceNum_; |
-}, popFront:function() { |
- var pieceNum = this.pieceNum_; |
- if (pieceNum < this.pieces_.length) { |
- pieceNum++; |
- } |
- return new fb.core.util.Path(this.pieces_, pieceNum); |
-}, getBack:function() { |
- if (this.pieceNum_ < this.pieces_.length) { |
- return this.pieces_[this.pieces_.length - 1]; |
- } |
- return null; |
-}, toString:function() { |
- var pathString = ""; |
- for (var i = this.pieceNum_;i < this.pieces_.length;i++) { |
- if (this.pieces_[i] !== "") { |
- pathString += "/" + this.pieces_[i]; |
- } |
- } |
- return pathString || "/"; |
-}, toUrlEncodedString:function() { |
- var pathString = ""; |
- for (var i = this.pieceNum_;i < this.pieces_.length;i++) { |
- if (this.pieces_[i] !== "") { |
- pathString += "/" + goog.string.urlEncode(this.pieces_[i]); |
- } |
- } |
- return pathString || "/"; |
-}, slice:function(opt_begin) { |
- var begin = opt_begin || 0; |
- return this.pieces_.slice(this.pieceNum_ + begin); |
-}, parent:function() { |
- if (this.pieceNum_ >= this.pieces_.length) { |
- return null; |
- } |
- var pieces = []; |
- for (var i = this.pieceNum_;i < this.pieces_.length - 1;i++) { |
- pieces.push(this.pieces_[i]); |
- } |
- return new fb.core.util.Path(pieces, 0); |
-}, child:function(childPathObj) { |
- var pieces = []; |
- for (var i = this.pieceNum_;i < this.pieces_.length;i++) { |
- pieces.push(this.pieces_[i]); |
- } |
- if (childPathObj instanceof fb.core.util.Path) { |
- for (i = childPathObj.pieceNum_;i < childPathObj.pieces_.length;i++) { |
- pieces.push(childPathObj.pieces_[i]); |
- } |
- } else { |
- var childPieces = childPathObj.split("/"); |
- for (i = 0;i < childPieces.length;i++) { |
- if (childPieces[i].length > 0) { |
- pieces.push(childPieces[i]); |
- } |
- } |
- } |
- return new fb.core.util.Path(pieces, 0); |
-}, isEmpty:function() { |
- return this.pieceNum_ >= this.pieces_.length; |
-}, statics:{relativePath:function(outerPath, innerPath) { |
- var outer = outerPath.getFront(), inner = innerPath.getFront(); |
- if (outer === null) { |
- return innerPath; |
- } else { |
- if (outer === inner) { |
- return fb.core.util.Path.relativePath(outerPath.popFront(), innerPath.popFront()); |
- } else { |
- throw new Error("INTERNAL ERROR: innerPath (" + innerPath + ") is not within " + "outerPath (" + outerPath + ")"); |
- } |
- } |
-}}, equals:function(other) { |
- if (this.getLength() !== other.getLength()) { |
- return false; |
- } |
- for (var i = this.pieceNum_, j = other.pieceNum_;i <= this.pieces_.length;i++, j++) { |
- if (this.pieces_[i] !== other.pieces_[j]) { |
- return false; |
- } |
- } |
- return true; |
-}, contains:function(other) { |
- var i = this.pieceNum_; |
- var j = other.pieceNum_; |
- if (this.getLength() > other.getLength()) { |
- return false; |
- } |
- while (i < this.pieces_.length) { |
- if (this.pieces_[i] !== other.pieces_[j]) { |
- return false; |
- } |
- ++i; |
- ++j; |
- } |
- return true; |
-}}); |
-fb.core.util.Path.Empty = new fb.core.util.Path(""); |
-fb.core.util.ValidationPath = goog.defineClass(null, {constructor:function(path, errorPrefix) { |
- this.parts_ = path.slice(); |
- this.byteLength_ = Math.max(1, this.parts_.length); |
- this.errorPrefix_ = errorPrefix; |
- for (var i = 0;i < this.parts_.length;i++) { |
- this.byteLength_ += fb.util.utf8.stringLength(this.parts_[i]); |
- } |
- this.checkValid_(); |
-}, statics:{MAX_PATH_DEPTH:32, MAX_PATH_LENGTH_BYTES:768}, push:function(child) { |
- if (this.parts_.length > 0) { |
- this.byteLength_ += 1; |
- } |
- this.parts_.push(child); |
- this.byteLength_ += fb.util.utf8.stringLength(child); |
- this.checkValid_(); |
-}, pop:function() { |
- var last = this.parts_.pop(); |
- this.byteLength_ -= fb.util.utf8.stringLength(last); |
- if (this.parts_.length > 0) { |
- this.byteLength_ -= 1; |
- } |
-}, checkValid_:function() { |
- if (this.byteLength_ > fb.core.util.ValidationPath.MAX_PATH_LENGTH_BYTES) { |
- throw new Error(this.errorPrefix_ + "has a key path longer than " + fb.core.util.ValidationPath.MAX_PATH_LENGTH_BYTES + " bytes (" + this.byteLength_ + ")."); |
- } |
- if (this.parts_.length > fb.core.util.ValidationPath.MAX_PATH_DEPTH) { |
- throw new Error(this.errorPrefix_ + "path specified exceeds the maximum depth that can be written (" + fb.core.util.ValidationPath.MAX_PATH_DEPTH + ") or object contains a cycle " + this.toErrorString()); |
- } |
-}, toErrorString:function() { |
- if (this.parts_.length == 0) { |
- return ""; |
- } |
- return "in property '" + this.parts_.join(".") + "'"; |
-}}); |
goog.provide("fb.core.storage.MemoryStorage"); |
goog.require("fb.util.obj"); |
goog.scope(function() { |
@@ -4395,6 +4240,28 @@ fb.core.RepoInfo.prototype.updateHost = function(newHost) { |
} |
} |
}; |
+fb.core.RepoInfo.prototype.connectionURL = function(type, params) { |
+ fb.core.util.assert(typeof type === "string", "typeof type must == string"); |
+ fb.core.util.assert(typeof params === "object", "typeof params must == object"); |
+ var connURL; |
+ if (type === fb.realtime.Constants.WEBSOCKET) { |
+ connURL = (this.secure ? "wss://" : "ws://") + this.internalHost + "/.ws?"; |
+ } else { |
+ if (type === fb.realtime.Constants.LONG_POLLING) { |
+ connURL = (this.secure ? "https://" : "http://") + this.internalHost + "/.lp?"; |
+ } else { |
+ throw new Error("Unknown connection type: " + type); |
+ } |
+ } |
+ if (this.needsQueryParam()) { |
+ params["ns"] = this.namespace; |
+ } |
+ var pairs = []; |
+ goog.object.forEach(params, function(element, index, obj) { |
+ pairs.push(index + "=" + element); |
+ }); |
+ return connURL + pairs.join("&"); |
+}; |
fb.core.RepoInfo.prototype.toString = function() { |
var str = (this.secure ? "https://" : "http://") + this.host; |
if (this.persistenceKey) { |
@@ -5482,15 +5349,15 @@ fb.core.view.ViewProcessor.prototype.assertIndexed = function(viewCache) { |
}; |
fb.core.view.ViewProcessor.prototype.applyOperation = function(oldViewCache, operation, writesCache, optCompleteCache) { |
var accumulator = new fb.core.view.ChildChangeAccumulator; |
- var newViewCache, constrainNode; |
+ var newViewCache, filterServerNode; |
if (operation.type === fb.core.OperationType.OVERWRITE) { |
var overwrite = (operation); |
if (overwrite.source.fromUser) { |
newViewCache = this.applyUserOverwrite_(oldViewCache, overwrite.path, overwrite.snap, writesCache, optCompleteCache, accumulator); |
} else { |
fb.core.util.assert(overwrite.source.fromServer, "Unknown source."); |
- constrainNode = overwrite.source.tagged; |
- newViewCache = this.applyServerOverwrite_(oldViewCache, overwrite.path, overwrite.snap, writesCache, optCompleteCache, constrainNode, accumulator); |
+ filterServerNode = overwrite.source.tagged || oldViewCache.getServerCache().isFiltered() && !overwrite.path.isEmpty(); |
+ newViewCache = this.applyServerOverwrite_(oldViewCache, overwrite.path, overwrite.snap, writesCache, optCompleteCache, filterServerNode, accumulator); |
} |
} else { |
if (operation.type === fb.core.OperationType.MERGE) { |
@@ -5499,8 +5366,8 @@ fb.core.view.ViewProcessor.prototype.applyOperation = function(oldViewCache, ope |
newViewCache = this.applyUserMerge_(oldViewCache, merge.path, merge.children, writesCache, optCompleteCache, accumulator); |
} else { |
fb.core.util.assert(merge.source.fromServer, "Unknown source."); |
- constrainNode = merge.source.tagged; |
- newViewCache = this.applyServerMerge_(oldViewCache, merge.path, merge.children, writesCache, optCompleteCache, constrainNode, accumulator); |
+ filterServerNode = merge.source.tagged || oldViewCache.getServerCache().isFiltered(); |
+ newViewCache = this.applyServerMerge_(oldViewCache, merge.path, merge.children, writesCache, optCompleteCache, filterServerNode, accumulator); |
} |
} else { |
if (operation.type === fb.core.OperationType.ACK_USER_WRITE) { |
@@ -5586,10 +5453,10 @@ fb.core.view.ViewProcessor.prototype.generateEventCacheAfterServerEvent_ = funct |
return viewCache.updateEventSnap(newEventCache, oldEventSnap.isFullyInitialized() || changePath.isEmpty(), this.filter_.filtersNodes()); |
} |
}; |
-fb.core.view.ViewProcessor.prototype.applyServerOverwrite_ = function(oldViewCache, changePath, changedSnap, writesCache, optCompleteCache, constrainServerNode, accumulator) { |
+fb.core.view.ViewProcessor.prototype.applyServerOverwrite_ = function(oldViewCache, changePath, changedSnap, writesCache, optCompleteCache, filterServerNode, accumulator) { |
var oldServerSnap = oldViewCache.getServerCache(); |
var newServerCache; |
- var serverFilter = constrainServerNode ? this.filter_ : this.filter_.getIndexedFilter(); |
+ var serverFilter = filterServerNode ? this.filter_ : this.filter_.getIndexedFilter(); |
if (changePath.isEmpty()) { |
newServerCache = serverFilter.updateFullNode(oldServerSnap.getNode(), changedSnap, null); |
} else { |
@@ -5681,7 +5548,7 @@ fb.core.view.ViewProcessor.prototype.applyMerge_ = function(node, merge) { |
}); |
return node; |
}; |
-fb.core.view.ViewProcessor.prototype.applyServerMerge_ = function(viewCache, path, changedChildren, writesCache, serverCache, constrainServerNode, accumulator) { |
+fb.core.view.ViewProcessor.prototype.applyServerMerge_ = function(viewCache, path, changedChildren, writesCache, serverCache, filterServerNode, accumulator) { |
if (viewCache.getServerCache().getNode().isEmpty() && !viewCache.getServerCache().isFullyInitialized()) { |
return viewCache; |
} |
@@ -5698,7 +5565,7 @@ fb.core.view.ViewProcessor.prototype.applyServerMerge_ = function(viewCache, pat |
if (serverNode.hasChild(childKey)) { |
var serverChild = viewCache.getServerCache().getNode().getImmediateChild(childKey); |
var newChild = self.applyMerge_(serverChild, childTree); |
- curViewCache = self.applyServerOverwrite_(curViewCache, new fb.core.util.Path(childKey), newChild, writesCache, serverCache, constrainServerNode, accumulator); |
+ curViewCache = self.applyServerOverwrite_(curViewCache, new fb.core.util.Path(childKey), newChild, writesCache, serverCache, filterServerNode, accumulator); |
} |
}); |
viewMergeTree.children.inorderTraversal(function(childKey, childMergeTree) { |
@@ -5706,7 +5573,7 @@ fb.core.view.ViewProcessor.prototype.applyServerMerge_ = function(viewCache, pat |
if (!serverNode.hasChild(childKey) && !isUnknownDeepMerge) { |
var serverChild = viewCache.getServerCache().getNode().getImmediateChild(childKey); |
var newChild = self.applyMerge_(serverChild, childMergeTree); |
- curViewCache = self.applyServerOverwrite_(curViewCache, new fb.core.util.Path(childKey), newChild, writesCache, serverCache, constrainServerNode, accumulator); |
+ curViewCache = self.applyServerOverwrite_(curViewCache, new fb.core.util.Path(childKey), newChild, writesCache, serverCache, filterServerNode, accumulator); |
} |
}); |
return curViewCache; |
@@ -5715,17 +5582,18 @@ fb.core.view.ViewProcessor.prototype.ackUserWrite_ = function(viewCache, ackPath |
if (writesCache.shadowingWrite(ackPath) != null) { |
return viewCache; |
} |
+ var filterServerNode = viewCache.getServerCache().isFiltered(); |
var serverCache = viewCache.getServerCache(); |
if (affectedTree.value != null) { |
if (ackPath.isEmpty() && serverCache.isFullyInitialized() || serverCache.isCompleteForPath(ackPath)) { |
- return this.applyServerOverwrite_(viewCache, ackPath, serverCache.getNode().getChild(ackPath), writesCache, optCompleteCache, false, accumulator); |
+ return this.applyServerOverwrite_(viewCache, ackPath, serverCache.getNode().getChild(ackPath), writesCache, optCompleteCache, filterServerNode, accumulator); |
} else { |
if (ackPath.isEmpty()) { |
var changedChildren = (fb.core.util.ImmutableTree.Empty); |
serverCache.getNode().forEachChild(fb.core.snap.KeyIndex, function(name, node) { |
changedChildren = changedChildren.set(new fb.core.util.Path(name), node); |
}); |
- return this.applyServerMerge_(viewCache, ackPath, changedChildren, writesCache, optCompleteCache, false, accumulator); |
+ return this.applyServerMerge_(viewCache, ackPath, changedChildren, writesCache, optCompleteCache, filterServerNode, accumulator); |
} else { |
return viewCache; |
} |
@@ -5738,7 +5606,7 @@ fb.core.view.ViewProcessor.prototype.ackUserWrite_ = function(viewCache, ackPath |
changedChildren = changedChildren.set(mergePath, serverCache.getNode().getChild(serverCachePath)); |
} |
}); |
- return this.applyServerMerge_(viewCache, ackPath, changedChildren, writesCache, optCompleteCache, false, accumulator); |
+ return this.applyServerMerge_(viewCache, ackPath, changedChildren, writesCache, optCompleteCache, filterServerNode, accumulator); |
} |
}; |
fb.core.view.ViewProcessor.prototype.revertUserWrite_ = function(viewCache, path, writesCache, optCompleteServerCache, accumulator) { |
@@ -5792,9 +5660,10 @@ fb.core.view.ViewProcessor.prototype.listenComplete_ = function(viewCache, path, |
return this.generateEventCacheAfterServerEvent_(newViewCache, path, writesCache, fb.core.view.NO_COMPLETE_CHILD_SOURCE, accumulator); |
}; |
goog.provide("fb.core.snap.Index"); |
+goog.provide("fb.core.snap.KeyIndex"); |
+goog.provide("fb.core.snap.PathIndex"); |
goog.provide("fb.core.snap.PriorityIndex"); |
-goog.provide("fb.core.snap.SubKeyIndex"); |
-goog.require("fb.core.snap.comparators"); |
+goog.provide("fb.core.snap.ValueIndex"); |
goog.require("fb.core.util"); |
fb.core.snap.Index = function() { |
}; |
@@ -5816,18 +5685,19 @@ fb.core.snap.Index.prototype.minPost = function() { |
fb.core.snap.Index.prototype.maxPost = goog.abstractMethod; |
fb.core.snap.Index.prototype.makePost = goog.abstractMethod; |
fb.core.snap.Index.prototype.toString = goog.abstractMethod; |
-fb.core.snap.SubKeyIndex = function(indexKey) { |
+fb.core.snap.PathIndex = function(indexPath) { |
fb.core.snap.Index.call(this); |
- this.indexKey_ = indexKey; |
+ fb.core.util.assert(!indexPath.isEmpty() && indexPath.getFront() !== ".priority", "Can't create PathIndex with empty path or .priority key"); |
+ this.indexPath_ = indexPath; |
}; |
-goog.inherits(fb.core.snap.SubKeyIndex, fb.core.snap.Index); |
-fb.core.snap.SubKeyIndex.prototype.extractChild = function(snap) { |
- return snap.getImmediateChild(this.indexKey_); |
+goog.inherits(fb.core.snap.PathIndex, fb.core.snap.Index); |
+fb.core.snap.PathIndex.prototype.extractChild = function(snap) { |
+ return snap.getChild(this.indexPath_); |
}; |
-fb.core.snap.SubKeyIndex.prototype.isDefinedOn = function(node) { |
- return!node.getImmediateChild(this.indexKey_).isEmpty(); |
+fb.core.snap.PathIndex.prototype.isDefinedOn = function(node) { |
+ return!node.getChild(this.indexPath_).isEmpty(); |
}; |
-fb.core.snap.SubKeyIndex.prototype.compare = function(a, b) { |
+fb.core.snap.PathIndex.prototype.compare = function(a, b) { |
var aChild = this.extractChild(a.node); |
var bChild = this.extractChild(b.node); |
var indexCmp = aChild.compareTo(bChild); |
@@ -5837,17 +5707,17 @@ fb.core.snap.SubKeyIndex.prototype.compare = function(a, b) { |
return indexCmp; |
} |
}; |
-fb.core.snap.SubKeyIndex.prototype.makePost = function(indexValue, name) { |
+fb.core.snap.PathIndex.prototype.makePost = function(indexValue, name) { |
var valueNode = fb.core.snap.NodeFromJSON(indexValue); |
- var node = fb.core.snap.EMPTY_NODE.updateImmediateChild(this.indexKey_, valueNode); |
+ var node = fb.core.snap.EMPTY_NODE.updateChild(this.indexPath_, valueNode); |
return new fb.core.snap.NamedNode(name, node); |
}; |
-fb.core.snap.SubKeyIndex.prototype.maxPost = function() { |
- var node = fb.core.snap.EMPTY_NODE.updateImmediateChild(this.indexKey_, fb.core.snap.MAX_NODE); |
+fb.core.snap.PathIndex.prototype.maxPost = function() { |
+ var node = fb.core.snap.EMPTY_NODE.updateChild(this.indexPath_, fb.core.snap.MAX_NODE); |
return new fb.core.snap.NamedNode(fb.core.util.MAX_NAME, node); |
}; |
-fb.core.snap.SubKeyIndex.prototype.toString = function() { |
- return this.indexKey_; |
+fb.core.snap.PathIndex.prototype.toString = function() { |
+ return this.indexPath_.slice().join("/"); |
}; |
fb.core.snap.PriorityIndex_ = function() { |
fb.core.snap.Index.call(this); |
@@ -6155,7 +6025,7 @@ fb.core.view.QueryParams.prototype.toRestQueryStringParameters = function() { |
if (this.index_ === fb.core.snap.KeyIndex) { |
orderBy = REST_CONSTANTS.KEY_INDEX; |
} else { |
- fb.core.util.assert(this.index_ instanceof fb.core.snap.SubKeyIndex, "Unrecognized index type!"); |
+ fb.core.util.assert(this.index_ instanceof fb.core.snap.PathIndex, "Unrecognized index type!"); |
orderBy = this.index_.toString(); |
} |
} |
@@ -7159,19 +7029,328 @@ fb.core.ReadonlyRestClient = goog.defineClass(null, {constructor:function(repoIn |
} |
callback(xhr.status); |
} |
- callback = null; |
+ callback = null; |
+ } |
+ }; |
+ xhr.open("GET", url, true); |
+ xhr.send(); |
+}, statics:{getListenId_:function(query, opt_tag) { |
+ if (goog.isDef(opt_tag)) { |
+ return "tag$" + opt_tag; |
+ } else { |
+ fb.core.util.assert(query.getQueryParams().isDefault(), "should have a tag if it's not a default query."); |
+ return query.path.toString(); |
+ } |
+}}}); |
+goog.provide("fb.core.util.EventEmitter"); |
+goog.require("fb.core.util"); |
+goog.require("goog.array"); |
+fb.core.util.EventEmitter = goog.defineClass(null, {constructor:function(allowedEvents) { |
+ fb.core.util.assert(goog.isArray(allowedEvents) && allowedEvents.length > 0, "Requires a non-empty array"); |
+ this.allowedEvents_ = allowedEvents; |
+ this.listeners_ = {}; |
+}, getInitialEvent:goog.abstractMethod, trigger:function(eventType, var_args) { |
+ var listeners = goog.array.clone(this.listeners_[eventType] || []); |
+ for (var i = 0;i < listeners.length;i++) { |
+ listeners[i].callback.apply(listeners[i].context, Array.prototype.slice.call(arguments, 1)); |
+ } |
+}, on:function(eventType, callback, context) { |
+ this.validateEventType_(eventType); |
+ this.listeners_[eventType] = this.listeners_[eventType] || []; |
+ this.listeners_[eventType].push({callback:callback, context:context}); |
+ var eventData = this.getInitialEvent(eventType); |
+ if (eventData) { |
+ callback.apply(context, eventData); |
+ } |
+}, off:function(eventType, callback, context) { |
+ this.validateEventType_(eventType); |
+ var listeners = this.listeners_[eventType] || []; |
+ for (var i = 0;i < listeners.length;i++) { |
+ if (listeners[i].callback === callback && (!context || context === listeners[i].context)) { |
+ listeners.splice(i, 1); |
+ return; |
+ } |
+ } |
+}, validateEventType_:function(eventType) { |
+ fb.core.util.assert(goog.array.find(this.allowedEvents_, function(et) { |
+ return et === eventType; |
+ }), "Unknown event: " + eventType); |
+}}); |
+goog.provide("fb.core.util.nextPushId"); |
+goog.require("fb.core.util"); |
+fb.core.util.nextPushId = function() { |
+ var PUSH_CHARS = "-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"; |
+ var lastPushTime = 0; |
+ var lastRandChars = []; |
+ return function(now) { |
+ var duplicateTime = now === lastPushTime; |
+ lastPushTime = now; |
+ var timeStampChars = new Array(8); |
+ for (var i = 7;i >= 0;i--) { |
+ timeStampChars[i] = PUSH_CHARS.charAt(now % 64); |
+ now = Math.floor(now / 64); |
+ } |
+ fb.core.util.assert(now === 0, "Cannot push at time == 0"); |
+ var id = timeStampChars.join(""); |
+ if (!duplicateTime) { |
+ for (i = 0;i < 12;i++) { |
+ lastRandChars[i] = Math.floor(Math.random() * 64); |
+ } |
+ } else { |
+ for (i = 11;i >= 0 && lastRandChars[i] === 63;i--) { |
+ lastRandChars[i] = 0; |
+ } |
+ lastRandChars[i]++; |
+ } |
+ for (i = 0;i < 12;i++) { |
+ id += PUSH_CHARS.charAt(lastRandChars[i]); |
+ } |
+ fb.core.util.assert(id.length === 20, "nextPushId: Length should be 20."); |
+ return id; |
+ }; |
+}(); |
+goog.provide("fb.core.util.OnlineMonitor"); |
+goog.require("fb.core.util"); |
+goog.require("fb.core.util.EventEmitter"); |
+fb.core.util.OnlineMonitor = goog.defineClass(fb.core.util.EventEmitter, {constructor:function() { |
+ fb.core.util.EventEmitter.call(this, ["online"]); |
+ this.online_ = true; |
+ if (typeof window !== "undefined" && typeof window.addEventListener !== "undefined") { |
+ var self = this; |
+ window.addEventListener("online", function() { |
+ if (!self.online_) { |
+ self.online_ = true; |
+ self.trigger("online", true); |
+ } |
+ }, false); |
+ window.addEventListener("offline", function() { |
+ if (self.online_) { |
+ self.online_ = false; |
+ self.trigger("online", false); |
+ } |
+ }, false); |
+ } |
+}, getInitialEvent:function(eventType) { |
+ fb.core.util.assert(eventType === "online", "Unknown event type: " + eventType); |
+ return[this.online_]; |
+}, currentlyOnline:function() { |
+ return this.online_; |
+}}); |
+goog.addSingletonGetter(fb.core.util.OnlineMonitor); |
+goog.provide("fb.core.util.VisibilityMonitor"); |
+goog.require("fb.core.util"); |
+goog.require("fb.core.util.EventEmitter"); |
+fb.core.util.VisibilityMonitor = goog.defineClass(fb.core.util.EventEmitter, {constructor:function() { |
+ fb.core.util.EventEmitter.call(this, ["visible"]); |
+ var hidden, visibilityChange; |
+ if (typeof document !== "undefined" && typeof document.addEventListener !== "undefined") { |
+ if (typeof document["hidden"] !== "undefined") { |
+ visibilityChange = "visibilitychange"; |
+ hidden = "hidden"; |
+ } else { |
+ if (typeof document["mozHidden"] !== "undefined") { |
+ visibilityChange = "mozvisibilitychange"; |
+ hidden = "mozHidden"; |
+ } else { |
+ if (typeof document["msHidden"] !== "undefined") { |
+ visibilityChange = "msvisibilitychange"; |
+ hidden = "msHidden"; |
+ } else { |
+ if (typeof document["webkitHidden"] !== "undefined") { |
+ visibilityChange = "webkitvisibilitychange"; |
+ hidden = "webkitHidden"; |
+ } |
+ } |
+ } |
+ } |
+ } |
+ this.visible_ = true; |
+ if (visibilityChange) { |
+ var self = this; |
+ document.addEventListener(visibilityChange, function() { |
+ var visible = !document[hidden]; |
+ if (visible !== self.visible_) { |
+ self.visible_ = visible; |
+ self.trigger("visible", visible); |
+ } |
+ }, false); |
+ } |
+}, getInitialEvent:function(eventType) { |
+ fb.core.util.assert(eventType === "visible", "Unknown event type: " + eventType); |
+ return[this.visible_]; |
+}}); |
+goog.addSingletonGetter(fb.core.util.VisibilityMonitor); |
+goog.provide("fb.core.util.Path"); |
+goog.provide("fb.core.util.ValidationPath"); |
+goog.require("fb.core.util"); |
+goog.require("fb.util.utf8"); |
+goog.require("goog.string"); |
+fb.core.util.Path = goog.defineClass(null, {constructor:function(pathOrString, opt_pieceNum) { |
+ if (arguments.length == 1) { |
+ this.pieces_ = pathOrString.split("/"); |
+ var copyTo = 0; |
+ for (var i = 0;i < this.pieces_.length;i++) { |
+ if (this.pieces_[i].length > 0) { |
+ this.pieces_[copyTo] = this.pieces_[i]; |
+ copyTo++; |
+ } |
+ } |
+ this.pieces_.length = copyTo; |
+ this.pieceNum_ = 0; |
+ } else { |
+ this.pieces_ = pathOrString; |
+ this.pieceNum_ = opt_pieceNum; |
+ } |
+}, getFront:function() { |
+ if (this.pieceNum_ >= this.pieces_.length) { |
+ return null; |
+ } |
+ return this.pieces_[this.pieceNum_]; |
+}, getLength:function() { |
+ return this.pieces_.length - this.pieceNum_; |
+}, popFront:function() { |
+ var pieceNum = this.pieceNum_; |
+ if (pieceNum < this.pieces_.length) { |
+ pieceNum++; |
+ } |
+ return new fb.core.util.Path(this.pieces_, pieceNum); |
+}, getBack:function() { |
+ if (this.pieceNum_ < this.pieces_.length) { |
+ return this.pieces_[this.pieces_.length - 1]; |
+ } |
+ return null; |
+}, toString:function() { |
+ var pathString = ""; |
+ for (var i = this.pieceNum_;i < this.pieces_.length;i++) { |
+ if (this.pieces_[i] !== "") { |
+ pathString += "/" + this.pieces_[i]; |
+ } |
+ } |
+ return pathString || "/"; |
+}, toUrlEncodedString:function() { |
+ var pathString = ""; |
+ for (var i = this.pieceNum_;i < this.pieces_.length;i++) { |
+ if (this.pieces_[i] !== "") { |
+ pathString += "/" + goog.string.urlEncode(this.pieces_[i]); |
+ } |
+ } |
+ return pathString || "/"; |
+}, slice:function(opt_begin) { |
+ var begin = opt_begin || 0; |
+ return this.pieces_.slice(this.pieceNum_ + begin); |
+}, parent:function() { |
+ if (this.pieceNum_ >= this.pieces_.length) { |
+ return null; |
+ } |
+ var pieces = []; |
+ for (var i = this.pieceNum_;i < this.pieces_.length - 1;i++) { |
+ pieces.push(this.pieces_[i]); |
+ } |
+ return new fb.core.util.Path(pieces, 0); |
+}, child:function(childPathObj) { |
+ var pieces = []; |
+ for (var i = this.pieceNum_;i < this.pieces_.length;i++) { |
+ pieces.push(this.pieces_[i]); |
+ } |
+ if (childPathObj instanceof fb.core.util.Path) { |
+ for (i = childPathObj.pieceNum_;i < childPathObj.pieces_.length;i++) { |
+ pieces.push(childPathObj.pieces_[i]); |
+ } |
+ } else { |
+ var childPieces = childPathObj.split("/"); |
+ for (i = 0;i < childPieces.length;i++) { |
+ if (childPieces[i].length > 0) { |
+ pieces.push(childPieces[i]); |
+ } |
} |
- }; |
- xhr.open("GET", url, true); |
- xhr.send(); |
-}, statics:{getListenId_:function(query, opt_tag) { |
- if (goog.isDef(opt_tag)) { |
- return "tag$" + opt_tag; |
+ } |
+ return new fb.core.util.Path(pieces, 0); |
+}, isEmpty:function() { |
+ return this.pieceNum_ >= this.pieces_.length; |
+}, statics:{relativePath:function(outerPath, innerPath) { |
+ var outer = outerPath.getFront(), inner = innerPath.getFront(); |
+ if (outer === null) { |
+ return innerPath; |
} else { |
- fb.core.util.assert(query.getQueryParams().isDefault(), "should have a tag if it's not a default query."); |
- return query.path.toString(); |
+ if (outer === inner) { |
+ return fb.core.util.Path.relativePath(outerPath.popFront(), innerPath.popFront()); |
+ } else { |
+ throw new Error("INTERNAL ERROR: innerPath (" + innerPath + ") is not within " + "outerPath (" + outerPath + ")"); |
+ } |
} |
-}}}); |
+}, comparePaths:function(left, right) { |
+ var leftKeys = left.slice(); |
+ var rightKeys = right.slice(); |
+ for (var i = 0;i < leftKeys.length && i < rightKeys.length;i++) { |
+ var cmp = fb.core.util.nameCompare(leftKeys[i], rightKeys[i]); |
+ if (cmp !== 0) { |
+ return cmp; |
+ } |
+ } |
+ if (leftKeys.length === rightKeys.length) { |
+ return 0; |
+ } |
+ return leftKeys.length < rightKeys.length ? -1 : 1; |
+}}, equals:function(other) { |
+ if (this.getLength() !== other.getLength()) { |
+ return false; |
+ } |
+ for (var i = this.pieceNum_, j = other.pieceNum_;i <= this.pieces_.length;i++, j++) { |
+ if (this.pieces_[i] !== other.pieces_[j]) { |
+ return false; |
+ } |
+ } |
+ return true; |
+}, contains:function(other) { |
+ var i = this.pieceNum_; |
+ var j = other.pieceNum_; |
+ if (this.getLength() > other.getLength()) { |
+ return false; |
+ } |
+ while (i < this.pieces_.length) { |
+ if (this.pieces_[i] !== other.pieces_[j]) { |
+ return false; |
+ } |
+ ++i; |
+ ++j; |
+ } |
+ return true; |
+}}); |
+fb.core.util.Path.Empty = new fb.core.util.Path(""); |
+fb.core.util.ValidationPath = goog.defineClass(null, {constructor:function(path, errorPrefix) { |
+ this.parts_ = path.slice(); |
+ this.byteLength_ = Math.max(1, this.parts_.length); |
+ this.errorPrefix_ = errorPrefix; |
+ for (var i = 0;i < this.parts_.length;i++) { |
+ this.byteLength_ += fb.util.utf8.stringLength(this.parts_[i]); |
+ } |
+ this.checkValid_(); |
+}, statics:{MAX_PATH_DEPTH:32, MAX_PATH_LENGTH_BYTES:768}, push:function(child) { |
+ if (this.parts_.length > 0) { |
+ this.byteLength_ += 1; |
+ } |
+ this.parts_.push(child); |
+ this.byteLength_ += fb.util.utf8.stringLength(child); |
+ this.checkValid_(); |
+}, pop:function() { |
+ var last = this.parts_.pop(); |
+ this.byteLength_ -= fb.util.utf8.stringLength(last); |
+ if (this.parts_.length > 0) { |
+ this.byteLength_ -= 1; |
+ } |
+}, checkValid_:function() { |
+ if (this.byteLength_ > fb.core.util.ValidationPath.MAX_PATH_LENGTH_BYTES) { |
+ throw new Error(this.errorPrefix_ + "has a key path longer than " + fb.core.util.ValidationPath.MAX_PATH_LENGTH_BYTES + " bytes (" + this.byteLength_ + ")."); |
+ } |
+ if (this.parts_.length > fb.core.util.ValidationPath.MAX_PATH_DEPTH) { |
+ throw new Error(this.errorPrefix_ + "path specified exceeds the maximum depth that can be written (" + fb.core.util.ValidationPath.MAX_PATH_DEPTH + ") or object contains a cycle " + this.toErrorString()); |
+ } |
+}, toErrorString:function() { |
+ if (this.parts_.length == 0) { |
+ return ""; |
+ } |
+ return "in property '" + this.parts_.join(".") + "'"; |
+}}); |
goog.provide("fb.core.util.ImmutableTree"); |
goog.require("fb.core.util"); |
goog.require("fb.core.util.Path"); |
@@ -8133,7 +8312,7 @@ fb.core.SyncTree.prototype.removeEventRegistration = function(query, eventRegist |
for (var i = 0;i < newViews.length;++i) { |
var view = newViews[i], newQuery = view.getQuery(); |
var listener = this.createListenerForView_(view); |
- this.listenProvider_.startListening(newQuery, this.tagForQuery_(newQuery), listener.hashFn, listener.onComplete); |
+ this.listenProvider_.startListening(this.queryForListening_(newQuery), this.tagForQuery_(newQuery), listener.hashFn, listener.onComplete); |
} |
} else { |
} |
@@ -8141,13 +8320,13 @@ fb.core.SyncTree.prototype.removeEventRegistration = function(query, eventRegist |
if (!covered && removed.length > 0 && !cancelError) { |
if (removingDefault) { |
var defaultTag = null; |
- this.listenProvider_.stopListening(query, defaultTag); |
+ this.listenProvider_.stopListening(this.queryForListening_(query), defaultTag); |
} else { |
var self = this; |
goog.array.forEach(removed, function(queryToRemove) { |
var queryIdToRemove = queryToRemove.queryIdentifier(); |
var tagToRemove = self.queryToTagMap_[self.makeQueryKey_(queryToRemove)]; |
- self.listenProvider_.stopListening(queryToRemove, tagToRemove); |
+ self.listenProvider_.stopListening(self.queryForListening_(queryToRemove), tagToRemove); |
}); |
} |
} |
@@ -8196,11 +8375,18 @@ fb.core.SyncTree.prototype.removeTags_ = function(queries) { |
} |
} |
}; |
+fb.core.SyncTree.prototype.queryForListening_ = function(query) { |
+ if (query.getQueryParams().loadsAllData() && !query.getQueryParams().isDefault()) { |
+ return(query.ref()); |
+ } else { |
+ return query; |
+ } |
+}; |
fb.core.SyncTree.prototype.setupListener_ = function(query, view) { |
var path = query.path; |
var tag = this.tagForQuery_(query); |
var listener = this.createListenerForView_(view); |
- var events = this.listenProvider_.startListening(query, tag, listener.hashFn, listener.onComplete); |
+ var events = this.listenProvider_.startListening(this.queryForListening_(query), tag, listener.hashFn, listener.onComplete); |
var subtree = this.syncPointTree_.subtree(path); |
if (tag) { |
fb.core.util.assert(!subtree.value.hasCompleteView(), "If we're adding a query, it shouldn't be shadowed"); |
@@ -8223,7 +8409,7 @@ fb.core.SyncTree.prototype.setupListener_ = function(query, view) { |
}); |
for (var i = 0;i < queriesToStop.length;++i) { |
var queryToStop = queriesToStop[i]; |
- this.listenProvider_.stopListening(queryToStop, this.tagForQuery_(queryToStop)); |
+ this.listenProvider_.stopListening(this.queryForListening_(queryToStop), this.tagForQuery_(queryToStop)); |
} |
} |
return events; |
@@ -8414,144 +8600,6 @@ fb.core.util.Tree = goog.defineClass(null, {constructor:function(opt_name, opt_p |
} |
} |
}}); |
-goog.provide("fb.core.util.EventEmitter"); |
-goog.require("fb.core.util"); |
-goog.require("goog.array"); |
-fb.core.util.EventEmitter = goog.defineClass(null, {constructor:function(allowedEvents) { |
- fb.core.util.assert(goog.isArray(allowedEvents) && allowedEvents.length > 0, "Requires a non-empty array"); |
- this.allowedEvents_ = allowedEvents; |
- this.listeners_ = {}; |
-}, getInitialEvent:goog.abstractMethod, trigger:function(eventType, var_args) { |
- var listeners = this.listeners_[eventType] || []; |
- for (var i = 0;i < listeners.length;i++) { |
- listeners[i].callback.apply(listeners[i].context, Array.prototype.slice.call(arguments, 1)); |
- } |
-}, on:function(eventType, callback, context) { |
- this.validateEventType_(eventType); |
- this.listeners_[eventType] = this.listeners_[eventType] || []; |
- this.listeners_[eventType].push({callback:callback, context:context}); |
- var eventData = this.getInitialEvent(eventType); |
- if (eventData) { |
- callback.apply(context, eventData); |
- } |
-}, off:function(eventType, callback, context) { |
- this.validateEventType_(eventType); |
- var listeners = this.listeners_[eventType] || []; |
- for (var i = 0;i < listeners.length;i++) { |
- if (listeners[i].callback === callback && (!context || context === listeners[i].context)) { |
- listeners.splice(i, 1); |
- return; |
- } |
- } |
-}, validateEventType_:function(eventType) { |
- fb.core.util.assert(goog.array.find(this.allowedEvents_, function(et) { |
- return et === eventType; |
- }), "Unknown event: " + eventType); |
-}}); |
-goog.provide("fb.core.util.nextPushId"); |
-goog.require("fb.core.util"); |
-fb.core.util.nextPushId = function() { |
- var PUSH_CHARS = "-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"; |
- var lastPushTime = 0; |
- var lastRandChars = []; |
- return function(now) { |
- var duplicateTime = now === lastPushTime; |
- lastPushTime = now; |
- var timeStampChars = new Array(8); |
- for (var i = 7;i >= 0;i--) { |
- timeStampChars[i] = PUSH_CHARS.charAt(now % 64); |
- now = Math.floor(now / 64); |
- } |
- fb.core.util.assert(now === 0, "Cannot push at time == 0"); |
- var id = timeStampChars.join(""); |
- if (!duplicateTime) { |
- for (i = 0;i < 12;i++) { |
- lastRandChars[i] = Math.floor(Math.random() * 64); |
- } |
- } else { |
- for (i = 11;i >= 0 && lastRandChars[i] === 63;i--) { |
- lastRandChars[i] = 0; |
- } |
- lastRandChars[i]++; |
- } |
- for (i = 0;i < 12;i++) { |
- id += PUSH_CHARS.charAt(lastRandChars[i]); |
- } |
- fb.core.util.assert(id.length === 20, "nextPushId: Length should be 20."); |
- return id; |
- }; |
-}(); |
-goog.provide("fb.core.util.OnlineMonitor"); |
-goog.require("fb.core.util"); |
-goog.require("fb.core.util.EventEmitter"); |
-fb.core.util.OnlineMonitor = goog.defineClass(fb.core.util.EventEmitter, {constructor:function() { |
- fb.core.util.EventEmitter.call(this, ["online"]); |
- this.online_ = true; |
- if (typeof window !== "undefined" && typeof window.addEventListener !== "undefined") { |
- var self = this; |
- window.addEventListener("online", function() { |
- if (!self.online_) { |
- self.online_ = true; |
- self.trigger("online", true); |
- } |
- }, false); |
- window.addEventListener("offline", function() { |
- if (self.online_) { |
- self.online_ = false; |
- self.trigger("online", false); |
- } |
- }, false); |
- } |
-}, getInitialEvent:function(eventType) { |
- fb.core.util.assert(eventType === "online", "Unknown event type: " + eventType); |
- return[this.online_]; |
-}, currentlyOnline:function() { |
- return this.online_; |
-}}); |
-goog.addSingletonGetter(fb.core.util.OnlineMonitor); |
-goog.provide("fb.core.util.VisibilityMonitor"); |
-goog.require("fb.core.util"); |
-goog.require("fb.core.util.EventEmitter"); |
-fb.core.util.VisibilityMonitor = goog.defineClass(fb.core.util.EventEmitter, {constructor:function() { |
- fb.core.util.EventEmitter.call(this, ["visible"]); |
- var hidden, visibilityChange; |
- if (typeof document !== "undefined" && typeof document.addEventListener !== "undefined") { |
- if (typeof document["hidden"] !== "undefined") { |
- visibilityChange = "visibilitychange"; |
- hidden = "hidden"; |
- } else { |
- if (typeof document["mozHidden"] !== "undefined") { |
- visibilityChange = "mozvisibilitychange"; |
- hidden = "mozHidden"; |
- } else { |
- if (typeof document["msHidden"] !== "undefined") { |
- visibilityChange = "msvisibilitychange"; |
- hidden = "msHidden"; |
- } else { |
- if (typeof document["webkitHidden"] !== "undefined") { |
- visibilityChange = "webkitvisibilitychange"; |
- hidden = "webkitHidden"; |
- } |
- } |
- } |
- } |
- } |
- this.visible_ = true; |
- if (visibilityChange) { |
- var self = this; |
- document.addEventListener(visibilityChange, function() { |
- var visible = !document[hidden]; |
- if (visible !== self.visible_) { |
- self.visible_ = visible; |
- self.trigger("visible", visible); |
- } |
- }, false); |
- } |
-}, getInitialEvent:function(eventType) { |
- fb.core.util.assert(eventType === "visible", "Unknown event type: " + eventType); |
- return[this.visible_]; |
-}}); |
-goog.addSingletonGetter(fb.core.util.VisibilityMonitor); |
goog.provide("fb.core.util.validation"); |
goog.require("fb.core.util"); |
goog.require("fb.core.util.Path"); |
@@ -8612,17 +8660,49 @@ fb.core.util.validation = {INVALID_KEY_REGEX_:/[\[\].#$\/\u0000-\u001F\u007F]/, |
throw new Error(errorPrefix + ' contains ".value" child ' + path.toErrorString() + " in addition to actual children."); |
} |
} |
+}, validateFirebaseMergePaths:function(errorPrefix, mergePaths) { |
+ var i, curPath; |
+ for (i = 0;i < mergePaths.length;i++) { |
+ curPath = mergePaths[i]; |
+ var keys = curPath.slice(); |
+ for (var j = 0;j < keys.length;j++) { |
+ if (keys[j] === ".priority" && j === keys.length - 1) { |
+ } else { |
+ if (!fb.core.util.validation.isValidKey(keys[j])) { |
+ throw new Error(errorPrefix + "contains an invalid key (" + keys[j] + ") in path " + curPath.toString() + ". Keys must be non-empty strings " + 'and can\'t contain ".", "#", "$", "/", "[", or "]"'); |
+ } |
+ } |
+ } |
+ } |
+ mergePaths.sort(fb.core.util.Path.comparePaths); |
+ var prevPath = null; |
+ for (i = 0;i < mergePaths.length;i++) { |
+ curPath = mergePaths[i]; |
+ if (prevPath !== null && prevPath.contains(curPath)) { |
+ throw new Error(errorPrefix + "contains a path " + prevPath.toString() + " that is ancestor of another path " + curPath.toString()); |
+ } |
+ prevPath = curPath; |
+ } |
}, validateFirebaseMergeDataArg:function(fnName, argumentNumber, data, path, optional) { |
if (optional && !goog.isDef(data)) { |
return; |
} |
+ var errorPrefix = fb.util.validation.errorPrefix(fnName, argumentNumber, optional); |
if (!goog.isObject(data) || goog.isArray(data)) { |
- throw new Error(fb.util.validation.errorPrefix(fnName, argumentNumber, optional) + " must be an Object containing " + "the children to replace."); |
+ throw new Error(errorPrefix + " must be an object containing the children to replace."); |
} |
- if (fb.util.obj.contains(data, ".value")) { |
- throw new Error(fb.util.validation.errorPrefix(fnName, argumentNumber, optional) + ' must not contain ".value". ' + "To overwrite with a leaf value, just use .set() instead."); |
- } |
- fb.core.util.validation.validateFirebaseDataArg(fnName, argumentNumber, data, path, optional); |
+ var mergePaths = []; |
+ fb.util.obj.foreach(data, function(key, value) { |
+ var curPath = new fb.core.util.Path(key); |
+ fb.core.util.validation.validateFirebaseData(errorPrefix, value, path.child(curPath)); |
+ if (curPath.getBack() === ".priority") { |
+ if (!fb.core.util.validation.isValidPriority(value)) { |
+ throw new Error(errorPrefix + "contains an invalid value for '" + curPath.toString() + "', which must be a valid " + "Firebase priority (a string, finite number, server value, or null)."); |
+ } |
+ } |
+ mergePaths.push(curPath); |
+ }); |
+ fb.core.util.validation.validateFirebaseMergePaths(errorPrefix, mergePaths); |
}, validatePriority:function(fnName, argumentNumber, priority, optional) { |
if (optional && !goog.isDef(priority)) { |
return; |
@@ -9947,7 +10027,7 @@ fb.realtime.Transport.prototype.send = function(data) { |
fb.realtime.Transport.prototype.bytesReceived; |
fb.realtime.Transport.prototype.bytesSent; |
goog.provide("fb.realtime.Constants"); |
-fb.realtime.Constants = {PROTOCOL_VERSION:"5", VERSION_PARAM:"v", SESSION_PARAM:"s", REFERER_PARAM:"r", FORGE_REF:"f", FORGE_DOMAIN:"firebaseio.com"}; |
+fb.realtime.Constants = {PROTOCOL_VERSION:"5", VERSION_PARAM:"v", TRANSPORT_SESSION_PARAM:"s", REFERER_PARAM:"r", FORGE_REF:"f", FORGE_DOMAIN:"firebaseio.com", LAST_SESSION_PARAM:"ls", WEBSOCKET:"websocket", LONG_POLLING:"long_polling"}; |
goog.provide("fb.realtime.polling.PacketReceiver"); |
fb.realtime.polling.PacketReceiver = function(onMessage) { |
this.onMessage_ = onMessage; |
@@ -10015,26 +10095,18 @@ var SEG_HEADER_SIZE = 30; |
var MAX_PAYLOAD_SIZE = MAX_URL_DATA_SIZE - SEG_HEADER_SIZE; |
var KEEPALIVE_REQUEST_INTERVAL = 25E3; |
var LP_CONNECT_TIMEOUT = 3E4; |
-fb.realtime.BrowserPollConnection = function(connId, repoInfo, sessionId) { |
+fb.realtime.BrowserPollConnection = function(connId, repoInfo, opt_transportSessionId, opt_lastSessionId) { |
this.connId = connId; |
this.log_ = fb.core.util.logWrapper(connId); |
this.repoInfo = repoInfo; |
this.bytesSent = 0; |
this.bytesReceived = 0; |
this.stats_ = fb.core.stats.StatsManager.getCollection(repoInfo); |
- this.sessionId = sessionId; |
+ this.transportSessionId = opt_transportSessionId; |
this.everConnected_ = false; |
+ this.lastSessionId = opt_lastSessionId; |
this.urlFn = function(params) { |
- if (repoInfo.needsQueryParam()) { |
- params["ns"] = repoInfo.namespace; |
- } |
- var pairs = []; |
- for (var k in params) { |
- if (params.hasOwnProperty(k)) { |
- pairs.push(k + "=" + params[k]); |
- } |
- } |
- return(repoInfo.secure ? "https://" : "http://") + repoInfo.internalHost + "/.lp?" + pairs.join("&"); |
+ return repoInfo.connectionURL(fb.realtime.Constants.LONG_POLLING, params); |
}; |
}; |
fb.realtime.BrowserPollConnection.prototype.open = function(onMessage, onDisconnect) { |
@@ -10092,8 +10164,11 @@ fb.realtime.BrowserPollConnection.prototype.open = function(onMessage, onDisconn |
urlParams[FIREBASE_LONGPOLL_CALLBACK_ID_PARAM] = self.scriptTagHolder.uniqueCallbackIdentifier; |
} |
urlParams[fb.realtime.Constants.VERSION_PARAM] = fb.realtime.Constants.PROTOCOL_VERSION; |
- if (self.sessionId) { |
- urlParams[fb.realtime.Constants.SESSION_PARAM] = self.sessionId; |
+ if (self.transportSessionId) { |
+ urlParams[fb.realtime.Constants.TRANSPORT_SESSION_PARAM] = self.transportSessionId; |
+ } |
+ if (self.lastSessionId) { |
+ urlParams[fb.realtime.Constants.LAST_SESSION_PARAM] = self.lastSessionId; |
} |
if (!NODE_CLIENT && typeof location !== "undefined" && location.href && location.href.indexOf(fb.realtime.Constants.FORGE_DOMAIN) !== -1) { |
urlParams[fb.realtime.Constants.REFERER_PARAM] = fb.realtime.Constants.FORGE_REF; |
@@ -10408,7 +10483,7 @@ if (NODE_CLIENT) { |
} |
} |
} |
-fb.realtime.WebSocketConnection = function(connId, repoInfo, sessionId) { |
+fb.realtime.WebSocketConnection = function(connId, repoInfo, opt_transportSessionId, opt_lastSessionId) { |
this.connId = connId; |
this.log_ = fb.core.util.logWrapper(this.connId); |
this.keepaliveTimer = null; |
@@ -10417,16 +10492,21 @@ fb.realtime.WebSocketConnection = function(connId, repoInfo, sessionId) { |
this.bytesSent = 0; |
this.bytesReceived = 0; |
this.stats_ = fb.core.stats.StatsManager.getCollection(repoInfo); |
- this.connURL = (repoInfo.secure ? "wss://" : "ws://") + repoInfo.internalHost + "/.ws?" + fb.realtime.Constants.VERSION_PARAM + "=" + fb.realtime.Constants.PROTOCOL_VERSION; |
+ this.connURL = this.connectionURL_(repoInfo, opt_transportSessionId, opt_lastSessionId); |
+}; |
+fb.realtime.WebSocketConnection.prototype.connectionURL_ = function(repoInfo, opt_transportSessionId, opt_lastSessionId) { |
+ var urlParams = {}; |
+ urlParams[fb.realtime.Constants.VERSION_PARAM] = fb.realtime.Constants.PROTOCOL_VERSION; |
if (!NODE_CLIENT && typeof location !== "undefined" && location.href && location.href.indexOf(fb.realtime.Constants.FORGE_DOMAIN) !== -1) { |
- this.connURL = this.connURL + "&" + fb.realtime.Constants.REFERER_PARAM + "=" + fb.realtime.Constants.FORGE_REF; |
+ urlParams[fb.realtime.Constants.REFERER_PARAM] = fb.realtime.Constants.FORGE_REF; |
} |
- if (repoInfo.needsQueryParam()) { |
- this.connURL = this.connURL + "&ns=" + repoInfo.namespace; |
+ if (opt_transportSessionId) { |
+ urlParams[fb.realtime.Constants.TRANSPORT_SESSION_PARAM] = opt_transportSessionId; |
} |
- if (sessionId) { |
- this.connURL = this.connURL + "&" + fb.realtime.Constants.SESSION_PARAM + "=" + sessionId; |
+ if (opt_lastSessionId) { |
+ urlParams[fb.realtime.Constants.LAST_SESSION_PARAM] = opt_lastSessionId; |
} |
+ return repoInfo.connectionURL(fb.realtime.Constants.WEBSOCKET, urlParams); |
}; |
fb.realtime.WebSocketConnection.prototype.open = function(onMess, onDisconn) { |
this.onDisconnect = onDisconn; |
@@ -10655,7 +10735,7 @@ var SWITCH_ACK = "a"; |
var END_TRANSMISSION = "n"; |
var PING = "p"; |
var SERVER_HELLO = "h"; |
-fb.realtime.Connection = function(connId, repoInfo, onMessage, onReady, onDisconnect, onKill) { |
+fb.realtime.Connection = function(connId, repoInfo, onMessage, onReady, onDisconnect, onKill, lastSessionId) { |
this.id = connId; |
this.log_ = fb.core.util.logWrapper("c:" + this.id + ":"); |
this.onMessage_ = onMessage; |
@@ -10667,12 +10747,13 @@ fb.realtime.Connection = function(connId, repoInfo, onMessage, onReady, onDiscon |
this.connectionCount = 0; |
this.transportManager_ = new fb.realtime.TransportManager(repoInfo); |
this.state_ = REALTIME_STATE_CONNECTING; |
+ this.lastSessionId = lastSessionId; |
this.log_("Connection created"); |
this.start_(); |
}; |
fb.realtime.Connection.prototype.start_ = function() { |
var conn = this.transportManager_.initialTransport(); |
- this.conn_ = new conn(this.nextTransportId_(), this.repoInfo_); |
+ this.conn_ = new conn(this.nextTransportId_(), this.repoInfo_, undefined, this.lastSessionId); |
this.primaryResponsesRequired_ = conn["responsesRequiredToBeHealthy"] || 0; |
var onMessageReceived = this.connReceiver_(this.conn_); |
var onConnectionLost = this.disconnReceiver_(this.conn_); |
@@ -10919,7 +11000,7 @@ fb.realtime.Connection.prototype.onConnectionEstablished_ = function(conn, times |
this.conn_ = conn; |
this.state_ = REALTIME_STATE_CONNECTED; |
if (this.onReady_) { |
- this.onReady_(timestamp); |
+ this.onReady_(timestamp, this.sessionId); |
this.onReady_ = null; |
} |
var self = this; |
@@ -11033,6 +11114,7 @@ fb.core.PersistentConnection = goog.defineClass(null, {constructor:function(repo |
this.onServerInfoUpdate_ = onServerInfoUpdate; |
this.repoInfo_ = repoInfo; |
this.securityDebugCallback_ = null; |
+ this.lastSessionId = null; |
this.realtime_ = null; |
this.credential_ = null; |
this.establishConnectionTimer_ = null; |
@@ -11061,6 +11143,7 @@ fb.core.PersistentConnection = goog.defineClass(null, {constructor:function(repo |
var pathString = query.path.toString(); |
this.log_("Listen called for " + pathString + " " + queryId); |
this.listens_[pathString] = this.listens_[pathString] || {}; |
+ fb.core.util.assert(query.getQueryParams().isDefault() || !query.getQueryParams().loadsAllData(), "listen() called for non-default but complete query"); |
fb.core.util.assert(!this.listens_[pathString][queryId], "listen() called twice for same path/queryId."); |
var listenSpec = {onComplete:onComplete, hashFn:currentHashFn, query:query, tag:tag}; |
this.listens_[pathString][queryId] = listenSpec; |
@@ -11151,6 +11234,7 @@ fb.core.PersistentConnection = goog.defineClass(null, {constructor:function(repo |
var pathString = query.path.toString(); |
var queryId = query.queryIdentifier(); |
this.log_("Unlisten called for " + pathString + " " + queryId); |
+ fb.core.util.assert(query.getQueryParams().isDefault() || !query.getQueryParams().loadsAllData(), "unlisten() called for non-default but complete query"); |
var listen = this.removeListen_(pathString, queryId); |
if (listen && this.connected_) { |
this.sendUnlisten_(pathString, queryId, query.queryObject(), tag); |
@@ -11281,11 +11365,12 @@ fb.core.PersistentConnection = goog.defineClass(null, {constructor:function(repo |
} |
} |
} |
-}, onReady_:function(timestamp) { |
+}, onReady_:function(timestamp, sessionId) { |
this.log_("connection ready"); |
this.connected_ = true; |
this.lastConnectionEstablishedTime_ = (new Date).getTime(); |
this.handleTimestamp_(timestamp); |
+ this.lastSessionId = sessionId; |
if (this.firstConnection_) { |
this.sendConnectStats_(); |
} |
@@ -11362,10 +11447,11 @@ fb.core.PersistentConnection = goog.defineClass(null, {constructor:function(repo |
var onDisconnect = goog.bind(this.onRealtimeDisconnect_, this); |
var connId = this.id + ":" + fb.core.PersistentConnection.nextConnectionId_++; |
var self = this; |
+ var lastSessionId = this.lastSessionId; |
this.realtime_ = new fb.realtime.Connection(connId, this.repoInfo_, onDataMessage, onReady, onDisconnect, function(reason) { |
fb.core.util.warn(reason + " (" + self.repoInfo_.toString() + ")"); |
self.killed_ = true; |
- }); |
+ }, lastSessionId); |
} |
}, interrupt:function() { |
this.interrupted_ = true; |
@@ -12381,7 +12467,7 @@ fb.api.Query = goog.defineClass(null, {constructor:function(repo, path, queryPar |
throw new Error("Query: When ordering by priority, the first argument passed to startAt(), " + "endAt(), or equalTo() must be a valid priority value (null, a number, or a string)."); |
} |
} else { |
- fb.core.util.assert(params.getIndex() instanceof fb.core.snap.SubKeyIndex || params.getIndex() === fb.core.snap.ValueIndex, "unknown index type."); |
+ fb.core.util.assert(params.getIndex() instanceof fb.core.snap.PathIndex || params.getIndex() === fb.core.snap.ValueIndex, "unknown index type."); |
if (startNode != null && typeof startNode === "object" || endNode != null && typeof endNode === "object") { |
throw new Error("Query: First argument passed to startAt(), endAt(), or equalTo() cannot be " + "an object."); |
} |
@@ -12488,22 +12574,26 @@ fb.api.Query = goog.defineClass(null, {constructor:function(repo, path, queryPar |
throw new Error("Query.limitToLast: Limit was already set (by another call to limit, " + "limitToFirst, or limitToLast)."); |
} |
return new fb.api.Query(this.repo, this.path, this.queryParams_.limitToLast(limit), this.orderByCalled_); |
-}, orderByChild:function(key) { |
+}, orderByChild:function(path) { |
fb.util.validation.validateArgCount("Query.orderByChild", 1, 1, arguments.length); |
- if (key === "$key") { |
+ if (path === "$key") { |
throw new Error('Query.orderByChild: "$key" is invalid. Use Query.orderByKey() instead.'); |
} else { |
- if (key === "$priority") { |
+ if (path === "$priority") { |
throw new Error('Query.orderByChild: "$priority" is invalid. Use Query.orderByPriority() instead.'); |
} else { |
- if (key === "$value") { |
+ if (path === "$value") { |
throw new Error('Query.orderByChild: "$value" is invalid. Use Query.orderByValue() instead.'); |
} |
} |
} |
- fb.core.util.validation.validateKey("Query.orderByChild", 1, key, false); |
+ fb.core.util.validation.validatePathString("Query.orderByChild", 1, path, false); |
this.validateNoPreviousOrderByCall_("Query.orderByChild"); |
- var index = new fb.core.snap.SubKeyIndex(key); |
+ var parsedPath = new fb.core.util.Path(path); |
+ if (parsedPath.isEmpty()) { |
+ throw new Error("Query.orderByChild: cannot pass in empty path. Use Query.orderByValue() instead."); |
+ } |
+ var index = new fb.core.snap.PathIndex(parsedPath); |
var newParams = this.queryParams_.orderBy(index); |
this.validateQueryEndpoints_(newParams); |
return new fb.api.Query(this.repo, this.path, newParams, true); |