| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // Licensed to the Apache Software Foundation (ASF) under one or more |
| // contributor license agreements. See the NOTICE file distributed with |
| // this work for additional information regarding copyright ownership. |
| // The ASF licenses this file to You under the Apache License, Version 2.0 |
| // (the "License"); you may not use this file except in compliance with |
| // the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| package mx.utils |
| { |
| |
| import flash.events.Event; |
| import flash.events.EventDispatcher; |
| import flash.utils.getQualifiedClassName; |
| import flash.utils.IDataInput; |
| import flash.utils.IDataOutput; |
| import flash.utils.IExternalizable; |
| import flash.utils.Proxy; |
| import flash.utils.flash_proxy; |
| import mx.core.IPropertyChangeNotifier; |
| import mx.events.PropertyChangeEvent; |
| import mx.events.PropertyChangeEventKind; |
| |
| use namespace flash_proxy; |
| use namespace object_proxy; |
| |
| [Bindable("propertyChange")] |
| [RemoteClass(alias="flex.messaging.io.ObjectProxy")] |
| |
| /** |
| * This class provides the ability to track changes to an item |
| * managed by this proxy. |
| * Any number of objects can "listen" for changes on this |
| * object, by using the <code>addEventListener()</code> method. |
| * |
| * @example |
| * <pre> |
| * import mx.events.PropertyChangeEvent; |
| * import mx.utils.ObjectUtil; |
| * import mx.utils.ObjectProxy; |
| * import mx.utils.StringUtil; |
| * |
| * var a:Object = { name: "Tyler", age: 5, ssnum: "555-55-5555" }; |
| * var p:ObjectProxy = new ObjectProxy(a); |
| * p.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, updateHandler); |
| * p.name = "Jacey"; |
| * p.age = 2; |
| * delete p.ssnum; |
| * |
| * // handler function |
| * function updateHandler(event:ChangeEvent):void |
| * { |
| * trace(StringUtil.substitute("updateHandler('{0}', {1}, {2}, {3}, '{4}')", |
| * event.kind, |
| * event.property, |
| * event.oldValue, |
| * event.newValue, |
| * event.target.object_proxy::UUID)); |
| * } |
| * |
| * // The trace output appears as: |
| * // updateHandler('opUpdate', name, Tyler, Jacey, '698AF8CB-B3D9-21A3-1AFFDGHT89075CD2') |
| * // updateHandler('opUpdate', age, 5, 2, '698AF8CB-B3D9-21A3-1AFFDGHT89075CD2') |
| * // updateHandler('opDelete', ssnum, 555-55-5555, null, '698AF8CB-B3D9-21A3-1AFFDGHT89075CD2') |
| * </pre> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public dynamic class ObjectProxy extends Proxy |
| implements IExternalizable, |
| IPropertyChangeNotifier |
| { |
| //-------------------------------------------------------------------------- |
| // |
| // Constructor |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Initializes this proxy with the specified object, id and proxy depth. |
| * |
| * @param item Object to proxy. |
| * If no item is specified, an anonymous object will be constructed |
| * and assigned. |
| * |
| * @param uid String containing the unique id |
| * for this object instance. |
| * Required for IPropertyChangeNotifier compliance as every object must |
| * provide a unique way of identifying it. |
| * If no value is specified, a random id will be assigned. |
| * |
| * @param proxyDepth An integer indicating how many levels in a complex |
| * object graph should have a proxy created during property access. |
| * The default is -1, meaning "proxy to infinite depth". |
| * |
| * @example |
| * |
| * <pre> |
| * import mx.events.PropertyChangeEvent; |
| * import mx.utils.ObjectUtil; |
| * import mx.utils.ObjectProxy; |
| * import mx.utils.StringUtil; |
| * |
| * var a:Object = { name: "Tyler", age: 5, ssnum: "555-55-5555" }; |
| * var p:ObjectProxy = new ObjectProxy(a); |
| * p.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, updateHandler); |
| * p.name = "Jacey"; |
| * p.age = 2; |
| * delete p.ssnum; |
| * |
| * // handler function |
| * function updateHandler(event:PropertyChangeEvent):void |
| * { |
| * trace(StringUtil.substitute("updateHandler('{0}', {1}, {2}, {3}, '{4}')", |
| * event.kind, |
| * event.property, |
| * event.oldValue, |
| * event.newValue, |
| * event.target.uid)); |
| * } |
| * |
| * // trace output |
| * updateHandler('opUpdate', name, Jacey, '698AF8CB-B3D9-21A3-1AFFDGHT89075CD2') |
| * updateHandler('opUpdate', age, 2, '698AF8CB-B3D9-21A3-1AFFDGHT89075CD2') |
| * updateHandler('opDelete', ssnum, null, '698AF8CB-B3D9-21A3-1AFFDGHT89075CD2') |
| * </pre> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function ObjectProxy(item:Object = null, uid:String = null, |
| proxyDepth:int = -1) |
| { |
| super(); |
| |
| if (!item) |
| item = {}; |
| _item = item; |
| |
| _proxyLevel = proxyDepth; |
| |
| notifiers = {}; |
| |
| dispatcher = new EventDispatcher(this); |
| |
| // If we got an id, use it. Otherwise the UID is lazily |
| // created in the getter for UID. |
| if (uid) |
| _id = uid; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Variables |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * A reference to the EventDispatcher for this proxy. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected var dispatcher:EventDispatcher; |
| |
| /** |
| * A hashmap of property change notifiers that this proxy is |
| * listening for changes from; the key of the map is the property name. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected var notifiers:Object; |
| |
| /** |
| * Indicates what kind of proxy to create |
| * when proxying complex properties. |
| * Subclasses should assign this value appropriately. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected var proxyClass:Class = ObjectProxy; |
| |
| /** |
| * Contains a list of all of the property names for the proxied object. |
| * Descendants need to fill this list by overriding the |
| * <code>setupPropertyList()</code> method. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected var propertyList:Array; |
| |
| /** |
| * Indicates how deep proxying should be performed. |
| * If -1 (default), always proxy; |
| * if this value is zero, no proxying will be performed. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| private var _proxyLevel:int; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Properties |
| // |
| //-------------------------------------------------------------------------- |
| |
| //---------------------------------- |
| // object |
| //---------------------------------- |
| |
| /** |
| * Storage for the object property. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| private var _item:Object; |
| |
| /** |
| * The object being proxied. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| object_proxy function get object():Object |
| { |
| return _item; |
| } |
| |
| //---------------------------------- |
| // type |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for the qualified type name. |
| */ |
| private var _type:QName; |
| |
| /** |
| * The qualified type name associated with this object. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| object_proxy function get type():QName |
| { |
| return _type; |
| } |
| |
| /** |
| * @private |
| */ |
| object_proxy function set type(value:QName):void |
| { |
| _type = value; |
| } |
| |
| //---------------------------------- |
| // uid |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for the uid property. |
| */ |
| private var _id:String; |
| |
| /** |
| * The unique identifier for this object. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get uid():String |
| { |
| if (_id === null) |
| _id = UIDUtil.createUID(); |
| |
| return _id; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set uid(value:String):void |
| { |
| _id = value; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Overridden methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Returns the specified property value of the proxied object. |
| * |
| * @param name Typically a string containing the name of the property, |
| * or possibly a QName where the property name is found by |
| * inspecting the <code>localName</code> property. |
| * |
| * @return The value of the property. |
| * In some instances this value may be an instance of |
| * <code>ObjectProxy</code>. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| override flash_proxy function getProperty(name:*):* |
| { |
| // if we have a data proxy for this then |
| var result:*; |
| |
| if (notifiers[name.toString()]) |
| return notifiers[name]; |
| |
| result = _item[name]; |
| |
| if (result) |
| { |
| if (_proxyLevel == 0 || ObjectUtil.isSimple(result)) |
| { |
| return result; |
| } |
| else |
| { |
| result = object_proxy::getComplexProperty(name, result); |
| } // if we are proxying |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Returns the value of the proxied object's method with the specified name. |
| * |
| * @param name The name of the method being invoked. |
| * |
| * @param rest An array specifying the arguments to the |
| * called method. |
| * |
| * @return The return value of the called method. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| override flash_proxy function callProperty(name:*, ... rest):* |
| { |
| return _item[name].apply(_item, rest) |
| } |
| |
| /** |
| * Deletes the specified property on the proxied object and |
| * sends notification of the delete to the handler. |
| * |
| * @param name Typically a string containing the name of the property, |
| * or possibly a QName where the property name is found by |
| * inspecting the <code>localName</code> property. |
| * |
| * @return A Boolean indicating if the property was deleted. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| override flash_proxy function deleteProperty(name:*):Boolean |
| { |
| var notifier:IPropertyChangeNotifier = IPropertyChangeNotifier(notifiers[name]); |
| if (notifier) |
| { |
| notifier.removeEventListener(PropertyChangeEvent.PROPERTY_CHANGE, |
| propertyChangeHandler); |
| delete notifiers[name]; |
| } |
| |
| var oldVal:* = _item[name]; |
| var deleted:Boolean = delete _item[name]; |
| |
| if (dispatcher.hasEventListener(PropertyChangeEvent.PROPERTY_CHANGE)) |
| { |
| var event:PropertyChangeEvent = new PropertyChangeEvent(PropertyChangeEvent.PROPERTY_CHANGE); |
| event.kind = PropertyChangeEventKind.DELETE; |
| event.property = name; |
| event.oldValue = oldVal; |
| event.source = this; |
| dispatcher.dispatchEvent(event); |
| } |
| |
| return deleted; |
| } |
| |
| /** |
| * @private |
| */ |
| override flash_proxy function hasProperty(name:*):Boolean |
| { |
| return(name in _item); |
| } |
| |
| /** |
| * @private |
| */ |
| override flash_proxy function nextName(index:int):String |
| { |
| return propertyList[index -1]; |
| } |
| |
| /** |
| * @private |
| */ |
| override flash_proxy function nextNameIndex(index:int):int |
| { |
| if (index == 0) |
| { |
| setupPropertyList(); |
| } |
| |
| if (index < propertyList.length) |
| { |
| return index + 1; |
| } |
| else |
| { |
| return 0; |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| override flash_proxy function nextValue(index:int):* |
| { |
| return _item[propertyList[index -1]]; |
| } |
| |
| /** |
| * Updates the specified property on the proxied object |
| * and sends notification of the update to the handler. |
| * |
| * @param name Object containing the name of the property that |
| * should be updated on the proxied object. |
| * |
| * @param value Value that should be set on the proxied object. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| override flash_proxy function setProperty(name:*, value:*):void |
| { |
| var oldVal:* = _item[name]; |
| if (oldVal !== value) |
| { |
| // Update item. |
| _item[name] = value; |
| |
| // Stop listening for events on old item if we currently are. |
| var notifier:IPropertyChangeNotifier = |
| IPropertyChangeNotifier(notifiers[name]); |
| if (notifier) |
| { |
| notifier.removeEventListener( |
| PropertyChangeEvent.PROPERTY_CHANGE, |
| propertyChangeHandler); |
| delete notifiers[name]; |
| } |
| |
| // Notify anyone interested. |
| if (dispatcher.hasEventListener(PropertyChangeEvent.PROPERTY_CHANGE)) |
| { |
| if (name is QName) |
| name = QName(name).localName; |
| var event:PropertyChangeEvent = |
| PropertyChangeEvent.createUpdateEvent( |
| this, name.toString(), oldVal, value); |
| dispatcher.dispatchEvent(event); |
| } |
| } |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // object_proxy methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Provides a place for subclasses to override how a complex property that |
| * needs to be either proxied or daisy chained for event bubbling is managed. |
| * |
| * @param name Typically a string containing the name of the property, |
| * or possibly a QName where the property name is found by |
| * inspecting the <code>localName</code> property. |
| * |
| * @param value The property value. |
| * |
| * @return The property value or an instance of <code>ObjectProxy</code>. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| object_proxy function getComplexProperty(name:*, value:*):* |
| { |
| if (value is IPropertyChangeNotifier) |
| { |
| value.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, |
| propertyChangeHandler); |
| notifiers[name] = value; |
| return value; |
| } |
| |
| if (getQualifiedClassName(value) == "Object") |
| { |
| value = new proxyClass(_item[name], null, |
| _proxyLevel > 0 ? _proxyLevel - 1 : _proxyLevel); |
| value.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, |
| propertyChangeHandler); |
| notifiers[name] = value; |
| return value; |
| } |
| |
| return value; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // IExternalizable Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Since Flex only uses ObjectProxy to wrap anonymous objects, |
| * the server flex.messaging.io.ObjectProxy instance serializes itself |
| * as a Map that will be returned as a plain ActionScript object. |
| * You can then set the object_proxy object property to this value. |
| * |
| * @param input The source object from which the ObjectProxy is |
| * deserialized. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function readExternal(input:IDataInput):void |
| { |
| var value:Object = input.readObject(); |
| _item = value; |
| } |
| |
| /** |
| * Since Flex only serializes the inner ActionScript object that it wraps, |
| * the server flex.messaging.io.ObjectProxy populates itself |
| * with this anonymous object's contents and appears to the user |
| * as a Map. |
| * |
| * @param output The source object from which the ObjectProxy is |
| * deserialized. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function writeExternal(output:IDataOutput):void |
| { |
| output.writeObject(_item); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Registers an event listener object |
| * so that the listener receives notification of an event. |
| * For more information, including descriptions of the parameters see |
| * <code>addEventListener()</code> in the |
| * flash.events.EventDispatcher class. |
| * |
| * @param type The type of event. |
| * |
| * @param listener The listener function that processes the event. This function must accept |
| * an Event object as its only parameter and must return nothing. |
| * |
| * @param useCapture Determines whether the listener works in the capture phase or the |
| * target and bubbling phases. If <code>useCapture</code> is set to <code>true</code>, |
| * the listener processes the event only during the capture phase and not in the |
| * target or bubbling phase. If <code>useCapture</code> is <code>false</code>, the |
| * listener processes the event only during the target or bubbling phase. To listen for |
| * the event in all three phases, call <code>addEventListener</code> twice, once with |
| * <code>useCapture</code> set to <code>true</code>, then again with |
| * <code>useCapture</code> set to <code>false</code>. |
| * |
| * @param priority The priority level of the event listener. |
| * |
| * @param useWeakReference Determines whether the reference to the listener is strong or |
| * weak. A strong reference (the default) prevents your listener from being garbage-collected. |
| * A weak reference does not. |
| * |
| * @see flash.events.EventDispatcher#addEventListener() |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function addEventListener(type:String, listener:Function, |
| useCapture:Boolean = false, |
| priority:int = 0, |
| useWeakReference:Boolean = false):void |
| { |
| dispatcher.addEventListener(type, listener, useCapture, |
| priority, useWeakReference); |
| } |
| |
| /** |
| * Removes an event listener. |
| * If there is no matching listener registered with the EventDispatcher object, |
| * a call to this method has no effect. |
| * For more information, see |
| * the flash.events.EventDispatcher class. |
| * |
| * @param type The type of event. |
| * |
| * @param listener The listener object to remove. |
| * |
| * @param useCapture Specifies whether the listener was registered for the capture |
| * phase or the target and bubbling phases. If the listener was registered for both |
| * the capture phase and the target and bubbling phases, two calls to |
| * <code>removeEventListener()</code> are required to remove both, one call with |
| * <code>useCapture</code> |
| * set to <code>true</code>, and another call with <code>useCapture</code> |
| * set to <code>false</code>. |
| * |
| * @see flash.events.EventDispatcher#removeEventListener() |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function removeEventListener(type:String, listener:Function, |
| useCapture:Boolean = false):void |
| { |
| dispatcher.removeEventListener(type, listener, useCapture); |
| } |
| |
| /** |
| * Dispatches an event into the event flow. |
| * For more information, see |
| * the flash.events.EventDispatcher class. |
| * |
| * @param event The Event object that is dispatched into the event flow. If the |
| * event is being redispatched, a clone of the event is created automatically. |
| * After an event is dispatched, its target property cannot be changed, so you |
| * must create a new copy of the event for redispatching to work. |
| * |
| * @return Returns <code>true</code> if the event was successfully dispatched. |
| * A value |
| * of <code>false</code> indicates failure or that <code>preventDefault()</code> |
| * was called on the event. |
| * |
| * @see flash.events.EventDispatcher#dispatchEvent() |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function dispatchEvent(event:Event):Boolean |
| { |
| return dispatcher.dispatchEvent(event); |
| } |
| |
| /** |
| * Checks whether there are any event listeners registered |
| * for a specific type of event. |
| * This allows you to determine where an object has altered handling |
| * of an event type in the event flow hierarchy. |
| * For more information, see |
| * the flash.events.EventDispatcher class. |
| * |
| * @param type The type of event |
| * |
| * @return Returns <code>true</code> if a listener of the specified type is |
| * registered; <code>false</code> otherwise. |
| * |
| * @see flash.events.EventDispatcher#hasEventListener() |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function hasEventListener(type:String):Boolean |
| { |
| return dispatcher.hasEventListener(type); |
| } |
| |
| /** |
| * Checks whether an event listener is registered with this object |
| * or any of its ancestors for the specified event type. |
| * This method returns <code>true</code> if an event listener is triggered |
| * during any phase of the event flow when an event of the specified |
| * type is dispatched to this object or any of its descendants. |
| * For more information, see the flash.events.EventDispatcher class. |
| * |
| * @param type The type of event. |
| * |
| * @return Returns <code>true</code> if a listener of the specified type will |
| * be triggered; <code>false</code> otherwise. |
| * |
| * @see flash.events.EventDispatcher#willTrigger() |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function willTrigger(type:String):Boolean |
| { |
| return dispatcher.willTrigger(type); |
| } |
| |
| /** |
| * Called when a complex property is updated. |
| * |
| * @param event An event object that has changed. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function propertyChangeHandler(event:PropertyChangeEvent):void |
| { |
| dispatcher.dispatchEvent(event); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Protected Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * This method creates an array of all of the property names for the |
| * proxied object. |
| * Descendants must override this method if they wish to add more |
| * properties to this list. |
| * Be sure to call <code>super.setupPropertyList</code> before making any |
| * changes to the <code>propertyList</code> property. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function setupPropertyList():void |
| { |
| if (getQualifiedClassName(_item) == "Object") |
| { |
| propertyList = []; |
| for (var prop:String in _item) |
| propertyList.push(prop); |
| } |
| else |
| { |
| propertyList = ObjectUtil.getClassInfo(_item, null, {includeReadOnly:true, uris:["*"]}).properties; |
| } |
| } |
| } |
| |
| } |