Convert k/v to string before storing them. Fixes #3357
[potlatch2.git] / net / systemeD / potlatch2 / mapfeatures / Feature.as
1 package net.systemeD.potlatch2.mapfeatures {
2
3     import flash.events.EventDispatcher;
4     import flash.events.Event;
5     import flash.net.*;
6     import flash.utils.ByteArray;
7     import mx.core.BitmapAsset;
8     import mx.graphics.codec.PNGEncoder;
9     
10     import net.systemeD.halcyon.connection.Entity;
11     import net.systemeD.potlatch2.utils.CachedDataLoader;
12
13         public class Feature extends EventDispatcher {
14         private var mapFeatures:MapFeatures;
15         private var _xml:XML;
16         private static var variablesPattern:RegExp = /[$][{]([^}]+)[}]/g;
17         private var _tags:Array;
18                 private var _withins:Array;
19         private var _editors:Array;
20
21         [Embed(source="../../../../embedded/missing_icon.png")]
22         [Bindable]
23         public var missingIconCls:Class;
24
25
26         public function Feature(mapFeatures:MapFeatures, _xml:XML) {
27             this.mapFeatures = mapFeatures;
28             this._xml = _xml;
29             parseConditions();
30             parseEditors();
31         }
32         
33         private function parseConditions():void {
34             _tags = [];
35                         _withins = [];
36             
37                         // parse tags
38             for each(var tag:XML in definition.tag) {
39                 _tags.push( { k:String(tag.@k), v:String(tag.@v)} );
40             }
41
42                         // parse 'within'
43             for each(var within:XML in definition.within) {
44                                 var obj:Object= { entity:within.@entity, k:within.@k };
45                                 if (within.attribute('v'      ).length()>0) { obj['v'      ]=within.@v;       }
46                                 if (within.attribute('minimum').length()>0) { obj['minimum']=within.@minimum; }
47                                 if (within.attribute('role'   ).length()>0) { obj['role'   ]=within.@role;    }
48                 _withins.push(obj);
49             }
50         }
51         
52         private function parseEditors():void {
53             _editors = new Array();
54             
55             addEditors(definition);
56             
57             _editors.sortOn(["sortOrder", "name"], [Array.DESCENDING | Array.NUMERIC, Array.CASEINSENSITIVE]);
58         }
59         
60         private function addEditors(xml:XML):void {
61             var inputXML:XML;
62             
63             for each(var inputSetRef:XML in xml.inputSet) {
64                 var setName:String = String(inputSetRef.@ref);
65                 for each (inputXML in mapFeatures.definition.inputSet.(@id==setName)) {
66                     addEditors(inputXML);
67                 }
68             }
69             
70             for each(inputXML in xml.input) {
71                 addEditor(inputXML);
72             }
73         }
74         
75         private function addEditor(inputXML:XML):void {
76             var inputType:String = inputXML.@type;
77             var presenceStr:String = inputXML.@presence;
78             var sortOrderStr:String = inputXML.@priority;
79             var editor:EditorFactory = EditorFactory.createFactory(inputType, inputXML);
80             if ( editor != null ) {
81                 editor.presence = Presence.getPresence(presenceStr);
82                 editor.sortOrder = EditorFactory.getPriority(sortOrderStr);
83                 _editors.push(editor);
84             }
85         }
86         
87         public function get editors():Array {
88             return _editors;
89         }
90         
91         public function get definition():XML {
92             return _xml;
93         }
94     
95         [Bindable(event="nameChanged")]
96         public function get name():String {
97                         if (_xml.attribute('name').length()>0) { return _xml.@name; }
98                         return null;
99         }
100     
101         [Bindable(event="imageChanged")]
102         public function get image():ByteArray {
103             var icon:XMLList = _xml.icon;
104             var imageURL:String = null;
105             var img:ByteArray;
106
107             if ( icon.length() > 0 && icon[0].hasOwnProperty("@image") )
108                 imageURL = icon[0].@image;
109
110             if ( imageURL != null ) {
111                 img = CachedDataLoader.loadData(imageURL, imageLoaded);
112             }
113             if (img) {
114               return img;
115             }
116             var bitmap:BitmapAsset = new missingIconCls() as BitmapAsset;
117             return new PNGEncoder().encode(bitmap.bitmapData);
118         }
119         
120         private function imageLoaded(url:String, data:ByteArray):void {
121             dispatchEvent(new Event("imageChanged"));
122         }
123         
124         public function htmlDetails(entity:Entity):String {
125             var icon:XMLList = _xml.icon;
126             return makeHTMLIcon(icon, entity);
127         }   
128
129         public static function makeHTMLIcon(icon:XMLList, entity:Entity):String {
130             if ( icon == null )
131                 return "";
132             
133             var txt:String = icon.children().toXMLString();
134             var replaceTag:Function = function():String {
135                 var value:String = entity.getTag(arguments[1]);
136                 return value == null ? "" : htmlEscape(value);
137             };
138             txt = txt.replace(variablesPattern, replaceTag);
139             return txt;
140         }
141         
142         public static function htmlEscape(str:String):String {
143             var newStr:String = str.replace(/&/g, "&");
144             newStr = newStr.replace(/</g, "&lt;");
145             newStr = newStr.replace(/>/g, "&gt;");
146             newStr = newStr.replace(/"/g, "&quot;");    // "
147             newStr = newStr.replace(/'/g, "&apos;");    // '
148             return newStr;
149         }
150       
151
152         public function isInCategory(category:String):Boolean {
153             var cats:XMLList = _xml.category;
154             if ( cats.length() == 0 )
155                 return false;
156                 
157             for each( var cat:XML in cats )
158                 if ( cat.text()[0] == category )
159                     return true;
160             return false;
161         }
162         
163         public function get tags():Array {
164             return _tags;
165         }
166
167         public function get withins():Array {
168             return _withins;
169         }
170         
171         public function findFirstCategory():Category {
172             for each( var cat:Category in mapFeatures.categories ) {
173                 if ( isInCategory(cat.id) )
174                     return cat;
175             }
176             return null;
177         }
178         
179         public function isType(type:String):Boolean {
180             if (type=='area') {
181                             return (_xml.elements(type).length() > 0) || (_xml.elements('line').length() > 0);
182             } else {
183                             return _xml.elements(type).length() > 0;
184                         }
185         }
186         
187         public function hasHelpURL():Boolean {
188             return _xml.help.length() > 0;
189         }
190         
191         public function get helpURL():String {
192             return _xml.help;
193         }
194     }
195 }
196