import net.systemeD.halcyon.connection.actions.*;
import net.systemeD.halcyon.connection.bboxes.*;
import net.systemeD.halcyon.Globals;
+ import net.systemeD.halcyon.styleparser.CSSTransform;
public class Connection extends EventDispatcher {
public var name:String;
public var statusFetcher:StatusFetcher;
public var inlineStatus:Boolean = false;
+ public var cssTransform:CSSTransform;
protected var apiBaseURL:String;
protected var policyURL:String;
protected var params:Object;
- public function Connection(cname:String,api:String,policy:String,initparams:Object=null) {
+ public function Connection(cname:String,api:String,policy:String,initparams:Object=null,transform:CSSTransform=null) {
initparams = (initparams!=null ? initparams:{});
name=cname;
apiBaseURL=api;
policyURL=policy;
params=initparams;
+ cssTransform=transform;
}
public function getParam(name:String, defaultValue:String):String {
this._version = version;
this._uid = uid;
this._timestamp = timestamp;
+ if (connection.cssTransform) tags=connection.cssTransform.run(this,tags);
this.tags = tags;
this._loaded = loaded;
modified = id < 0;
--- /dev/null
+package net.systemeD.halcyon.styleparser {
+
+ import flash.net.*;
+ import flash.events.*;
+ import net.systemeD.halcyon.connection.Entity;
+
+ public class CSSTransform {
+
+ private static const GLOBAL_INSTANCE:CSSTransform = new CSSTransform();
+ public static function getInstance():CSSTransform { return GLOBAL_INSTANCE; }
+
+ [Bindable] public var url:String='';
+ private var ruleset:RuleSet;
+
+ public function loadFromUrl(filename:String):void {
+ url=filename;
+ ruleset=new RuleSet(0,30,cssReady);
+ ruleset.loadFromCSS(url);
+ }
+
+ public function clear():void {
+ ruleset=null;
+ url='';
+ }
+
+ private function cssReady():void {
+ }
+
+ public function run(entity:Entity,tags:Object):Object {
+ if (ruleset) return ruleset.runInstructions(entity,tags);
+ return tags;
+ }
+
+ }
+}
public class InstructionStyle extends Style {
- public var set_tags:Object;
+ public var set_tags:Object={};
public var breaker:Boolean=false;
+ public var set_tags_order:Array=[];
public function addSetTag(k:String,v:*):void {
+ if (v is Eval) { evals[k]=v; }
+ else if (v is TagValue) { tagvalues[k]=v; }
+
edited=true;
- if (!set_tags) { set_tags=new Object(); }
+ set_tags_order.push(k);
set_tags[k]=v;
}
+ public function assignSetTags(tags:Object):void {
+ for (var i:uint=0; i<set_tags_order.length; i++) {
+ var k:String=set_tags_order[i];
+ var v:*=set_tags[k];
+ if (v is TagValue) { v=v.getValue(tags); }
+ if (v=='') { delete tags[k]; }
+ else { tags[k]=v; }
+ }
+ }
}
}
private static const CONDITION_LE:RegExp =/^ \s* ([:\w]+) \s* <= \s* (.+) \s* $/sx;
private static const CONDITION_REGEX:RegExp =/^ \s* ([:\w]+) \s* =~\/ \s* (.+) \/ \s* $/sx;
- private static const ASSIGNMENT_EVAL:RegExp =/^ \s* (\S+) \s* \: \s* eval \s* \( \s* ' (.+?) ' \s* \) \s* $/isx;
- private static const ASSIGNMENT:RegExp =/^ \s* (\S+) \s* \: \s* (.+?) \s* $/sx;
- private static const SET_TAG_EVAL:RegExp =/^ \s* set \s+(\S+)\s* = \s* eval \s* \( \s* ' (.+?) ' \s* \) \s* $/isx;
- private static const SET_TAG:RegExp =/^ \s* set \s+(\S+)\s* = \s* (.+?) \s* $/isx;
- private static const SET_TAG_TRUE:RegExp =/^ \s* set \s+(\S+)\s* $/isx;
- private static const EXIT:RegExp =/^ \s* exit \s* $/isx;
+ private static const ASSIGNMENT_EVAL:RegExp =/^ \s* (\S+) \s* \: \s* eval \s* \( \s* ' (.+?) ' \s* \) \s* $/isx;
+ private static const ASSIGNMENT_TAGVALUE:RegExp =/^ \s* (\S+) \s* \: \s* tag \s* \( \s* ' (.+?) ' \s* \) \s* $/isx;
+ private static const ASSIGNMENT:RegExp =/^ \s* (\S+) \s* \: \s* (.+?) \s* $/sx;
+ private static const SET_TAG_EVAL:RegExp =/^ \s* set \s+(\S+)\s* = \s* eval \s* \( \s* ' (.+?) ' \s* \) \s* $/isx;
+ private static const SET_TAG_TAGVALUE:RegExp =/^ \s* set \s+(\S+)\s* = \s* tag \s* \( \s* ' (.+?) ' \s* \) \s* $/isx;
+ private static const SET_TAG:RegExp =/^ \s* set \s+(\S+)\s* = \s* (.+?) \s* $/isx;
+ private static const SET_TAG_TRUE:RegExp =/^ \s* set \s+(\S+)\s* $/isx;
+ private static const DELETE_TAG:RegExp =/^ \s* delete \s+(\S+)\s* $/isx;
+ private static const EXIT:RegExp =/^ \s* exit \s* $/isx;
private static const oZOOM:uint=2;
private static const oGROUP:uint=3;
return sl;
}
+ /** Run instruction styles only, for CSSTransform. */
+ public function runInstructions(obj:Entity, tags:Object):Object {
+ for each (var sc:StyleChooser in choosers) {
+ tags=sc.runInstructions(obj,tags);
+ }
+ return tags;
+ }
+
// ---------------------------------------------------------------------------------------------------------
// Loading stylesheet
var xs:InstructionStyle=new InstructionStyle();
for each (a in s.split(';')) {
- if ((o=ASSIGNMENT_EVAL.exec(a))) { t[o[1].replace(DASH,'_')]=saveEval(o[2]); }
- else if ((o=ASSIGNMENT.exec(a))) { t[o[1].replace(DASH,'_')]=o[2]; }
- else if ((o=SET_TAG_EVAL.exec(a))) { xs.addSetTag(o[1],saveEval(o[2])); }
- else if ((o=SET_TAG.exec(a))) { xs.addSetTag(o[1],o[2]); }
- else if ((o=SET_TAG_TRUE.exec(a))) { xs.addSetTag(o[1],true); }
+ if ((o=ASSIGNMENT_EVAL.exec(a))) { t[o[1].replace(DASH,'_')]=saveEval(o[2]); }
+ else if ((o=ASSIGNMENT_TAGVALUE.exec(a))) { t[o[1].replace(DASH,'_')]=new TagValue(o[2]); }
+ else if ((o=ASSIGNMENT.exec(a))) { t[o[1].replace(DASH,'_')]=o[2]; }
+ else if ((o=SET_TAG_EVAL.exec(a))) { xs.addSetTag(o[1],saveEval(o[2])); }
+ else if ((o=SET_TAG_TAGVALUE.exec(a))) { xs.addSetTag(o[1],new TagValue(o[2])); }
+ else if ((o=SET_TAG.exec(a))) { xs.addSetTag(o[1],o[2]); }
+ else if ((o=SET_TAG_TRUE.exec(a))) { xs.addSetTag(o[1],true); }
+ else if ((o=DELETE_TAG.exec(a))) { xs.addSetTag(o[1],''); }
else if ((o=EXIT.exec(a))) { xs.setPropertyFromString('breaker',true); }
}
/** Compiled SWFs for each eval. We keep it here, not in the property itself, so that we can retain typing for each property. */
public var evals:Object={};
+ /** TagValue assignments, e.g. { width: tag('lanes'); } */
+ public var tagvalues:Object={};
+
/** Make an exact copy of an object.
Used when merging cascading styles. (FIXME: this needs some benchmarking - it may be quicker to simply iterate over .properties,
copying each one. */
return false;
}
- /** Are there any eval functions defined? */
+ /** Are there any eval functions defined? (This isn't used.) */
public function hasEvals():Boolean {
for (var k:String in evals) { return true; }
return false;
/** Run all evals for this Style over the supplied tags.
If, for example, the stylesheet contains width=eval('_width+2'), then this will set Style.width to 7. */
public function runEvals(tags:Object):void {
- for (var k:String in evals) {
- // ** Do we need to do typing here?
- this[k]=evals[k].exec(tags);
- }
+ for (var k:String in evals) this[k]=evals[k].exec(tags);
}
-
+
/** Set a property, casting as correct type. */
public function setPropertyFromString(k:String,v:*):Boolean {
if (!this.hasOwnProperty(k)) { return false; }
if (v is Eval) { evals[k]=v; v=1; }
+ else if (v is TagValue) { tagvalues[k]=v; v=1; }
// Arrays don't return a proper typeof, so check manually
// Note that undefined class variables always have typeof=object,
for each (var k:String in this.properties) {
if (this.hasOwnProperty(k)) { str+=k+"="+this[k]+"; "; }
}
+ for each (k in tagvalues) str+=k+";";
return str;
}
}
sl.addSubpart(c.subpart);
// Update StyleList
- for each (var r:Style in styles) {
+ for (var i:uint=0; i<styles.length; i++) {
+ var r:Style=styles[i];
var a:Object;
if (r is ShapeStyle) {
a=sl.shapeStyles;
if (w>sl.maxwidth) { sl.maxwidth=w; }
} else if (r is InstructionStyle) {
if (InstructionStyle(r).breaker) { return; }
- if (InstructionStyle(r).set_tags) {
- for (var k:String in InstructionStyle(r).set_tags) { tags[k]=InstructionStyle(r).set_tags[k]; }
- }
+ InstructionStyle(r).assignSetTags(tags);
continue;
}
if (r.drawn) { tags[':drawn']='yes'; }
}
}
+ /** Cut-down version of updateStyles that runs InstructionStyles only - for CSSTransform usage. */
+
+ public function runInstructions(obj:Entity, tags:Object):Object {
+ for each (var c:RuleChain in ruleChains) {
+ if (c.test(-1,obj,tags,10)) {
+ for (var i:uint=0; i<styles.length; i++) {
+ var r:Style=styles[i];
+ if (r is InstructionStyle) {
+ if (InstructionStyle(r).breaker) { return tags; }
+ InstructionStyle(r).assignSetTags(tags);
+ }
+ r.runEvals(tags);
+ }
+ }
+ }
+ return tags;
+ }
+
// ---------------------------------------------------------------------------------------------
// Methods to add properties (used by parsers such as MapCSS)
--- /dev/null
+package net.systemeD.halcyon.styleparser {
+
+ /*
+ === TagValue ===
+
+ This is a custom declaration value that means 'use the value of this tag'.
+ In other words,
+ { set ref=tag('dftnumber'); }
+ parses to
+ TagValue('dftnumber')
+ and returns the value of the dftnumber tag.
+
+ There isn't really any logic contained within this class, it's just here
+ so that we can store it as a custom property within Styles (like Eval).
+
+ */
+
+ public class TagValue {
+ private var key:String;
+
+ public function TagValue(k:String) {
+ key=k;
+ }
+
+ public function getValue(tags:Object):String {
+ return tags[key];
+ }
+
+ public function toString():String {
+ return "TagValue("+key+")";
+ }
+ }
+}
xmlns:mx="library://ns.adobe.com/flex/mx"
layout="vertical" showCloseButton="true"
horizontalAlign="center" title="Load vector file"
- width="500" height="350" verticalGap="0">
+ width="500" height="450" verticalGap="0">
<mx:DataGrid editable="true" width="100%" height="100%" id="dataGrid"
dataProvider="{vectorLayers}" itemEditEnd="dataEdited(event)">
</mx:HBox>
<mx:VBox width="100%" paddingTop="10">
- <mx:Label htmlText="<b>Add new vector layer</b>" />
+ <mx:HRule width="100%" />
+ <!-- ** FIXME: this looks horrid - make into a nicely laid out form -->
+ <mx:Label text="Add new vector layer" fontSize="12" fontWeight="bold" />
<mx:HBox>
<mx:RadioButtonGroup id="filetype" />
<mx:RadioButton width="100%" groupName="filetype" value="gpx" id="gpx" label="GPX" selected="true" />
<fx:Object label="NAD83" data="EPSG:4269" />
</mx:ArrayCollection>
</mx:ComboBox>
- <mx:CheckBox width="100%" label="Simplify paths" selected="true" id="simplify" />
+ <mx:CheckBox width="100%" label="Simplify paths" id="simplify" />
</mx:HBox>
<mx:HBox>
<mx:Text text="URL:"/>
<mx:TextInput width="100%" id="src" text="" change="selectByExtension()" />
<mx:Button label="Load" click="loadFiles(src.text, filetype.selectedValue.toString(), simplify.selected, projection.selectedItem.data);" enabled="{src.text == '' ? false : true}"/>
</mx:HBox>
+ <mx:HBox>
+ <mx:Text text="Tag transform:"/>
+ <mx:TextInput width="100%" id="transformsrc" text="{CSSTransform.getInstance().url}"/>
+ <mx:Button label="Load" click="loadTransform(transformsrc.text);" enabled="{transformsrc.text == '' ? false : true}"/>
+ <mx:Button label="Clear" click="clearTransform();" enabled="{transformsrc.text == '' ? false : true}"/>
+ </mx:HBox>
</mx:VBox>
<fx:Script><![CDATA[
import net.systemeD.halcyon.MapPaint;
import net.systemeD.halcyon.Globals;
import net.systemeD.halcyon.connection.Connection;
- import net.systemeD.potlatch2.utils.Importer;
- import net.systemeD.potlatch2.utils.GpxImporter;
- import net.systemeD.potlatch2.utils.KmlImporter;
- import net.systemeD.potlatch2.utils.ShpImporter;
- import net.systemeD.potlatch2.utils.OsmImporter;
+ import net.systemeD.halcyon.styleparser.CSSTransform;
+ import net.systemeD.potlatch2.utils.*;
import net.systemeD.potlatch2.collections.Stylesheets;
import mx.managers.PopUpManager;
import mx.events.DataGridEvent;
var stylesheet:String="stylesheets/potlatch.css";
if (type=='gpx') { stylesheet="stylesheets/gpx.css"; }
- var connection:Connection = new Connection(name, url, null, null);
+ var connection:Connection = new Connection(name, url, null, null, CSSTransform.getInstance());
var filesLoaded:Function = function(success:Boolean,message:String=null):void {
if (success) {
[url+".shp",url+".shx",url+".dbf"], filesLoaded, simplify, projection);
}
}
+
+ private function loadTransform(url:String):void {
+ CSSTransform.getInstance().loadFromUrl(url);
+ }
+ private function clearTransform():void {
+ CSSTransform.getInstance().clear();
+ }
]]>
</fx:Script>