Mercurial > hg > orthanc-stone
comparison Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp @ 1813:53aa3f72b539
synchronization of annotations across viewports
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 25 May 2021 15:27:35 +0200 |
parents | db341679dc9f |
children | 53f3411bf94b |
comparison
equal
deleted
inserted
replaced
1812:db341679dc9f | 1813:53aa3f72b539 |
---|---|
1170 } | 1170 } |
1171 } | 1171 } |
1172 | 1172 |
1173 | 1173 |
1174 | 1174 |
1175 class StoneAnnotationsRegistry : public boost::noncopyable | |
1176 { | |
1177 private: | |
1178 class Index | |
1179 { | |
1180 private: | |
1181 std::string sopInstanceUid_; | |
1182 size_t frame_; | |
1183 | |
1184 public: | |
1185 Index(const std::string& sopInstanceUid, | |
1186 size_t frame) : | |
1187 sopInstanceUid_(sopInstanceUid), | |
1188 frame_(frame) | |
1189 { | |
1190 } | |
1191 | |
1192 const std::string& GetSopInstanceUid() const | |
1193 { | |
1194 return sopInstanceUid_; | |
1195 } | |
1196 | |
1197 size_t GetFrame() const | |
1198 { | |
1199 return frame_; | |
1200 } | |
1201 | |
1202 bool operator< (const Index& other) const | |
1203 { | |
1204 if (sopInstanceUid_ < other.sopInstanceUid_) | |
1205 { | |
1206 return true; | |
1207 } | |
1208 else if (sopInstanceUid_ > other.sopInstanceUid_) | |
1209 { | |
1210 return false; | |
1211 } | |
1212 else | |
1213 { | |
1214 return frame_ < other.frame_; | |
1215 } | |
1216 } | |
1217 }; | |
1218 | |
1219 typedef std::map<Index, Json::Value*> Content; | |
1220 | |
1221 Content content_; | |
1222 | |
1223 void Clear() | |
1224 { | |
1225 for (Content::iterator it = content_.begin(); it != content_.end(); ++it) | |
1226 { | |
1227 assert(it->second != NULL); | |
1228 delete it->second; | |
1229 } | |
1230 | |
1231 content_.clear(); | |
1232 } | |
1233 | |
1234 StoneAnnotationsRegistry() | |
1235 { | |
1236 } | |
1237 | |
1238 public: | |
1239 ~StoneAnnotationsRegistry() | |
1240 { | |
1241 Clear(); | |
1242 } | |
1243 | |
1244 static StoneAnnotationsRegistry& GetInstance() | |
1245 { | |
1246 static StoneAnnotationsRegistry singleton; | |
1247 return singleton; | |
1248 } | |
1249 | |
1250 void Save(const std::string& sopInstanceUid, | |
1251 size_t frame, | |
1252 const OrthancStone::AnnotationsSceneLayer& layer) | |
1253 { | |
1254 std::unique_ptr<Json::Value> serialized; | |
1255 layer.Serialize(*serialized); | |
1256 | |
1257 const Index index(sopInstanceUid, frame); | |
1258 | |
1259 Content::iterator found = content_.find(index); | |
1260 if (found == content_.end()) | |
1261 { | |
1262 content_[index] = serialized.release(); | |
1263 } | |
1264 else | |
1265 { | |
1266 assert(found->second != NULL); | |
1267 delete found->second; | |
1268 found->second = serialized.release(); | |
1269 } | |
1270 } | |
1271 | |
1272 void Load(OrthancStone::AnnotationsSceneLayer& layer, | |
1273 const std::string& sopInstanceUid, | |
1274 size_t frame) const | |
1275 { | |
1276 const Index index(sopInstanceUid, frame); | |
1277 | |
1278 Content::const_iterator found = content_.find(index); | |
1279 if (found == content_.end()) | |
1280 { | |
1281 layer.Clear(); | |
1282 } | |
1283 else | |
1284 { | |
1285 assert(found->second != NULL); | |
1286 layer.Unserialize(*found->second); | |
1287 } | |
1288 } | |
1289 }; | |
1290 | |
1291 | |
1292 | |
1175 class ViewerViewport : public OrthancStone::ObserverBase<ViewerViewport> | 1293 class ViewerViewport : public OrthancStone::ObserverBase<ViewerViewport> |
1176 { | 1294 { |
1177 public: | 1295 public: |
1178 typedef std::map<std::string, boost::shared_ptr<Json::Value> > StoneAnnotationsRegistry; | |
1179 | |
1180 class IObserver : public boost::noncopyable | 1296 class IObserver : public boost::noncopyable |
1181 { | 1297 { |
1182 public: | 1298 public: |
1183 virtual ~IObserver() | 1299 virtual ~IObserver() |
1184 { | 1300 { |
1201 const OrthancStone::Vector& normal) = 0; | 1317 const OrthancStone::Vector& normal) = 0; |
1202 | 1318 |
1203 virtual void SignalWindowingUpdated(const ViewerViewport& viewport, | 1319 virtual void SignalWindowingUpdated(const ViewerViewport& viewport, |
1204 double windowingCenter, | 1320 double windowingCenter, |
1205 double windowingWidth) = 0; | 1321 double windowingWidth) = 0; |
1322 | |
1323 virtual void SignalStoneAnnotationsChanged(const ViewerViewport& viewport, | |
1324 const std::string& sopInstanceUid, | |
1325 size_t frame) = 0; | |
1206 }; | 1326 }; |
1207 | 1327 |
1208 private: | 1328 private: |
1209 static const int LAYER_TEXTURE = 0; | 1329 static const int LAYER_TEXTURE = 0; |
1210 static const int LAYER_REFERENCE_LINES = 1; | 1330 static const int LAYER_REFERENCE_LINES = 1; |
1586 | 1706 |
1587 // The coordinates of Stone annotations are expressed in 2D | 1707 // The coordinates of Stone annotations are expressed in 2D |
1588 // coordinates of the current texture, with (0,0) corresponding to | 1708 // coordinates of the current texture, with (0,0) corresponding to |
1589 // the center of the top-left pixel | 1709 // the center of the top-left pixel |
1590 boost::shared_ptr<OrthancStone::AnnotationsSceneLayer> stoneAnnotations_; | 1710 boost::shared_ptr<OrthancStone::AnnotationsSceneLayer> stoneAnnotations_; |
1591 boost::shared_ptr<StoneAnnotationsRegistry> stoneAnnotationsRegistry_; | |
1592 | 1711 |
1593 | 1712 |
1594 void ScheduleNextPrefetch() | 1713 void ScheduleNextPrefetch() |
1595 { | 1714 { |
1596 while (!prefetchQueue_.empty()) | 1715 while (!prefetchQueue_.empty()) |
1682 } | 1801 } |
1683 | 1802 |
1684 | 1803 |
1685 void RenderCurrentScene(const Orthanc::ImageAccessor& frame, | 1804 void RenderCurrentScene(const Orthanc::ImageAccessor& frame, |
1686 const OrthancStone::DicomInstanceParameters& instance, | 1805 const OrthancStone::DicomInstanceParameters& instance, |
1806 size_t frameIndex, | |
1687 const OrthancStone::CoordinateSystem3D& plane) | 1807 const OrthancStone::CoordinateSystem3D& plane) |
1688 { | 1808 { |
1689 /** | 1809 /** |
1690 * IMPORTANT - DO NOT use "instance.GetWidth()" and | 1810 * IMPORTANT - DO NOT use "instance.GetWidth()" and |
1691 * "instance.GetHeight()" in this method. Use the information from | 1811 * "instance.GetHeight()" in this method. Use the information from |
1784 annotationsOsiriX->AddLayer(factory.Create(annotation, plane)); | 1904 annotationsOsiriX->AddLayer(factory.Create(annotation, plane)); |
1785 } | 1905 } |
1786 } | 1906 } |
1787 } | 1907 } |
1788 | 1908 |
1789 if (stoneAnnotationsRegistry_.get() == NULL) | 1909 StoneAnnotationsRegistry::GetInstance().Load(*stoneAnnotations_, instance.GetSopInstanceUid(), frameIndex); |
1790 { | 1910 |
1791 stoneAnnotations_->Clear(); | 1911 { |
1792 } | 1912 std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock()); |
1793 else | 1913 |
1794 { | 1914 OrthancStone::Scene2D& scene = lock->GetController().GetScene(); |
1795 StoneAnnotationsRegistry::const_iterator found = stoneAnnotationsRegistry_->find(instance.GetSopInstanceUid()); | 1915 |
1796 if (found == stoneAnnotationsRegistry_->end()) | 1916 scene.SetLayer(LAYER_TEXTURE, layer.release()); |
1797 { | 1917 |
1798 stoneAnnotations_->Clear(); | 1918 if (annotationsOsiriX.get() != NULL) |
1919 { | |
1920 scene.SetLayer(LAYER_ANNOTATIONS_OSIRIX, annotationsOsiriX.release()); | |
1799 } | 1921 } |
1800 else | 1922 else |
1801 { | 1923 { |
1802 stoneAnnotations_->Unserialize(*found->second); | |
1803 } | |
1804 } | |
1805 | |
1806 { | |
1807 std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock()); | |
1808 | |
1809 OrthancStone::Scene2D& scene = lock->GetController().GetScene(); | |
1810 | |
1811 scene.SetLayer(LAYER_TEXTURE, layer.release()); | |
1812 | |
1813 if (annotationsOsiriX.get() != NULL) | |
1814 { | |
1815 scene.SetLayer(LAYER_ANNOTATIONS_OSIRIX, annotationsOsiriX.release()); | |
1816 } | |
1817 else | |
1818 { | |
1819 scene.DeleteLayer(LAYER_ANNOTATIONS_OSIRIX); | 1924 scene.DeleteLayer(LAYER_ANNOTATIONS_OSIRIX); |
1820 } | 1925 } |
1926 | |
1927 stoneAnnotations_->Render(scene); // Necessary for "FitContent()" to work | |
1821 | 1928 |
1822 if (fitNextContent_) | 1929 if (fitNextContent_) |
1823 { | 1930 { |
1824 lock->RefreshCanvasSize(); | 1931 lock->RefreshCanvasSize(); |
1825 lock->GetCompositor().FitContent(scene); | 1932 lock->GetCompositor().FitContent(scene); |
1933 stoneAnnotations_->Render(scene); | |
1826 fitNextContent_ = false; | 1934 fitNextContent_ = false; |
1827 } | 1935 } |
1828 | |
1829 stoneAnnotations_->Render(scene); | |
1830 | 1936 |
1831 //lock->GetCompositor().Refresh(scene); | 1937 //lock->GetCompositor().Refresh(scene); |
1832 lock->Invalidate(); | 1938 lock->Invalidate(); |
1833 } | 1939 } |
1834 } | 1940 } |
1857 FramesCache::Accessor accessor(*framesCache_, instance.GetSopInstanceUid(), frameNumber); | 1963 FramesCache::Accessor accessor(*framesCache_, instance.GetSopInstanceUid(), frameNumber); |
1858 if (accessor.IsValid() && | 1964 if (accessor.IsValid() && |
1859 accessor.GetQuality() == QUALITY_FULL) | 1965 accessor.GetQuality() == QUALITY_FULL) |
1860 { | 1966 { |
1861 // A high-res image was downloaded in between: Use this cached image instead of the low-res | 1967 // A high-res image was downloaded in between: Use this cached image instead of the low-res |
1862 RenderCurrentScene(accessor.GetImage(), instance, plane); | 1968 RenderCurrentScene(accessor.GetImage(), instance, frameNumber, plane); |
1863 SetupPrefetchAfterRendering(frame, DisplayedFrameQuality_High); | 1969 SetupPrefetchAfterRendering(frame, DisplayedFrameQuality_High); |
1864 } | 1970 } |
1865 else | 1971 else |
1866 { | 1972 { |
1867 // This frame is only available in low-res: Download the full DICOM | 1973 // This frame is only available in low-res: Download the full DICOM |
1868 RenderCurrentScene(frame, instance, plane); | 1974 RenderCurrentScene(frame, instance, frameNumber, plane); |
1869 SetupPrefetchAfterRendering(frame, quality); | 1975 SetupPrefetchAfterRendering(frame, quality); |
1870 | 1976 |
1871 /** | 1977 /** |
1872 * The command "SetupPrefetchAfterRendering()" must be | 1978 * The command "SetupPrefetchAfterRendering()" must be |
1873 * after "SetupPrefetchAfterRendering(quality)", as the | 1979 * after "SetupPrefetchAfterRendering(quality)", as the |
1881 } | 1987 } |
1882 else | 1988 else |
1883 { | 1989 { |
1884 assert(quality == DisplayedFrameQuality_High); | 1990 assert(quality == DisplayedFrameQuality_High); |
1885 SetupPrefetchAfterRendering(frame, quality); | 1991 SetupPrefetchAfterRendering(frame, quality); |
1886 RenderCurrentScene(frame, instance, plane); | 1992 RenderCurrentScene(frame, instance, frameNumber, plane); |
1887 } | 1993 } |
1888 } | 1994 } |
1889 } | 1995 } |
1890 } | 1996 } |
1891 | 1997 |
2029 hasFocusOnInstance_(false), | 2135 hasFocusOnInstance_(false), |
2030 focusFrameNumber_(0), | 2136 focusFrameNumber_(0), |
2031 synchronizationOffset_(OrthancStone::LinearAlgebra::CreateVector(0, 0, 0)), | 2137 synchronizationOffset_(OrthancStone::LinearAlgebra::CreateVector(0, 0, 0)), |
2032 synchronizationEnabled_(false), | 2138 synchronizationEnabled_(false), |
2033 centralPhysicalWidth_(1), | 2139 centralPhysicalWidth_(1), |
2034 centralPhysicalHeight_(1), | 2140 centralPhysicalHeight_(1) |
2035 stoneAnnotationsRegistry_(NULL) | |
2036 { | 2141 { |
2037 if (!framesCache_) | 2142 if (!framesCache_) |
2038 { | 2143 { |
2039 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); | 2144 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); |
2040 } | 2145 } |
2155 if (cursor_.get() != NULL && | 2260 if (cursor_.get() != NULL && |
2156 frames_.get() != NULL) | 2261 frames_.get() != NULL) |
2157 { | 2262 { |
2158 const size_t cursorIndex = cursor_->GetCurrentIndex(); | 2263 const size_t cursorIndex = cursor_->GetCurrentIndex(); |
2159 const OrthancStone::DicomInstanceParameters& instance = frames_->GetInstanceOfFrame(cursorIndex); | 2264 const OrthancStone::DicomInstanceParameters& instance = frames_->GetInstanceOfFrame(cursorIndex); |
2160 | 2265 const size_t frameNumber = frames_->GetFrameNumberInInstance(cursorIndex); |
2161 boost::shared_ptr<Json::Value> v(new Json::Value); | 2266 |
2162 stoneAnnotations_->Serialize(*v); | 2267 StoneAnnotationsRegistry::GetInstance().Save(instance.GetSopInstanceUid(), frameNumber, *stoneAnnotations_); |
2163 (*stoneAnnotationsRegistry_) [instance.GetSopInstanceUid()] = v; | 2268 |
2269 if (observer_.get() != NULL) | |
2270 { | |
2271 observer_->SignalStoneAnnotationsChanged(*this, instance.GetSopInstanceUid(), frameNumber); | |
2272 } | |
2164 } | 2273 } |
2165 } | 2274 } |
2166 } | 2275 } |
2167 | 2276 |
2168 void Handle(const OrthancStone::ViewportController::SceneTransformChanged& message) | 2277 void Handle(const OrthancStone::ViewportController::SceneTransformChanged& message) |
2352 const size_t frameNumber = frames_->GetFrameNumberInInstance(cursorIndex); | 2461 const size_t frameNumber = frames_->GetFrameNumberInInstance(cursorIndex); |
2353 | 2462 |
2354 FramesCache::Accessor accessor(*framesCache_, instance.GetSopInstanceUid(), frameNumber); | 2463 FramesCache::Accessor accessor(*framesCache_, instance.GetSopInstanceUid(), frameNumber); |
2355 if (accessor.IsValid()) | 2464 if (accessor.IsValid()) |
2356 { | 2465 { |
2357 RenderCurrentScene(accessor.GetImage(), instance, frames_->GetFrameGeometry(cursorIndex)); | 2466 RenderCurrentScene(accessor.GetImage(), instance, frameNumber, frames_->GetFrameGeometry(cursorIndex)); |
2358 | 2467 |
2359 DisplayedFrameQuality quality; | 2468 DisplayedFrameQuality quality; |
2360 | 2469 |
2361 if (accessor.GetQuality() < QUALITY_FULL) | 2470 if (accessor.GetQuality() < QUALITY_FULL) |
2362 { | 2471 { |
2802 OrthancStone::LinearAlgebra::AssignVector(synchronizationOffset_, 0, 0, 0); | 2911 OrthancStone::LinearAlgebra::AssignVector(synchronizationOffset_, 0, 0, 0); |
2803 synchronizationEnabled_ = enabled; | 2912 synchronizationEnabled_ = enabled; |
2804 } | 2913 } |
2805 | 2914 |
2806 | 2915 |
2807 void SetStoneAnnotationsRegistry(boost::shared_ptr<ViewerViewport::StoneAnnotationsRegistry>& registry) | 2916 void SignalStoneAnnotationsChanged(const std::string& sopInstanceUid, |
2808 { | 2917 size_t frame) |
2809 stoneAnnotationsRegistry_ = registry; | 2918 { |
2919 if (cursor_.get() != NULL && | |
2920 frames_.get() != NULL) | |
2921 { | |
2922 const size_t cursorIndex = cursor_->GetCurrentIndex(); | |
2923 const OrthancStone::DicomInstanceParameters& instance = frames_->GetInstanceOfFrame(cursorIndex); | |
2924 const size_t frameNumber = frames_->GetFrameNumberInInstance(cursorIndex); | |
2925 | |
2926 if (instance.GetSopInstanceUid() == sopInstanceUid && | |
2927 frameNumber == frame) | |
2928 { | |
2929 StoneAnnotationsRegistry::GetInstance().Load(*stoneAnnotations_, instance.GetSopInstanceUid(), frame); | |
2930 | |
2931 { | |
2932 std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock()); | |
2933 stoneAnnotations_->Render(lock->GetController().GetScene()); | |
2934 lock->Invalidate(); | |
2935 } | |
2936 } | |
2937 } | |
2810 } | 2938 } |
2811 }; | 2939 }; |
2812 | 2940 |
2813 | 2941 |
2814 | 2942 |
2817 typedef std::map<std::string, boost::shared_ptr<ViewerViewport> > Viewports; | 2945 typedef std::map<std::string, boost::shared_ptr<ViewerViewport> > Viewports; |
2818 | 2946 |
2819 static Viewports allViewports_; | 2947 static Viewports allViewports_; |
2820 static bool showReferenceLines_ = true; | 2948 static bool showReferenceLines_ = true; |
2821 static boost::shared_ptr<OrthancStone::OsiriX::CollectionOfAnnotations> osiriXAnnotations_; | 2949 static boost::shared_ptr<OrthancStone::OsiriX::CollectionOfAnnotations> osiriXAnnotations_; |
2822 static boost::shared_ptr<ViewerViewport::StoneAnnotationsRegistry> stoneAnnotationsRegistry_; | |
2823 | 2950 |
2824 | 2951 |
2825 static void UpdateReferenceLines() | 2952 static void UpdateReferenceLines() |
2826 { | 2953 { |
2827 if (showReferenceLines_) | 2954 if (showReferenceLines_) |
3022 static_cast<int>(boost::math::iround<double>(windowingCenter)), | 3149 static_cast<int>(boost::math::iround<double>(windowingCenter)), |
3023 static_cast<int>(boost::math::iround<double>(windowingWidth))); | 3150 static_cast<int>(boost::math::iround<double>(windowingWidth))); |
3024 | 3151 |
3025 UpdateReferenceLines(); | 3152 UpdateReferenceLines(); |
3026 } | 3153 } |
3154 | |
3155 virtual void SignalStoneAnnotationsChanged(const ViewerViewport& viewport, | |
3156 const std::string& sopInstanceUid, | |
3157 size_t frame) ORTHANC_OVERRIDE | |
3158 { | |
3159 for (Viewports::const_iterator it = allViewports_.begin(); it != allViewports_.end(); ++it) | |
3160 { | |
3161 assert(it->second.get() != NULL); | |
3162 | |
3163 if (it->second.get() != &viewport) | |
3164 { | |
3165 it->second->SignalStoneAnnotationsChanged(sopInstanceUid, frame); | |
3166 } | |
3167 } | |
3168 } | |
3027 }; | 3169 }; |
3028 | 3170 |
3029 | 3171 |
3030 | 3172 |
3031 static OrthancStone::DicomSource source_; | 3173 static OrthancStone::DicomSource source_; |
3081 boost::shared_ptr<ViewerViewport> viewport( | 3223 boost::shared_ptr<ViewerViewport> viewport( |
3082 ViewerViewport::Create(*context_, source_, canvas, framesCache_, softwareRendering_)); | 3224 ViewerViewport::Create(*context_, source_, canvas, framesCache_, softwareRendering_)); |
3083 viewport->SetMouseButtonActions(leftButtonAction_, middleButtonAction_, rightButtonAction_); | 3225 viewport->SetMouseButtonActions(leftButtonAction_, middleButtonAction_, rightButtonAction_); |
3084 viewport->AcquireObserver(new WebAssemblyObserver); | 3226 viewport->AcquireObserver(new WebAssemblyObserver); |
3085 viewport->SetOsiriXAnnotations(osiriXAnnotations_); | 3227 viewport->SetOsiriXAnnotations(osiriXAnnotations_); |
3086 viewport->SetStoneAnnotationsRegistry(stoneAnnotationsRegistry_); | |
3087 allViewports_[canvas] = viewport; | 3228 allViewports_[canvas] = viewport; |
3088 return viewport; | 3229 return viewport; |
3089 } | 3230 } |
3090 else | 3231 else |
3091 { | 3232 { |
3106 context_.reset(new OrthancStone::WebAssemblyLoadersContext(1, 4, 1)); | 3247 context_.reset(new OrthancStone::WebAssemblyLoadersContext(1, 4, 1)); |
3107 context_->SetDicomCacheSize(128 * 1024 * 1024); // 128MB | 3248 context_->SetDicomCacheSize(128 * 1024 * 1024); // 128MB |
3108 | 3249 |
3109 framesCache_.reset(new FramesCache); | 3250 framesCache_.reset(new FramesCache); |
3110 osiriXAnnotations_.reset(new OrthancStone::OsiriX::CollectionOfAnnotations); | 3251 osiriXAnnotations_.reset(new OrthancStone::OsiriX::CollectionOfAnnotations); |
3111 stoneAnnotationsRegistry_.reset(new ViewerViewport::StoneAnnotationsRegistry); | |
3112 | 3252 |
3113 { | 3253 { |
3114 // TODO - TEST | 3254 // TODO - TEST |
3115 OrthancStone::AnnotationsSceneLayer l(0); | 3255 OrthancStone::AnnotationsSceneLayer l(0); |
3116 l.AddSegmentAnnotation(OrthancStone::ScenePoint2D(0, 0), | 3256 l.AddSegmentAnnotation(OrthancStone::ScenePoint2D(0, 0), |
3119 OrthancStone::ScenePoint2D(150, 40), | 3259 OrthancStone::ScenePoint2D(150, 40), |
3120 OrthancStone::ScenePoint2D(200, 50)); | 3260 OrthancStone::ScenePoint2D(200, 50)); |
3121 l.AddCircleAnnotation(OrthancStone::ScenePoint2D(50, 200), | 3261 l.AddCircleAnnotation(OrthancStone::ScenePoint2D(50, 200), |
3122 OrthancStone::ScenePoint2D(100, 250)); | 3262 OrthancStone::ScenePoint2D(100, 250)); |
3123 l.SetActiveTool(OrthancStone::AnnotationsSceneLayer::Tool_Edit); | 3263 l.SetActiveTool(OrthancStone::AnnotationsSceneLayer::Tool_Edit); |
3124 boost::shared_ptr<Json::Value> s(new Json::Value); | 3264 StoneAnnotationsRegistry::GetInstance().Save("1.2.840.113543.6.6.4.7.64234348190163144631511103849051737563212", 0, l); |
3125 l.Serialize(*s); | |
3126 (*stoneAnnotationsRegistry_) ["1.2.840.113543.6.6.4.7.64234348190163144631511103849051737563212"] = s; | |
3127 } | 3265 } |
3128 | 3266 |
3129 | 3267 |
3130 DISPATCH_JAVASCRIPT_EVENT("StoneInitialized"); | 3268 DISPATCH_JAVASCRIPT_EVENT("StoneInitialized"); |
3131 } | 3269 } |