aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTORS.txt1
-rw-r--r--OpenSim/Framework/Servers/BaseHttpServer.cs3
-rw-r--r--OpenSim/Framework/SimStats.cs9
-rw-r--r--OpenSim/Framework/Statistics/SimExtraStatsCollector.cs65
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs4
-rw-r--r--OpenSim/Region/Environment/Scenes/Scene.cs78
-rw-r--r--OpenSim/Region/Environment/Scenes/SceneGraph.cs11
-rw-r--r--OpenSim/Region/Environment/Scenes/SimStatsReporter.cs13
-rw-r--r--OpenSim/Region/UserStatistics/ActiveConnectionsAJAX.cs185
-rw-r--r--OpenSim/Region/UserStatistics/Default_Report.cs215
-rw-r--r--OpenSim/Region/UserStatistics/HTMLUtil.cs185
-rw-r--r--OpenSim/Region/UserStatistics/IStatsReport.cs10
-rw-r--r--OpenSim/Region/UserStatistics/Prototype_distributor.cs33
-rw-r--r--OpenSim/Region/UserStatistics/SimStatsAJAX.cs190
-rw-r--r--OpenSim/Region/UserStatistics/Updater_distributor.cs35
-rw-r--r--OpenSim/Region/UserStatistics/WebStatsModule.cs1083
-rw-r--r--ThirdPartyLicenses/Prototype.txt16
-rw-r--r--bin/data/prototype.js4222
-rw-r--r--bin/data/updater.js20
-rw-r--r--prebuild.xml48
20 files changed, 6360 insertions, 66 deletions
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index b4fff79..bb6503e 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -113,6 +113,7 @@ This software uses components from the following developers:
113* Prebuild (http://sourceforge.net/projects/dnpb/) 113* Prebuild (http://sourceforge.net/projects/dnpb/)
114* LibSecondLife (http://www.libsecondlife.org/wiki/Main_Page) 114* LibSecondLife (http://www.libsecondlife.org/wiki/Main_Page)
115* DotNetOpenMail v0.5.8b (http://dotnetopenmail.sourceforge.net) 115* DotNetOpenMail v0.5.8b (http://dotnetopenmail.sourceforge.net)
116* Prototype JavaScript Framework ajax (http://www.prototypejs.org/)
116 117
117 118
118In addition, we would like to thank: 119In addition, we would like to thank:
diff --git a/OpenSim/Framework/Servers/BaseHttpServer.cs b/OpenSim/Framework/Servers/BaseHttpServer.cs
index 51658f3..ea356ac 100644
--- a/OpenSim/Framework/Servers/BaseHttpServer.cs
+++ b/OpenSim/Framework/Servers/BaseHttpServer.cs
@@ -1091,6 +1091,7 @@ namespace OpenSim.Framework.Servers
1091 { 1091 {
1092 host = (string)headervals["Host"]; 1092 host = (string)headervals["Host"];
1093 } 1093 }
1094 keysvals.Add("headers",headervals);
1094 1095
1095 if (keysvals.Contains("method")) 1096 if (keysvals.Contains("method"))
1096 { 1097 {
@@ -1238,7 +1239,7 @@ namespace OpenSim.Framework.Servers
1238 1239
1239 byte[] buffer; 1240 byte[] buffer;
1240 1241
1241 if (!contentType.Contains("image")) 1242 if (!(contentType.Contains("image") || contentType.Contains("x-shockwave-flash")))
1242 { 1243 {
1243 buffer = Encoding.UTF8.GetBytes(responseString); 1244 buffer = Encoding.UTF8.GetBytes(responseString);
1244 } 1245 }
diff --git a/OpenSim/Framework/SimStats.cs b/OpenSim/Framework/SimStats.cs
index c77d0d4..865dee2 100644
--- a/OpenSim/Framework/SimStats.cs
+++ b/OpenSim/Framework/SimStats.cs
@@ -72,11 +72,18 @@ namespace OpenSim.Framework
72 get { return m_objectCapacity; } 72 get { return m_objectCapacity; }
73 } 73 }
74 private uint m_objectCapacity; 74 private uint m_objectCapacity;
75
76 public OpenMetaverse.UUID RegionUUID
77 {
78 get { return regionUUID;}
79 }
80 private OpenMetaverse.UUID regionUUID;
75 81
76 public SimStats( 82 public SimStats(
77 uint regionX, uint regionY, uint regionFlags, uint objectCapacity, 83 uint regionX, uint regionY, uint regionFlags, uint objectCapacity,
78 SimStatsPacket.RegionBlock regionBlock, SimStatsPacket.StatBlock[] statsBlock) 84 SimStatsPacket.RegionBlock regionBlock, SimStatsPacket.StatBlock[] statsBlock, OpenMetaverse.UUID pRUUID)
79 { 85 {
86 regionUUID = pRUUID;
80 m_regionX = regionX; 87 m_regionX = regionX;
81 m_regionY = regionY; 88 m_regionY = regionY;
82 m_regionFlags = regionFlags; 89 m_regionFlags = regionFlags;
diff --git a/OpenSim/Framework/Statistics/SimExtraStatsCollector.cs b/OpenSim/Framework/Statistics/SimExtraStatsCollector.cs
index 48bed81..43c73b2 100644
--- a/OpenSim/Framework/Statistics/SimExtraStatsCollector.cs
+++ b/OpenSim/Framework/Statistics/SimExtraStatsCollector.cs
@@ -51,27 +51,27 @@ namespace OpenSim.Framework.Statistics
51 private long assetServiceRequestFailures; 51 private long assetServiceRequestFailures;
52 private long inventoryServiceRetrievalFailures; 52 private long inventoryServiceRetrievalFailures;
53 53
54 private float timeDilation; 54 private volatile float timeDilation;
55 private float simFps; 55 private volatile float simFps;
56 private float physicsFps; 56 private volatile float physicsFps;
57 private float agentUpdates; 57 private volatile float agentUpdates;
58 private float rootAgents; 58 private volatile float rootAgents;
59 private float childAgents; 59 private volatile float childAgents;
60 private float totalPrims; 60 private volatile float totalPrims;
61 private float activePrims; 61 private volatile float activePrims;
62 private float totalFrameTime; 62 private volatile float totalFrameTime;
63 private float netFrameTime; 63 private volatile float netFrameTime;
64 private float physicsFrameTime; 64 private volatile float physicsFrameTime;
65 private float otherFrameTime; 65 private volatile float otherFrameTime;
66 private float imageFrameTime; 66 private volatile float imageFrameTime;
67 private float inPacketsPerSecond; 67 private volatile float inPacketsPerSecond;
68 private float outPacketsPerSecond; 68 private volatile float outPacketsPerSecond;
69 private float unackedBytes; 69 private volatile float unackedBytes;
70 private float agentFrameTime; 70 private volatile float agentFrameTime;
71 private float pendingDownloads; 71 private volatile float pendingDownloads;
72 private float pendingUploads; 72 private volatile float pendingUploads;
73 private float activeScripts; 73 private volatile float activeScripts;
74 private float scriptLinesPerSecond; 74 private volatile float scriptLinesPerSecond;
75 75
76 /// <summary> 76 /// <summary>
77 /// Number of times that a client thread terminated because of an exception 77 /// Number of times that a client thread terminated because of an exception
@@ -87,7 +87,28 @@ namespace OpenSim.Framework.Statistics
87 public long TexturesInCache { get { return texturesInCache; } } 87 public long TexturesInCache { get { return texturesInCache; } }
88 public long AssetCacheMemoryUsage { get { return assetCacheMemoryUsage; } } 88 public long AssetCacheMemoryUsage { get { return assetCacheMemoryUsage; } }
89 public long TextureCacheMemoryUsage { get { return textureCacheMemoryUsage; } } 89 public long TextureCacheMemoryUsage { get { return textureCacheMemoryUsage; } }
90 90
91 public float TimeDilation { get { return timeDilation; } }
92 public float SimFps { get { return simFps; } }
93 public float PhysicsFps { get { return physicsFps; } }
94 public float AgentUpdates { get { return agentUpdates; } }
95 public float RootAgents { get { return rootAgents; } }
96 public float ChildAgents { get { return childAgents; } }
97 public float TotalPrims { get { return totalPrims; } }
98 public float ActivePrims { get { return activePrims; } }
99 public float TotalFrameTime { get { return totalFrameTime; } }
100 public float NetFrameTime { get { return netFrameTime; } }
101 public float PhysicsFrameTime { get { return physicsFrameTime; } }
102 public float OtherFrameTime { get { return otherFrameTime; } }
103 public float ImageFrameTime { get { return imageFrameTime; } }
104 public float InPacketsPerSecond { get { return inPacketsPerSecond; } }
105 public float OutPacketsPerSecond { get { return outPacketsPerSecond; } }
106 public float UnackedBytes { get { return unackedBytes; } }
107 public float AgentFrameTime { get { return agentFrameTime; } }
108 public float PendingDownloads { get { return pendingDownloads; } }
109 public float PendingUploads { get { return pendingUploads; } }
110 public float ActiveScripts { get { return activeScripts; } }
111 public float ScriptLinesPerSecond { get { return scriptLinesPerSecond; } }
91 /// <summary> 112 /// <summary>
92 /// This is the time it took for the last asset request made in response to a cache miss. 113 /// This is the time it took for the last asset request made in response to a cache miss.
93 /// </summary> 114 /// </summary>
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs b/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs
index a698d41..29cfd99 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs
@@ -603,10 +603,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
603 public void Flush() 603 public void Flush()
604 { 604 {
605 m_PacketQueue.Flush(); 605 m_PacketQueue.Flush();
606 m_UnackedBytes = (-1 * m_UnackedBytes);
607 SendPacketStats();
606 } 608 }
607 609
608 public void Clear() 610 public void Clear()
609 { 611 {
612 m_UnackedBytes = (-1 * m_UnackedBytes);
613 SendPacketStats();
610 m_NeedAck.Clear(); 614 m_NeedAck.Clear();
611 m_PendingAcks.Clear(); 615 m_PendingAcks.Clear();
612 m_Sequence += 1000000; 616 m_Sequence += 1000000;
diff --git a/OpenSim/Region/Environment/Scenes/Scene.cs b/OpenSim/Region/Environment/Scenes/Scene.cs
index 408f100..daeb186 100644
--- a/OpenSim/Region/Environment/Scenes/Scene.cs
+++ b/OpenSim/Region/Environment/Scenes/Scene.cs
@@ -69,7 +69,7 @@ namespace OpenSim.Region.Environment.Scenes
69 69
70 protected Timer m_restartWaitTimer = new Timer(); 70 protected Timer m_restartWaitTimer = new Timer();
71 71
72 protected SimStatsReporter m_statsReporter; 72 public SimStatsReporter StatsReporter;
73 73
74 protected List<RegionInfo> m_regionRestartNotifyList = new List<RegionInfo>(); 74 protected List<RegionInfo> m_regionRestartNotifyList = new List<RegionInfo>();
75 protected List<RegionInfo> m_neighbours = new List<RegionInfo>(); 75 protected List<RegionInfo> m_neighbours = new List<RegionInfo>();
@@ -244,7 +244,7 @@ namespace OpenSim.Region.Environment.Scenes
244 /// </summary> 244 /// </summary>
245 public float SimulatorFPS 245 public float SimulatorFPS
246 { 246 {
247 get { return m_statsReporter.getLastReportedSimFPS(); } 247 get { return StatsReporter.getLastReportedSimFPS(); }
248 } 248 }
249 249
250 public int TimePhase 250 public int TimePhase
@@ -358,10 +358,10 @@ namespace OpenSim.Region.Environment.Scenes
358 358
359 m_physics_enabled = !RegionInfo.RegionSettings.DisablePhysics; 359 m_physics_enabled = !RegionInfo.RegionSettings.DisablePhysics;
360 360
361 m_statsReporter = new SimStatsReporter(this); 361 StatsReporter = new SimStatsReporter(this);
362 m_statsReporter.OnSendStatsResult += SendSimStatsPackets; 362 StatsReporter.OnSendStatsResult += SendSimStatsPackets;
363 363
364 m_statsReporter.SetObjectCapacity(objectCapacity); 364 StatsReporter.SetObjectCapacity(objectCapacity);
365 365
366 m_simulatorVersion = simulatorVersion 366 m_simulatorVersion = simulatorVersion
367 + " (OS " + Util.GetOperatingSystemInformation() + ")" 367 + " (OS " + Util.GetOperatingSystemInformation() + ")"
@@ -814,7 +814,7 @@ namespace OpenSim.Region.Environment.Scenes
814 if (m_update_entities == 1) 814 if (m_update_entities == 1)
815 { 815 {
816 m_update_entities = 5; 816 m_update_entities = 5;
817 m_statsReporter.SetUpdateMS(6000); 817 StatsReporter.SetUpdateMS(6000);
818 } 818 }
819 } 819 }
820 else 820 else
@@ -822,7 +822,7 @@ namespace OpenSim.Region.Environment.Scenes
822 if (m_update_entities == 5) 822 if (m_update_entities == 5)
823 { 823 {
824 m_update_entities = 1; 824 m_update_entities = 1;
825 m_statsReporter.SetUpdateMS(3000); 825 StatsReporter.SetUpdateMS(3000);
826 } 826 }
827 } 827 }
828 828
@@ -891,20 +891,20 @@ namespace OpenSim.Region.Environment.Scenes
891 otherMS = System.Environment.TickCount - otherMS; 891 otherMS = System.Environment.TickCount - otherMS;
892 // if (m_frame%m_update_avatars == 0) 892 // if (m_frame%m_update_avatars == 0)
893 // UpdateInWorldTime(); 893 // UpdateInWorldTime();
894 m_statsReporter.AddPhysicsFPS(physicsFPS); 894 StatsReporter.AddPhysicsFPS(physicsFPS);
895 m_statsReporter.AddTimeDilation(m_timedilation); 895 StatsReporter.AddTimeDilation(m_timedilation);
896 m_statsReporter.AddFPS(1); 896 StatsReporter.AddFPS(1);
897 m_statsReporter.AddInPackets(0); 897 StatsReporter.AddInPackets(0);
898 m_statsReporter.SetRootAgents(m_sceneGraph.GetRootAgentCount()); 898 StatsReporter.SetRootAgents(m_sceneGraph.GetRootAgentCount());
899 m_statsReporter.SetChildAgents(m_sceneGraph.GetChildAgentCount()); 899 StatsReporter.SetChildAgents(m_sceneGraph.GetChildAgentCount());
900 m_statsReporter.SetObjects(m_sceneGraph.GetTotalObjectsCount()); 900 StatsReporter.SetObjects(m_sceneGraph.GetTotalObjectsCount());
901 m_statsReporter.SetActiveObjects(m_sceneGraph.GetActiveObjectsCount()); 901 StatsReporter.SetActiveObjects(m_sceneGraph.GetActiveObjectsCount());
902 frameMS = System.Environment.TickCount - frameMS; 902 frameMS = System.Environment.TickCount - frameMS;
903 m_statsReporter.addFrameMS(frameMS); 903 StatsReporter.addFrameMS(frameMS);
904 m_statsReporter.addPhysicsMS(physicsMS); 904 StatsReporter.addPhysicsMS(physicsMS);
905 m_statsReporter.addOtherMS(otherMS); 905 StatsReporter.addOtherMS(otherMS);
906 m_statsReporter.SetActiveScripts(m_sceneGraph.GetActiveScriptsCount()); 906 StatsReporter.SetActiveScripts(m_sceneGraph.GetActiveScriptsCount());
907 m_statsReporter.addScriptLines(m_sceneGraph.GetScriptLPS()); 907 StatsReporter.addScriptLines(m_sceneGraph.GetScriptLPS());
908 } 908 }
909 } 909 }
910 catch (NotImplementedException) 910 catch (NotImplementedException)
@@ -2380,8 +2380,8 @@ namespace OpenSim.Region.Environment.Scenes
2380 2380
2381 client.OnObjectOwner += ObjectOwner; 2381 client.OnObjectOwner += ObjectOwner;
2382 2382
2383 if (m_statsReporter != null) 2383 if (StatsReporter != null)
2384 client.OnNetworkStatsUpdate += m_statsReporter.AddPacketsFromClientStats; 2384 client.OnNetworkStatsUpdate += StatsReporter.AddPacketsFromClientStats;
2385 2385
2386 // EventManager.TriggerOnNewClient(client); 2386 // EventManager.TriggerOnNewClient(client);
2387 } 2387 }
@@ -3027,14 +3027,14 @@ namespace OpenSim.Region.Environment.Scenes
3027 if (presence != null) 3027 if (presence != null)
3028 { 3028 {
3029 // Nothing is removed here, so down count it as such 3029 // Nothing is removed here, so down count it as such
3030 // if (presence.IsChildAgent) 3030 if (presence.IsChildAgent)
3031 // { 3031 {
3032 // m_sceneGraph.removeUserCount(false); 3032 m_sceneGraph.removeUserCount(false);
3033 // } 3033 }
3034 // else 3034 else
3035 // { 3035 {
3036 // m_sceneGraph.removeUserCount(true); 3036 m_sceneGraph.removeUserCount(true);
3037 // } 3037 }
3038 3038
3039 // Don't do this to root agents on logout, it's not nice for the viewer 3039 // Don't do this to root agents on logout, it's not nice for the viewer
3040 if (presence.IsChildAgent) 3040 if (presence.IsChildAgent)
@@ -3322,9 +3322,9 @@ namespace OpenSim.Region.Environment.Scenes
3322 if (RegionInfo.ObjectCapacity != 0) 3322 if (RegionInfo.ObjectCapacity != 0)
3323 objects = RegionInfo.ObjectCapacity; 3323 objects = RegionInfo.ObjectCapacity;
3324 3324
3325 if (m_statsReporter != null) 3325 if (StatsReporter != null)
3326 { 3326 {
3327 m_statsReporter.SetObjectCapacity(objects); 3327 StatsReporter.SetObjectCapacity(objects);
3328 } 3328 }
3329 objectCapacity = objects; 3329 objectCapacity = objects;
3330 } 3330 }
@@ -3430,25 +3430,25 @@ namespace OpenSim.Region.Environment.Scenes
3430 3430
3431 public void AddPacketStats(int inPackets, int outPackets, int unAckedBytes) 3431 public void AddPacketStats(int inPackets, int outPackets, int unAckedBytes)
3432 { 3432 {
3433 m_statsReporter.AddInPackets(inPackets); 3433 StatsReporter.AddInPackets(inPackets);
3434 m_statsReporter.AddOutPackets(outPackets); 3434 StatsReporter.AddOutPackets(outPackets);
3435 m_statsReporter.AddunAckedBytes(unAckedBytes); 3435 StatsReporter.AddunAckedBytes(unAckedBytes);
3436 } 3436 }
3437 3437
3438 public void AddAgentTime(int ms) 3438 public void AddAgentTime(int ms)
3439 { 3439 {
3440 m_statsReporter.addFrameMS(ms); 3440 StatsReporter.addFrameMS(ms);
3441 m_statsReporter.addAgentMS(ms); 3441 StatsReporter.addAgentMS(ms);
3442 } 3442 }
3443 3443
3444 public void AddAgentUpdates(int count) 3444 public void AddAgentUpdates(int count)
3445 { 3445 {
3446 m_statsReporter.AddAgentUpdates(count); 3446 StatsReporter.AddAgentUpdates(count);
3447 } 3447 }
3448 3448
3449 public void AddPendingDownloads(int count) 3449 public void AddPendingDownloads(int count)
3450 { 3450 {
3451 m_statsReporter.addPendingDownload(count); 3451 StatsReporter.addPendingDownload(count);
3452 } 3452 }
3453 3453
3454 #endregion 3454 #endregion
diff --git a/OpenSim/Region/Environment/Scenes/SceneGraph.cs b/OpenSim/Region/Environment/Scenes/SceneGraph.cs
index d998dbb..3ffa5c3 100644
--- a/OpenSim/Region/Environment/Scenes/SceneGraph.cs
+++ b/OpenSim/Region/Environment/Scenes/SceneGraph.cs
@@ -306,8 +306,19 @@ namespace OpenSim.Region.Environment.Scenes
306 if (!resultOfObjectLinked) 306 if (!resultOfObjectLinked)
307 { 307 {
308 m_numPrim -= ((SceneObjectGroup) Entities[uuid]).Children.Count; 308 m_numPrim -= ((SceneObjectGroup) Entities[uuid]).Children.Count;
309
310 if ((((SceneObjectGroup)Entities[uuid]).RootPart.Flags & PrimFlags.Physics) == PrimFlags.Physics)
311 {
312 RemovePhysicalPrim(((SceneObjectGroup)Entities[uuid]).Children.Count);
313 }
309 } 314 }
315
316
317
310 Entities.Remove(uuid); 318 Entities.Remove(uuid);
319 //SceneObjectGroup part;
320 //((part.RootPart.Flags & PrimFlags.Physics) == PrimFlags.Physics)
321
311 322
312 return true; 323 return true;
313 } 324 }
diff --git a/OpenSim/Region/Environment/Scenes/SimStatsReporter.cs b/OpenSim/Region/Environment/Scenes/SimStatsReporter.cs
index c614f78..e2afa5a 100644
--- a/OpenSim/Region/Environment/Scenes/SimStatsReporter.cs
+++ b/OpenSim/Region/Environment/Scenes/SimStatsReporter.cs
@@ -26,6 +26,7 @@
26 */ 26 */
27 27
28using System; 28using System;
29//using System.Collections.Generic;
29using System.Timers; 30using System.Timers;
30using OpenMetaverse.Packets; 31using OpenMetaverse.Packets;
31using OpenSim.Framework; 32using OpenSim.Framework;
@@ -189,7 +190,7 @@ namespace OpenSim.Region.Environment.Scenes
189 } 190 }
190 191
191 sb[0].StatID = (uint) Stats.TimeDilation; 192 sb[0].StatID = (uint) Stats.TimeDilation;
192 sb[0].StatValue = m_timeDilation ; //((((m_timeDilation + (0.10f * statsUpdateFactor)) /10) / statsUpdateFactor)); 193 sb[0].StatValue = (Single.IsNaN(m_timeDilation)) ? 0.1f : m_timeDilation ; //((((m_timeDilation + (0.10f * statsUpdateFactor)) /10) / statsUpdateFactor));
193 194
194 sb[1].StatID = (uint) Stats.SimFPS; 195 sb[1].StatID = (uint) Stats.SimFPS;
195 sb[1].StatValue = simfps/statsUpdateFactor; 196 sb[1].StatValue = simfps/statsUpdateFactor;
@@ -253,7 +254,7 @@ namespace OpenSim.Region.Environment.Scenes
253 254
254 SimStats simStats 255 SimStats simStats
255 = new SimStats( 256 = new SimStats(
256 ReportingRegion.RegionLocX, ReportingRegion.RegionLocY, regionFlags, (uint)objectCapacity, rb, sb); 257 ReportingRegion.RegionLocX, ReportingRegion.RegionLocY, regionFlags, (uint)objectCapacity, rb, sb, m_scene.RegionInfo.originRegionID);
257 258
258 handlerSendStatResult = OnSendStatsResult; 259 handlerSendStatResult = OnSendStatsResult;
259 if (handlerSendStatResult != null) 260 if (handlerSendStatResult != null)
@@ -309,7 +310,11 @@ namespace OpenSim.Region.Environment.Scenes
309 310
310 public void SetChildAgents(int childAgents) 311 public void SetChildAgents(int childAgents)
311 { 312 {
312 m_childAgents = childAgents; 313 m_childAgents = (childAgents > 0) ? childAgents : 0;
314 if (childAgents < 0)
315 {
316 //List<ScenePresence> avs= m_scene.GetScenePresences();
317 }
313 } 318 }
314 319
315 public void SetObjects(int objects) 320 public void SetObjects(int objects)
@@ -350,6 +355,7 @@ namespace OpenSim.Region.Environment.Scenes
350 public void AddunAckedBytes(int numBytes) 355 public void AddunAckedBytes(int numBytes)
351 { 356 {
352 m_unAckedBytes += numBytes; 357 m_unAckedBytes += numBytes;
358 if (m_unAckedBytes < 0) m_unAckedBytes = 0;
353 } 359 }
354 360
355 public void addFrameMS(int ms) 361 public void addFrameMS(int ms)
@@ -383,6 +389,7 @@ namespace OpenSim.Region.Environment.Scenes
383 public void addPendingDownload(int count) 389 public void addPendingDownload(int count)
384 { 390 {
385 m_pendingDownloads += count; 391 m_pendingDownloads += count;
392 if (m_pendingDownloads < 0) m_pendingDownloads = 0;
386 //m_log.InfoFormat("[stats]: Adding {0} to pending downloads to make {1}", count, m_pendingDownloads); 393 //m_log.InfoFormat("[stats]: Adding {0} to pending downloads to make {1}", count, m_pendingDownloads);
387 } 394 }
388 395
diff --git a/OpenSim/Region/UserStatistics/ActiveConnectionsAJAX.cs b/OpenSim/Region/UserStatistics/ActiveConnectionsAJAX.cs
new file mode 100644
index 0000000..2dcd2b8
--- /dev/null
+++ b/OpenSim/Region/UserStatistics/ActiveConnectionsAJAX.cs
@@ -0,0 +1,185 @@
1using System;
2using System.Collections;
3using System.Collections.Generic;
4using System.Reflection;
5using System.Text;
6using Mono.Data.SqliteClient;
7using OpenMetaverse;
8using OpenSim.Region.Environment.Scenes;
9using OpenSim.Framework.Statistics;
10
11namespace OpenSim.Region.UserStatistics
12{
13 public class ActiveConnectionsAJAX : IStatsController
14 {
15 #region IStatsController Members
16
17 public Hashtable ProcessModel(Hashtable pParams)
18 {
19
20 List<Scene> m_scene = (List<Scene>)pParams["Scenes"];
21
22 Hashtable nh = new Hashtable();
23 nh.Add("hdata", m_scene);
24
25 return nh;
26 }
27
28 public string RenderView(Hashtable pModelResult)
29 {
30 List<Scene> all_scenes = (List<Scene>) pModelResult["hdata"];
31
32 StringBuilder output = new StringBuilder();
33 HTMLUtil.OL_O(ref output, "");
34 foreach (Scene scene in all_scenes)
35 {
36 List<ScenePresence> avatarInScene = scene.GetScenePresences();
37
38 HTMLUtil.LI_O(ref output, "");
39 output.Append(scene.RegionInfo.RegionName);
40 HTMLUtil.OL_O(ref output, "");
41 foreach (ScenePresence av in avatarInScene)
42 {
43 Dictionary<string,string> queues = new Dictionary<string, string>();
44 if (av.ControllingClient is IStatsCollector)
45 {
46 IStatsCollector isClient = (IStatsCollector) av.ControllingClient;
47 queues = decodeQueueReport(isClient.Report());
48 }
49 HTMLUtil.LI_O(ref output, "");
50 output.Append(av.Name);
51 output.Append("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");
52 output.Append((av.IsChildAgent ? "Child" : "Root"));
53
54 Dictionary<string, int> throttles = DecodeClientThrottles(av.ControllingClient.GetThrottlesPacked(1));
55
56 HTMLUtil.UL_O(ref output, "");
57 foreach (string throttlename in throttles.Keys)
58 {
59 HTMLUtil.LI_O(ref output, "");
60 output.Append(throttlename);
61 output.Append(":");
62 output.Append(throttles[throttlename].ToString());
63 if (queues.ContainsKey(throttlename))
64 {
65 output.Append("/");
66 output.Append(queues[throttlename]);
67 }
68 HTMLUtil.LI_C(ref output);
69 }
70 if (queues.ContainsKey("Incoming") && queues.ContainsKey("Outgoing"))
71 {
72 HTMLUtil.LI_O(ref output, "red");
73 output.Append("SEND:");
74 output.Append(queues["Outgoing"]);
75 output.Append("/");
76 output.Append(queues["Incoming"]);
77 HTMLUtil.LI_C(ref output);
78 }
79
80 HTMLUtil.UL_C(ref output);
81 HTMLUtil.LI_C(ref output);
82 }
83 HTMLUtil.OL_C(ref output);
84 }
85 HTMLUtil.OL_C(ref output);
86 return output.ToString();
87 }
88
89 public Dictionary<string, int> DecodeClientThrottles(byte[] throttle)
90 {
91 Dictionary<string, int> returndict = new Dictionary<string, int>();
92 // From mantis http://opensimulator.org/mantis/view.php?id=1374
93 // it appears that sometimes we are receiving empty throttle byte arrays.
94 // TODO: Investigate this behaviour
95 if (throttle.Length == 0)
96 {
97 return new Dictionary<string, int>();
98 }
99
100 int tResend = -1;
101 int tLand = -1;
102 int tWind = -1;
103 int tCloud = -1;
104 int tTask = -1;
105 int tTexture = -1;
106 int tAsset = -1;
107 int tall = -1;
108 const int singlefloat = 4;
109
110 //Agent Throttle Block contains 7 single floatingpoint values.
111 int j = 0;
112
113 // Some Systems may be big endian...
114 // it might be smart to do this check more often...
115 if (!BitConverter.IsLittleEndian)
116 for (int i = 0; i < 7; i++)
117 Array.Reverse(throttle, j + i * singlefloat, singlefloat);
118
119 // values gotten from OpenMetaverse.org/wiki/Throttle. Thanks MW_
120 // bytes
121 // Convert to integer, since.. the full fp space isn't used.
122 tResend = (int)BitConverter.ToSingle(throttle, j);
123 returndict.Add("Resend", tResend);
124 j += singlefloat;
125 tLand = (int)BitConverter.ToSingle(throttle, j);
126 returndict.Add("Land", tLand);
127 j += singlefloat;
128 tWind = (int)BitConverter.ToSingle(throttle, j);
129 returndict.Add("Wind", tWind);
130 j += singlefloat;
131 tCloud = (int)BitConverter.ToSingle(throttle, j);
132 returndict.Add("Cloud", tCloud);
133 j += singlefloat;
134 tTask = (int)BitConverter.ToSingle(throttle, j);
135 returndict.Add("Task", tTask);
136 j += singlefloat;
137 tTexture = (int)BitConverter.ToSingle(throttle, j);
138 returndict.Add("Texture", tTexture);
139 j += singlefloat;
140 tAsset = (int)BitConverter.ToSingle(throttle, j);
141 returndict.Add("Asset", tAsset);
142
143 tall = tResend + tLand + tWind + tCloud + tTask + tTexture + tAsset;
144 returndict.Add("All", tall);
145
146 return returndict;
147 }
148 public Dictionary<string,string> decodeQueueReport(string rep)
149 {
150 Dictionary<string, string> returndic = new Dictionary<string, string>();
151 if (rep.Length == 79)
152 {
153 int pos = 1;
154 returndic.Add("All", rep.Substring((6 * pos), 8)); pos++;
155 returndic.Add("Incoming", rep.Substring((7 * pos), 8)); pos++;
156 returndic.Add("Outgoing", rep.Substring((7 * pos) , 8)); pos++;
157 returndic.Add("Resend", rep.Substring((7 * pos) , 8)); pos++;
158 returndic.Add("Land", rep.Substring((7 * pos) , 8)); pos++;
159 returndic.Add("Wind", rep.Substring((7 * pos) , 8)); pos++;
160 returndic.Add("Cloud", rep.Substring((7 * pos) , 8)); pos++;
161 returndic.Add("Task", rep.Substring((7 * pos) , 8)); pos++;
162 returndic.Add("Texture", rep.Substring((7 * pos), 8)); pos++;
163 returndic.Add("Asset", rep.Substring((7 * pos), 8));
164 /*
165 * return string.Format("{0,7} {1,7} {2,7} {3,7} {4,7} {5,7} {6,7} {7,7} {8,7} {9,7}",
166 SendQueue.Count(),
167 IncomingPacketQueue.Count,
168 OutgoingPacketQueue.Count,
169 ResendOutgoingPacketQueue.Count,
170 LandOutgoingPacketQueue.Count,
171 WindOutgoingPacketQueue.Count,
172 CloudOutgoingPacketQueue.Count,
173 TaskOutgoingPacketQueue.Count,
174 TextureOutgoingPacketQueue.Count,
175 AssetOutgoingPacketQueue.Count);
176 */
177 }
178
179
180
181 return returndic;
182 }
183 #endregion
184 }
185}
diff --git a/OpenSim/Region/UserStatistics/Default_Report.cs b/OpenSim/Region/UserStatistics/Default_Report.cs
new file mode 100644
index 0000000..02b15ad
--- /dev/null
+++ b/OpenSim/Region/UserStatistics/Default_Report.cs
@@ -0,0 +1,215 @@
1using System;
2using System.Collections;
3using System.Collections.Generic;
4using System.Reflection;
5using System.Text;
6using Mono.Data.SqliteClient;
7using OpenMetaverse;
8using OpenSim.Region.Environment.Scenes;
9using OpenSim.Framework.Statistics;
10
11
12namespace OpenSim.Region.UserStatistics
13{
14 public class Default_Report : IStatsController
15 {
16
17 public Default_Report()
18 {
19
20 }
21
22 #region IStatsController Members
23
24 public Hashtable ProcessModel(Hashtable pParams)
25 {
26 SqliteConnection conn = (SqliteConnection)pParams["DatabaseConnection"];
27 List<Scene> m_scene = (List<Scene>)pParams["Scenes"];
28
29 stats_default_page_values mData = rep_DefaultReport_data(conn, m_scene);
30 mData.sim_stat_data = (Dictionary<UUID,USimStatsData>)pParams["SimStats"];
31
32 Hashtable nh = new Hashtable();
33 nh.Add("hdata", mData);
34
35 return nh;
36 }
37
38 public string RenderView(Hashtable pModelResult)
39 {
40 stats_default_page_values mData = (stats_default_page_values) pModelResult["hdata"];
41 return rep_Default_report_view(mData);
42 }
43
44 #endregion
45
46 public string rep_Default_report_view(stats_default_page_values values)
47 {
48
49 StringBuilder output = new StringBuilder();
50
51
52
53 const string TableClass = "defaultr";
54 const string TRClass = "defaultr";
55 const string TDHeaderClass = "header";
56 const string TDDataClass = "content";
57 //const string TDDataClassRight = "contentright";
58 const string TDDataClassCenter = "contentcenter";
59
60 const string STYLESHEET =
61 @"
62<STYLE>
63body
64{
65 font-size:15px; font-family:Helvetica, Verdana; color:Black;
66}
67TABLE.defaultr { }
68TR.defaultr { padding: 5px; }
69TD.header { font-weight:bold; padding:5px; }
70TD.content {}
71TD.contentright { text-align: right; }
72TD.contentcenter { text-align: center; }
73TD.align_top { vertical-align: top; }
74</STYLE>
75";
76 HTMLUtil.HtmlHeaders_O(ref output);
77
78 HTMLUtil.InsertProtoTypeAJAX(ref output);
79 string[] ajaxUpdaterDivs = new string[2];
80 int[] ajaxUpdaterSeconds = new int[2];
81 string[] ajaxUpdaterReportFragments = new string[2];
82
83 ajaxUpdaterDivs[0] = "activeconnections";
84 ajaxUpdaterSeconds[0] = 10;
85 ajaxUpdaterReportFragments[0] = "activeconnectionsajax.ajax";
86
87 ajaxUpdaterDivs[1] = "activesimstats";
88 ajaxUpdaterSeconds[1] = 20;
89 ajaxUpdaterReportFragments[1] = "simstatsajax.ajax";
90
91 HTMLUtil.InsertPeriodicUpdaters(ref output, ajaxUpdaterDivs, ajaxUpdaterSeconds, ajaxUpdaterReportFragments);
92
93 output.Append(STYLESHEET);
94 HTMLUtil.HtmlHeaders_C(ref output);
95
96 HTMLUtil.TABLE_O(ref output, TableClass);
97 HTMLUtil.TR_O(ref output, TRClass);
98 HTMLUtil.TD_O(ref output, TDHeaderClass);
99 output.Append("# Users Total");
100 HTMLUtil.TD_C(ref output);
101 HTMLUtil.TD_O(ref output, TDHeaderClass);
102 output.Append("# Sessions Total");
103 HTMLUtil.TD_C(ref output);
104 HTMLUtil.TD_O(ref output, TDHeaderClass);
105 output.Append("Avg Client FPS");
106 HTMLUtil.TD_C(ref output);
107 HTMLUtil.TD_O(ref output, TDHeaderClass);
108 output.Append("Avg Client Mem Use");
109 HTMLUtil.TD_C(ref output);
110 HTMLUtil.TD_O(ref output, TDHeaderClass);
111 output.Append("Avg Sim FPS");
112 HTMLUtil.TD_C(ref output);
113 HTMLUtil.TD_O(ref output, TDHeaderClass);
114 output.Append("Avg Ping");
115 HTMLUtil.TD_C(ref output);
116 HTMLUtil.TD_O(ref output, TDHeaderClass);
117 output.Append("KB Out Total");
118 HTMLUtil.TD_C(ref output);
119 HTMLUtil.TD_O(ref output, TDHeaderClass);
120 output.Append("KB In Total");
121 HTMLUtil.TD_C(ref output);
122 HTMLUtil.TR_C(ref output);
123 HTMLUtil.TR_O(ref output, TRClass);
124 HTMLUtil.TD_O(ref output, TDDataClass);
125 output.Append(values.total_num_users);
126 HTMLUtil.TD_C(ref output);
127 HTMLUtil.TD_O(ref output, TDDataClass);
128 output.Append(values.total_num_sessions);
129 HTMLUtil.TD_C(ref output);
130 HTMLUtil.TD_O(ref output, TDDataClassCenter);
131 output.Append(values.avg_client_fps);
132 HTMLUtil.TD_C(ref output);
133 HTMLUtil.TD_O(ref output, TDDataClassCenter);
134 output.Append(values.avg_client_mem_use);
135 HTMLUtil.TD_C(ref output);
136 HTMLUtil.TD_O(ref output, TDDataClassCenter);
137 output.Append(values.avg_sim_fps);
138 HTMLUtil.TD_C(ref output);
139 HTMLUtil.TD_O(ref output, TDDataClassCenter);
140 output.Append(values.avg_ping);
141 HTMLUtil.TD_C(ref output);
142 HTMLUtil.TD_O(ref output, TDDataClassCenter);
143 output.Append(values.total_kb_out);
144 HTMLUtil.TD_C(ref output);
145 HTMLUtil.TD_O(ref output, TDDataClassCenter);
146 output.Append(values.total_kb_in);
147 HTMLUtil.TD_C(ref output);
148 HTMLUtil.TR_C(ref output);
149 HTMLUtil.TABLE_C(ref output);
150
151 HTMLUtil.HR(ref output, "");
152 HTMLUtil.TABLE_O(ref output, "");
153 HTMLUtil.TR_O(ref output, "");
154 HTMLUtil.TD_O(ref output, "align_top");
155 output.Append("<DIV id=\"activeconnections\">loading...</DIV>");
156 HTMLUtil.TD_C(ref output);
157 HTMLUtil.TD_O(ref output, "align_top");
158 output.Append("<DIV id=\"activesimstats\">loading...</DIV>");
159
160 HTMLUtil.TD_C(ref output);
161 HTMLUtil.TR_C(ref output);
162 HTMLUtil.TABLE_C(ref output);
163 output.Append("</BODY></HTML>");
164 // TODO: FIXME: template
165 return output.ToString();
166 }
167
168
169
170 public stats_default_page_values rep_DefaultReport_data(SqliteConnection db, List<Scene> m_scene)
171 {
172 stats_default_page_values returnstruct = new stats_default_page_values();
173 returnstruct.all_scenes = m_scene.ToArray();
174 lock (db)
175 {
176 string SQL = @"SELECT COUNT(DISTINCT agent_id) as agents, COUNT(*) as sessions, AVG(avg_fps) as client_fps,
177 AVG(avg_sim_fps) as savg_sim_fps, AVG(avg_ping) as sav_ping, SUM(n_out_kb) as num_in_kb,
178 SUM(n_out_pk) as num_in_packets, SUM(n_in_kb) as num_out_kb, SUM(n_in_pk) as num_out_packets, AVG(mem_use) as sav_mem_use
179 FROM stats_session_data;";
180 SqliteCommand cmd = new SqliteCommand(SQL, db);
181 SqliteDataReader sdr = cmd.ExecuteReader();
182 if (sdr.HasRows)
183 {
184 sdr.Read();
185 returnstruct.total_num_users = Convert.ToInt32(sdr["agents"]);
186 returnstruct.total_num_sessions = Convert.ToInt32(sdr["sessions"]);
187 returnstruct.avg_client_fps = Convert.ToSingle(sdr["client_fps"]);
188 returnstruct.avg_sim_fps = Convert.ToSingle(sdr["savg_sim_fps"]);
189 returnstruct.avg_ping = Convert.ToSingle(sdr["sav_ping"]);
190 returnstruct.total_kb_out = Convert.ToSingle(sdr["num_out_kb"]);
191 returnstruct.total_kb_in = Convert.ToSingle(sdr["num_in_kb"]);
192 returnstruct.avg_client_mem_use = Convert.ToSingle(sdr["sav_mem_use"]);
193
194 }
195 }
196 return returnstruct;
197 }
198
199 }
200
201 public struct stats_default_page_values
202 {
203 public int total_num_users;
204 public int total_num_sessions;
205 public float avg_client_fps;
206 public float avg_client_mem_use;
207 public float avg_sim_fps;
208 public float avg_ping;
209 public float total_kb_out;
210 public float total_kb_in;
211 public float avg_client_resends;
212 public Scene[] all_scenes;
213 public Dictionary<UUID, USimStatsData> sim_stat_data;
214 }
215}
diff --git a/OpenSim/Region/UserStatistics/HTMLUtil.cs b/OpenSim/Region/UserStatistics/HTMLUtil.cs
new file mode 100644
index 0000000..ed8cc98
--- /dev/null
+++ b/OpenSim/Region/UserStatistics/HTMLUtil.cs
@@ -0,0 +1,185 @@
1using System;
2using System.Collections.Generic;
3using System.Text;
4
5namespace OpenSim.Region.UserStatistics
6{
7 public static class HTMLUtil
8 {
9
10 public static void TR_O(ref StringBuilder o, string pclass)
11 {
12 o.Append("<tr");
13 if (pclass.Length > 0)
14 {
15 GenericClass(ref o, pclass);
16 }
17 o.Append(">\n\t");
18 }
19 public static void TR_C(ref StringBuilder o)
20 {
21 o.Append("</tr>\n");
22 }
23
24 public static void TD_O(ref StringBuilder o, string pclass)
25 {
26 o.Append("<td");
27 if (pclass.Length > 0)
28 {
29 GenericClass(ref o, pclass);
30 }
31 o.Append(">");
32 }
33 public static void TD_C(ref StringBuilder o)
34 {
35 o.Append("</td>");
36 }
37 public static void TABLE_O(ref StringBuilder o, string pclass)
38 {
39 o.Append("<table");
40 if (pclass.Length > 0)
41 {
42 GenericClass(ref o, pclass);
43 }
44 o.Append(">\n\t");
45 }
46 public static void TABLE_C(ref StringBuilder o)
47 {
48 o.Append("</table>\n");
49 }
50
51 public static void BLOCKQUOTE_O(ref StringBuilder o, string pclass)
52 {
53 o.Append("<blockquote");
54 if (pclass.Length > 0)
55 {
56 GenericClass(ref o, pclass);
57 }
58 o.Append(" />\n");
59 }
60
61 public static void BLOCKQUOTE_C(ref StringBuilder o)
62 {
63 o.Append("</blockquote>\n");
64 }
65
66 public static void BR(ref StringBuilder o)
67 {
68 o.Append("<br />\n");
69 }
70
71 public static void HR(ref StringBuilder o, string pclass)
72 {
73 o.Append("<hr");
74 if (pclass.Length > 0)
75 {
76 GenericClass(ref o, pclass);
77 }
78 o.Append(" />\n");
79 }
80
81 public static void UL_O(ref StringBuilder o, string pclass)
82 {
83 o.Append("<ul");
84 if (pclass.Length > 0)
85 {
86 GenericClass(ref o, pclass);
87 }
88 o.Append(" />\n");
89 }
90
91 public static void UL_C(ref StringBuilder o)
92 {
93 o.Append("</ul>\n");
94 }
95
96 public static void OL_O(ref StringBuilder o, string pclass)
97 {
98 o.Append("<ol");
99 if (pclass.Length > 0)
100 {
101 GenericClass(ref o, pclass);
102 }
103 o.Append(" />\n");
104 }
105
106 public static void OL_C(ref StringBuilder o)
107 {
108 o.Append("</ol>\n");
109 }
110
111 public static void LI_O(ref StringBuilder o, string pclass)
112 {
113 o.Append("<li");
114 if (pclass.Length > 0)
115 {
116 GenericClass(ref o, pclass);
117 }
118 o.Append(" />\n");
119 }
120
121 public static void LI_C(ref StringBuilder o)
122 {
123 o.Append("</li>\n");
124 }
125
126 public static void GenericClass(ref StringBuilder o, string pclass)
127 {
128 o.Append(" class=\"");
129 o.Append(pclass);
130 o.Append("\"");
131 }
132
133 public static void InsertProtoTypeAJAX(ref StringBuilder o)
134 {
135 o.Append("<script type=\"text/javascript\" src=\"prototype.js\"></script>\n");
136 o.Append("<script type=\"text/javascript\" src=\"updater.js\"></script>\n");
137 }
138
139 public static void InsertPeriodicUpdaters(ref StringBuilder o, string[] divID, int[] seconds, string[] reportfrag)
140 {
141 o.Append("<script type=\"text/javascript\">\n");
142 o.Append(
143 @"
144 // <![CDATA[
145 document.observe('dom:loaded', function() {
146 /*
147 first arg : div to update
148 second arg : interval to poll in seconds
149 third arg : file to get data
150 */
151");
152 for (int i = 0; i < divID.Length; i++)
153 {
154
155 o.Append("new updater('");
156 o.Append(divID[i]);
157 o.Append("', ");
158 o.Append(seconds[i]);
159 o.Append(", '");
160 o.Append(reportfrag[i]);
161 o.Append("');\n");
162 }
163
164 o.Append(@"
165 });
166 // ]]>
167 </script>");
168
169 }
170
171 public static void HtmlHeaders_O ( ref StringBuilder o)
172 {
173 o.Append("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n");
174 o.Append("<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"nl\">");
175 o.Append("<head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\" />");
176
177
178 }
179 public static void HtmlHeaders_C ( ref StringBuilder o)
180 {
181 o.Append("</HEAD>");
182 o.Append("<BODY>");
183 }
184 }
185}
diff --git a/OpenSim/Region/UserStatistics/IStatsReport.cs b/OpenSim/Region/UserStatistics/IStatsReport.cs
new file mode 100644
index 0000000..4295c82
--- /dev/null
+++ b/OpenSim/Region/UserStatistics/IStatsReport.cs
@@ -0,0 +1,10 @@
1using System.Collections;
2
3namespace OpenSim.Region.UserStatistics
4{
5 public interface IStatsController
6 {
7 Hashtable ProcessModel(Hashtable pParams);
8 string RenderView(Hashtable pModelResult);
9 }
10}
diff --git a/OpenSim/Region/UserStatistics/Prototype_distributor.cs b/OpenSim/Region/UserStatistics/Prototype_distributor.cs
new file mode 100644
index 0000000..8a7a18f
--- /dev/null
+++ b/OpenSim/Region/UserStatistics/Prototype_distributor.cs
@@ -0,0 +1,33 @@
1using System;
2using System.IO;
3using System.Collections;
4using System.Collections.Generic;
5using System.Text;
6using OpenSim.Framework;
7
8namespace OpenSim.Region.UserStatistics
9{
10 public class Prototype_distributor : IStatsController
11 {
12 private string prototypejs=string.Empty;
13
14
15 public Hashtable ProcessModel(Hashtable pParams)
16 {
17 Hashtable pResult = new Hashtable();
18 if (prototypejs.Length == 0)
19 {
20 StreamReader fs = new StreamReader(new FileStream(Util.dataDir() + "/data/prototype.js", FileMode.Open));
21 prototypejs = fs.ReadToEnd();
22 }
23 pResult["js"] = prototypejs;
24 return pResult;
25 }
26
27 public string RenderView(Hashtable pModelResult)
28 {
29 return pModelResult["js"].ToString();
30 }
31
32 }
33}
diff --git a/OpenSim/Region/UserStatistics/SimStatsAJAX.cs b/OpenSim/Region/UserStatistics/SimStatsAJAX.cs
new file mode 100644
index 0000000..f6dd1d6
--- /dev/null
+++ b/OpenSim/Region/UserStatistics/SimStatsAJAX.cs
@@ -0,0 +1,190 @@
1using System;
2using System.Collections;
3using System.Collections.Generic;
4using System.Reflection;
5using System.Text;
6using Mono.Data.SqliteClient;
7using OpenMetaverse;
8using OpenSim.Region.Environment.Scenes;
9using OpenSim.Framework.Statistics;
10
11namespace OpenSim.Region.UserStatistics
12{
13 public class SimStatsAJAX : IStatsController
14 {
15 #region IStatsController Members
16
17 public Hashtable ProcessModel(Hashtable pParams)
18 {
19 List<Scene> m_scene = (List<Scene>)pParams["Scenes"];
20
21 Hashtable nh = new Hashtable();
22 nh.Add("hdata", m_scene);
23 nh.Add("simstats", pParams["SimStats"]);
24
25 return nh;
26 }
27
28 public string RenderView(Hashtable pModelResult)
29 {
30 StringBuilder output = new StringBuilder();
31 List<Scene> all_scenes = (List<Scene>) pModelResult["hdata"];
32 Dictionary<UUID, USimStatsData> sdatadic = (Dictionary<UUID,USimStatsData>)pModelResult["simstats"];
33
34 const string TableClass = "defaultr";
35 const string TRClass = "defaultr";
36 const string TDHeaderClass = "header";
37 const string TDDataClass = "content";
38 //const string TDDataClassRight = "contentright";
39 const string TDDataClassCenter = "contentcenter";
40
41 foreach (USimStatsData sdata in sdatadic.Values)
42 {
43
44
45 foreach (Scene sn in all_scenes)
46 {
47 if (sn.RegionInfo.RegionID == sdata.RegionId)
48 {
49 output.Append("<H2>");
50 output.Append(sn.RegionInfo.RegionName);
51 output.Append("</H2>");
52 }
53 }
54 HTMLUtil.TABLE_O(ref output, TableClass);
55 HTMLUtil.TR_O(ref output, TRClass);
56 HTMLUtil.TD_O(ref output, TDHeaderClass);
57 output.Append("Dilatn");
58 HTMLUtil.TD_C(ref output);
59 HTMLUtil.TD_O(ref output, TDHeaderClass);
60 output.Append("SimFPS");
61 HTMLUtil.TD_C(ref output);
62 HTMLUtil.TD_O(ref output, TDHeaderClass);
63 output.Append("PhysFPS");
64 HTMLUtil.TD_C(ref output);
65 HTMLUtil.TD_O(ref output, TDHeaderClass);
66 output.Append("AgntUp");
67 HTMLUtil.TD_C(ref output);
68 HTMLUtil.TD_O(ref output, TDHeaderClass);
69 output.Append("RootAg");
70 HTMLUtil.TD_C(ref output);
71 HTMLUtil.TD_O(ref output, TDHeaderClass);
72 output.Append("ChldAg");
73 HTMLUtil.TD_C(ref output);
74 HTMLUtil.TD_O(ref output, TDHeaderClass);
75 output.Append("Prims");
76 HTMLUtil.TD_C(ref output);
77 HTMLUtil.TD_O(ref output, TDHeaderClass);
78 output.Append("ATvPrm");
79 HTMLUtil.TD_C(ref output);
80 HTMLUtil.TD_O(ref output, TDHeaderClass);
81 output.Append("AtvScr");
82 HTMLUtil.TD_C(ref output);
83 HTMLUtil.TD_O(ref output, TDHeaderClass);
84 output.Append("ScrLPS");
85 HTMLUtil.TD_C(ref output);
86 HTMLUtil.TR_C(ref output);
87 HTMLUtil.TR_O(ref output, TRClass);
88 HTMLUtil.TD_O(ref output, TDDataClass);
89 output.Append(sdata.TimeDilation);
90 HTMLUtil.TD_C(ref output);
91 HTMLUtil.TD_O(ref output, TDDataClass);
92 output.Append(sdata.SimFps);
93 HTMLUtil.TD_C(ref output);
94 HTMLUtil.TD_O(ref output, TDDataClassCenter);
95 output.Append(sdata.PhysicsFps);
96 HTMLUtil.TD_C(ref output);
97 HTMLUtil.TD_O(ref output, TDDataClassCenter);
98 output.Append(sdata.AgentUpdates);
99 HTMLUtil.TD_C(ref output);
100 HTMLUtil.TD_O(ref output, TDDataClassCenter);
101 output.Append(sdata.RootAgents);
102 HTMLUtil.TD_C(ref output);
103 HTMLUtil.TD_O(ref output, TDDataClassCenter);
104 output.Append(sdata.ChildAgents);
105 HTMLUtil.TD_C(ref output);
106 HTMLUtil.TD_O(ref output, TDDataClassCenter);
107 output.Append(sdata.TotalPrims);
108 HTMLUtil.TD_C(ref output);
109 HTMLUtil.TD_O(ref output, TDDataClassCenter);
110 output.Append(sdata.ActivePrims);
111 HTMLUtil.TD_C(ref output);
112 HTMLUtil.TD_O(ref output, TDDataClassCenter);
113 output.Append(sdata.ActiveScripts);
114 HTMLUtil.TD_C(ref output);
115 HTMLUtil.TD_O(ref output, TDDataClassCenter);
116 output.Append(sdata.ScriptLinesPerSecond);
117 HTMLUtil.TD_C(ref output);
118 HTMLUtil.TR_C(ref output);
119 HTMLUtil.TR_O(ref output, TRClass);
120 HTMLUtil.TD_O(ref output, TDHeaderClass);
121 output.Append("FrmMS");
122 HTMLUtil.TD_C(ref output);
123 HTMLUtil.TD_O(ref output, TDHeaderClass);
124 output.Append("AgtMS");
125 HTMLUtil.TD_C(ref output);
126 HTMLUtil.TD_O(ref output, TDHeaderClass);
127 output.Append("PhysMS");
128 HTMLUtil.TD_C(ref output);
129 HTMLUtil.TD_O(ref output, TDHeaderClass);
130 output.Append("OthrMS");
131 HTMLUtil.TD_C(ref output);
132 HTMLUtil.TD_O(ref output, TDHeaderClass);
133 output.Append("ScrLPS");
134 HTMLUtil.TD_C(ref output);
135 HTMLUtil.TD_O(ref output, TDHeaderClass);
136 output.Append("OutPPS");
137 HTMLUtil.TD_C(ref output);
138 HTMLUtil.TD_O(ref output, TDHeaderClass);
139 output.Append("InPPS");
140 HTMLUtil.TD_C(ref output);
141 HTMLUtil.TD_O(ref output, TDHeaderClass);
142 output.Append("NoAckKB");
143 HTMLUtil.TD_C(ref output);
144 HTMLUtil.TD_O(ref output, TDHeaderClass);
145 output.Append("PndDWN");
146 HTMLUtil.TD_C(ref output);
147 HTMLUtil.TD_O(ref output, TDHeaderClass);
148 output.Append("PndUP");
149 HTMLUtil.TD_C(ref output);
150 HTMLUtil.TR_C(ref output);
151 HTMLUtil.TR_O(ref output, TRClass);
152 HTMLUtil.TD_O(ref output, TDDataClass);
153 output.Append(sdata.TotalFrameTime);
154 HTMLUtil.TD_C(ref output);
155 HTMLUtil.TD_O(ref output, TDDataClass);
156 output.Append(sdata.AgentFrameTime);
157 HTMLUtil.TD_C(ref output);
158 HTMLUtil.TD_O(ref output, TDDataClassCenter);
159 output.Append(sdata.PhysicsFrameTime);
160 HTMLUtil.TD_C(ref output);
161 HTMLUtil.TD_O(ref output, TDDataClassCenter);
162 output.Append(sdata.OtherFrameTime);
163 HTMLUtil.TD_C(ref output);
164 HTMLUtil.TD_O(ref output, TDDataClassCenter);
165 output.Append(sdata.ScriptLinesPerSecond);
166 HTMLUtil.TD_C(ref output);
167 HTMLUtil.TD_O(ref output, TDDataClassCenter);
168 output.Append(sdata.OutPacketsPerSecond);
169 HTMLUtil.TD_C(ref output);
170 HTMLUtil.TD_O(ref output, TDDataClassCenter);
171 output.Append(sdata.InPacketsPerSecond);
172 HTMLUtil.TD_C(ref output);
173 HTMLUtil.TD_O(ref output, TDDataClassCenter);
174 output.Append(sdata.UnackedBytes);
175 HTMLUtil.TD_C(ref output);
176 HTMLUtil.TD_O(ref output, TDDataClassCenter);
177 output.Append(sdata.PendingDownloads);
178 HTMLUtil.TD_C(ref output);
179 HTMLUtil.TD_O(ref output, TDDataClassCenter);
180 output.Append(sdata.PendingUploads);
181 HTMLUtil.TD_C(ref output);
182 HTMLUtil.TR_C(ref output);
183 HTMLUtil.TABLE_C(ref output);
184 }
185 return output.ToString();
186 }
187
188 #endregion
189 }
190}
diff --git a/OpenSim/Region/UserStatistics/Updater_distributor.cs b/OpenSim/Region/UserStatistics/Updater_distributor.cs
new file mode 100644
index 0000000..4d571fa
--- /dev/null
+++ b/OpenSim/Region/UserStatistics/Updater_distributor.cs
@@ -0,0 +1,35 @@
1using System;
2using System.IO;
3using System.Collections;
4using System.Collections.Generic;
5using System.Text;
6using OpenSim.Framework;
7
8namespace OpenSim.Region.UserStatistics
9{
10 public class Updater_distributor : IStatsController
11 {
12 private string updaterjs = string.Empty;
13
14
15 public Hashtable ProcessModel(Hashtable pParams)
16 {
17 Hashtable pResult = new Hashtable();
18 if (updaterjs.Length == 0)
19 {
20 StreamReader fs = new StreamReader(new FileStream(Util.dataDir() + "/data/updater.js", FileMode.Open));
21 updaterjs = fs.ReadToEnd();
22 fs.Close();
23 fs.Dispose();
24 }
25 pResult["js"] = updaterjs;
26 return pResult;
27 }
28
29 public string RenderView(Hashtable pModelResult)
30 {
31 return pModelResult["js"].ToString();
32 }
33
34 }
35} \ No newline at end of file
diff --git a/OpenSim/Region/UserStatistics/WebStatsModule.cs b/OpenSim/Region/UserStatistics/WebStatsModule.cs
new file mode 100644
index 0000000..4eb8cb7
--- /dev/null
+++ b/OpenSim/Region/UserStatistics/WebStatsModule.cs
@@ -0,0 +1,1083 @@
1using System;
2using System.Collections;
3using System.Collections.Generic;
4using System.Net; // to be used for REST-->Grid shortly
5using System.Reflection;
6using log4net;
7using Nini.Config;
8using OpenMetaverse;
9using OpenMetaverse.StructuredData;
10using OpenSim.Framework;
11using OpenSim.Framework.Servers;
12using OpenSim.Region.Environment.Interfaces;
13using OpenSim.Region.Environment.Scenes;
14using Mono.Data.SqliteClient;
15
16
17using Caps = OpenSim.Framework.Communications.Capabilities.Caps;
18
19using OSD = OpenMetaverse.StructuredData.OSD;
20using OSDMap = OpenMetaverse.StructuredData.OSDMap;
21
22namespace OpenSim.Region.UserStatistics
23{
24 public class WebStatsModule : IRegionModule
25 {
26 private static readonly ILog m_log =
27 LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
28 private static SqliteConnection dbConn;
29 private Dictionary<UUID, UserSessionID> m_sessions = new Dictionary<UUID, UserSessionID>();
30 private List<Scene> m_scene = new List<Scene>();
31 private Dictionary<string, IStatsController> reports = new Dictionary<string, IStatsController>();
32 private Dictionary<UUID, USimStatsData> m_simstatsCounters = new Dictionary<UUID, USimStatsData>();
33 private const int updateStatsMod = 6;
34 private volatile int concurrencyCounter = 0;
35 private bool enabled = false;
36
37
38 public virtual void Initialise(Scene scene, IConfigSource config)
39 {
40 IConfig cnfg;
41 try
42 {
43 cnfg = config.Configs["WebStats"];
44 enabled = cnfg.GetBoolean("enabled", false);
45
46 }
47 catch (Exception)
48 {
49 enabled = false;
50 }
51 if (!enabled)
52 {
53 return;
54 }
55
56 lock (m_scene)
57 {
58 if (m_scene.Count == 0)
59 {
60 IConfig startupConfig = config.Configs["Startup"];
61
62 dbConn = new SqliteConnection("URI=file:LocalUserStatistics.db,version=3");
63 dbConn.Open();
64 CheckAndUpdateDatabase(dbConn);
65
66 Default_Report rep = new Default_Report();
67 Prototype_distributor protodep = new Prototype_distributor();
68 Updater_distributor updatedep = new Updater_distributor();
69 ActiveConnectionsAJAX ajConnections = new ActiveConnectionsAJAX();
70 SimStatsAJAX ajSimStats = new SimStatsAJAX();
71 reports.Add("", rep);
72 reports.Add("index.aspx", rep);
73 reports.Add("prototype.js", protodep);
74 reports.Add("updater.js", updatedep);
75 reports.Add("activeconnectionsajax.ajax", ajConnections);
76 reports.Add("simstatsajax.ajax", ajSimStats);
77
78 scene.CommsManager.HttpServer.AddHTTPHandler("/SStats/", HandleStatsRequest);
79
80
81 }
82 m_scene.Add(scene);
83 m_simstatsCounters.Add(scene.RegionInfo.RegionID, new USimStatsData(scene.RegionInfo.RegionID));
84 scene.StatsReporter.OnSendStatsResult += ReceiveClassicSimStatsPacket;
85 }
86
87 }
88
89 public void ReceiveClassicSimStatsPacket(SimStats stats)
90 {
91 if (!enabled)
92 {
93 return;
94 }
95
96 try
97 {
98
99 if (concurrencyCounter > 0)
100 return;
101
102 USimStatsData ss = m_simstatsCounters[stats.RegionUUID];
103
104 if ((++ss.StatsCounter % updateStatsMod) == 0)
105 {
106 ss.ConsumeSimStats(stats);
107 }
108 }
109 catch (KeyNotFoundException)
110 {
111
112 }
113 }
114
115 public Hashtable HandleStatsRequest(Hashtable request)
116 {
117 Hashtable responsedata = new Hashtable();
118 string regpath = request["uri"].ToString();
119 int response_code = 404;
120 string contenttype = "text/html";
121
122 string strOut = string.Empty;
123
124 regpath = regpath.Remove(0, 8);
125 if (reports.ContainsKey(regpath))
126 {
127 IStatsController rep = reports[regpath];
128 Hashtable repParams = new Hashtable();
129
130 repParams["DatabaseConnection"] = dbConn;
131 repParams["Scenes"] = m_scene;
132 repParams["SimStats"] = m_simstatsCounters;
133
134 concurrencyCounter++;
135
136 strOut = rep.RenderView(rep.ProcessModel(repParams));
137
138 if (regpath.EndsWith("js"))
139 {
140 contenttype = "text/javascript";
141 }
142
143 concurrencyCounter--;
144
145 response_code = 200;
146
147 }
148 else
149 {
150 strOut = m_scene[0].CommsManager.HttpServer.GetHTTP404("");
151 }
152
153
154 responsedata["int_response_code"] = response_code;
155 responsedata["content_type"] = contenttype;
156 responsedata["keepalive"] = false;
157 responsedata["str_response_string"] = strOut;
158
159 return responsedata;
160 }
161
162
163
164 public void CheckAndUpdateDatabase(SqliteConnection db)
165 {
166 lock (db)
167 {
168 // TODO: FIXME: implement stats migrations
169 const string SQL = @"SELECT * FROM migrations LIMIT 1";
170
171 SqliteCommand cmd = new SqliteCommand(SQL, db);
172
173 try
174 {
175 cmd.ExecuteNonQuery();
176 }
177 catch (SqliteSyntaxException)
178 {
179 CreateTables(db);
180 }
181 }
182
183 }
184
185 public void CreateTables(SqliteConnection db)
186 {
187 SqliteCommand createcmd = new SqliteCommand(SQL_STATS_TABLE_CREATE, db);
188 createcmd.ExecuteNonQuery();
189
190 createcmd.CommandText = SQL_MIGRA_TABLE_CREATE;
191 createcmd.ExecuteNonQuery();
192 }
193
194
195
196 public virtual void PostInitialise()
197 {
198 if (!enabled)
199 {
200 return;
201 }
202 AddHandlers();
203 }
204
205 public virtual void Close()
206 {
207 if (!enabled)
208 {
209 return;
210 }
211 dbConn.Close();
212 dbConn.Dispose();
213 }
214
215 public virtual string Name
216 {
217 get { return "ViewerStatsModule"; }
218 }
219
220 public bool IsSharedModule
221 {
222 get { return true; }
223 }
224
225 public void OnRegisterCaps(UUID agentID, Caps caps)
226 {
227 m_log.DebugFormat("[VC]: OnRegisterCaps: agentID {0} caps {1}", agentID, caps);
228 string capsPath = "/CAPS/VS/" + UUID.Random();
229 caps.RegisterHandler("ViewerStats",
230 new RestStreamHandler("POST", capsPath,
231 delegate(string request, string path, string param,
232 OSHttpRequest httpRequest, OSHttpResponse httpResponse)
233 {
234 return ViewerStatsReport(request, path, param,
235 agentID, caps);
236 }));
237 }
238
239 public void OnDeRegisterCaps(UUID agentID, Caps caps)
240 {
241
242 }
243
244 protected virtual void AddHandlers()
245 {
246 lock (m_scene)
247 {
248 foreach (Scene scene in m_scene)
249 {
250 scene.EventManager.OnRegisterCaps += OnRegisterCaps;
251 scene.EventManager.OnDeregisterCaps += OnDeRegisterCaps;
252 scene.EventManager.OnClientClosed += OnClientClosed;
253 scene.EventManager.OnMakeRootAgent += OnMakeRootAgent;
254 scene.EventManager.OnMakeChildAgent += OnMakeChildAgent;
255 }
256 }
257 }
258
259 public void OnMakeRootAgent(ScenePresence agent)
260 {
261 UUID regionUUID = GetRegionUUIDFromHandle(agent.RegionHandle);
262
263 lock (m_sessions)
264 {
265 if (!m_sessions.ContainsKey(agent.UUID))
266 {
267 UserSessionData usd = UserSessionUtil.newUserSessionData();
268
269 UserSessionID uid = new UserSessionID();
270 uid.name_f = agent.Firstname;
271 uid.name_l = agent.Lastname;
272 uid.region_id = regionUUID;
273 uid.session_id = agent.ControllingClient.SessionId;
274 uid.session_data = usd;
275
276 m_sessions.Add(agent.UUID, uid);
277 }
278 else
279 {
280 UserSessionID uid = m_sessions[agent.UUID];
281 uid.region_id = regionUUID;
282 uid.session_id = agent.ControllingClient.SessionId;
283 m_sessions[agent.UUID] = uid;
284 }
285 }
286 }
287
288
289
290
291 public void OnMakeChildAgent(ScenePresence agent)
292 {
293 lock (m_sessions)
294 {
295 if (m_sessions.ContainsKey(agent.UUID))
296 {
297 if (m_sessions[agent.UUID].region_id == GetRegionUUIDFromHandle(agent.RegionHandle))
298 {
299 m_sessions.Remove(agent.UUID);
300 }
301 }
302 }
303 }
304
305
306 public void OnClientClosed(UUID agentID)
307 {
308 lock (m_sessions)
309 {
310 if (m_sessions.ContainsKey(agentID))
311 {
312 m_sessions.Remove(agentID);
313 }
314 }
315
316 }
317
318 public UUID GetRegionUUIDFromHandle(ulong regionhandle)
319 {
320 lock (m_scene)
321 {
322 foreach (Scene scene in m_scene)
323 {
324 if (scene.RegionInfo.RegionHandle == regionhandle)
325 return scene.RegionInfo.RegionID;
326 }
327 }
328 return UUID.Zero;
329 }
330 /// <summary>
331 /// Callback for a viewerstats cap
332 /// </summary>
333 /// <param name="request"></param>
334 /// <param name="path"></param>
335 /// <param name="param"></param>
336 /// <param name="agentID"></param>
337 /// <param name="caps"></param>
338 /// <returns></returns>
339 public string ViewerStatsReport(string request, string path, string param,
340 UUID agentID, Caps caps)
341 {
342 m_log.Debug(request);
343 UserSessionID uid;
344 UserSessionData usd;
345
346 lock (m_sessions)
347 {
348 if (!m_sessions.ContainsKey(agentID))
349 {
350 m_log.Warn("[VS]: no session for stat disclosure");
351 return string.Empty;
352 }
353 uid = m_sessions[agentID];
354 }
355
356 usd = uid.session_data;
357
358 OSD message = OSDParser.DeserializeLLSDXml(request);
359 if (message.Type != OSDType.Map)
360 return String.Empty;
361
362 OSDMap mmap = (OSDMap) message;
363 {
364 if (mmap["agent"].Type != OSDType.Map)
365 return String.Empty;
366 OSDMap agent_map = (OSDMap) mmap["agent"];
367 usd.agent_id = agentID;
368 usd.name_f = uid.name_f;
369 usd.name_l = uid.name_l;
370 usd.region_id = uid.region_id;
371 usd.a_language = agent_map["language"].AsString();
372 usd.mem_use = (float) agent_map["mem_use"].AsReal();
373 usd.meters_traveled = (float) agent_map["meters_traveled"].AsReal();
374 usd.regions_visited = agent_map["regions_visited"].AsInteger();
375 usd.run_time = (float) agent_map["run_time"].AsReal();
376 usd.start_time = (float) agent_map["start_time"].AsReal();
377 usd.client_version = agent_map["version"].AsString();
378
379 UserSessionUtil.UpdateMultiItems(ref usd, agent_map["agents_in_view"].AsInteger(),
380 (float) agent_map["ping"].AsReal(),
381 (float) agent_map["sim_fps"].AsReal(),
382 (float) agent_map["fps"].AsReal());
383
384 if (mmap["downloads"].Type != OSDType.Map)
385 return String.Empty;
386 OSDMap downloads_map = (OSDMap) mmap["downloads"];
387 usd.d_object_kb = (float) downloads_map["object_kbytes"].AsReal();
388 usd.d_texture_kb = (float) downloads_map["texture_kbytes"].AsReal();
389 usd.d_world_kb = (float) downloads_map["workd_kbytes"].AsReal();
390
391
392 usd.session_id = mmap["session_id"].AsUUID();
393
394 if (mmap["system"].Type != OSDType.Map)
395 return String.Empty;
396 OSDMap system_map = (OSDMap) mmap["system"];
397
398 usd.s_cpu = system_map["cpu"].AsString();
399 usd.s_gpu = system_map["gpu"].AsString();
400 usd.s_os = system_map["os"].AsString();
401 usd.s_ram = system_map["ram"].AsInteger();
402
403 if (mmap["stats"].Type != OSDType.Map)
404 return String.Empty;
405
406 OSDMap stats_map = (OSDMap) mmap["stats"];
407 {
408 if (mmap["failures"].Type != OSDType.Map)
409 return String.Empty;
410 OSDMap stats_failures = (OSDMap) stats_map["failures"];
411 usd.f_dropped = stats_failures["dropped"].AsInteger();
412 usd.f_failed_resends = stats_failures["failed_resends"].AsInteger();
413 usd.f_invalid = stats_failures["invalid"].AsInteger();
414 usd.f_resent = stats_failures["resent"].AsInteger();
415 usd.f_send_packet = stats_failures["send_packet"].AsInteger();
416
417 if (mmap["net"].Type != OSDType.Map)
418 return String.Empty;
419 OSDMap stats_net = (OSDMap) stats_map["net"];
420 {
421 if (mmap["in"].Type != OSDType.Map)
422 return String.Empty;
423
424 OSDMap net_in = (OSDMap) stats_net["in"];
425 usd.n_in_kb = (float) net_in["kbytes"].AsReal();
426 usd.n_in_pk = net_in["packets"].AsInteger();
427
428 if (mmap["out"].Type != OSDType.Map)
429 return String.Empty;
430 OSDMap net_out = (OSDMap) stats_net["out"];
431
432 usd.n_out_kb = (float) net_out["kbytes"].AsReal();
433 usd.n_out_pk = net_out["packets"].AsInteger();
434 }
435
436
437 }
438 }
439
440 uid.session_data = usd;
441 m_sessions[agentID] = uid;
442 UpdateUserStats(uid, dbConn);
443
444 return String.Empty;
445 }
446
447 public void UpdateUserStats(UserSessionID uid, SqliteConnection db)
448 {
449 lock (db)
450 {
451 SqliteCommand updatecmd = new SqliteCommand(SQL_STATS_TABLE_UPDATE, db);
452 updatecmd.Parameters.Add(new SqliteParameter(":session_id", uid.session_data.session_id.ToString()));
453 updatecmd.Parameters.Add(new SqliteParameter(":agent_id", uid.session_data.agent_id.ToString()));
454 updatecmd.Parameters.Add(new SqliteParameter(":region_id", uid.session_data.region_id.ToString()));
455 updatecmd.Parameters.Add(new SqliteParameter(":last_updated", (int) uid.session_data.last_updated));
456 updatecmd.Parameters.Add(new SqliteParameter(":remote_ip", uid.session_data.remote_ip));
457 updatecmd.Parameters.Add(new SqliteParameter(":name_f", uid.session_data.name_f));
458 updatecmd.Parameters.Add(new SqliteParameter(":name_l", uid.session_data.name_l));
459 updatecmd.Parameters.Add(new SqliteParameter(":avg_agents_in_view", uid.session_data.avg_agents_in_view));
460 updatecmd.Parameters.Add(new SqliteParameter(":min_agents_in_view",
461 (int) uid.session_data.min_agents_in_view));
462 updatecmd.Parameters.Add(new SqliteParameter(":max_agents_in_view",
463 (int) uid.session_data.max_agents_in_view));
464 updatecmd.Parameters.Add(new SqliteParameter(":mode_agents_in_view",
465 (int) uid.session_data.mode_agents_in_view));
466 updatecmd.Parameters.Add(new SqliteParameter(":avg_fps", uid.session_data.avg_fps));
467 updatecmd.Parameters.Add(new SqliteParameter(":min_fps", uid.session_data.min_fps));
468 updatecmd.Parameters.Add(new SqliteParameter(":max_fps", uid.session_data.max_fps));
469 updatecmd.Parameters.Add(new SqliteParameter(":mode_fps", uid.session_data.mode_fps));
470 updatecmd.Parameters.Add(new SqliteParameter(":a_language", uid.session_data.a_language));
471 updatecmd.Parameters.Add(new SqliteParameter(":mem_use", uid.session_data.mem_use));
472 updatecmd.Parameters.Add(new SqliteParameter(":meters_traveled", uid.session_data.meters_traveled));
473 updatecmd.Parameters.Add(new SqliteParameter(":avg_ping", uid.session_data.avg_ping));
474 updatecmd.Parameters.Add(new SqliteParameter(":min_ping", uid.session_data.min_ping));
475 updatecmd.Parameters.Add(new SqliteParameter(":max_ping", uid.session_data.max_ping));
476 updatecmd.Parameters.Add(new SqliteParameter(":mode_ping", uid.session_data.mode_ping));
477 updatecmd.Parameters.Add(new SqliteParameter(":regions_visited", uid.session_data.regions_visited));
478 updatecmd.Parameters.Add(new SqliteParameter(":run_time", uid.session_data.run_time));
479 updatecmd.Parameters.Add(new SqliteParameter(":avg_sim_fps", uid.session_data.avg_sim_fps));
480 updatecmd.Parameters.Add(new SqliteParameter(":min_sim_fps", uid.session_data.min_sim_fps));
481 updatecmd.Parameters.Add(new SqliteParameter(":max_sim_fps", uid.session_data.max_sim_fps));
482 updatecmd.Parameters.Add(new SqliteParameter(":mode_sim_fps", uid.session_data.mode_sim_fps));
483 updatecmd.Parameters.Add(new SqliteParameter(":start_time", uid.session_data.start_time));
484 updatecmd.Parameters.Add(new SqliteParameter(":client_version", uid.session_data.client_version));
485 updatecmd.Parameters.Add(new SqliteParameter(":s_cpu", uid.session_data.s_cpu));
486 updatecmd.Parameters.Add(new SqliteParameter(":s_gpu", uid.session_data.s_gpu));
487 updatecmd.Parameters.Add(new SqliteParameter(":s_os", uid.session_data.s_os));
488 updatecmd.Parameters.Add(new SqliteParameter(":s_ram", uid.session_data.s_ram));
489 updatecmd.Parameters.Add(new SqliteParameter(":d_object_kb", uid.session_data.d_object_kb));
490 updatecmd.Parameters.Add(new SqliteParameter(":d_texture_kb", uid.session_data.d_texture_kb));
491 updatecmd.Parameters.Add(new SqliteParameter(":d_world_kb", uid.session_data.d_world_kb));
492 updatecmd.Parameters.Add(new SqliteParameter(":n_in_kb", uid.session_data.n_in_kb));
493 updatecmd.Parameters.Add(new SqliteParameter(":n_in_pk", uid.session_data.n_in_pk));
494 updatecmd.Parameters.Add(new SqliteParameter(":n_out_kb", uid.session_data.n_out_kb));
495 updatecmd.Parameters.Add(new SqliteParameter(":n_out_pk", uid.session_data.n_out_pk));
496 updatecmd.Parameters.Add(new SqliteParameter(":f_dropped", uid.session_data.f_dropped));
497 updatecmd.Parameters.Add(new SqliteParameter(":f_failed_resends", uid.session_data.f_failed_resends));
498 updatecmd.Parameters.Add(new SqliteParameter(":f_invalid", uid.session_data.f_invalid));
499
500 updatecmd.Parameters.Add(new SqliteParameter(":f_off_circuit", uid.session_data.f_off_circuit));
501 updatecmd.Parameters.Add(new SqliteParameter(":f_resent", uid.session_data.f_resent));
502 updatecmd.Parameters.Add(new SqliteParameter(":f_send_packet", uid.session_data.f_send_packet));
503
504 updatecmd.Parameters.Add(new SqliteParameter(":session_key", uid.session_data.session_id.ToString()));
505 updatecmd.Parameters.Add(new SqliteParameter(":agent_key", uid.session_data.agent_id.ToString()));
506 updatecmd.Parameters.Add(new SqliteParameter(":region_key", uid.session_data.region_id.ToString()));
507 m_log.Debug("UPDATE");
508
509 int result = updatecmd.ExecuteNonQuery();
510
511 if (result == 0)
512 {
513 m_log.Debug("INSERT");
514 updatecmd.CommandText = SQL_STATS_TABLE_INSERT;
515 updatecmd.ExecuteNonQuery();
516 }
517 }
518
519 }
520
521 #region SQL
522 private const string SQL_MIGRA_TABLE_CREATE = @"create table migrations(name varchar(100), version int)";
523
524 private const string SQL_STATS_TABLE_CREATE = @"CREATE TABLE stats_session_data (
525 session_id VARCHAR(36) NOT NULL PRIMARY KEY,
526 agent_id VARCHAR(36) NOT NULL DEFAULT '',
527 region_id VARCHAR(36) NOT NULL DEFAULT '',
528 last_updated INT NOT NULL DEFAULT '0',
529 remote_ip VARCHAR(16) NOT NULL DEFAULT '',
530 name_f VARCHAR(50) NOT NULL DEFAULT '',
531 name_l VARCHAR(50) NOT NULL DEFAULT '',
532 avg_agents_in_view FLOAT NOT NULL DEFAULT '0',
533 min_agents_in_view INT NOT NULL DEFAULT '0',
534 max_agents_in_view INT NOT NULL DEFAULT '0',
535 mode_agents_in_view INT NOT NULL DEFAULT '0',
536 avg_fps FLOAT NOT NULL DEFAULT '0',
537 min_fps FLOAT NOT NULL DEFAULT '0',
538 max_fps FLOAT NOT NULL DEFAULT '0',
539 mode_fps FLOAT NOT NULL DEFAULT '0',
540 a_language VARCHAR(25) NOT NULL DEFAULT '',
541 mem_use FLOAT NOT NULL DEFAULT '0',
542 meters_traveled FLOAT NOT NULL DEFAULT '0',
543 avg_ping FLOAT NOT NULL DEFAULT '0',
544 min_ping FLOAT NOT NULL DEFAULT '0',
545 max_ping FLOAT NOT NULL DEFAULT '0',
546 mode_ping FLOAT NOT NULL DEFAULT '0',
547 regions_visited INT NOT NULL DEFAULT '0',
548 run_time FLOAT NOT NULL DEFAULT '0',
549 avg_sim_fps FLOAT NOT NULL DEFAULT '0',
550 min_sim_fps FLOAT NOT NULL DEFAULT '0',
551 max_sim_fps FLOAT NOT NULL DEFAULT '0',
552 mode_sim_fps FLOAT NOT NULL DEFAULT '0',
553 start_time FLOAT NOT NULL DEFAULT '0',
554 client_version VARCHAR(255) NOT NULL DEFAULT '',
555 s_cpu VARCHAR(255) NOT NULL DEFAULT '',
556 s_gpu VARCHAR(255) NOT NULL DEFAULT '',
557 s_os VARCHAR(2255) NOT NULL DEFAULT '',
558 s_ram INT NOT NULL DEFAULT '0',
559 d_object_kb FLOAT NOT NULL DEFAULT '0',
560 d_texture_kb FLOAT NOT NULL DEFAULT '0',
561 d_world_kb FLOAT NOT NULL DEFAULT '0',
562 n_in_kb FLOAT NOT NULL DEFAULT '0',
563 n_in_pk INT NOT NULL DEFAULT '0',
564 n_out_kb FLOAT NOT NULL DEFAULT '0',
565 n_out_pk INT NOT NULL DEFAULT '0',
566 f_dropped INT NOT NULL DEFAULT '0',
567 f_failed_resends INT NOT NULL DEFAULT '0',
568 f_invalid INT NOT NULL DEFAULT '0',
569 f_off_circuit INT NOT NULL DEFAULT '0',
570 f_resent INT NOT NULL DEFAULT '0',
571 f_send_packet INT NOT NULL DEFAULT '0'
572 );";
573
574 private const string SQL_STATS_TABLE_INSERT = @"INSERT INTO stats_session_data (
575session_id, agent_id, region_id, last_updated, remote_ip, name_f, name_l, avg_agents_in_view, min_agents_in_view, max_agents_in_view,
576mode_agents_in_view, avg_fps, min_fps, max_fps, mode_fps, a_language, mem_use, meters_traveled, avg_ping, min_ping, max_ping, mode_ping,
577regions_visited, run_time, avg_sim_fps, min_sim_fps, max_sim_fps, mode_sim_fps, start_time, client_version, s_cpu, s_gpu, s_os, s_ram,
578d_object_kb, d_texture_kb, n_in_kb, n_in_pk, n_out_kb, n_out_pk, f_dropped, f_failed_resends, f_invalid, f_invalid, f_off_circuit,
579f_resent, f_send_packet
580)
581VALUES
582(
583:session_id, :agent_id, :region_id, :last_updated, :remote_ip, :name_f, :name_l, :avg_agents_in_view, :min_agents_in_view, :max_agents_in_view,
584:mode_agents_in_view, :avg_fps, :min_fps, :max_fps, :mode_fps, :a_language, :mem_use, :meters_traveled, :avg_ping, :min_ping, :max_ping, :mode_ping,
585:regions_visited, :run_time, :avg_sim_fps, :min_sim_fps, :max_sim_fps, :mode_sim_fps, :start_time, :client_version, :s_cpu, :s_gpu, :s_os, :s_ram,
586:d_object_kb, :d_texture_kb, :n_in_kb, :n_in_pk, :n_out_kb, :n_out_pk, :f_dropped, :f_failed_resends, :f_invalid, :f_invalid, :f_off_circuit,
587:f_resent, :f_send_packet
588)
589";
590
591 private const string SQL_STATS_TABLE_UPDATE = @"
592UPDATE stats_session_data
593set session_id=:session_id,
594 agent_id=:agent_id,
595 region_id=:region_id,
596 last_updated=:last_updated,
597 remote_ip=:remote_ip,
598 name_f=:name_f,
599 name_l=:name_l,
600 avg_agents_in_view=:avg_agents_in_view,
601 min_agents_in_view=:min_agents_in_view,
602 max_agents_in_view=:max_agents_in_view,
603 mode_agents_in_view=:mode_agents_in_view,
604 avg_fps=:avg_fps,
605 min_fps=:min_fps,
606 max_fps=:max_fps,
607 mode_fps=:mode_fps,
608 a_language=:a_language,
609 mem_use=:mem_use,
610 meters_traveled=:meters_traveled,
611 avg_ping=:avg_ping,
612 min_ping=:min_ping,
613 max_ping=:max_ping,
614 mode_ping=:mode_ping,
615 regions_visited=:regions_visited,
616 run_time=:run_time,
617 avg_sim_fps=:avg_sim_fps,
618 min_sim_fps=:min_sim_fps,
619 max_sim_fps=:max_sim_fps,
620 mode_sim_fps=:mode_sim_fps,
621 start_time=:start_time,
622 client_version=:client_version,
623 s_cpu=:s_cpu,
624 s_gpu=:s_gpu,
625 s_os=:s_os,
626 s_ram=:s_ram,
627 d_object_kb=:d_object_kb,
628 d_texture_kb=:d_texture_kb,
629 d_world_kb=:d_world_kb,
630 n_in_kb=:n_in_kb,
631 n_in_pk=:n_in_pk,
632 n_out_kb=:n_out_kb,
633 n_out_pk=:n_out_pk,
634 f_dropped=:f_dropped,
635 f_failed_resends=:f_failed_resends,
636 f_invalid=:f_invalid,
637 f_off_circuit=:f_off_circuit,
638 f_resent=:f_resent,
639 f_send_packet=:f_send_packet
640WHERE session_id=:session_key AND agent_id=:agent_key AND region_id=:region_key";
641 #endregion
642 }
643 public static class UserSessionUtil
644 {
645 public static UserSessionData newUserSessionData()
646 {
647 UserSessionData obj = ZeroSession(new UserSessionData());
648 return obj;
649 }
650
651 public static void UpdateMultiItems(ref UserSessionData s, int agents_in_view, float ping, float sim_fps, float fps)
652 {
653 // don't insert zero values here or it'll skew the statistics.
654 if (agents_in_view == 0 && fps == 0 && sim_fps == 0 && ping == 0)
655 return;
656 s._agents_in_view.Add(agents_in_view);
657 s._fps.Add(fps);
658 s._sim_fps.Add(sim_fps);
659 s._ping.Add(ping);
660
661 int[] __agents_in_view = s._agents_in_view.ToArray();
662
663 s.avg_agents_in_view = ArrayAvg_i(__agents_in_view);
664 s.min_agents_in_view = ArrayMin_i(__agents_in_view);
665 s.max_agents_in_view = ArrayMax_i(__agents_in_view);
666 s.mode_agents_in_view = ArrayMode_i(__agents_in_view);
667
668 float[] __fps = s._fps.ToArray();
669 s.avg_fps = ArrayAvg_f(__fps);
670 s.min_fps = ArrayMin_f(__fps);
671 s.max_fps = ArrayMax_f(__fps);
672 s.mode_fps = ArrayMode_f(__fps);
673
674 float[] __sim_fps = s._sim_fps.ToArray();
675 s.avg_sim_fps = ArrayAvg_f(__sim_fps);
676 s.min_sim_fps = ArrayMin_f(__sim_fps);
677 s.max_sim_fps = ArrayMax_f(__sim_fps);
678 s.mode_sim_fps = ArrayMode_f(__sim_fps);
679
680 float[] __ping = s._ping.ToArray();
681 s.avg_ping = ArrayAvg_f(__ping);
682 s.min_ping = ArrayMin_f(__ping);
683 s.max_ping = ArrayMax_f(__ping);
684 s.mode_ping = ArrayMode_f(__ping);
685
686 }
687
688 #region Statistics
689
690 public static int ArrayMin_i(int[] arr)
691 {
692 int cnt = arr.Length;
693 if (cnt == 0)
694 return 0;
695
696 Array.Sort(arr);
697 return arr[0];
698 }
699
700 public static int ArrayMax_i(int[] arr)
701 {
702 int cnt = arr.Length;
703 if (cnt == 0)
704 return 0;
705
706 Array.Sort(arr);
707 return arr[cnt-1];
708 }
709
710 public static float ArrayMin_f(float[] arr)
711 {
712 int cnt = arr.Length;
713 if (cnt == 0)
714 return 0;
715
716 Array.Sort(arr);
717 return arr[0];
718 }
719
720 public static float ArrayMax_f(float[] arr)
721 {
722 int cnt = arr.Length;
723 if (cnt == 0)
724 return 0;
725
726 Array.Sort(arr);
727 return arr[cnt - 1];
728 }
729
730 public static float ArrayAvg_i(int[] arr)
731 {
732 int cnt = arr.Length;
733
734 if (cnt == 0)
735 return 0;
736
737 float result = arr[0];
738
739 for (int i = 1; i < cnt; i++)
740 result += arr[i];
741
742 return result / cnt;
743 }
744
745 public static float ArrayAvg_f(float[] arr)
746 {
747 int cnt = arr.Length;
748
749 if (cnt == 0)
750 return 0;
751
752 float result = arr[0];
753
754 for (int i = 1; i < cnt; i++)
755 result += arr[i];
756
757 return result / cnt;
758 }
759
760
761 public static float ArrayMode_f(float[] arr)
762 {
763 List<float> mode = new List<float>();
764
765 float[] srtArr = new float[arr.Length];
766 float[,] freq = new float[arr.Length, 2];
767 Array.Copy(arr, srtArr, arr.Length);
768 Array.Sort(srtArr);
769
770 float tmp = srtArr[0];
771 int index = 0;
772 int i = 0;
773 while (i < srtArr.Length)
774 {
775 freq[index, 0] = tmp;
776
777 while (tmp == srtArr[i])
778 {
779 freq[index, 1]++;
780 i++;
781
782 if (i > srtArr.Length - 1)
783 break;
784 }
785
786 if (i < srtArr.Length)
787 {
788 tmp = srtArr[i];
789 index++;
790 }
791
792 }
793
794 Array.Clear(srtArr, 0, srtArr.Length);
795
796 for (i = 0; i < srtArr.Length; i++)
797 srtArr[i] = freq[i, 1];
798
799 Array.Sort(srtArr);
800
801 if ((srtArr[srtArr.Length - 1]) == 0 || (srtArr[srtArr.Length - 1]) == 1)
802 return 0;
803
804 float freqtest = (float)freq.Length / freq.Rank;
805
806 for (i = 0; i < freqtest; i++)
807 {
808 if (freq[i, 1] == srtArr[index])
809 mode.Add(freq[i, 0]);
810
811 }
812
813 return mode.ToArray()[0];
814
815 }
816
817
818 public static int ArrayMode_i(int[] arr)
819 {
820 List<int> mode = new List<int>();
821
822 int[] srtArr = new int[arr.Length];
823 int[,] freq = new int[arr.Length, 2];
824 Array.Copy(arr, srtArr, arr.Length);
825 Array.Sort(srtArr);
826
827 int tmp = srtArr[0];
828 int index = 0;
829 int i = 0;
830 while (i < srtArr.Length)
831 {
832 freq[index, 0] = tmp;
833
834 while (tmp == srtArr[i])
835 {
836 freq[index, 1]++;
837 i++;
838
839 if (i > srtArr.Length - 1)
840 break;
841 }
842
843 if (i < srtArr.Length)
844 {
845 tmp = srtArr[i];
846 index++;
847 }
848
849 }
850
851 Array.Clear(srtArr, 0, srtArr.Length);
852
853 for (i = 0; i < srtArr.Length; i++)
854 srtArr[i] = freq[i, 1];
855
856 Array.Sort(srtArr);
857
858 if ((srtArr[srtArr.Length - 1]) == 0 || (srtArr[srtArr.Length - 1]) == 1)
859 return 0;
860
861 float freqtest = (float)freq.Length / freq.Rank;
862
863 for (i = 0; i < freqtest; i++)
864 {
865 if (freq[i, 1] == srtArr[index])
866 mode.Add(freq[i, 0]);
867
868 }
869
870 return mode.ToArray()[0];
871
872 }
873
874 #endregion
875
876 private static UserSessionData ZeroSession(UserSessionData s)
877 {
878 s.session_id = UUID.Zero;
879 s.agent_id = UUID.Zero;
880 s.region_id = UUID.Zero;
881 s.last_updated = Util.UnixTimeSinceEpoch();
882 s.remote_ip = "";
883 s.name_f = "";
884 s.name_l = "";
885 s.avg_agents_in_view = 0;
886 s.min_agents_in_view = 0;
887 s.max_agents_in_view = 0;
888 s.mode_agents_in_view = 0;
889 s.avg_fps = 0;
890 s.min_fps = 0;
891 s.max_fps = 0;
892 s.mode_fps = 0;
893 s.a_language = "";
894 s.mem_use = 0;
895 s.meters_traveled = 0;
896 s.avg_ping = 0;
897 s.min_ping = 0;
898 s.max_ping = 0;
899 s.mode_ping = 0;
900 s.regions_visited = 0;
901 s.run_time = 0;
902 s.avg_sim_fps = 0;
903 s.min_sim_fps = 0;
904 s.max_sim_fps = 0;
905 s.mode_sim_fps = 0;
906 s.start_time = 0;
907 s.client_version = "";
908 s.s_cpu = "";
909 s.s_gpu = "";
910 s.s_os = "";
911 s.s_ram = 0;
912 s.d_object_kb = 0;
913 s.d_texture_kb = 0;
914 s.d_world_kb = 0;
915 s.n_in_kb = 0;
916 s.n_in_pk = 0;
917 s.n_out_kb = 0;
918 s.n_out_pk = 0;
919 s.f_dropped = 0;
920 s.f_failed_resends = 0;
921 s.f_invalid = 0;
922 s.f_off_circuit = 0;
923 s.f_resent = 0;
924 s.f_send_packet = 0;
925 s._ping = new List<float>();
926 s._fps = new List<float>();
927 s._sim_fps = new List<float>();
928 s._agents_in_view = new List<int>();
929 return s;
930 }
931 }
932 #region structs
933
934 public struct UserSessionID
935 {
936 public UUID session_id;
937 public UUID region_id;
938 public string name_f;
939 public string name_l;
940 public UserSessionData session_data;
941 }
942
943 public struct UserSessionData
944 {
945 public UUID session_id;
946 public UUID agent_id;
947 public UUID region_id;
948 public float last_updated;
949 public string remote_ip;
950 public string name_f;
951 public string name_l;
952 public float avg_agents_in_view;
953 public float min_agents_in_view;
954 public float max_agents_in_view;
955 public float mode_agents_in_view;
956 public float avg_fps;
957 public float min_fps;
958 public float max_fps;
959 public float mode_fps;
960 public string a_language;
961 public float mem_use;
962 public float meters_traveled;
963 public float avg_ping;
964 public float min_ping;
965 public float max_ping;
966 public float mode_ping;
967 public int regions_visited;
968 public float run_time;
969 public float avg_sim_fps;
970 public float min_sim_fps;
971 public float max_sim_fps;
972 public float mode_sim_fps;
973 public float start_time;
974 public string client_version;
975 public string s_cpu;
976 public string s_gpu;
977 public string s_os;
978 public int s_ram;
979 public float d_object_kb;
980 public float d_texture_kb;
981 public float d_world_kb;
982 public float n_in_kb;
983 public int n_in_pk;
984 public float n_out_kb;
985 public int n_out_pk;
986 public int f_dropped;
987 public int f_failed_resends;
988 public int f_invalid;
989 public int f_off_circuit;
990 public int f_resent;
991 public int f_send_packet;
992 public List<float> _ping;
993 public List<float> _fps;
994 public List<float> _sim_fps;
995 public List<int> _agents_in_view;
996 }
997
998
999 #endregion
1000
1001 public class USimStatsData
1002 {
1003 private UUID m_regionID = UUID.Zero;
1004 private volatile int m_statcounter = 0;
1005 private volatile float m_timeDilation;
1006 private volatile float m_simFps;
1007 private volatile float m_physicsFps;
1008 private volatile float m_agentUpdates;
1009 private volatile float m_rootAgents;
1010 private volatile float m_childAgents;
1011 private volatile float m_totalPrims;
1012 private volatile float m_activePrims;
1013 private volatile float m_totalFrameTime;
1014 private volatile float m_netFrameTime;
1015 private volatile float m_physicsFrameTime;
1016 private volatile float m_otherFrameTime;
1017 private volatile float m_imageFrameTime;
1018 private volatile float m_inPacketsPerSecond;
1019 private volatile float m_outPacketsPerSecond;
1020 private volatile float m_unackedBytes;
1021 private volatile float m_agentFrameTime;
1022 private volatile float m_pendingDownloads;
1023 private volatile float m_pendingUploads;
1024 private volatile float m_activeScripts;
1025 private volatile float m_scriptLinesPerSecond;
1026
1027 public UUID RegionId { get { return m_regionID; } }
1028 public int StatsCounter { get { return m_statcounter; } set { m_statcounter = value;}}
1029 public float TimeDilation { get { return m_timeDilation; } }
1030 public float SimFps { get { return m_simFps; } }
1031 public float PhysicsFps { get { return m_physicsFps; } }
1032 public float AgentUpdates { get { return m_agentUpdates; } }
1033 public float RootAgents { get { return m_rootAgents; } }
1034 public float ChildAgents { get { return m_childAgents; } }
1035 public float TotalPrims { get { return m_totalPrims; } }
1036 public float ActivePrims { get { return m_activePrims; } }
1037 public float TotalFrameTime { get { return m_totalFrameTime; } }
1038 public float NetFrameTime { get { return m_netFrameTime; } }
1039 public float PhysicsFrameTime { get { return m_physicsFrameTime; } }
1040 public float OtherFrameTime { get { return m_otherFrameTime; } }
1041 public float ImageFrameTime { get { return m_imageFrameTime; } }
1042 public float InPacketsPerSecond { get { return m_inPacketsPerSecond; } }
1043 public float OutPacketsPerSecond { get { return m_outPacketsPerSecond; } }
1044 public float UnackedBytes { get { return m_unackedBytes; } }
1045 public float AgentFrameTime { get { return m_agentFrameTime; } }
1046 public float PendingDownloads { get { return m_pendingDownloads; } }
1047 public float PendingUploads { get { return m_pendingUploads; } }
1048 public float ActiveScripts { get { return m_activeScripts; } }
1049 public float ScriptLinesPerSecond { get { return m_scriptLinesPerSecond; } }
1050
1051 public USimStatsData(UUID pRegionID)
1052 {
1053 m_regionID = pRegionID;
1054 }
1055
1056 public void ConsumeSimStats(SimStats stats)
1057 {
1058 m_regionID = stats.RegionUUID;
1059 m_timeDilation = stats.StatsBlock[0].StatValue;
1060 m_simFps = stats.StatsBlock[1].StatValue;
1061 m_physicsFps = stats.StatsBlock[2].StatValue;
1062 m_agentUpdates = stats.StatsBlock[3].StatValue;
1063 m_rootAgents = stats.StatsBlock[4].StatValue;
1064 m_childAgents = stats.StatsBlock[5].StatValue;
1065 m_totalPrims = stats.StatsBlock[6].StatValue;
1066 m_activePrims = stats.StatsBlock[7].StatValue;
1067 m_totalFrameTime = stats.StatsBlock[8].StatValue;
1068 m_netFrameTime = stats.StatsBlock[9].StatValue;
1069 m_physicsFrameTime = stats.StatsBlock[10].StatValue;
1070 m_otherFrameTime = stats.StatsBlock[11].StatValue;
1071 m_imageFrameTime = stats.StatsBlock[12].StatValue;
1072 m_inPacketsPerSecond = stats.StatsBlock[13].StatValue;
1073 m_outPacketsPerSecond = stats.StatsBlock[14].StatValue;
1074 m_unackedBytes = stats.StatsBlock[15].StatValue;
1075 m_agentFrameTime = stats.StatsBlock[16].StatValue;
1076 m_pendingDownloads = stats.StatsBlock[17].StatValue;
1077 m_pendingUploads = stats.StatsBlock[18].StatValue;
1078 m_activeScripts = stats.StatsBlock[19].StatValue;
1079 m_scriptLinesPerSecond = stats.StatsBlock[20].StatValue;
1080 }
1081 }
1082
1083}
diff --git a/ThirdPartyLicenses/Prototype.txt b/ThirdPartyLicenses/Prototype.txt
new file mode 100644
index 0000000..61e4918
--- /dev/null
+++ b/ThirdPartyLicenses/Prototype.txt
@@ -0,0 +1,16 @@
1Copyright (c) 2005-2008 Sam Stephenson
2
3Permission is hereby granted, free of charge, to any person obtaining a copy
4of this software and associated documentation files (the "Software"), to deal
5in the Software without restriction, including without limitation the rights
6to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7copies of the Software, and to permit persons to whom the Software is
8furnished to do so, subject to the following conditions:
9
10THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
11IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
13AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
14LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
15OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
16SOFTWARE.
diff --git a/bin/data/prototype.js b/bin/data/prototype.js
new file mode 100644
index 0000000..6765129
--- /dev/null
+++ b/bin/data/prototype.js
@@ -0,0 +1,4222 @@
1/* Prototype JavaScript framework, version 1.6.0.2
2 * (c) 2005-2008 Sam Stephenson
3 *
4 * Prototype is freely distributable under the terms of an MIT-style license.
5 * For details, see the Prototype web site: http://www.prototypejs.org/
6 *
7 *--------------------------------------------------------------------------*/
8
9var Prototype = {
10 Version: '1.6.0.2',
11
12 Browser: {
13 IE: !!(window.attachEvent && !window.opera),
14 Opera: !!window.opera,
15 WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
16 Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,
17 MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
18 },
19
20 BrowserFeatures: {
21 XPath: !!document.evaluate,
22 ElementExtensions: !!window.HTMLElement,
23 SpecificElementExtensions:
24 document.createElement('div').__proto__ &&
25 document.createElement('div').__proto__ !==
26 document.createElement('form').__proto__
27 },
28
29 ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
30 JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
31
32 emptyFunction: function() { },
33 K: function(x) { return x }
34};
35
36if (Prototype.Browser.MobileSafari)
37 Prototype.BrowserFeatures.SpecificElementExtensions = false;
38
39
40/* Based on Alex Arnell's inheritance implementation. */
41var Class = {
42 create: function() {
43 var parent = null, properties = $A(arguments);
44 if (Object.isFunction(properties[0]))
45 parent = properties.shift();
46
47 function klass() {
48 this.initialize.apply(this, arguments);
49 }
50
51 Object.extend(klass, Class.Methods);
52 klass.superclass = parent;
53 klass.subclasses = [];
54
55 if (parent) {
56 var subclass = function() { };
57 subclass.prototype = parent.prototype;
58 klass.prototype = new subclass;
59 parent.subclasses.push(klass);
60 }
61
62 for (var i = 0; i < properties.length; i++)
63 klass.addMethods(properties[i]);
64
65 if (!klass.prototype.initialize)
66 klass.prototype.initialize = Prototype.emptyFunction;
67
68 klass.prototype.constructor = klass;
69
70 return klass;
71 }
72};
73
74Class.Methods = {
75 addMethods: function(source) {
76 var ancestor = this.superclass && this.superclass.prototype;
77 var properties = Object.keys(source);
78
79 if (!Object.keys({ toString: true }).length)
80 properties.push("toString", "valueOf");
81
82 for (var i = 0, length = properties.length; i < length; i++) {
83 var property = properties[i], value = source[property];
84 if (ancestor && Object.isFunction(value) &&
85 value.argumentNames().first() == "$super") {
86 var method = value, value = Object.extend((function(m) {
87 return function() { return ancestor[m].apply(this, arguments) };
88 })(property).wrap(method), {
89 valueOf: function() { return method },
90 toString: function() { return method.toString() }
91 });
92 }
93 this.prototype[property] = value;
94 }
95
96 return this;
97 }
98};
99
100var Abstract = { };
101
102Object.extend = function(destination, source) {
103 for (var property in source)
104 destination[property] = source[property];
105 return destination;
106};
107
108Object.extend(Object, {
109 inspect: function(object) {
110 try {
111 if (Object.isUndefined(object)) return 'undefined';
112 if (object === null) return 'null';
113 return object.inspect ? object.inspect() : String(object);
114 } catch (e) {
115 if (e instanceof RangeError) return '...';
116 throw e;
117 }
118 },
119
120 toJSON: function(object) {
121 var type = typeof object;
122 switch (type) {
123 case 'undefined':
124 case 'function':
125 case 'unknown': return;
126 case 'boolean': return object.toString();
127 }
128
129 if (object === null) return 'null';
130 if (object.toJSON) return object.toJSON();
131 if (Object.isElement(object)) return;
132
133 var results = [];
134 for (var property in object) {
135 var value = Object.toJSON(object[property]);
136 if (!Object.isUndefined(value))
137 results.push(property.toJSON() + ': ' + value);
138 }
139
140 return '{' + results.join(', ') + '}';
141 },
142
143 toQueryString: function(object) {
144 return $H(object).toQueryString();
145 },
146
147 toHTML: function(object) {
148 return object && object.toHTML ? object.toHTML() : String.interpret(object);
149 },
150
151 keys: function(object) {
152 var keys = [];
153 for (var property in object)
154 keys.push(property);
155 return keys;
156 },
157
158 values: function(object) {
159 var values = [];
160 for (var property in object)
161 values.push(object[property]);
162 return values;
163 },
164
165 clone: function(object) {
166 return Object.extend({ }, object);
167 },
168
169 isElement: function(object) {
170 return object && object.nodeType == 1;
171 },
172
173 isArray: function(object) {
174 return object != null && typeof object == "object" &&
175 'splice' in object && 'join' in object;
176 },
177
178 isHash: function(object) {
179 return object instanceof Hash;
180 },
181
182 isFunction: function(object) {
183 return typeof object == "function";
184 },
185
186 isString: function(object) {
187 return typeof object == "string";
188 },
189
190 isNumber: function(object) {
191 return typeof object == "number";
192 },
193
194 isUndefined: function(object) {
195 return typeof object == "undefined";
196 }
197});
198
199Object.extend(Function.prototype, {
200 argumentNames: function() {
201 var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip");
202 return names.length == 1 && !names[0] ? [] : names;
203 },
204
205 bind: function() {
206 if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
207 var __method = this, args = $A(arguments), object = args.shift();
208 return function() {
209 return __method.apply(object, args.concat($A(arguments)));
210 }
211 },
212
213 bindAsEventListener: function() {
214 var __method = this, args = $A(arguments), object = args.shift();
215 return function(event) {
216 return __method.apply(object, [event || window.event].concat(args));
217 }
218 },
219
220 curry: function() {
221 if (!arguments.length) return this;
222 var __method = this, args = $A(arguments);
223 return function() {
224 return __method.apply(this, args.concat($A(arguments)));
225 }
226 },
227
228 delay: function() {
229 var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
230 return window.setTimeout(function() {
231 return __method.apply(__method, args);
232 }, timeout);
233 },
234
235 wrap: function(wrapper) {
236 var __method = this;
237 return function() {
238 return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
239 }
240 },
241
242 methodize: function() {
243 if (this._methodized) return this._methodized;
244 var __method = this;
245 return this._methodized = function() {
246 return __method.apply(null, [this].concat($A(arguments)));
247 };
248 }
249});
250
251Function.prototype.defer = Function.prototype.delay.curry(0.01);
252
253Date.prototype.toJSON = function() {
254 return '"' + this.getUTCFullYear() + '-' +
255 (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
256 this.getUTCDate().toPaddedString(2) + 'T' +
257 this.getUTCHours().toPaddedString(2) + ':' +
258 this.getUTCMinutes().toPaddedString(2) + ':' +
259 this.getUTCSeconds().toPaddedString(2) + 'Z"';
260};
261
262var Try = {
263 these: function() {
264 var returnValue;
265
266 for (var i = 0, length = arguments.length; i < length; i++) {
267 var lambda = arguments[i];
268 try {
269 returnValue = lambda();
270 break;
271 } catch (e) { }
272 }
273
274 return returnValue;
275 }
276};
277
278RegExp.prototype.match = RegExp.prototype.test;
279
280RegExp.escape = function(str) {
281 return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
282};
283
284/*--------------------------------------------------------------------------*/
285
286var PeriodicalExecuter = Class.create({
287 initialize: function(callback, frequency) {
288 this.callback = callback;
289 this.frequency = frequency;
290 this.currentlyExecuting = false;
291
292 this.registerCallback();
293 },
294
295 registerCallback: function() {
296 this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
297 },
298
299 execute: function() {
300 this.callback(this);
301 },
302
303 stop: function() {
304 if (!this.timer) return;
305 clearInterval(this.timer);
306 this.timer = null;
307 },
308
309 onTimerEvent: function() {
310 if (!this.currentlyExecuting) {
311 try {
312 this.currentlyExecuting = true;
313 this.execute();
314 } finally {
315 this.currentlyExecuting = false;
316 }
317 }
318 }
319});
320Object.extend(String, {
321 interpret: function(value) {
322 return value == null ? '' : String(value);
323 },
324 specialChar: {
325 '\b': '\\b',
326 '\t': '\\t',
327 '\n': '\\n',
328 '\f': '\\f',
329 '\r': '\\r',
330 '\\': '\\\\'
331 }
332});
333
334Object.extend(String.prototype, {
335 gsub: function(pattern, replacement) {
336 var result = '', source = this, match;
337 replacement = arguments.callee.prepareReplacement(replacement);
338
339 while (source.length > 0) {
340 if (match = source.match(pattern)) {
341 result += source.slice(0, match.index);
342 result += String.interpret(replacement(match));
343 source = source.slice(match.index + match[0].length);
344 } else {
345 result += source, source = '';
346 }
347 }
348 return result;
349 },
350
351 sub: function(pattern, replacement, count) {
352 replacement = this.gsub.prepareReplacement(replacement);
353 count = Object.isUndefined(count) ? 1 : count;
354
355 return this.gsub(pattern, function(match) {
356 if (--count < 0) return match[0];
357 return replacement(match);
358 });
359 },
360
361 scan: function(pattern, iterator) {
362 this.gsub(pattern, iterator);
363 return String(this);
364 },
365
366 truncate: function(length, truncation) {
367 length = length || 30;
368 truncation = Object.isUndefined(truncation) ? '...' : truncation;
369 return this.length > length ?
370 this.slice(0, length - truncation.length) + truncation : String(this);
371 },
372
373 strip: function() {
374 return this.replace(/^\s+/, '').replace(/\s+$/, '');
375 },
376
377 stripTags: function() {
378 return this.replace(/<\/?[^>]+>/gi, '');
379 },
380
381 stripScripts: function() {
382 return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
383 },
384
385 extractScripts: function() {
386 var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
387 var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
388 return (this.match(matchAll) || []).map(function(scriptTag) {
389 return (scriptTag.match(matchOne) || ['', ''])[1];
390 });
391 },
392
393 evalScripts: function() {
394 return this.extractScripts().map(function(script) { return eval(script) });
395 },
396
397 escapeHTML: function() {
398 var self = arguments.callee;
399 self.text.data = this;
400 return self.div.innerHTML;
401 },
402
403 unescapeHTML: function() {
404 var div = new Element('div');
405 div.innerHTML = this.stripTags();
406 return div.childNodes[0] ? (div.childNodes.length > 1 ?
407 $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
408 div.childNodes[0].nodeValue) : '';
409 },
410
411 toQueryParams: function(separator) {
412 var match = this.strip().match(/([^?#]*)(#.*)?$/);
413 if (!match) return { };
414
415 return match[1].split(separator || '&').inject({ }, function(hash, pair) {
416 if ((pair = pair.split('='))[0]) {
417 var key = decodeURIComponent(pair.shift());
418 var value = pair.length > 1 ? pair.join('=') : pair[0];
419 if (value != undefined) value = decodeURIComponent(value);
420
421 if (key in hash) {
422 if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
423 hash[key].push(value);
424 }
425 else hash[key] = value;
426 }
427 return hash;
428 });
429 },
430
431 toArray: function() {
432 return this.split('');
433 },
434
435 succ: function() {
436 return this.slice(0, this.length - 1) +
437 String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
438 },
439
440 times: function(count) {
441 return count < 1 ? '' : new Array(count + 1).join(this);
442 },
443
444 camelize: function() {
445 var parts = this.split('-'), len = parts.length;
446 if (len == 1) return parts[0];
447
448 var camelized = this.charAt(0) == '-'
449 ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
450 : parts[0];
451
452 for (var i = 1; i < len; i++)
453 camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
454
455 return camelized;
456 },
457
458 capitalize: function() {
459 return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
460 },
461
462 underscore: function() {
463 return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
464 },
465
466 dasherize: function() {
467 return this.gsub(/_/,'-');
468 },
469
470 inspect: function(useDoubleQuotes) {
471 var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
472 var character = String.specialChar[match[0]];
473 return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
474 });
475 if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
476 return "'" + escapedString.replace(/'/g, '\\\'') + "'";
477 },
478
479 toJSON: function() {
480 return this.inspect(true);
481 },
482
483 unfilterJSON: function(filter) {
484 return this.sub(filter || Prototype.JSONFilter, '#{1}');
485 },
486
487 isJSON: function() {
488 var str = this;
489 if (str.blank()) return false;
490 str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
491 return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
492 },
493
494 evalJSON: function(sanitize) {
495 var json = this.unfilterJSON();
496 try {
497 if (!sanitize || json.isJSON()) return eval('(' + json + ')');
498 } catch (e) { }
499 throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
500 },
501
502 include: function(pattern) {
503 return this.indexOf(pattern) > -1;
504 },
505
506 startsWith: function(pattern) {
507 return this.indexOf(pattern) === 0;
508 },
509
510 endsWith: function(pattern) {
511 var d = this.length - pattern.length;
512 return d >= 0 && this.lastIndexOf(pattern) === d;
513 },
514
515 empty: function() {
516 return this == '';
517 },
518
519 blank: function() {
520 return /^\s*$/.test(this);
521 },
522
523 interpolate: function(object, pattern) {
524 return new Template(this, pattern).evaluate(object);
525 }
526});
527
528if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
529 escapeHTML: function() {
530 return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
531 },
532 unescapeHTML: function() {
533 return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
534 }
535});
536
537String.prototype.gsub.prepareReplacement = function(replacement) {
538 if (Object.isFunction(replacement)) return replacement;
539 var template = new Template(replacement);
540 return function(match) { return template.evaluate(match) };
541};
542
543String.prototype.parseQuery = String.prototype.toQueryParams;
544
545Object.extend(String.prototype.escapeHTML, {
546 div: document.createElement('div'),
547 text: document.createTextNode('')
548});
549
550with (String.prototype.escapeHTML) div.appendChild(text);
551
552var Template = Class.create({
553 initialize: function(template, pattern) {
554 this.template = template.toString();
555 this.pattern = pattern || Template.Pattern;
556 },
557
558 evaluate: function(object) {
559 if (Object.isFunction(object.toTemplateReplacements))
560 object = object.toTemplateReplacements();
561
562 return this.template.gsub(this.pattern, function(match) {
563 if (object == null) return '';
564
565 var before = match[1] || '';
566 if (before == '\\') return match[2];
567
568 var ctx = object, expr = match[3];
569 var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
570 match = pattern.exec(expr);
571 if (match == null) return before;
572
573 while (match != null) {
574 var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1];
575 ctx = ctx[comp];
576 if (null == ctx || '' == match[3]) break;
577 expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
578 match = pattern.exec(expr);
579 }
580
581 return before + String.interpret(ctx);
582 });
583 }
584});
585Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
586
587var $break = { };
588
589var Enumerable = {
590 each: function(iterator, context) {
591 var index = 0;
592 iterator = iterator.bind(context);
593 try {
594 this._each(function(value) {
595 iterator(value, index++);
596 });
597 } catch (e) {
598 if (e != $break) throw e;
599 }
600 return this;
601 },
602
603 eachSlice: function(number, iterator, context) {
604 iterator = iterator ? iterator.bind(context) : Prototype.K;
605 var index = -number, slices = [], array = this.toArray();
606 while ((index += number) < array.length)
607 slices.push(array.slice(index, index+number));
608 return slices.collect(iterator, context);
609 },
610
611 all: function(iterator, context) {
612 iterator = iterator ? iterator.bind(context) : Prototype.K;
613 var result = true;
614 this.each(function(value, index) {
615 result = result && !!iterator(value, index);
616 if (!result) throw $break;
617 });
618 return result;
619 },
620
621 any: function(iterator, context) {
622 iterator = iterator ? iterator.bind(context) : Prototype.K;
623 var result = false;
624 this.each(function(value, index) {
625 if (result = !!iterator(value, index))
626 throw $break;
627 });
628 return result;
629 },
630
631 collect: function(iterator, context) {
632 iterator = iterator ? iterator.bind(context) : Prototype.K;
633 var results = [];
634 this.each(function(value, index) {
635 results.push(iterator(value, index));
636 });
637 return results;
638 },
639
640 detect: function(iterator, context) {
641 iterator = iterator.bind(context);
642 var result;
643 this.each(function(value, index) {
644 if (iterator(value, index)) {
645 result = value;
646 throw $break;
647 }
648 });
649 return result;
650 },
651
652 findAll: function(iterator, context) {
653 iterator = iterator.bind(context);
654 var results = [];
655 this.each(function(value, index) {
656 if (iterator(value, index))
657 results.push(value);
658 });
659 return results;
660 },
661
662 grep: function(filter, iterator, context) {
663 iterator = iterator ? iterator.bind(context) : Prototype.K;
664 var results = [];
665
666 if (Object.isString(filter))
667 filter = new RegExp(filter);
668
669 this.each(function(value, index) {
670 if (filter.match(value))
671 results.push(iterator(value, index));
672 });
673 return results;
674 },
675
676 include: function(object) {
677 if (Object.isFunction(this.indexOf))
678 if (this.indexOf(object) != -1) return true;
679
680 var found = false;
681 this.each(function(value) {
682 if (value == object) {
683 found = true;
684 throw $break;
685 }
686 });
687 return found;
688 },
689
690 inGroupsOf: function(number, fillWith) {
691 fillWith = Object.isUndefined(fillWith) ? null : fillWith;
692 return this.eachSlice(number, function(slice) {
693 while(slice.length < number) slice.push(fillWith);
694 return slice;
695 });
696 },
697
698 inject: function(memo, iterator, context) {
699 iterator = iterator.bind(context);
700 this.each(function(value, index) {
701 memo = iterator(memo, value, index);
702 });
703 return memo;
704 },
705
706 invoke: function(method) {
707 var args = $A(arguments).slice(1);
708 return this.map(function(value) {
709 return value[method].apply(value, args);
710 });
711 },
712
713 max: function(iterator, context) {
714 iterator = iterator ? iterator.bind(context) : Prototype.K;
715 var result;
716 this.each(function(value, index) {
717 value = iterator(value, index);
718 if (result == null || value >= result)
719 result = value;
720 });
721 return result;
722 },
723
724 min: function(iterator, context) {
725 iterator = iterator ? iterator.bind(context) : Prototype.K;
726 var result;
727 this.each(function(value, index) {
728 value = iterator(value, index);
729 if (result == null || value < result)
730 result = value;
731 });
732 return result;
733 },
734
735 partition: function(iterator, context) {
736 iterator = iterator ? iterator.bind(context) : Prototype.K;
737 var trues = [], falses = [];
738 this.each(function(value, index) {
739 (iterator(value, index) ?
740 trues : falses).push(value);
741 });
742 return [trues, falses];
743 },
744
745 pluck: function(property) {
746 var results = [];
747 this.each(function(value) {
748 results.push(value[property]);
749 });
750 return results;
751 },
752
753 reject: function(iterator, context) {
754 iterator = iterator.bind(context);
755 var results = [];
756 this.each(function(value, index) {
757 if (!iterator(value, index))
758 results.push(value);
759 });
760 return results;
761 },
762
763 sortBy: function(iterator, context) {
764 iterator = iterator.bind(context);
765 return this.map(function(value, index) {
766 return {value: value, criteria: iterator(value, index)};
767 }).sort(function(left, right) {
768 var a = left.criteria, b = right.criteria;
769 return a < b ? -1 : a > b ? 1 : 0;
770 }).pluck('value');
771 },
772
773 toArray: function() {
774 return this.map();
775 },
776
777 zip: function() {
778 var iterator = Prototype.K, args = $A(arguments);
779 if (Object.isFunction(args.last()))
780 iterator = args.pop();
781
782 var collections = [this].concat(args).map($A);
783 return this.map(function(value, index) {
784 return iterator(collections.pluck(index));
785 });
786 },
787
788 size: function() {
789 return this.toArray().length;
790 },
791
792 inspect: function() {
793 return '#<Enumerable:' + this.toArray().inspect() + '>';
794 }
795};
796
797Object.extend(Enumerable, {
798 map: Enumerable.collect,
799 find: Enumerable.detect,
800 select: Enumerable.findAll,
801 filter: Enumerable.findAll,
802 member: Enumerable.include,
803 entries: Enumerable.toArray,
804 every: Enumerable.all,
805 some: Enumerable.any
806});
807function $A(iterable) {
808 if (!iterable) return [];
809 if (iterable.toArray) return iterable.toArray();
810 var length = iterable.length || 0, results = new Array(length);
811 while (length--) results[length] = iterable[length];
812 return results;
813}
814
815if (Prototype.Browser.WebKit) {
816 $A = function(iterable) {
817 if (!iterable) return [];
818 if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') &&
819 iterable.toArray) return iterable.toArray();
820 var length = iterable.length || 0, results = new Array(length);
821 while (length--) results[length] = iterable[length];
822 return results;
823 };
824}
825
826Array.from = $A;
827
828Object.extend(Array.prototype, Enumerable);
829
830if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;
831
832Object.extend(Array.prototype, {
833 _each: function(iterator) {
834 for (var i = 0, length = this.length; i < length; i++)
835 iterator(this[i]);
836 },
837
838 clear: function() {
839 this.length = 0;
840 return this;
841 },
842
843 first: function() {
844 return this[0];
845 },
846
847 last: function() {
848 return this[this.length - 1];
849 },
850
851 compact: function() {
852 return this.select(function(value) {
853 return value != null;
854 });
855 },
856
857 flatten: function() {
858 return this.inject([], function(array, value) {
859 return array.concat(Object.isArray(value) ?
860 value.flatten() : [value]);
861 });
862 },
863
864 without: function() {
865 var values = $A(arguments);
866 return this.select(function(value) {
867 return !values.include(value);
868 });
869 },
870
871 reverse: function(inline) {
872 return (inline !== false ? this : this.toArray())._reverse();
873 },
874
875 reduce: function() {
876 return this.length > 1 ? this : this[0];
877 },
878
879 uniq: function(sorted) {
880 return this.inject([], function(array, value, index) {
881 if (0 == index || (sorted ? array.last() != value : !array.include(value)))
882 array.push(value);
883 return array;
884 });
885 },
886
887 intersect: function(array) {
888 return this.uniq().findAll(function(item) {
889 return array.detect(function(value) { return item === value });
890 });
891 },
892
893 clone: function() {
894 return [].concat(this);
895 },
896
897 size: function() {
898 return this.length;
899 },
900
901 inspect: function() {
902 return '[' + this.map(Object.inspect).join(', ') + ']';
903 },
904
905 toJSON: function() {
906 var results = [];
907 this.each(function(object) {
908 var value = Object.toJSON(object);
909 if (!Object.isUndefined(value)) results.push(value);
910 });
911 return '[' + results.join(', ') + ']';
912 }
913});
914
915// use native browser JS 1.6 implementation if available
916if (Object.isFunction(Array.prototype.forEach))
917 Array.prototype._each = Array.prototype.forEach;
918
919if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {
920 i || (i = 0);
921 var length = this.length;
922 if (i < 0) i = length + i;
923 for (; i < length; i++)
924 if (this[i] === item) return i;
925 return -1;
926};
927
928if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) {
929 i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
930 var n = this.slice(0, i).reverse().indexOf(item);
931 return (n < 0) ? n : i - n - 1;
932};
933
934Array.prototype.toArray = Array.prototype.clone;
935
936function $w(string) {
937 if (!Object.isString(string)) return [];
938 string = string.strip();
939 return string ? string.split(/\s+/) : [];
940}
941
942if (Prototype.Browser.Opera){
943 Array.prototype.concat = function() {
944 var array = [];
945 for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
946 for (var i = 0, length = arguments.length; i < length; i++) {
947 if (Object.isArray(arguments[i])) {
948 for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
949 array.push(arguments[i][j]);
950 } else {
951 array.push(arguments[i]);
952 }
953 }
954 return array;
955 };
956}
957Object.extend(Number.prototype, {
958 toColorPart: function() {
959 return this.toPaddedString(2, 16);
960 },
961
962 succ: function() {
963 return this + 1;
964 },
965
966 times: function(iterator) {
967 $R(0, this, true).each(iterator);
968 return this;
969 },
970
971 toPaddedString: function(length, radix) {
972 var string = this.toString(radix || 10);
973 return '0'.times(length - string.length) + string;
974 },
975
976 toJSON: function() {
977 return isFinite(this) ? this.toString() : 'null';
978 }
979});
980
981$w('abs round ceil floor').each(function(method){
982 Number.prototype[method] = Math[method].methodize();
983});
984function $H(object) {
985 return new Hash(object);
986};
987
988var Hash = Class.create(Enumerable, (function() {
989
990 function toQueryPair(key, value) {
991 if (Object.isUndefined(value)) return key;
992 return key + '=' + encodeURIComponent(String.interpret(value));
993 }
994
995 return {
996 initialize: function(object) {
997 this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
998 },
999
1000 _each: function(iterator) {
1001 for (var key in this._object) {
1002 var value = this._object[key], pair = [key, value];
1003 pair.key = key;
1004 pair.value = value;
1005 iterator(pair);
1006 }
1007 },
1008
1009 set: function(key, value) {
1010 return this._object[key] = value;
1011 },
1012
1013 get: function(key) {
1014 return this._object[key];
1015 },
1016
1017 unset: function(key) {
1018 var value = this._object[key];
1019 delete this._object[key];
1020 return value;
1021 },
1022
1023 toObject: function() {
1024 return Object.clone(this._object);
1025 },
1026
1027 keys: function() {
1028 return this.pluck('key');
1029 },
1030
1031 values: function() {
1032 return this.pluck('value');
1033 },
1034
1035 index: function(value) {
1036 var match = this.detect(function(pair) {
1037 return pair.value === value;
1038 });
1039 return match && match.key;
1040 },
1041
1042 merge: function(object) {
1043 return this.clone().update(object);
1044 },
1045
1046 update: function(object) {
1047 return new Hash(object).inject(this, function(result, pair) {
1048 result.set(pair.key, pair.value);
1049 return result;
1050 });
1051 },
1052
1053 toQueryString: function() {
1054 return this.map(function(pair) {
1055 var key = encodeURIComponent(pair.key), values = pair.value;
1056
1057 if (values && typeof values == 'object') {
1058 if (Object.isArray(values))
1059 return values.map(toQueryPair.curry(key)).join('&');
1060 }
1061 return toQueryPair(key, values);
1062 }).join('&');
1063 },
1064
1065 inspect: function() {
1066 return '#<Hash:{' + this.map(function(pair) {
1067 return pair.map(Object.inspect).join(': ');
1068 }).join(', ') + '}>';
1069 },
1070
1071 toJSON: function() {
1072 return Object.toJSON(this.toObject());
1073 },
1074
1075 clone: function() {
1076 return new Hash(this);
1077 }
1078 }
1079})());
1080
1081Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
1082Hash.from = $H;
1083var ObjectRange = Class.create(Enumerable, {
1084 initialize: function(start, end, exclusive) {
1085 this.start = start;
1086 this.end = end;
1087 this.exclusive = exclusive;
1088 },
1089
1090 _each: function(iterator) {
1091 var value = this.start;
1092 while (this.include(value)) {
1093 iterator(value);
1094 value = value.succ();
1095 }
1096 },
1097
1098 include: function(value) {
1099 if (value < this.start)
1100 return false;
1101 if (this.exclusive)
1102 return value < this.end;
1103 return value <= this.end;
1104 }
1105});
1106
1107var $R = function(start, end, exclusive) {
1108 return new ObjectRange(start, end, exclusive);
1109};
1110
1111var Ajax = {
1112 getTransport: function() {
1113 return Try.these(
1114 function() {return new XMLHttpRequest()},
1115 function() {return new ActiveXObject('Msxml2.XMLHTTP')},
1116 function() {return new ActiveXObject('Microsoft.XMLHTTP')}
1117 ) || false;
1118 },
1119
1120 activeRequestCount: 0
1121};
1122
1123Ajax.Responders = {
1124 responders: [],
1125
1126 _each: function(iterator) {
1127 this.responders._each(iterator);
1128 },
1129
1130 register: function(responder) {
1131 if (!this.include(responder))
1132 this.responders.push(responder);
1133 },
1134
1135 unregister: function(responder) {
1136 this.responders = this.responders.without(responder);
1137 },
1138
1139 dispatch: function(callback, request, transport, json) {
1140 this.each(function(responder) {
1141 if (Object.isFunction(responder[callback])) {
1142 try {
1143 responder[callback].apply(responder, [request, transport, json]);
1144 } catch (e) { }
1145 }
1146 });
1147 }
1148};
1149
1150Object.extend(Ajax.Responders, Enumerable);
1151
1152Ajax.Responders.register({
1153 onCreate: function() { Ajax.activeRequestCount++ },
1154 onComplete: function() { Ajax.activeRequestCount-- }
1155});
1156
1157Ajax.Base = Class.create({
1158 initialize: function(options) {
1159 this.options = {
1160 method: 'post',
1161 asynchronous: true,
1162 contentType: 'application/x-www-form-urlencoded',
1163 encoding: 'UTF-8',
1164 parameters: '',
1165 evalJSON: true,
1166 evalJS: true
1167 };
1168 Object.extend(this.options, options || { });
1169
1170 this.options.method = this.options.method.toLowerCase();
1171
1172 if (Object.isString(this.options.parameters))
1173 this.options.parameters = this.options.parameters.toQueryParams();
1174 else if (Object.isHash(this.options.parameters))
1175 this.options.parameters = this.options.parameters.toObject();
1176 }
1177});
1178
1179Ajax.Request = Class.create(Ajax.Base, {
1180 _complete: false,
1181
1182 initialize: function($super, url, options) {
1183 $super(options);
1184 this.transport = Ajax.getTransport();
1185 this.request(url);
1186 },
1187
1188 request: function(url) {
1189 this.url = url;
1190 this.method = this.options.method;
1191 var params = Object.clone(this.options.parameters);
1192
1193 if (!['get', 'post'].include(this.method)) {
1194 // simulate other verbs over post
1195 params['_method'] = this.method;
1196 this.method = 'post';
1197 }
1198
1199 this.parameters = params;
1200
1201 if (params = Object.toQueryString(params)) {
1202 // when GET, append parameters to URL
1203 if (this.method == 'get')
1204 this.url += (this.url.include('?') ? '&' : '?') + params;
1205 else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
1206 params += '&_=';
1207 }
1208
1209 try {
1210 var response = new Ajax.Response(this);
1211 if (this.options.onCreate) this.options.onCreate(response);
1212 Ajax.Responders.dispatch('onCreate', this, response);
1213
1214 this.transport.open(this.method.toUpperCase(), this.url,
1215 this.options.asynchronous);
1216
1217 if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
1218
1219 this.transport.onreadystatechange = this.onStateChange.bind(this);
1220 this.setRequestHeaders();
1221
1222 this.body = this.method == 'post' ? (this.options.postBody || params) : null;
1223 this.transport.send(this.body);
1224
1225 /* Force Firefox to handle ready state 4 for synchronous requests */
1226 if (!this.options.asynchronous && this.transport.overrideMimeType)
1227 this.onStateChange();
1228
1229 }
1230 catch (e) {
1231 this.dispatchException(e);
1232 }
1233 },
1234
1235 onStateChange: function() {
1236 var readyState = this.transport.readyState;
1237 if (readyState > 1 && !((readyState == 4) && this._complete))
1238 this.respondToReadyState(this.transport.readyState);
1239 },
1240
1241 setRequestHeaders: function() {
1242 var headers = {
1243 'X-Requested-With': 'XMLHttpRequest',
1244 'X-Prototype-Version': Prototype.Version,
1245 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
1246 };
1247
1248 if (this.method == 'post') {
1249 headers['Content-type'] = this.options.contentType +
1250 (this.options.encoding ? '; charset=' + this.options.encoding : '');
1251
1252 /* Force "Connection: close" for older Mozilla browsers to work
1253 * around a bug where XMLHttpRequest sends an incorrect
1254 * Content-length header. See Mozilla Bugzilla #246651.
1255 */
1256 if (this.transport.overrideMimeType &&
1257 (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
1258 headers['Connection'] = 'close';
1259 }
1260
1261 // user-defined headers
1262 if (typeof this.options.requestHeaders == 'object') {
1263 var extras = this.options.requestHeaders;
1264
1265 if (Object.isFunction(extras.push))
1266 for (var i = 0, length = extras.length; i < length; i += 2)
1267 headers[extras[i]] = extras[i+1];
1268 else
1269 $H(extras).each(function(pair) { headers[pair.key] = pair.value });
1270 }
1271
1272 for (var name in headers)
1273 this.transport.setRequestHeader(name, headers[name]);
1274 },
1275
1276 success: function() {
1277 var status = this.getStatus();
1278 return !status || (status >= 200 && status < 300);
1279 },
1280
1281 getStatus: function() {
1282 try {
1283 return this.transport.status || 0;
1284 } catch (e) { return 0 }
1285 },
1286
1287 respondToReadyState: function(readyState) {
1288 var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
1289
1290 if (state == 'Complete') {
1291 try {
1292 this._complete = true;
1293 (this.options['on' + response.status]
1294 || this.options['on' + (this.success() ? 'Success' : 'Failure')]
1295 || Prototype.emptyFunction)(response, response.headerJSON);
1296 } catch (e) {
1297 this.dispatchException(e);
1298 }
1299
1300 var contentType = response.getHeader('Content-type');
1301 if (this.options.evalJS == 'force'
1302 || (this.options.evalJS && this.isSameOrigin() && contentType
1303 && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
1304 this.evalResponse();
1305 }
1306
1307 try {
1308 (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
1309 Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
1310 } catch (e) {
1311 this.dispatchException(e);
1312 }
1313
1314 if (state == 'Complete') {
1315 // avoid memory leak in MSIE: clean up
1316 this.transport.onreadystatechange = Prototype.emptyFunction;
1317 }
1318 },
1319
1320 isSameOrigin: function() {
1321 var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
1322 return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
1323 protocol: location.protocol,
1324 domain: document.domain,
1325 port: location.port ? ':' + location.port : ''
1326 }));
1327 },
1328
1329 getHeader: function(name) {
1330 try {
1331 return this.transport.getResponseHeader(name) || null;
1332 } catch (e) { return null }
1333 },
1334
1335 evalResponse: function() {
1336 try {
1337 return eval((this.transport.responseText || '').unfilterJSON());
1338 } catch (e) {
1339 this.dispatchException(e);
1340 }
1341 },
1342
1343 dispatchException: function(exception) {
1344 (this.options.onException || Prototype.emptyFunction)(this, exception);
1345 Ajax.Responders.dispatch('onException', this, exception);
1346 }
1347});
1348
1349Ajax.Request.Events =
1350 ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
1351
1352Ajax.Response = Class.create({
1353 initialize: function(request){
1354 this.request = request;
1355 var transport = this.transport = request.transport,
1356 readyState = this.readyState = transport.readyState;
1357
1358 if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
1359 this.status = this.getStatus();
1360 this.statusText = this.getStatusText();
1361 this.responseText = String.interpret(transport.responseText);
1362 this.headerJSON = this._getHeaderJSON();
1363 }
1364
1365 if(readyState == 4) {
1366 var xml = transport.responseXML;
1367 this.responseXML = Object.isUndefined(xml) ? null : xml;
1368 this.responseJSON = this._getResponseJSON();
1369 }
1370 },
1371
1372 status: 0,
1373 statusText: '',
1374
1375 getStatus: Ajax.Request.prototype.getStatus,
1376
1377 getStatusText: function() {
1378 try {
1379 return this.transport.statusText || '';
1380 } catch (e) { return '' }
1381 },
1382
1383 getHeader: Ajax.Request.prototype.getHeader,
1384
1385 getAllHeaders: function() {
1386 try {
1387 return this.getAllResponseHeaders();
1388 } catch (e) { return null }
1389 },
1390
1391 getResponseHeader: function(name) {
1392 return this.transport.getResponseHeader(name);
1393 },
1394
1395 getAllResponseHeaders: function() {
1396 return this.transport.getAllResponseHeaders();
1397 },
1398
1399 _getHeaderJSON: function() {
1400 var json = this.getHeader('X-JSON');
1401 if (!json) return null;
1402 json = decodeURIComponent(escape(json));
1403 try {
1404 return json.evalJSON(this.request.options.sanitizeJSON ||
1405 !this.request.isSameOrigin());
1406 } catch (e) {
1407 this.request.dispatchException(e);
1408 }
1409 },
1410
1411 _getResponseJSON: function() {
1412 var options = this.request.options;
1413 if (!options.evalJSON || (options.evalJSON != 'force' &&
1414 !(this.getHeader('Content-type') || '').include('application/json')) ||
1415 this.responseText.blank())
1416 return null;
1417 try {
1418 return this.responseText.evalJSON(options.sanitizeJSON ||
1419 !this.request.isSameOrigin());
1420 } catch (e) {
1421 this.request.dispatchException(e);
1422 }
1423 }
1424});
1425
1426Ajax.Updater = Class.create(Ajax.Request, {
1427 initialize: function($super, container, url, options) {
1428 this.container = {
1429 success: (container.success || container),
1430 failure: (container.failure || (container.success ? null : container))
1431 };
1432
1433 options = Object.clone(options);
1434 var onComplete = options.onComplete;
1435 options.onComplete = (function(response, json) {
1436 this.updateContent(response.responseText);
1437 if (Object.isFunction(onComplete)) onComplete(response, json);
1438 }).bind(this);
1439
1440 $super(url, options);
1441 },
1442
1443 updateContent: function(responseText) {
1444 var receiver = this.container[this.success() ? 'success' : 'failure'],
1445 options = this.options;
1446
1447 if (!options.evalScripts) responseText = responseText.stripScripts();
1448
1449 if (receiver = $(receiver)) {
1450 if (options.insertion) {
1451 if (Object.isString(options.insertion)) {
1452 var insertion = { }; insertion[options.insertion] = responseText;
1453 receiver.insert(insertion);
1454 }
1455 else options.insertion(receiver, responseText);
1456 }
1457 else receiver.update(responseText);
1458 }
1459 }
1460});
1461
1462Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
1463 initialize: function($super, container, url, options) {
1464 $super(options);
1465 this.onComplete = this.options.onComplete;
1466
1467 this.frequency = (this.options.frequency || 2);
1468 this.decay = (this.options.decay || 1);
1469
1470 this.updater = { };
1471 this.container = container;
1472 this.url = url;
1473
1474 this.start();
1475 },
1476
1477 start: function() {
1478 this.options.onComplete = this.updateComplete.bind(this);
1479 this.onTimerEvent();
1480 },
1481
1482 stop: function() {
1483 this.updater.options.onComplete = undefined;
1484 clearTimeout(this.timer);
1485 (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
1486 },
1487
1488 updateComplete: function(response) {
1489 if (this.options.decay) {
1490 this.decay = (response.responseText == this.lastText ?
1491 this.decay * this.options.decay : 1);
1492
1493 this.lastText = response.responseText;
1494 }
1495 this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
1496 },
1497
1498 onTimerEvent: function() {
1499 this.updater = new Ajax.Updater(this.container, this.url, this.options);
1500 }
1501});
1502function $(element) {
1503 if (arguments.length > 1) {
1504 for (var i = 0, elements = [], length = arguments.length; i < length; i++)
1505 elements.push($(arguments[i]));
1506 return elements;
1507 }
1508 if (Object.isString(element))
1509 element = document.getElementById(element);
1510 return Element.extend(element);
1511}
1512
1513if (Prototype.BrowserFeatures.XPath) {
1514 document._getElementsByXPath = function(expression, parentElement) {
1515 var results = [];
1516 var query = document.evaluate(expression, $(parentElement) || document,
1517 null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
1518 for (var i = 0, length = query.snapshotLength; i < length; i++)
1519 results.push(Element.extend(query.snapshotItem(i)));
1520 return results;
1521 };
1522}
1523
1524/*--------------------------------------------------------------------------*/
1525
1526if (!window.Node) var Node = { };
1527
1528if (!Node.ELEMENT_NODE) {
1529 // DOM level 2 ECMAScript Language Binding
1530 Object.extend(Node, {
1531 ELEMENT_NODE: 1,
1532 ATTRIBUTE_NODE: 2,
1533 TEXT_NODE: 3,
1534 CDATA_SECTION_NODE: 4,
1535 ENTITY_REFERENCE_NODE: 5,
1536 ENTITY_NODE: 6,
1537 PROCESSING_INSTRUCTION_NODE: 7,
1538 COMMENT_NODE: 8,
1539 DOCUMENT_NODE: 9,
1540 DOCUMENT_TYPE_NODE: 10,
1541 DOCUMENT_FRAGMENT_NODE: 11,
1542 NOTATION_NODE: 12
1543 });
1544}
1545
1546(function() {
1547 var element = this.Element;
1548 this.Element = function(tagName, attributes) {
1549 attributes = attributes || { };
1550 tagName = tagName.toLowerCase();
1551 var cache = Element.cache;
1552 if (Prototype.Browser.IE && attributes.name) {
1553 tagName = '<' + tagName + ' name="' + attributes.name + '">';
1554 delete attributes.name;
1555 return Element.writeAttribute(document.createElement(tagName), attributes);
1556 }
1557 if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
1558 return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
1559 };
1560 Object.extend(this.Element, element || { });
1561}).call(window);
1562
1563Element.cache = { };
1564
1565Element.Methods = {
1566 visible: function(element) {
1567 return $(element).style.display != 'none';
1568 },
1569
1570 toggle: function(element) {
1571 element = $(element);
1572 Element[Element.visible(element) ? 'hide' : 'show'](element);
1573 return element;
1574 },
1575
1576 hide: function(element) {
1577 $(element).style.display = 'none';
1578 return element;
1579 },
1580
1581 show: function(element) {
1582 $(element).style.display = '';
1583 return element;
1584 },
1585
1586 remove: function(element) {
1587 element = $(element);
1588 element.parentNode.removeChild(element);
1589 return element;
1590 },
1591
1592 update: function(element, content) {
1593 element = $(element);
1594 if (content && content.toElement) content = content.toElement();
1595 if (Object.isElement(content)) return element.update().insert(content);
1596 content = Object.toHTML(content);
1597 element.innerHTML = content.stripScripts();
1598 content.evalScripts.bind(content).defer();
1599 return element;
1600 },
1601
1602 replace: function(element, content) {
1603 element = $(element);
1604 if (content && content.toElement) content = content.toElement();
1605 else if (!Object.isElement(content)) {
1606 content = Object.toHTML(content);
1607 var range = element.ownerDocument.createRange();
1608 range.selectNode(element);
1609 content.evalScripts.bind(content).defer();
1610 content = range.createContextualFragment(content.stripScripts());
1611 }
1612 element.parentNode.replaceChild(content, element);
1613 return element;
1614 },
1615
1616 insert: function(element, insertions) {
1617 element = $(element);
1618
1619 if (Object.isString(insertions) || Object.isNumber(insertions) ||
1620 Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
1621 insertions = {bottom:insertions};
1622
1623 var content, insert, tagName, childNodes;
1624
1625 for (var position in insertions) {
1626 content = insertions[position];
1627 position = position.toLowerCase();
1628 insert = Element._insertionTranslations[position];
1629
1630 if (content && content.toElement) content = content.toElement();
1631 if (Object.isElement(content)) {
1632 insert(element, content);
1633 continue;
1634 }
1635
1636 content = Object.toHTML(content);
1637
1638 tagName = ((position == 'before' || position == 'after')
1639 ? element.parentNode : element).tagName.toUpperCase();
1640
1641 childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
1642
1643 if (position == 'top' || position == 'after') childNodes.reverse();
1644 childNodes.each(insert.curry(element));
1645
1646 content.evalScripts.bind(content).defer();
1647 }
1648
1649 return element;
1650 },
1651
1652 wrap: function(element, wrapper, attributes) {
1653 element = $(element);
1654 if (Object.isElement(wrapper))
1655 $(wrapper).writeAttribute(attributes || { });
1656 else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
1657 else wrapper = new Element('div', wrapper);
1658 if (element.parentNode)
1659 element.parentNode.replaceChild(wrapper, element);
1660 wrapper.appendChild(element);
1661 return wrapper;
1662 },
1663
1664 inspect: function(element) {
1665 element = $(element);
1666 var result = '<' + element.tagName.toLowerCase();
1667 $H({'id': 'id', 'className': 'class'}).each(function(pair) {
1668 var property = pair.first(), attribute = pair.last();
1669 var value = (element[property] || '').toString();
1670 if (value) result += ' ' + attribute + '=' + value.inspect(true);
1671 });
1672 return result + '>';
1673 },
1674
1675 recursivelyCollect: function(element, property) {
1676 element = $(element);
1677 var elements = [];
1678 while (element = element[property])
1679 if (element.nodeType == 1)
1680 elements.push(Element.extend(element));
1681 return elements;
1682 },
1683
1684 ancestors: function(element) {
1685 return $(element).recursivelyCollect('parentNode');
1686 },
1687
1688 descendants: function(element) {
1689 return $(element).select("*");
1690 },
1691
1692 firstDescendant: function(element) {
1693 element = $(element).firstChild;
1694 while (element && element.nodeType != 1) element = element.nextSibling;
1695 return $(element);
1696 },
1697
1698 immediateDescendants: function(element) {
1699 if (!(element = $(element).firstChild)) return [];
1700 while (element && element.nodeType != 1) element = element.nextSibling;
1701 if (element) return [element].concat($(element).nextSiblings());
1702 return [];
1703 },
1704
1705 previousSiblings: function(element) {
1706 return $(element).recursivelyCollect('previousSibling');
1707 },
1708
1709 nextSiblings: function(element) {
1710 return $(element).recursivelyCollect('nextSibling');
1711 },
1712
1713 siblings: function(element) {
1714 element = $(element);
1715 return element.previousSiblings().reverse().concat(element.nextSiblings());
1716 },
1717
1718 match: function(element, selector) {
1719 if (Object.isString(selector))
1720 selector = new Selector(selector);
1721 return selector.match($(element));
1722 },
1723
1724 up: function(element, expression, index) {
1725 element = $(element);
1726 if (arguments.length == 1) return $(element.parentNode);
1727 var ancestors = element.ancestors();
1728 return Object.isNumber(expression) ? ancestors[expression] :
1729 Selector.findElement(ancestors, expression, index);
1730 },
1731
1732 down: function(element, expression, index) {
1733 element = $(element);
1734 if (arguments.length == 1) return element.firstDescendant();
1735 return Object.isNumber(expression) ? element.descendants()[expression] :
1736 element.select(expression)[index || 0];
1737 },
1738
1739 previous: function(element, expression, index) {
1740 element = $(element);
1741 if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
1742 var previousSiblings = element.previousSiblings();
1743 return Object.isNumber(expression) ? previousSiblings[expression] :
1744 Selector.findElement(previousSiblings, expression, index);
1745 },
1746
1747 next: function(element, expression, index) {
1748 element = $(element);
1749 if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
1750 var nextSiblings = element.nextSiblings();
1751 return Object.isNumber(expression) ? nextSiblings[expression] :
1752 Selector.findElement(nextSiblings, expression, index);
1753 },
1754
1755 select: function() {
1756 var args = $A(arguments), element = $(args.shift());
1757 return Selector.findChildElements(element, args);
1758 },
1759
1760 adjacent: function() {
1761 var args = $A(arguments), element = $(args.shift());
1762 return Selector.findChildElements(element.parentNode, args).without(element);
1763 },
1764
1765 identify: function(element) {
1766 element = $(element);
1767 var id = element.readAttribute('id'), self = arguments.callee;
1768 if (id) return id;
1769 do { id = 'anonymous_element_' + self.counter++ } while ($(id));
1770 element.writeAttribute('id', id);
1771 return id;
1772 },
1773
1774 readAttribute: function(element, name) {
1775 element = $(element);
1776 if (Prototype.Browser.IE) {
1777 var t = Element._attributeTranslations.read;
1778 if (t.values[name]) return t.values[name](element, name);
1779 if (t.names[name]) name = t.names[name];
1780 if (name.include(':')) {
1781 return (!element.attributes || !element.attributes[name]) ? null :
1782 element.attributes[name].value;
1783 }
1784 }
1785 return element.getAttribute(name);
1786 },
1787
1788 writeAttribute: function(element, name, value) {
1789 element = $(element);
1790 var attributes = { }, t = Element._attributeTranslations.write;
1791
1792 if (typeof name == 'object') attributes = name;
1793 else attributes[name] = Object.isUndefined(value) ? true : value;
1794
1795 for (var attr in attributes) {
1796 name = t.names[attr] || attr;
1797 value = attributes[attr];
1798 if (t.values[attr]) name = t.values[attr](element, value);
1799 if (value === false || value === null)
1800 element.removeAttribute(name);
1801 else if (value === true)
1802 element.setAttribute(name, name);
1803 else element.setAttribute(name, value);
1804 }
1805 return element;
1806 },
1807
1808 getHeight: function(element) {
1809 return $(element).getDimensions().height;
1810 },
1811
1812 getWidth: function(element) {
1813 return $(element).getDimensions().width;
1814 },
1815
1816 classNames: function(element) {
1817 return new Element.ClassNames(element);
1818 },
1819
1820 hasClassName: function(element, className) {
1821 if (!(element = $(element))) return;
1822 var elementClassName = element.className;
1823 return (elementClassName.length > 0 && (elementClassName == className ||
1824 new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
1825 },
1826
1827 addClassName: function(element, className) {
1828 if (!(element = $(element))) return;
1829 if (!element.hasClassName(className))
1830 element.className += (element.className ? ' ' : '') + className;
1831 return element;
1832 },
1833
1834 removeClassName: function(element, className) {
1835 if (!(element = $(element))) return;
1836 element.className = element.className.replace(
1837 new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
1838 return element;
1839 },
1840
1841 toggleClassName: function(element, className) {
1842 if (!(element = $(element))) return;
1843 return element[element.hasClassName(className) ?
1844 'removeClassName' : 'addClassName'](className);
1845 },
1846
1847 // removes whitespace-only text node children
1848 cleanWhitespace: function(element) {
1849 element = $(element);
1850 var node = element.firstChild;
1851 while (node) {
1852 var nextNode = node.nextSibling;
1853 if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
1854 element.removeChild(node);
1855 node = nextNode;
1856 }
1857 return element;
1858 },
1859
1860 empty: function(element) {
1861 return $(element).innerHTML.blank();
1862 },
1863
1864 descendantOf: function(element, ancestor) {
1865 element = $(element), ancestor = $(ancestor);
1866 var originalAncestor = ancestor;
1867
1868 if (element.compareDocumentPosition)
1869 return (element.compareDocumentPosition(ancestor) & 8) === 8;
1870
1871 if (element.sourceIndex && !Prototype.Browser.Opera) {
1872 var e = element.sourceIndex, a = ancestor.sourceIndex,
1873 nextAncestor = ancestor.nextSibling;
1874 if (!nextAncestor) {
1875 do { ancestor = ancestor.parentNode; }
1876 while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode);
1877 }
1878 if (nextAncestor && nextAncestor.sourceIndex)
1879 return (e > a && e < nextAncestor.sourceIndex);
1880 }
1881
1882 while (element = element.parentNode)
1883 if (element == originalAncestor) return true;
1884 return false;
1885 },
1886
1887 scrollTo: function(element) {
1888 element = $(element);
1889 var pos = element.cumulativeOffset();
1890 window.scrollTo(pos[0], pos[1]);
1891 return element;
1892 },
1893
1894 getStyle: function(element, style) {
1895 element = $(element);
1896 style = style == 'float' ? 'cssFloat' : style.camelize();
1897 var value = element.style[style];
1898 if (!value) {
1899 var css = document.defaultView.getComputedStyle(element, null);
1900 value = css ? css[style] : null;
1901 }
1902 if (style == 'opacity') return value ? parseFloat(value) : 1.0;
1903 return value == 'auto' ? null : value;
1904 },
1905
1906 getOpacity: function(element) {
1907 return $(element).getStyle('opacity');
1908 },
1909
1910 setStyle: function(element, styles) {
1911 element = $(element);
1912 var elementStyle = element.style, match;
1913 if (Object.isString(styles)) {
1914 element.style.cssText += ';' + styles;
1915 return styles.include('opacity') ?
1916 element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
1917 }
1918 for (var property in styles)
1919 if (property == 'opacity') element.setOpacity(styles[property]);
1920 else
1921 elementStyle[(property == 'float' || property == 'cssFloat') ?
1922 (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
1923 property] = styles[property];
1924
1925 return element;
1926 },
1927
1928 setOpacity: function(element, value) {
1929 element = $(element);
1930 element.style.opacity = (value == 1 || value === '') ? '' :
1931 (value < 0.00001) ? 0 : value;
1932 return element;
1933 },
1934
1935 getDimensions: function(element) {
1936 element = $(element);
1937 var display = $(element).getStyle('display');
1938 if (display != 'none' && display != null) // Safari bug
1939 return {width: element.offsetWidth, height: element.offsetHeight};
1940
1941 // All *Width and *Height properties give 0 on elements with display none,
1942 // so enable the element temporarily
1943 var els = element.style;
1944 var originalVisibility = els.visibility;
1945 var originalPosition = els.position;
1946 var originalDisplay = els.display;
1947 els.visibility = 'hidden';
1948 els.position = 'absolute';
1949 els.display = 'block';
1950 var originalWidth = element.clientWidth;
1951 var originalHeight = element.clientHeight;
1952 els.display = originalDisplay;
1953 els.position = originalPosition;
1954 els.visibility = originalVisibility;
1955 return {width: originalWidth, height: originalHeight};
1956 },
1957
1958 makePositioned: function(element) {
1959 element = $(element);
1960 var pos = Element.getStyle(element, 'position');
1961 if (pos == 'static' || !pos) {
1962 element._madePositioned = true;
1963 element.style.position = 'relative';
1964 // Opera returns the offset relative to the positioning context, when an
1965 // element is position relative but top and left have not been defined
1966 if (window.opera) {
1967 element.style.top = 0;
1968 element.style.left = 0;
1969 }
1970 }
1971 return element;
1972 },
1973
1974 undoPositioned: function(element) {
1975 element = $(element);
1976 if (element._madePositioned) {
1977 element._madePositioned = undefined;
1978 element.style.position =
1979 element.style.top =
1980 element.style.left =
1981 element.style.bottom =
1982 element.style.right = '';
1983 }
1984 return element;
1985 },
1986
1987 makeClipping: function(element) {
1988 element = $(element);
1989 if (element._overflow) return element;
1990 element._overflow = Element.getStyle(element, 'overflow') || 'auto';
1991 if (element._overflow !== 'hidden')
1992 element.style.overflow = 'hidden';
1993 return element;
1994 },
1995
1996 undoClipping: function(element) {
1997 element = $(element);
1998 if (!element._overflow) return element;
1999 element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
2000 element._overflow = null;
2001 return element;
2002 },
2003
2004 cumulativeOffset: function(element) {
2005 var valueT = 0, valueL = 0;
2006 do {
2007 valueT += element.offsetTop || 0;
2008 valueL += element.offsetLeft || 0;
2009 element = element.offsetParent;
2010 } while (element);
2011 return Element._returnOffset(valueL, valueT);
2012 },
2013
2014 positionedOffset: function(element) {
2015 var valueT = 0, valueL = 0;
2016 do {
2017 valueT += element.offsetTop || 0;
2018 valueL += element.offsetLeft || 0;
2019 element = element.offsetParent;
2020 if (element) {
2021 if (element.tagName == 'BODY') break;
2022 var p = Element.getStyle(element, 'position');
2023 if (p !== 'static') break;
2024 }
2025 } while (element);
2026 return Element._returnOffset(valueL, valueT);
2027 },
2028
2029 absolutize: function(element) {
2030 element = $(element);
2031 if (element.getStyle('position') == 'absolute') return;
2032 // Position.prepare(); // To be done manually by Scripty when it needs it.
2033
2034 var offsets = element.positionedOffset();
2035 var top = offsets[1];
2036 var left = offsets[0];
2037 var width = element.clientWidth;
2038 var height = element.clientHeight;
2039
2040 element._originalLeft = left - parseFloat(element.style.left || 0);
2041 element._originalTop = top - parseFloat(element.style.top || 0);
2042 element._originalWidth = element.style.width;
2043 element._originalHeight = element.style.height;
2044
2045 element.style.position = 'absolute';
2046 element.style.top = top + 'px';
2047 element.style.left = left + 'px';
2048 element.style.width = width + 'px';
2049 element.style.height = height + 'px';
2050 return element;
2051 },
2052
2053 relativize: function(element) {
2054 element = $(element);
2055 if (element.getStyle('position') == 'relative') return;
2056 // Position.prepare(); // To be done manually by Scripty when it needs it.
2057
2058 element.style.position = 'relative';
2059 var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
2060 var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
2061
2062 element.style.top = top + 'px';
2063 element.style.left = left + 'px';
2064 element.style.height = element._originalHeight;
2065 element.style.width = element._originalWidth;
2066 return element;
2067 },
2068
2069 cumulativeScrollOffset: function(element) {
2070 var valueT = 0, valueL = 0;
2071 do {
2072 valueT += element.scrollTop || 0;
2073 valueL += element.scrollLeft || 0;
2074 element = element.parentNode;
2075 } while (element);
2076 return Element._returnOffset(valueL, valueT);
2077 },
2078
2079 getOffsetParent: function(element) {
2080 if (element.offsetParent) return $(element.offsetParent);
2081 if (element == document.body) return $(element);
2082
2083 while ((element = element.parentNode) && element != document.body)
2084 if (Element.getStyle(element, 'position') != 'static')
2085 return $(element);
2086
2087 return $(document.body);
2088 },
2089
2090 viewportOffset: function(forElement) {
2091 var valueT = 0, valueL = 0;
2092
2093 var element = forElement;
2094 do {
2095 valueT += element.offsetTop || 0;
2096 valueL += element.offsetLeft || 0;
2097
2098 // Safari fix
2099 if (element.offsetParent == document.body &&
2100 Element.getStyle(element, 'position') == 'absolute') break;
2101
2102 } while (element = element.offsetParent);
2103
2104 element = forElement;
2105 do {
2106 if (!Prototype.Browser.Opera || element.tagName == 'BODY') {
2107 valueT -= element.scrollTop || 0;
2108 valueL -= element.scrollLeft || 0;
2109 }
2110 } while (element = element.parentNode);
2111
2112 return Element._returnOffset(valueL, valueT);
2113 },
2114
2115 clonePosition: function(element, source) {
2116 var options = Object.extend({
2117 setLeft: true,
2118 setTop: true,
2119 setWidth: true,
2120 setHeight: true,
2121 offsetTop: 0,
2122 offsetLeft: 0
2123 }, arguments[2] || { });
2124
2125 // find page position of source
2126 source = $(source);
2127 var p = source.viewportOffset();
2128
2129 // find coordinate system to use
2130 element = $(element);
2131 var delta = [0, 0];
2132 var parent = null;
2133 // delta [0,0] will do fine with position: fixed elements,
2134 // position:absolute needs offsetParent deltas
2135 if (Element.getStyle(element, 'position') == 'absolute') {
2136 parent = element.getOffsetParent();
2137 delta = parent.viewportOffset();
2138 }
2139
2140 // correct by body offsets (fixes Safari)
2141 if (parent == document.body) {
2142 delta[0] -= document.body.offsetLeft;
2143 delta[1] -= document.body.offsetTop;
2144 }
2145
2146 // set position
2147 if (options.setLeft) element.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
2148 if (options.setTop) element.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
2149 if (options.setWidth) element.style.width = source.offsetWidth + 'px';
2150 if (options.setHeight) element.style.height = source.offsetHeight + 'px';
2151 return element;
2152 }
2153};
2154
2155Element.Methods.identify.counter = 1;
2156
2157Object.extend(Element.Methods, {
2158 getElementsBySelector: Element.Methods.select,
2159 childElements: Element.Methods.immediateDescendants
2160});
2161
2162Element._attributeTranslations = {
2163 write: {
2164 names: {
2165 className: 'class',
2166 htmlFor: 'for'
2167 },
2168 values: { }
2169 }
2170};
2171
2172if (Prototype.Browser.Opera) {
2173 Element.Methods.getStyle = Element.Methods.getStyle.wrap(
2174 function(proceed, element, style) {
2175 switch (style) {
2176 case 'left': case 'top': case 'right': case 'bottom':
2177 if (proceed(element, 'position') === 'static') return null;
2178 case 'height': case 'width':
2179 // returns '0px' for hidden elements; we want it to return null
2180 if (!Element.visible(element)) return null;
2181
2182 // returns the border-box dimensions rather than the content-box
2183 // dimensions, so we subtract padding and borders from the value
2184 var dim = parseInt(proceed(element, style), 10);
2185
2186 if (dim !== element['offset' + style.capitalize()])
2187 return dim + 'px';
2188
2189 var properties;
2190 if (style === 'height') {
2191 properties = ['border-top-width', 'padding-top',
2192 'padding-bottom', 'border-bottom-width'];
2193 }
2194 else {
2195 properties = ['border-left-width', 'padding-left',
2196 'padding-right', 'border-right-width'];
2197 }
2198 return properties.inject(dim, function(memo, property) {
2199 var val = proceed(element, property);
2200 return val === null ? memo : memo - parseInt(val, 10);
2201 }) + 'px';
2202 default: return proceed(element, style);
2203 }
2204 }
2205 );
2206
2207 Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
2208 function(proceed, element, attribute) {
2209 if (attribute === 'title') return element.title;
2210 return proceed(element, attribute);
2211 }
2212 );
2213}
2214
2215else if (Prototype.Browser.IE) {
2216 // IE doesn't report offsets correctly for static elements, so we change them
2217 // to "relative" to get the values, then change them back.
2218 Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
2219 function(proceed, element) {
2220 element = $(element);
2221 var position = element.getStyle('position');
2222 if (position !== 'static') return proceed(element);
2223 element.setStyle({ position: 'relative' });
2224 var value = proceed(element);
2225 element.setStyle({ position: position });
2226 return value;
2227 }
2228 );
2229
2230 $w('positionedOffset viewportOffset').each(function(method) {
2231 Element.Methods[method] = Element.Methods[method].wrap(
2232 function(proceed, element) {
2233 element = $(element);
2234 var position = element.getStyle('position');
2235 if (position !== 'static') return proceed(element);
2236 // Trigger hasLayout on the offset parent so that IE6 reports
2237 // accurate offsetTop and offsetLeft values for position: fixed.
2238 var offsetParent = element.getOffsetParent();
2239 if (offsetParent && offsetParent.getStyle('position') === 'fixed')
2240 offsetParent.setStyle({ zoom: 1 });
2241 element.setStyle({ position: 'relative' });
2242 var value = proceed(element);
2243 element.setStyle({ position: position });
2244 return value;
2245 }
2246 );
2247 });
2248
2249 Element.Methods.getStyle = function(element, style) {
2250 element = $(element);
2251 style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
2252 var value = element.style[style];
2253 if (!value && element.currentStyle) value = element.currentStyle[style];
2254
2255 if (style == 'opacity') {
2256 if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
2257 if (value[1]) return parseFloat(value[1]) / 100;
2258 return 1.0;
2259 }
2260
2261 if (value == 'auto') {
2262 if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
2263 return element['offset' + style.capitalize()] + 'px';
2264 return null;
2265 }
2266 return value;
2267 };
2268
2269 Element.Methods.setOpacity = function(element, value) {
2270 function stripAlpha(filter){
2271 return filter.replace(/alpha\([^\)]*\)/gi,'');
2272 }
2273 element = $(element);
2274 var currentStyle = element.currentStyle;
2275 if ((currentStyle && !currentStyle.hasLayout) ||
2276 (!currentStyle && element.style.zoom == 'normal'))
2277 element.style.zoom = 1;
2278
2279 var filter = element.getStyle('filter'), style = element.style;
2280 if (value == 1 || value === '') {
2281 (filter = stripAlpha(filter)) ?
2282 style.filter = filter : style.removeAttribute('filter');
2283 return element;
2284 } else if (value < 0.00001) value = 0;
2285 style.filter = stripAlpha(filter) +
2286 'alpha(opacity=' + (value * 100) + ')';
2287 return element;
2288 };
2289
2290 Element._attributeTranslations = {
2291 read: {
2292 names: {
2293 'class': 'className',
2294 'for': 'htmlFor'
2295 },
2296 values: {
2297 _getAttr: function(element, attribute) {
2298 return element.getAttribute(attribute, 2);
2299 },
2300 _getAttrNode: function(element, attribute) {
2301 var node = element.getAttributeNode(attribute);
2302 return node ? node.value : "";
2303 },
2304 _getEv: function(element, attribute) {
2305 attribute = element.getAttribute(attribute);
2306 return attribute ? attribute.toString().slice(23, -2) : null;
2307 },
2308 _flag: function(element, attribute) {
2309 return $(element).hasAttribute(attribute) ? attribute : null;
2310 },
2311 style: function(element) {
2312 return element.style.cssText.toLowerCase();
2313 },
2314 title: function(element) {
2315 return element.title;
2316 }
2317 }
2318 }
2319 };
2320
2321 Element._attributeTranslations.write = {
2322 names: Object.extend({
2323 cellpadding: 'cellPadding',
2324 cellspacing: 'cellSpacing'
2325 }, Element._attributeTranslations.read.names),
2326 values: {
2327 checked: function(element, value) {
2328 element.checked = !!value;
2329 },
2330
2331 style: function(element, value) {
2332 element.style.cssText = value ? value : '';
2333 }
2334 }
2335 };
2336
2337 Element._attributeTranslations.has = {};
2338
2339 $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
2340 'encType maxLength readOnly longDesc').each(function(attr) {
2341 Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
2342 Element._attributeTranslations.has[attr.toLowerCase()] = attr;
2343 });
2344
2345 (function(v) {
2346 Object.extend(v, {
2347 href: v._getAttr,
2348 src: v._getAttr,
2349 type: v._getAttr,
2350 action: v._getAttrNode,
2351 disabled: v._flag,
2352 checked: v._flag,
2353 readonly: v._flag,
2354 multiple: v._flag,
2355 onload: v._getEv,
2356 onunload: v._getEv,
2357 onclick: v._getEv,
2358 ondblclick: v._getEv,
2359 onmousedown: v._getEv,
2360 onmouseup: v._getEv,
2361 onmouseover: v._getEv,
2362 onmousemove: v._getEv,
2363 onmouseout: v._getEv,
2364 onfocus: v._getEv,
2365 onblur: v._getEv,
2366 onkeypress: v._getEv,
2367 onkeydown: v._getEv,
2368 onkeyup: v._getEv,
2369 onsubmit: v._getEv,
2370 onreset: v._getEv,
2371 onselect: v._getEv,
2372 onchange: v._getEv
2373 });
2374 })(Element._attributeTranslations.read.values);
2375}
2376
2377else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
2378 Element.Methods.setOpacity = function(element, value) {
2379 element = $(element);
2380 element.style.opacity = (value == 1) ? 0.999999 :
2381 (value === '') ? '' : (value < 0.00001) ? 0 : value;
2382 return element;
2383 };
2384}
2385
2386else if (Prototype.Browser.WebKit) {
2387 Element.Methods.setOpacity = function(element, value) {
2388 element = $(element);
2389 element.style.opacity = (value == 1 || value === '') ? '' :
2390 (value < 0.00001) ? 0 : value;
2391
2392 if (value == 1)
2393 if(element.tagName == 'IMG' && element.width) {
2394 element.width++; element.width--;
2395 } else try {
2396 var n = document.createTextNode(' ');
2397 element.appendChild(n);
2398 element.removeChild(n);
2399 } catch (e) { }
2400
2401 return element;
2402 };
2403
2404 // Safari returns margins on body which is incorrect if the child is absolutely
2405 // positioned. For performance reasons, redefine Element#cumulativeOffset for
2406 // KHTML/WebKit only.
2407 Element.Methods.cumulativeOffset = function(element) {
2408 var valueT = 0, valueL = 0;
2409 do {
2410 valueT += element.offsetTop || 0;
2411 valueL += element.offsetLeft || 0;
2412 if (element.offsetParent == document.body)
2413 if (Element.getStyle(element, 'position') == 'absolute') break;
2414
2415 element = element.offsetParent;
2416 } while (element);
2417
2418 return Element._returnOffset(valueL, valueT);
2419 };
2420}
2421
2422if (Prototype.Browser.IE || Prototype.Browser.Opera) {
2423 // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements
2424 Element.Methods.update = function(element, content) {
2425 element = $(element);
2426
2427 if (content && content.toElement) content = content.toElement();
2428 if (Object.isElement(content)) return element.update().insert(content);
2429
2430 content = Object.toHTML(content);
2431 var tagName = element.tagName.toUpperCase();
2432
2433 if (tagName in Element._insertionTranslations.tags) {
2434 $A(element.childNodes).each(function(node) { element.removeChild(node) });
2435 Element._getContentFromAnonymousElement(tagName, content.stripScripts())
2436 .each(function(node) { element.appendChild(node) });
2437 }
2438 else element.innerHTML = content.stripScripts();
2439
2440 content.evalScripts.bind(content).defer();
2441 return element;
2442 };
2443}
2444
2445if ('outerHTML' in document.createElement('div')) {
2446 Element.Methods.replace = function(element, content) {
2447 element = $(element);
2448
2449 if (content && content.toElement) content = content.toElement();
2450 if (Object.isElement(content)) {
2451 element.parentNode.replaceChild(content, element);
2452 return element;
2453 }
2454
2455 content = Object.toHTML(content);
2456 var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
2457
2458 if (Element._insertionTranslations.tags[tagName]) {
2459 var nextSibling = element.next();
2460 var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
2461 parent.removeChild(element);
2462 if (nextSibling)
2463 fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
2464 else
2465 fragments.each(function(node) { parent.appendChild(node) });
2466 }
2467 else element.outerHTML = content.stripScripts();
2468
2469 content.evalScripts.bind(content).defer();
2470 return element;
2471 };
2472}
2473
2474Element._returnOffset = function(l, t) {
2475 var result = [l, t];
2476 result.left = l;
2477 result.top = t;
2478 return result;
2479};
2480
2481Element._getContentFromAnonymousElement = function(tagName, html) {
2482 var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
2483 if (t) {
2484 div.innerHTML = t[0] + html + t[1];
2485 t[2].times(function() { div = div.firstChild });
2486 } else div.innerHTML = html;
2487 return $A(div.childNodes);
2488};
2489
2490Element._insertionTranslations = {
2491 before: function(element, node) {
2492 element.parentNode.insertBefore(node, element);
2493 },
2494 top: function(element, node) {
2495 element.insertBefore(node, element.firstChild);
2496 },
2497 bottom: function(element, node) {
2498 element.appendChild(node);
2499 },
2500 after: function(element, node) {
2501 element.parentNode.insertBefore(node, element.nextSibling);
2502 },
2503 tags: {
2504 TABLE: ['<table>', '</table>', 1],
2505 TBODY: ['<table><tbody>', '</tbody></table>', 2],
2506 TR: ['<table><tbody><tr>', '</tr></tbody></table>', 3],
2507 TD: ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
2508 SELECT: ['<select>', '</select>', 1]
2509 }
2510};
2511
2512(function() {
2513 Object.extend(this.tags, {
2514 THEAD: this.tags.TBODY,
2515 TFOOT: this.tags.TBODY,
2516 TH: this.tags.TD
2517 });
2518}).call(Element._insertionTranslations);
2519
2520Element.Methods.Simulated = {
2521 hasAttribute: function(element, attribute) {
2522 attribute = Element._attributeTranslations.has[attribute] || attribute;
2523 var node = $(element).getAttributeNode(attribute);
2524 return node && node.specified;
2525 }
2526};
2527
2528Element.Methods.ByTag = { };
2529
2530Object.extend(Element, Element.Methods);
2531
2532if (!Prototype.BrowserFeatures.ElementExtensions &&
2533 document.createElement('div').__proto__) {
2534 window.HTMLElement = { };
2535 window.HTMLElement.prototype = document.createElement('div').__proto__;
2536 Prototype.BrowserFeatures.ElementExtensions = true;
2537}
2538
2539Element.extend = (function() {
2540 if (Prototype.BrowserFeatures.SpecificElementExtensions)
2541 return Prototype.K;
2542
2543 var Methods = { }, ByTag = Element.Methods.ByTag;
2544
2545 var extend = Object.extend(function(element) {
2546 if (!element || element._extendedByPrototype ||
2547 element.nodeType != 1 || element == window) return element;
2548
2549 var methods = Object.clone(Methods),
2550 tagName = element.tagName, property, value;
2551
2552 // extend methods for specific tags
2553 if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
2554
2555 for (property in methods) {
2556 value = methods[property];
2557 if (Object.isFunction(value) && !(property in element))
2558 element[property] = value.methodize();
2559 }
2560
2561 element._extendedByPrototype = Prototype.emptyFunction;
2562 return element;
2563
2564 }, {
2565 refresh: function() {
2566 // extend methods for all tags (Safari doesn't need this)
2567 if (!Prototype.BrowserFeatures.ElementExtensions) {
2568 Object.extend(Methods, Element.Methods);
2569 Object.extend(Methods, Element.Methods.Simulated);
2570 }
2571 }
2572 });
2573
2574 extend.refresh();
2575 return extend;
2576})();
2577
2578Element.hasAttribute = function(element, attribute) {
2579 if (element.hasAttribute) return element.hasAttribute(attribute);
2580 return Element.Methods.Simulated.hasAttribute(element, attribute);
2581};
2582
2583Element.addMethods = function(methods) {
2584 var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
2585
2586 if (!methods) {
2587 Object.extend(Form, Form.Methods);
2588 Object.extend(Form.Element, Form.Element.Methods);
2589 Object.extend(Element.Methods.ByTag, {
2590 "FORM": Object.clone(Form.Methods),
2591 "INPUT": Object.clone(Form.Element.Methods),
2592 "SELECT": Object.clone(Form.Element.Methods),
2593 "TEXTAREA": Object.clone(Form.Element.Methods)
2594 });
2595 }
2596
2597 if (arguments.length == 2) {
2598 var tagName = methods;
2599 methods = arguments[1];
2600 }
2601
2602 if (!tagName) Object.extend(Element.Methods, methods || { });
2603 else {
2604 if (Object.isArray(tagName)) tagName.each(extend);
2605 else extend(tagName);
2606 }
2607
2608 function extend(tagName) {
2609 tagName = tagName.toUpperCase();
2610 if (!Element.Methods.ByTag[tagName])
2611 Element.Methods.ByTag[tagName] = { };
2612 Object.extend(Element.Methods.ByTag[tagName], methods);
2613 }
2614
2615 function copy(methods, destination, onlyIfAbsent) {
2616 onlyIfAbsent = onlyIfAbsent || false;
2617 for (var property in methods) {
2618 var value = methods[property];
2619 if (!Object.isFunction(value)) continue;
2620 if (!onlyIfAbsent || !(property in destination))
2621 destination[property] = value.methodize();
2622 }
2623 }
2624
2625 function findDOMClass(tagName) {
2626 var klass;
2627 var trans = {
2628 "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
2629 "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
2630 "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
2631 "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
2632 "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
2633 "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
2634 "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
2635 "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
2636 "FrameSet", "IFRAME": "IFrame"
2637 };
2638 if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
2639 if (window[klass]) return window[klass];
2640 klass = 'HTML' + tagName + 'Element';
2641 if (window[klass]) return window[klass];
2642 klass = 'HTML' + tagName.capitalize() + 'Element';
2643 if (window[klass]) return window[klass];
2644
2645 window[klass] = { };
2646 window[klass].prototype = document.createElement(tagName).__proto__;
2647 return window[klass];
2648 }
2649
2650 if (F.ElementExtensions) {
2651 copy(Element.Methods, HTMLElement.prototype);
2652 copy(Element.Methods.Simulated, HTMLElement.prototype, true);
2653 }
2654
2655 if (F.SpecificElementExtensions) {
2656 for (var tag in Element.Methods.ByTag) {
2657 var klass = findDOMClass(tag);
2658 if (Object.isUndefined(klass)) continue;
2659 copy(T[tag], klass.prototype);
2660 }
2661 }
2662
2663 Object.extend(Element, Element.Methods);
2664 delete Element.ByTag;
2665
2666 if (Element.extend.refresh) Element.extend.refresh();
2667 Element.cache = { };
2668};
2669
2670document.viewport = {
2671 getDimensions: function() {
2672 var dimensions = { };
2673 var B = Prototype.Browser;
2674 $w('width height').each(function(d) {
2675 var D = d.capitalize();
2676 dimensions[d] = (B.WebKit && !document.evaluate) ? self['inner' + D] :
2677 (B.Opera) ? document.body['client' + D] : document.documentElement['client' + D];
2678 });
2679 return dimensions;
2680 },
2681
2682 getWidth: function() {
2683 return this.getDimensions().width;
2684 },
2685
2686 getHeight: function() {
2687 return this.getDimensions().height;
2688 },
2689
2690 getScrollOffsets: function() {
2691 return Element._returnOffset(
2692 window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
2693 window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
2694 }
2695};
2696/* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
2697 * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
2698 * license. Please see http://www.yui-ext.com/ for more information. */
2699
2700var Selector = Class.create({
2701 initialize: function(expression) {
2702 this.expression = expression.strip();
2703 this.compileMatcher();
2704 },
2705
2706 shouldUseXPath: function() {
2707 if (!Prototype.BrowserFeatures.XPath) return false;
2708
2709 var e = this.expression;
2710
2711 // Safari 3 chokes on :*-of-type and :empty
2712 if (Prototype.Browser.WebKit &&
2713 (e.include("-of-type") || e.include(":empty")))
2714 return false;
2715
2716 // XPath can't do namespaced attributes, nor can it read
2717 // the "checked" property from DOM nodes
2718 if ((/(\[[\w-]*?:|:checked)/).test(this.expression))
2719 return false;
2720
2721 return true;
2722 },
2723
2724 compileMatcher: function() {
2725 if (this.shouldUseXPath())
2726 return this.compileXPathMatcher();
2727
2728 var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
2729 c = Selector.criteria, le, p, m;
2730
2731 if (Selector._cache[e]) {
2732 this.matcher = Selector._cache[e];
2733 return;
2734 }
2735
2736 this.matcher = ["this.matcher = function(root) {",
2737 "var r = root, h = Selector.handlers, c = false, n;"];
2738
2739 while (e && le != e && (/\S/).test(e)) {
2740 le = e;
2741 for (var i in ps) {
2742 p = ps[i];
2743 if (m = e.match(p)) {
2744 this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
2745 new Template(c[i]).evaluate(m));
2746 e = e.replace(m[0], '');
2747 break;
2748 }
2749 }
2750 }
2751
2752 this.matcher.push("return h.unique(n);\n}");
2753 eval(this.matcher.join('\n'));
2754 Selector._cache[this.expression] = this.matcher;
2755 },
2756
2757 compileXPathMatcher: function() {
2758 var e = this.expression, ps = Selector.patterns,
2759 x = Selector.xpath, le, m;
2760
2761 if (Selector._cache[e]) {
2762 this.xpath = Selector._cache[e]; return;
2763 }
2764
2765 this.matcher = ['.//*'];
2766 while (e && le != e && (/\S/).test(e)) {
2767 le = e;
2768 for (var i in ps) {
2769 if (m = e.match(ps[i])) {
2770 this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :
2771 new Template(x[i]).evaluate(m));
2772 e = e.replace(m[0], '');
2773 break;
2774 }
2775 }
2776 }
2777
2778 this.xpath = this.matcher.join('');
2779 Selector._cache[this.expression] = this.xpath;
2780 },
2781
2782 findElements: function(root) {
2783 root = root || document;
2784 if (this.xpath) return document._getElementsByXPath(this.xpath, root);
2785 return this.matcher(root);
2786 },
2787
2788 match: function(element) {
2789 this.tokens = [];
2790
2791 var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
2792 var le, p, m;
2793
2794 while (e && le !== e && (/\S/).test(e)) {
2795 le = e;
2796 for (var i in ps) {
2797 p = ps[i];
2798 if (m = e.match(p)) {
2799 // use the Selector.assertions methods unless the selector
2800 // is too complex.
2801 if (as[i]) {
2802 this.tokens.push([i, Object.clone(m)]);
2803 e = e.replace(m[0], '');
2804 } else {
2805 // reluctantly do a document-wide search
2806 // and look for a match in the array
2807 return this.findElements(document).include(element);
2808 }
2809 }
2810 }
2811 }
2812
2813 var match = true, name, matches;
2814 for (var i = 0, token; token = this.tokens[i]; i++) {
2815 name = token[0], matches = token[1];
2816 if (!Selector.assertions[name](element, matches)) {
2817 match = false; break;
2818 }
2819 }
2820
2821 return match;
2822 },
2823
2824 toString: function() {
2825 return this.expression;
2826 },
2827
2828 inspect: function() {
2829 return "#<Selector:" + this.expression.inspect() + ">";
2830 }
2831});
2832
2833Object.extend(Selector, {
2834 _cache: { },
2835
2836 xpath: {
2837 descendant: "//*",
2838 child: "/*",
2839 adjacent: "/following-sibling::*[1]",
2840 laterSibling: '/following-sibling::*',
2841 tagName: function(m) {
2842 if (m[1] == '*') return '';
2843 return "[local-name()='" + m[1].toLowerCase() +
2844 "' or local-name()='" + m[1].toUpperCase() + "']";
2845 },
2846 className: "[contains(concat(' ', @class, ' '), ' #{1} ')]",
2847 id: "[@id='#{1}']",
2848 attrPresence: function(m) {
2849 m[1] = m[1].toLowerCase();
2850 return new Template("[@#{1}]").evaluate(m);
2851 },
2852 attr: function(m) {
2853 m[1] = m[1].toLowerCase();
2854 m[3] = m[5] || m[6];
2855 return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
2856 },
2857 pseudo: function(m) {
2858 var h = Selector.xpath.pseudos[m[1]];
2859 if (!h) return '';
2860 if (Object.isFunction(h)) return h(m);
2861 return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
2862 },
2863 operators: {
2864 '=': "[@#{1}='#{3}']",
2865 '!=': "[@#{1}!='#{3}']",
2866 '^=': "[starts-with(@#{1}, '#{3}')]",
2867 '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
2868 '*=': "[contains(@#{1}, '#{3}')]",
2869 '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
2870 '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
2871 },
2872 pseudos: {
2873 'first-child': '[not(preceding-sibling::*)]',
2874 'last-child': '[not(following-sibling::*)]',
2875 'only-child': '[not(preceding-sibling::* or following-sibling::*)]',
2876 'empty': "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
2877 'checked': "[@checked]",
2878 'disabled': "[@disabled]",
2879 'enabled': "[not(@disabled)]",
2880 'not': function(m) {
2881 var e = m[6], p = Selector.patterns,
2882 x = Selector.xpath, le, v;
2883
2884 var exclusion = [];
2885 while (e && le != e && (/\S/).test(e)) {
2886 le = e;
2887 for (var i in p) {
2888 if (m = e.match(p[i])) {
2889 v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);
2890 exclusion.push("(" + v.substring(1, v.length - 1) + ")");
2891 e = e.replace(m[0], '');
2892 break;
2893 }
2894 }
2895 }
2896 return "[not(" + exclusion.join(" and ") + ")]";
2897 },
2898 'nth-child': function(m) {
2899 return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
2900 },
2901 'nth-last-child': function(m) {
2902 return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
2903 },
2904 'nth-of-type': function(m) {
2905 return Selector.xpath.pseudos.nth("position() ", m);
2906 },
2907 'nth-last-of-type': function(m) {
2908 return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
2909 },
2910 'first-of-type': function(m) {
2911 m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
2912 },
2913 'last-of-type': function(m) {
2914 m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
2915 },
2916 'only-of-type': function(m) {
2917 var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
2918 },
2919 nth: function(fragment, m) {
2920 var mm, formula = m[6], predicate;
2921 if (formula == 'even') formula = '2n+0';
2922 if (formula == 'odd') formula = '2n+1';
2923 if (mm = formula.match(/^(\d+)$/)) // digit only
2924 return '[' + fragment + "= " + mm[1] + ']';
2925 if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
2926 if (mm[1] == "-") mm[1] = -1;
2927 var a = mm[1] ? Number(mm[1]) : 1;
2928 var b = mm[2] ? Number(mm[2]) : 0;
2929 predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
2930 "((#{fragment} - #{b}) div #{a} >= 0)]";
2931 return new Template(predicate).evaluate({
2932 fragment: fragment, a: a, b: b });
2933 }
2934 }
2935 }
2936 },
2937
2938 criteria: {
2939 tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;',
2940 className: 'n = h.className(n, r, "#{1}", c); c = false;',
2941 id: 'n = h.id(n, r, "#{1}", c); c = false;',
2942 attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;',
2943 attr: function(m) {
2944 m[3] = (m[5] || m[6]);
2945 return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m);
2946 },
2947 pseudo: function(m) {
2948 if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
2949 return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
2950 },
2951 descendant: 'c = "descendant";',
2952 child: 'c = "child";',
2953 adjacent: 'c = "adjacent";',
2954 laterSibling: 'c = "laterSibling";'
2955 },
2956
2957 patterns: {
2958 // combinators must be listed first
2959 // (and descendant needs to be last combinator)
2960 laterSibling: /^\s*~\s*/,
2961 child: /^\s*>\s*/,
2962 adjacent: /^\s*\+\s*/,
2963 descendant: /^\s/,
2964
2965 // selectors follow
2966 tagName: /^\s*(\*|[\w\-]+)(\b|$)?/,
2967 id: /^#([\w\-\*]+)(\b|$)/,
2968 className: /^\.([\w\-\*]+)(\b|$)/,
2969 pseudo:
2970/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/,
2971 attrPresence: /^\[([\w]+)\]/,
2972 attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
2973 },
2974
2975 // for Selector.match and Element#match
2976 assertions: {
2977 tagName: function(element, matches) {
2978 return matches[1].toUpperCase() == element.tagName.toUpperCase();
2979 },
2980
2981 className: function(element, matches) {
2982 return Element.hasClassName(element, matches[1]);
2983 },
2984
2985 id: function(element, matches) {
2986 return element.id === matches[1];
2987 },
2988
2989 attrPresence: function(element, matches) {
2990 return Element.hasAttribute(element, matches[1]);
2991 },
2992
2993 attr: function(element, matches) {
2994 var nodeValue = Element.readAttribute(element, matches[1]);
2995 return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);
2996 }
2997 },
2998
2999 handlers: {
3000 // UTILITY FUNCTIONS
3001 // joins two collections
3002 concat: function(a, b) {
3003 for (var i = 0, node; node = b[i]; i++)
3004 a.push(node);
3005 return a;
3006 },
3007
3008 // marks an array of nodes for counting
3009 mark: function(nodes) {
3010 var _true = Prototype.emptyFunction;
3011 for (var i = 0, node; node = nodes[i]; i++)
3012 node._countedByPrototype = _true;
3013 return nodes;
3014 },
3015
3016 unmark: function(nodes) {
3017 for (var i = 0, node; node = nodes[i]; i++)
3018 node._countedByPrototype = undefined;
3019 return nodes;
3020 },
3021
3022 // mark each child node with its position (for nth calls)
3023 // "ofType" flag indicates whether we're indexing for nth-of-type
3024 // rather than nth-child
3025 index: function(parentNode, reverse, ofType) {
3026 parentNode._countedByPrototype = Prototype.emptyFunction;
3027 if (reverse) {
3028 for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
3029 var node = nodes[i];
3030 if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
3031 }
3032 } else {
3033 for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
3034 if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
3035 }
3036 },
3037
3038 // filters out duplicates and extends all nodes
3039 unique: function(nodes) {
3040 if (nodes.length == 0) return nodes;
3041 var results = [], n;
3042 for (var i = 0, l = nodes.length; i < l; i++)
3043 if (!(n = nodes[i])._countedByPrototype) {
3044 n._countedByPrototype = Prototype.emptyFunction;
3045 results.push(Element.extend(n));
3046 }
3047 return Selector.handlers.unmark(results);
3048 },
3049
3050 // COMBINATOR FUNCTIONS
3051 descendant: function(nodes) {
3052 var h = Selector.handlers;
3053 for (var i = 0, results = [], node; node = nodes[i]; i++)
3054 h.concat(results, node.getElementsByTagName('*'));
3055 return results;
3056 },
3057
3058 child: function(nodes) {
3059 var h = Selector.handlers;
3060 for (var i = 0, results = [], node; node = nodes[i]; i++) {
3061 for (var j = 0, child; child = node.childNodes[j]; j++)
3062 if (child.nodeType == 1 && child.tagName != '!') results.push(child);
3063 }
3064 return results;
3065 },
3066
3067 adjacent: function(nodes) {
3068 for (var i = 0, results = [], node; node = nodes[i]; i++) {
3069 var next = this.nextElementSibling(node);
3070 if (next) results.push(next);
3071 }
3072 return results;
3073 },
3074
3075 laterSibling: function(nodes) {
3076 var h = Selector.handlers;
3077 for (var i = 0, results = [], node; node = nodes[i]; i++)
3078 h.concat(results, Element.nextSiblings(node));
3079 return results;
3080 },
3081
3082 nextElementSibling: function(node) {
3083 while (node = node.nextSibling)
3084 if (node.nodeType == 1) return node;
3085 return null;
3086 },
3087
3088 previousElementSibling: function(node) {
3089 while (node = node.previousSibling)
3090 if (node.nodeType == 1) return node;
3091 return null;
3092 },
3093
3094 // TOKEN FUNCTIONS
3095 tagName: function(nodes, root, tagName, combinator) {
3096 var uTagName = tagName.toUpperCase();
3097 var results = [], h = Selector.handlers;
3098 if (nodes) {
3099 if (combinator) {
3100 // fastlane for ordinary descendant combinators
3101 if (combinator == "descendant") {
3102 for (var i = 0, node; node = nodes[i]; i++)
3103 h.concat(results, node.getElementsByTagName(tagName));
3104 return results;
3105 } else nodes = this[combinator](nodes);
3106 if (tagName == "*") return nodes;
3107 }
3108 for (var i = 0, node; node = nodes[i]; i++)
3109 if (node.tagName.toUpperCase() === uTagName) results.push(node);
3110 return results;
3111 } else return root.getElementsByTagName(tagName);
3112 },
3113
3114 id: function(nodes, root, id, combinator) {
3115 var targetNode = $(id), h = Selector.handlers;
3116 if (!targetNode) return [];
3117 if (!nodes && root == document) return [targetNode];
3118 if (nodes) {
3119 if (combinator) {
3120 if (combinator == 'child') {
3121 for (var i = 0, node; node = nodes[i]; i++)
3122 if (targetNode.parentNode == node) return [targetNode];
3123 } else if (combinator == 'descendant') {
3124 for (var i = 0, node; node = nodes[i]; i++)
3125 if (Element.descendantOf(targetNode, node)) return [targetNode];
3126 } else if (combinator == 'adjacent') {
3127 for (var i = 0, node; node = nodes[i]; i++)
3128 if (Selector.handlers.previousElementSibling(targetNode) == node)
3129 return [targetNode];
3130 } else nodes = h[combinator](nodes);
3131 }
3132 for (var i = 0, node; node = nodes[i]; i++)
3133 if (node == targetNode) return [targetNode];
3134 return [];
3135 }
3136 return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
3137 },
3138
3139 className: function(nodes, root, className, combinator) {
3140 if (nodes && combinator) nodes = this[combinator](nodes);
3141 return Selector.handlers.byClassName(nodes, root, className);
3142 },
3143
3144 byClassName: function(nodes, root, className) {
3145 if (!nodes) nodes = Selector.handlers.descendant([root]);
3146 var needle = ' ' + className + ' ';
3147 for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
3148 nodeClassName = node.className;
3149 if (nodeClassName.length == 0) continue;
3150 if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
3151 results.push(node);
3152 }
3153 return results;
3154 },
3155
3156 attrPresence: function(nodes, root, attr, combinator) {
3157 if (!nodes) nodes = root.getElementsByTagName("*");
3158 if (nodes && combinator) nodes = this[combinator](nodes);
3159 var results = [];
3160 for (var i = 0, node; node = nodes[i]; i++)
3161 if (Element.hasAttribute(node, attr)) results.push(node);
3162 return results;
3163 },
3164
3165 attr: function(nodes, root, attr, value, operator, combinator) {
3166 if (!nodes) nodes = root.getElementsByTagName("*");
3167 if (nodes && combinator) nodes = this[combinator](nodes);
3168 var handler = Selector.operators[operator], results = [];
3169 for (var i = 0, node; node = nodes[i]; i++) {
3170 var nodeValue = Element.readAttribute(node, attr);
3171 if (nodeValue === null) continue;
3172 if (handler(nodeValue, value)) results.push(node);
3173 }
3174 return results;
3175 },
3176
3177 pseudo: function(nodes, name, value, root, combinator) {
3178 if (nodes && combinator) nodes = this[combinator](nodes);
3179 if (!nodes) nodes = root.getElementsByTagName("*");
3180 return Selector.pseudos[name](nodes, value, root);
3181 }
3182 },
3183
3184 pseudos: {
3185 'first-child': function(nodes, value, root) {
3186 for (var i = 0, results = [], node; node = nodes[i]; i++) {
3187 if (Selector.handlers.previousElementSibling(node)) continue;
3188 results.push(node);
3189 }
3190 return results;
3191 },
3192 'last-child': function(nodes, value, root) {
3193 for (var i = 0, results = [], node; node = nodes[i]; i++) {
3194 if (Selector.handlers.nextElementSibling(node)) continue;
3195 results.push(node);
3196 }
3197 return results;
3198 },
3199 'only-child': function(nodes, value, root) {
3200 var h = Selector.handlers;
3201 for (var i = 0, results = [], node; node = nodes[i]; i++)
3202 if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
3203 results.push(node);
3204 return results;
3205 },
3206 'nth-child': function(nodes, formula, root) {
3207 return Selector.pseudos.nth(nodes, formula, root);
3208 },
3209 'nth-last-child': function(nodes, formula, root) {
3210 return Selector.pseudos.nth(nodes, formula, root, true);
3211 },
3212 'nth-of-type': function(nodes, formula, root) {
3213 return Selector.pseudos.nth(nodes, formula, root, false, true);
3214 },
3215 'nth-last-of-type': function(nodes, formula, root) {
3216 return Selector.pseudos.nth(nodes, formula, root, true, true);
3217 },
3218 'first-of-type': function(nodes, formula, root) {
3219 return Selector.pseudos.nth(nodes, "1", root, false, true);
3220 },
3221 'last-of-type': function(nodes, formula, root) {
3222 return Selector.pseudos.nth(nodes, "1", root, true, true);
3223 },
3224 'only-of-type': function(nodes, formula, root) {
3225 var p = Selector.pseudos;
3226 return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
3227 },
3228
3229 // handles the an+b logic
3230 getIndices: function(a, b, total) {
3231 if (a == 0) return b > 0 ? [b] : [];
3232 return $R(1, total).inject([], function(memo, i) {
3233 if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
3234 return memo;
3235 });
3236 },
3237
3238 // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
3239 nth: function(nodes, formula, root, reverse, ofType) {
3240 if (nodes.length == 0) return [];
3241 if (formula == 'even') formula = '2n+0';
3242 if (formula == 'odd') formula = '2n+1';
3243 var h = Selector.handlers, results = [], indexed = [], m;
3244 h.mark(nodes);
3245 for (var i = 0, node; node = nodes[i]; i++) {
3246 if (!node.parentNode._countedByPrototype) {
3247 h.index(node.parentNode, reverse, ofType);
3248 indexed.push(node.parentNode);
3249 }
3250 }
3251 if (formula.match(/^\d+$/)) { // just a number
3252 formula = Number(formula);
3253 for (var i = 0, node; node = nodes[i]; i++)
3254 if (node.nodeIndex == formula) results.push(node);
3255 } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
3256 if (m[1] == "-") m[1] = -1;
3257 var a = m[1] ? Number(m[1]) : 1;
3258 var b = m[2] ? Number(m[2]) : 0;
3259 var indices = Selector.pseudos.getIndices(a, b, nodes.length);
3260 for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
3261 for (var j = 0; j < l; j++)
3262 if (node.nodeIndex == indices[j]) results.push(node);
3263 }
3264 }
3265 h.unmark(nodes);
3266 h.unmark(indexed);
3267 return results;
3268 },
3269
3270 'empty': function(nodes, value, root) {
3271 for (var i = 0, results = [], node; node = nodes[i]; i++) {
3272 // IE treats comments as element nodes
3273 if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
3274 results.push(node);
3275 }
3276 return results;
3277 },
3278
3279 'not': function(nodes, selector, root) {
3280 var h = Selector.handlers, selectorType, m;
3281 var exclusions = new Selector(selector).findElements(root);
3282 h.mark(exclusions);
3283 for (var i = 0, results = [], node; node = nodes[i]; i++)
3284 if (!node._countedByPrototype) results.push(node);
3285 h.unmark(exclusions);
3286 return results;
3287 },
3288
3289 'enabled': function(nodes, value, root) {
3290 for (var i = 0, results = [], node; node = nodes[i]; i++)
3291 if (!node.disabled) results.push(node);
3292 return results;
3293 },
3294
3295 'disabled': function(nodes, value, root) {
3296 for (var i = 0, results = [], node; node = nodes[i]; i++)
3297 if (node.disabled) results.push(node);
3298 return results;
3299 },
3300
3301 'checked': function(nodes, value, root) {
3302 for (var i = 0, results = [], node; node = nodes[i]; i++)
3303 if (node.checked) results.push(node);
3304 return results;
3305 }
3306 },
3307
3308 operators: {
3309 '=': function(nv, v) { return nv == v; },
3310 '!=': function(nv, v) { return nv != v; },
3311 '^=': function(nv, v) { return nv.startsWith(v); },
3312 '$=': function(nv, v) { return nv.endsWith(v); },
3313 '*=': function(nv, v) { return nv.include(v); },
3314 '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
3315 '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
3316 },
3317
3318 split: function(expression) {
3319 var expressions = [];
3320 expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
3321 expressions.push(m[1].strip());
3322 });
3323 return expressions;
3324 },
3325
3326 matchElements: function(elements, expression) {
3327 var matches = $$(expression), h = Selector.handlers;
3328 h.mark(matches);
3329 for (var i = 0, results = [], element; element = elements[i]; i++)
3330 if (element._countedByPrototype) results.push(element);
3331 h.unmark(matches);
3332 return results;
3333 },
3334
3335 findElement: function(elements, expression, index) {
3336 if (Object.isNumber(expression)) {
3337 index = expression; expression = false;
3338 }
3339 return Selector.matchElements(elements, expression || '*')[index || 0];
3340 },
3341
3342 findChildElements: function(element, expressions) {
3343 expressions = Selector.split(expressions.join(','));
3344 var results = [], h = Selector.handlers;
3345 for (var i = 0, l = expressions.length, selector; i < l; i++) {
3346 selector = new Selector(expressions[i].strip());
3347 h.concat(results, selector.findElements(element));
3348 }
3349 return (l > 1) ? h.unique(results) : results;
3350 }
3351});
3352
3353if (Prototype.Browser.IE) {
3354 Object.extend(Selector.handlers, {
3355 // IE returns comment nodes on getElementsByTagName("*").
3356 // Filter them out.
3357 concat: function(a, b) {
3358 for (var i = 0, node; node = b[i]; i++)
3359 if (node.tagName !== "!") a.push(node);
3360 return a;
3361 },
3362
3363 // IE improperly serializes _countedByPrototype in (inner|outer)HTML.
3364 unmark: function(nodes) {
3365 for (var i = 0, node; node = nodes[i]; i++)
3366 node.removeAttribute('_countedByPrototype');
3367 return nodes;
3368 }
3369 });
3370}
3371
3372function $$() {
3373 return Selector.findChildElements(document, $A(arguments));
3374}
3375var Form = {
3376 reset: function(form) {
3377 $(form).reset();
3378 return form;
3379 },
3380
3381 serializeElements: function(elements, options) {
3382 if (typeof options != 'object') options = { hash: !!options };
3383 else if (Object.isUndefined(options.hash)) options.hash = true;
3384 var key, value, submitted = false, submit = options.submit;
3385
3386 var data = elements.inject({ }, function(result, element) {
3387 if (!element.disabled && element.name) {
3388 key = element.name; value = $(element).getValue();
3389 if (value != null && (element.type != 'submit' || (!submitted &&
3390 submit !== false && (!submit || key == submit) && (submitted = true)))) {
3391 if (key in result) {
3392 // a key is already present; construct an array of values
3393 if (!Object.isArray(result[key])) result[key] = [result[key]];
3394 result[key].push(value);
3395 }
3396 else result[key] = value;
3397 }
3398 }
3399 return result;
3400 });
3401
3402 return options.hash ? data : Object.toQueryString(data);
3403 }
3404};
3405
3406
3407Form.Methods = {
3408 serialize: function(form, options) {
3409 return Form.serializeElements(Form.getElements(form), options);
3410 },
3411
3412 getElements: function(form) {
3413 return $A($(form).getElementsByTagName('*')).inject([],
3414 function(elements, child) {
3415 if (Form.Element.Serializers[child.tagName.toLowerCase()])
3416 elements.push(Element.extend(child));
3417 return elements;
3418 }
3419 );
3420 },
3421
3422 getInputs: function(form, typeName, name) {
3423 form = $(form);
3424 var inputs = form.getElementsByTagName('input');
3425
3426 if (!typeName && !name) return $A(inputs).map(Element.extend);
3427
3428 for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
3429 var input = inputs[i];
3430 if ((typeName && input.type != typeName) || (name && input.name != name))
3431 continue;
3432 matchingInputs.push(Element.extend(input));
3433 }
3434
3435 return matchingInputs;
3436 },
3437
3438 disable: function(form) {
3439 form = $(form);
3440 Form.getElements(form).invoke('disable');
3441 return form;
3442 },
3443
3444 enable: function(form) {
3445 form = $(form);
3446 Form.getElements(form).invoke('enable');
3447 return form;
3448 },
3449
3450 findFirstElement: function(form) {
3451 var elements = $(form).getElements().findAll(function(element) {
3452 return 'hidden' != element.type && !element.disabled;
3453 });
3454 var firstByIndex = elements.findAll(function(element) {
3455 return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
3456 }).sortBy(function(element) { return element.tabIndex }).first();
3457
3458 return firstByIndex ? firstByIndex : elements.find(function(element) {
3459 return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
3460 });
3461 },
3462
3463 focusFirstElement: function(form) {
3464 form = $(form);
3465 form.findFirstElement().activate();
3466 return form;
3467 },
3468
3469 request: function(form, options) {
3470 form = $(form), options = Object.clone(options || { });
3471
3472 var params = options.parameters, action = form.readAttribute('action') || '';
3473 if (action.blank()) action = window.location.href;
3474 options.parameters = form.serialize(true);
3475
3476 if (params) {
3477 if (Object.isString(params)) params = params.toQueryParams();
3478 Object.extend(options.parameters, params);
3479 }
3480
3481 if (form.hasAttribute('method') && !options.method)
3482 options.method = form.method;
3483
3484 return new Ajax.Request(action, options);
3485 }
3486};
3487
3488/*--------------------------------------------------------------------------*/
3489
3490Form.Element = {
3491 focus: function(element) {
3492 $(element).focus();
3493 return element;
3494 },
3495
3496 select: function(element) {
3497 $(element).select();
3498 return element;
3499 }
3500};
3501
3502Form.Element.Methods = {
3503 serialize: function(element) {
3504 element = $(element);
3505 if (!element.disabled && element.name) {
3506 var value = element.getValue();
3507 if (value != undefined) {
3508 var pair = { };
3509 pair[element.name] = value;
3510 return Object.toQueryString(pair);
3511 }
3512 }
3513 return '';
3514 },
3515
3516 getValue: function(element) {
3517 element = $(element);
3518 var method = element.tagName.toLowerCase();
3519 return Form.Element.Serializers[method](element);
3520 },
3521
3522 setValue: function(element, value) {
3523 element = $(element);
3524 var method = element.tagName.toLowerCase();
3525 Form.Element.Serializers[method](element, value);
3526 return element;
3527 },
3528
3529 clear: function(element) {
3530 $(element).value = '';
3531 return element;
3532 },
3533
3534 present: function(element) {
3535 return $(element).value != '';
3536 },
3537
3538 activate: function(element) {
3539 element = $(element);
3540 try {
3541 element.focus();
3542 if (element.select && (element.tagName.toLowerCase() != 'input' ||
3543 !['button', 'reset', 'submit'].include(element.type)))
3544 element.select();
3545 } catch (e) { }
3546 return element;
3547 },
3548
3549 disable: function(element) {
3550 element = $(element);
3551 element.blur();
3552 element.disabled = true;
3553 return element;
3554 },
3555
3556 enable: function(element) {
3557 element = $(element);
3558 element.disabled = false;
3559 return element;
3560 }
3561};
3562
3563/*--------------------------------------------------------------------------*/
3564
3565var Field = Form.Element;
3566var $F = Form.Element.Methods.getValue;
3567
3568/*--------------------------------------------------------------------------*/
3569
3570Form.Element.Serializers = {
3571 input: function(element, value) {
3572 switch (element.type.toLowerCase()) {
3573 case 'checkbox':
3574 case 'radio':
3575 return Form.Element.Serializers.inputSelector(element, value);
3576 default:
3577 return Form.Element.Serializers.textarea(element, value);
3578 }
3579 },
3580
3581 inputSelector: function(element, value) {
3582 if (Object.isUndefined(value)) return element.checked ? element.value : null;
3583 else element.checked = !!value;
3584 },
3585
3586 textarea: function(element, value) {
3587 if (Object.isUndefined(value)) return element.value;
3588 else element.value = value;
3589 },
3590
3591 select: function(element, index) {
3592 if (Object.isUndefined(index))
3593 return this[element.type == 'select-one' ?
3594 'selectOne' : 'selectMany'](element);
3595 else {
3596 var opt, value, single = !Object.isArray(index);
3597 for (var i = 0, length = element.length; i < length; i++) {
3598 opt = element.options[i];
3599 value = this.optionValue(opt);
3600 if (single) {
3601 if (value == index) {
3602 opt.selected = true;
3603 return;
3604 }
3605 }
3606 else opt.selected = index.include(value);
3607 }
3608 }
3609 },
3610
3611 selectOne: function(element) {
3612 var index = element.selectedIndex;
3613 return index >= 0 ? this.optionValue(element.options[index]) : null;
3614 },
3615
3616 selectMany: function(element) {
3617 var values, length = element.length;
3618 if (!length) return null;
3619
3620 for (var i = 0, values = []; i < length; i++) {
3621 var opt = element.options[i];
3622 if (opt.selected) values.push(this.optionValue(opt));
3623 }
3624 return values;
3625 },
3626
3627 optionValue: function(opt) {
3628 // extend element because hasAttribute may not be native
3629 return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
3630 }
3631};
3632
3633/*--------------------------------------------------------------------------*/
3634
3635Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
3636 initialize: function($super, element, frequency, callback) {
3637 $super(callback, frequency);
3638 this.element = $(element);
3639 this.lastValue = this.getValue();
3640 },
3641
3642 execute: function() {
3643 var value = this.getValue();
3644 if (Object.isString(this.lastValue) && Object.isString(value) ?
3645 this.lastValue != value : String(this.lastValue) != String(value)) {
3646 this.callback(this.element, value);
3647 this.lastValue = value;
3648 }
3649 }
3650});
3651
3652Form.Element.Observer = Class.create(Abstract.TimedObserver, {
3653 getValue: function() {
3654 return Form.Element.getValue(this.element);
3655 }
3656});
3657
3658Form.Observer = Class.create(Abstract.TimedObserver, {
3659 getValue: function() {
3660 return Form.serialize(this.element);
3661 }
3662});
3663
3664/*--------------------------------------------------------------------------*/
3665
3666Abstract.EventObserver = Class.create({
3667 initialize: function(element, callback) {
3668 this.element = $(element);
3669 this.callback = callback;
3670
3671 this.lastValue = this.getValue();
3672 if (this.element.tagName.toLowerCase() == 'form')
3673 this.registerFormCallbacks();
3674 else
3675 this.registerCallback(this.element);
3676 },
3677
3678 onElementEvent: function() {
3679 var value = this.getValue();
3680 if (this.lastValue != value) {
3681 this.callback(this.element, value);
3682 this.lastValue = value;
3683 }
3684 },
3685
3686 registerFormCallbacks: function() {
3687 Form.getElements(this.element).each(this.registerCallback, this);
3688 },
3689
3690 registerCallback: function(element) {
3691 if (element.type) {
3692 switch (element.type.toLowerCase()) {
3693 case 'checkbox':
3694 case 'radio':
3695 Event.observe(element, 'click', this.onElementEvent.bind(this));
3696 break;
3697 default:
3698 Event.observe(element, 'change', this.onElementEvent.bind(this));
3699 break;
3700 }
3701 }
3702 }
3703});
3704
3705Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
3706 getValue: function() {
3707 return Form.Element.getValue(this.element);
3708 }
3709});
3710
3711Form.EventObserver = Class.create(Abstract.EventObserver, {
3712 getValue: function() {
3713 return Form.serialize(this.element);
3714 }
3715});
3716if (!window.Event) var Event = { };
3717
3718Object.extend(Event, {
3719 KEY_BACKSPACE: 8,
3720 KEY_TAB: 9,
3721 KEY_RETURN: 13,
3722 KEY_ESC: 27,
3723 KEY_LEFT: 37,
3724 KEY_UP: 38,
3725 KEY_RIGHT: 39,
3726 KEY_DOWN: 40,
3727 KEY_DELETE: 46,
3728 KEY_HOME: 36,
3729 KEY_END: 35,
3730 KEY_PAGEUP: 33,
3731 KEY_PAGEDOWN: 34,
3732 KEY_INSERT: 45,
3733
3734 cache: { },
3735
3736 relatedTarget: function(event) {
3737 var element;
3738 switch(event.type) {
3739 case 'mouseover': element = event.fromElement; break;
3740 case 'mouseout': element = event.toElement; break;
3741 default: return null;
3742 }
3743 return Element.extend(element);
3744 }
3745});
3746
3747Event.Methods = (function() {
3748 var isButton;
3749
3750 if (Prototype.Browser.IE) {
3751 var buttonMap = { 0: 1, 1: 4, 2: 2 };
3752 isButton = function(event, code) {
3753 return event.button == buttonMap[code];
3754 };
3755
3756 } else if (Prototype.Browser.WebKit) {
3757 isButton = function(event, code) {
3758 switch (code) {
3759 case 0: return event.which == 1 && !event.metaKey;
3760 case 1: return event.which == 1 && event.metaKey;
3761 default: return false;
3762 }
3763 };
3764
3765 } else {
3766 isButton = function(event, code) {
3767 return event.which ? (event.which === code + 1) : (event.button === code);
3768 };
3769 }
3770
3771 return {
3772 isLeftClick: function(event) { return isButton(event, 0) },
3773 isMiddleClick: function(event) { return isButton(event, 1) },
3774 isRightClick: function(event) { return isButton(event, 2) },
3775
3776 element: function(event) {
3777 var node = Event.extend(event).target;
3778 return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node);
3779 },
3780
3781 findElement: function(event, expression) {
3782 var element = Event.element(event);
3783 if (!expression) return element;
3784 var elements = [element].concat(element.ancestors());
3785 return Selector.findElement(elements, expression, 0);
3786 },
3787
3788 pointer: function(event) {
3789 return {
3790 x: event.pageX || (event.clientX +
3791 (document.documentElement.scrollLeft || document.body.scrollLeft)),
3792 y: event.pageY || (event.clientY +
3793 (document.documentElement.scrollTop || document.body.scrollTop))
3794 };
3795 },
3796
3797 pointerX: function(event) { return Event.pointer(event).x },
3798 pointerY: function(event) { return Event.pointer(event).y },
3799
3800 stop: function(event) {
3801 Event.extend(event);
3802 event.preventDefault();
3803 event.stopPropagation();
3804 event.stopped = true;
3805 }
3806 };
3807})();
3808
3809Event.extend = (function() {
3810 var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
3811 m[name] = Event.Methods[name].methodize();
3812 return m;
3813 });
3814
3815 if (Prototype.Browser.IE) {
3816 Object.extend(methods, {
3817 stopPropagation: function() { this.cancelBubble = true },
3818 preventDefault: function() { this.returnValue = false },
3819 inspect: function() { return "[object Event]" }
3820 });
3821
3822 return function(event) {
3823 if (!event) return false;
3824 if (event._extendedByPrototype) return event;
3825
3826 event._extendedByPrototype = Prototype.emptyFunction;
3827 var pointer = Event.pointer(event);
3828 Object.extend(event, {
3829 target: event.srcElement,
3830 relatedTarget: Event.relatedTarget(event),
3831 pageX: pointer.x,
3832 pageY: pointer.y
3833 });
3834 return Object.extend(event, methods);
3835 };
3836
3837 } else {
3838 Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__;
3839 Object.extend(Event.prototype, methods);
3840 return Prototype.K;
3841 }
3842})();
3843
3844Object.extend(Event, (function() {
3845 var cache = Event.cache;
3846
3847 function getEventID(element) {
3848 if (element._prototypeEventID) return element._prototypeEventID[0];
3849 arguments.callee.id = arguments.callee.id || 1;
3850 return element._prototypeEventID = [++arguments.callee.id];
3851 }
3852
3853 function getDOMEventName(eventName) {
3854 if (eventName && eventName.include(':')) return "dataavailable";
3855 return eventName;
3856 }
3857
3858 function getCacheForID(id) {
3859 return cache[id] = cache[id] || { };
3860 }
3861
3862 function getWrappersForEventName(id, eventName) {
3863 var c = getCacheForID(id);
3864 return c[eventName] = c[eventName] || [];
3865 }
3866
3867 function createWrapper(element, eventName, handler) {
3868 var id = getEventID(element);
3869 var c = getWrappersForEventName(id, eventName);
3870 if (c.pluck("handler").include(handler)) return false;
3871
3872 var wrapper = function(event) {
3873 if (!Event || !Event.extend ||
3874 (event.eventName && event.eventName != eventName))
3875 return false;
3876
3877 Event.extend(event);
3878 handler.call(element, event);
3879 };
3880
3881 wrapper.handler = handler;
3882 c.push(wrapper);
3883 return wrapper;
3884 }
3885
3886 function findWrapper(id, eventName, handler) {
3887 var c = getWrappersForEventName(id, eventName);
3888 return c.find(function(wrapper) { return wrapper.handler == handler });
3889 }
3890
3891 function destroyWrapper(id, eventName, handler) {
3892 var c = getCacheForID(id);
3893 if (!c[eventName]) return false;
3894 c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
3895 }
3896
3897 function destroyCache() {
3898 for (var id in cache)
3899 for (var eventName in cache[id])
3900 cache[id][eventName] = null;
3901 }
3902
3903 if (window.attachEvent) {
3904 window.attachEvent("onunload", destroyCache);
3905 }
3906
3907 return {
3908 observe: function(element, eventName, handler) {
3909 element = $(element);
3910 var name = getDOMEventName(eventName);
3911
3912 var wrapper = createWrapper(element, eventName, handler);
3913 if (!wrapper) return element;
3914
3915 if (element.addEventListener) {
3916 element.addEventListener(name, wrapper, false);
3917 } else {
3918 element.attachEvent("on" + name, wrapper);
3919 }
3920
3921 return element;
3922 },
3923
3924 stopObserving: function(element, eventName, handler) {
3925 element = $(element);
3926 var id = getEventID(element), name = getDOMEventName(eventName);
3927
3928 if (!handler && eventName) {
3929 getWrappersForEventName(id, eventName).each(function(wrapper) {
3930 element.stopObserving(eventName, wrapper.handler);
3931 });
3932 return element;
3933
3934 } else if (!eventName) {
3935 Object.keys(getCacheForID(id)).each(function(eventName) {
3936 element.stopObserving(eventName);
3937 });
3938 return element;
3939 }
3940
3941 var wrapper = findWrapper(id, eventName, handler);
3942 if (!wrapper) return element;
3943
3944 if (element.removeEventListener) {
3945 element.removeEventListener(name, wrapper, false);
3946 } else {
3947 element.detachEvent("on" + name, wrapper);
3948 }
3949
3950 destroyWrapper(id, eventName, handler);
3951
3952 return element;
3953 },
3954
3955 fire: function(element, eventName, memo) {
3956 element = $(element);
3957 if (element == document && document.createEvent && !element.dispatchEvent)
3958 element = document.documentElement;
3959
3960 var event;
3961 if (document.createEvent) {
3962 event = document.createEvent("HTMLEvents");
3963 event.initEvent("dataavailable", true, true);
3964 } else {
3965 event = document.createEventObject();
3966 event.eventType = "ondataavailable";
3967 }
3968
3969 event.eventName = eventName;
3970 event.memo = memo || { };
3971
3972 if (document.createEvent) {
3973 element.dispatchEvent(event);
3974 } else {
3975 element.fireEvent(event.eventType, event);
3976 }
3977
3978 return Event.extend(event);
3979 }
3980 };
3981})());
3982
3983Object.extend(Event, Event.Methods);
3984
3985Element.addMethods({
3986 fire: Event.fire,
3987 observe: Event.observe,
3988 stopObserving: Event.stopObserving
3989});
3990
3991Object.extend(document, {
3992 fire: Element.Methods.fire.methodize(),
3993 observe: Element.Methods.observe.methodize(),
3994 stopObserving: Element.Methods.stopObserving.methodize(),
3995 loaded: false
3996});
3997
3998(function() {
3999 /* Support for the DOMContentLoaded event is based on work by Dan Webb,
4000 Matthias Miller, Dean Edwards and John Resig. */
4001
4002 var timer;
4003
4004 function fireContentLoadedEvent() {
4005 if (document.loaded) return;
4006 if (timer) window.clearInterval(timer);
4007 document.fire("dom:loaded");
4008 document.loaded = true;
4009 }
4010
4011 if (document.addEventListener) {
4012 if (Prototype.Browser.WebKit) {
4013 timer = window.setInterval(function() {
4014 if (/loaded|complete/.test(document.readyState))
4015 fireContentLoadedEvent();
4016 }, 0);
4017
4018 Event.observe(window, "load", fireContentLoadedEvent);
4019
4020 } else {
4021 document.addEventListener("DOMContentLoaded",
4022 fireContentLoadedEvent, false);
4023 }
4024
4025 } else {
4026 document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");
4027 $("__onDOMContentLoaded").onreadystatechange = function() {
4028 if (this.readyState == "complete") {
4029 this.onreadystatechange = null;
4030 fireContentLoadedEvent();
4031 }
4032 };
4033 }
4034})();
4035/*------------------------------- DEPRECATED -------------------------------*/
4036
4037Hash.toQueryString = Object.toQueryString;
4038
4039var Toggle = { display: Element.toggle };
4040
4041Element.Methods.childOf = Element.Methods.descendantOf;
4042
4043var Insertion = {
4044 Before: function(element, content) {
4045 return Element.insert(element, {before:content});
4046 },
4047
4048 Top: function(element, content) {
4049 return Element.insert(element, {top:content});
4050 },
4051
4052 Bottom: function(element, content) {
4053 return Element.insert(element, {bottom:content});
4054 },
4055
4056 After: function(element, content) {
4057 return Element.insert(element, {after:content});
4058 }
4059};
4060
4061var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
4062
4063// This should be moved to script.aculo.us; notice the deprecated methods
4064// further below, that map to the newer Element methods.
4065var Position = {
4066 // set to true if needed, warning: firefox performance problems
4067 // NOT neeeded for page scrolling, only if draggable contained in
4068 // scrollable elements
4069 includeScrollOffsets: false,
4070
4071 // must be called before calling withinIncludingScrolloffset, every time the
4072 // page is scrolled
4073 prepare: function() {
4074 this.deltaX = window.pageXOffset
4075 || document.documentElement.scrollLeft
4076 || document.body.scrollLeft
4077 || 0;
4078 this.deltaY = window.pageYOffset
4079 || document.documentElement.scrollTop
4080 || document.body.scrollTop
4081 || 0;
4082 },
4083
4084 // caches x/y coordinate pair to use with overlap
4085 within: function(element, x, y) {
4086 if (this.includeScrollOffsets)
4087 return this.withinIncludingScrolloffsets(element, x, y);
4088 this.xcomp = x;
4089 this.ycomp = y;
4090 this.offset = Element.cumulativeOffset(element);
4091
4092 return (y >= this.offset[1] &&
4093 y < this.offset[1] + element.offsetHeight &&
4094 x >= this.offset[0] &&
4095 x < this.offset[0] + element.offsetWidth);
4096 },
4097
4098 withinIncludingScrolloffsets: function(element, x, y) {
4099 var offsetcache = Element.cumulativeScrollOffset(element);
4100
4101 this.xcomp = x + offsetcache[0] - this.deltaX;
4102 this.ycomp = y + offsetcache[1] - this.deltaY;
4103 this.offset = Element.cumulativeOffset(element);
4104
4105 return (this.ycomp >= this.offset[1] &&
4106 this.ycomp < this.offset[1] + element.offsetHeight &&
4107 this.xcomp >= this.offset[0] &&
4108 this.xcomp < this.offset[0] + element.offsetWidth);
4109 },
4110
4111 // within must be called directly before
4112 overlap: function(mode, element) {
4113 if (!mode) return 0;
4114 if (mode == 'vertical')
4115 return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
4116 element.offsetHeight;
4117 if (mode == 'horizontal')
4118 return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
4119 element.offsetWidth;
4120 },
4121
4122 // Deprecation layer -- use newer Element methods now (1.5.2).
4123
4124 cumulativeOffset: Element.Methods.cumulativeOffset,
4125
4126 positionedOffset: Element.Methods.positionedOffset,
4127
4128 absolutize: function(element) {
4129 Position.prepare();
4130 return Element.absolutize(element);
4131 },
4132
4133 relativize: function(element) {
4134 Position.prepare();
4135 return Element.relativize(element);
4136 },
4137
4138 realOffset: Element.Methods.cumulativeScrollOffset,
4139
4140 offsetParent: Element.Methods.getOffsetParent,
4141
4142 page: Element.Methods.viewportOffset,
4143
4144 clone: function(source, target, options) {
4145 options = options || { };
4146 return Element.clonePosition(target, source, options);
4147 }
4148};
4149
4150/*--------------------------------------------------------------------------*/
4151
4152if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
4153 function iter(name) {
4154 return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
4155 }
4156
4157 instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
4158 function(element, className) {
4159 className = className.toString().strip();
4160 var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
4161 return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
4162 } : function(element, className) {
4163 className = className.toString().strip();
4164 var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
4165 if (!classNames && !className) return elements;
4166
4167 var nodes = $(element).getElementsByTagName('*');
4168 className = ' ' + className + ' ';
4169
4170 for (var i = 0, child, cn; child = nodes[i]; i++) {
4171 if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
4172 (classNames && classNames.all(function(name) {
4173 return !name.toString().blank() && cn.include(' ' + name + ' ');
4174 }))))
4175 elements.push(Element.extend(child));
4176 }
4177 return elements;
4178 };
4179
4180 return function(className, parentElement) {
4181 return $(parentElement || document.body).getElementsByClassName(className);
4182 };
4183}(Element.Methods);
4184
4185/*--------------------------------------------------------------------------*/
4186
4187Element.ClassNames = Class.create();
4188Element.ClassNames.prototype = {
4189 initialize: function(element) {
4190 this.element = $(element);
4191 },
4192
4193 _each: function(iterator) {
4194 this.element.className.split(/\s+/).select(function(name) {
4195 return name.length > 0;
4196 })._each(iterator);
4197 },
4198
4199 set: function(className) {
4200 this.element.className = className;
4201 },
4202
4203 add: function(classNameToAdd) {
4204 if (this.include(classNameToAdd)) return;
4205 this.set($A(this).concat(classNameToAdd).join(' '));
4206 },
4207
4208 remove: function(classNameToRemove) {
4209 if (!this.include(classNameToRemove)) return;
4210 this.set($A(this).without(classNameToRemove).join(' '));
4211 },
4212
4213 toString: function() {
4214 return $A(this).join(' ');
4215 }
4216};
4217
4218Object.extend(Element.ClassNames.prototype, Enumerable);
4219
4220/*--------------------------------------------------------------------------*/
4221
4222Element.addMethods(); \ No newline at end of file
diff --git a/bin/data/updater.js b/bin/data/updater.js
new file mode 100644
index 0000000..20201f8
--- /dev/null
+++ b/bin/data/updater.js
@@ -0,0 +1,20 @@
1var updater = Class.create({
2 initialize: function(divToUpdate, interval, file) {
3 this.divToUpdate = divToUpdate;
4 this.interval = interval;
5 this.file = file;
6 new PeriodicalExecuter(this.getUpdate.bind(this), this.interval);
7 },
8
9 getUpdate: function() {
10 var oOptions = {
11 method: "POST",
12 parameters: "intervalPeriod="+this.interval,
13 asynchronous: true,
14 onComplete: function (oXHR, Json) {
15 $(this.divToUpdate).innerHTML = oXHR.responseText;
16 }
17 };
18 var oRequest = new Ajax.Updater(this.divToUpdate, this.file, oOptions);
19 }
20}); \ No newline at end of file
diff --git a/prebuild.xml b/prebuild.xml
index e4bbda3..acad24d 100644
--- a/prebuild.xml
+++ b/prebuild.xml
@@ -2267,6 +2267,54 @@
2267 </Match> 2267 </Match>
2268 </Files> 2268 </Files>
2269 </Project> 2269 </Project>
2270 <Project name="OpenSim.Region.UserStatistics" path="OpenSim/Region/UserStatistics" type="Library">
2271 <Configuration name="Debug">
2272 <Options>
2273 <OutputPath>../../../bin/</OutputPath>
2274 </Options>
2275 </Configuration>
2276 <Configuration name="Release">
2277 <Options>
2278 <OutputPath>../../../bin/</OutputPath>
2279 </Options>
2280 </Configuration>
2281
2282 <ReferencePath>../../../bin/</ReferencePath>
2283 <Reference name="System" localCopy="false"/>
2284 <Reference name="System.Data"/>
2285 <Reference name="System.Xml"/>
2286 <Reference name="System.Drawing"/>
2287 <Reference name="System.Runtime.Remoting"/>
2288 <Reference name="System.Web"/>
2289 <Reference name="OpenMetaverseTypes.dll"/>
2290 <Reference name="OpenMetaverse.StructuredData.dll"/>
2291 <Reference name="OpenMetaverse.dll"/>
2292 <Reference name="OpenSim.Framework"/>
2293 <Reference name="OpenSim.Data" />
2294 <Reference name="OpenSim.Region.Interfaces" />
2295 <Reference name="OpenSim.Region.Environment" />
2296 <Reference name="OpenSim.Framework.Console"/>
2297 <Reference name="OpenSim.Framework.Servers"/>
2298 <Reference name="OpenSim.Framework.Statistics"/>
2299 <Reference name="OpenSim.Region.Physics.Manager"/>
2300 <Reference name="OpenSim.Grid.AssetServer"/>
2301 <Reference name="Mono.Data.SqliteClient"/>
2302
2303 <!-- For scripting in funny languages by default -->
2304 <Reference name="Microsoft.JScript"/>
2305 <Reference name="XMLRPC.dll"/>
2306 <Reference name="OpenSim.Framework.Communications"/>
2307 <Reference name="OpenSim.Data.Base"/>
2308 <Reference name="Nini.dll" />
2309 <Reference name="log4net.dll"/>
2310 <Reference name="DotNetOpenMail.dll"/>
2311
2312 <Files>
2313 <Match pattern="*.cs" recurse="true">
2314 <Exclude name="Tests" pattern="Tests" />
2315 </Match>
2316 </Files>
2317 </Project>
2270 2318
2271 <!-- Tools --> 2319 <!-- Tools -->
2272 2320