forked from MapComplete/MapComplete
		
	Merge develop branch
This commit is contained in:
		
						commit
						8ca24f4a05
					
				
					 13 changed files with 475 additions and 161 deletions
				
			
		|  | @ -41,7 +41,7 @@ export interface TagRenderingConfigJson { | |||
|         type?: string, | ||||
|         /** | ||||
|          * If a value is added with the textfield, these extra tag is addded. | ||||
|          * Usefull to add a 'fixme=freeform textfield used - to be checked' | ||||
|          * Useful to add a 'fixme=freeform textfield used - to be checked' | ||||
|          **/ | ||||
|         addExtraTags?: string[]; | ||||
|     }, | ||||
|  |  | |||
|  | @ -88,4 +88,4 @@ Given either a list of geojson features or a single layer name, gives the single | |||
| 
 | ||||
| ### memberships | ||||
| 
 | ||||
| Gives a list of {role: string, relation: Relation}-objects, containing all the relations that this feature is part of. For example: `_part_of_walking_routes=feat.memberships().map(r => r.relation.tags.name).join(';')` | ||||
| Gives a list of `{role: string, relation: Relation}`\-objects, containing all the relations that this feature is part of. For example: `_part_of_walking_routes=feat.memberships().map(r => r.relation.tags.name).join(';')` | ||||
							
								
								
									
										63
									
								
								Docs/SpecialInputElements
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								Docs/SpecialInputElements
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,63 @@ | |||
| # Available types for text fields | ||||
| 
 | ||||
| The listed types here trigger a special input element. Use them in `tagrendering.freeform.type` of your tagrendering to activate them | ||||
| 
 | ||||
| ## string | ||||
| 
 | ||||
| A basic string | ||||
| 
 | ||||
| ## text | ||||
| 
 | ||||
| A string, but allows input of longer strings more comfortably (a text area) | ||||
| 
 | ||||
| ## date | ||||
| 
 | ||||
| A date | ||||
| 
 | ||||
| ## wikidata | ||||
| 
 | ||||
| A wikidata identifier, e.g. Q42 | ||||
| 
 | ||||
| ## int | ||||
| 
 | ||||
| A number | ||||
| 
 | ||||
| ## nat | ||||
| 
 | ||||
| A positive number or zero | ||||
| 
 | ||||
| ## pnat | ||||
| 
 | ||||
| A strict positive number | ||||
| 
 | ||||
| ## direction | ||||
| 
 | ||||
| A geographical direction, in degrees. 0° is north, 90° is east, ... Will return a value between 0 (incl) and 360 (excl) | ||||
| 
 | ||||
| ## float | ||||
| 
 | ||||
| A decimal | ||||
| 
 | ||||
| ## pfloat | ||||
| 
 | ||||
| A positive decimal (incl zero) | ||||
| 
 | ||||
| ## email | ||||
| 
 | ||||
| An email adress | ||||
| 
 | ||||
| ## url | ||||
| 
 | ||||
| A url | ||||
| 
 | ||||
| ## phone | ||||
| 
 | ||||
| A phone number | ||||
| 
 | ||||
| ## opening_hours | ||||
| 
 | ||||
| Has extra elements to easily input when a POI is opened | ||||
| 
 | ||||
| ## color | ||||
| 
 | ||||
| Shows a color picker | ||||
|  | @ -1,7 +1,6 @@ | |||
| import {GeoOperations} from "./GeoOperations"; | ||||
| import {UIElement} from "../UI/UIElement"; | ||||
| import Combine from "../UI/Base/Combine"; | ||||
| import State from "../State"; | ||||
| import {Relation} from "./Osm/ExtractRelations"; | ||||
| 
 | ||||
| export class ExtraFunction { | ||||
|  | @ -65,9 +64,8 @@ Some advanced functions are available on <b>feat</b> as well: | |||
|         (featuresPerLayer, feature) => { | ||||
|             return (arg0, lat) => { | ||||
|                 if (typeof arg0 === "number") { | ||||
|                     const lon = arg0 | ||||
|                     // Feature._lon and ._lat is conveniently place by one of the other metatags
 | ||||
|                     return GeoOperations.distanceBetween([lon, lat], [feature._lon, feature._lat]); | ||||
|                     return GeoOperations.distanceBetween([arg0, lat], [feature._lon, feature._lat]); | ||||
|                 } else { | ||||
|                     // arg0 is probably a feature
 | ||||
|                     return GeoOperations.distanceBetween(GeoOperations.centerpointCoordinates(arg0), [feature._lon, feature._lat]) | ||||
|  | @ -117,9 +115,11 @@ Some advanced functions are available on <b>feat</b> as well: | |||
| 
 | ||||
|     private static readonly Memberships = new ExtraFunction( | ||||
|         "memberships", | ||||
|         "Gives a list of {role: string, relation: Relation}-objects, containing all the relations that this feature is part of. \n\nFor example: <code>_part_of_walking_routes=feat.memberships().map(r => r.relation.tags.name).join(';')</code>", | ||||
|         "Gives a list of <code>{role: string, relation: Relation}</code>-objects, containing all the relations that this feature is part of. " + | ||||
|         "\n\n" + | ||||
|         "For example: <code>_part_of_walking_routes=feat.memberships().map(r => r.relation.tags.name).join(';')</code>", | ||||
|         [], | ||||
|         (params, feature) => { | ||||
|         (params, _) => { | ||||
|             return () =>   params.relations ?? []; | ||||
|         } | ||||
|     ) | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ import { Utils } from "../Utils"; | |||
| 
 | ||||
| export default class Constants { | ||||
|      | ||||
|     public static vNumber = "0.7.1-rc1"; | ||||
|     public static vNumber = "0.7.2-dev"; | ||||
| 
 | ||||
|     // The user journey states thresholds when a new feature gets unlocked
 | ||||
|     public static userJourney = { | ||||
|  |  | |||
							
								
								
									
										60
									
								
								UI/Input/ColorPicker.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								UI/Input/ColorPicker.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,60 @@ | |||
| import {InputElement} from "./InputElement"; | ||||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import {Utils} from "../../Utils"; | ||||
| 
 | ||||
| export default class ColorPicker extends InputElement<string> { | ||||
| 
 | ||||
|     private readonly value: UIEventSource<string> | ||||
| 
 | ||||
|     constructor( | ||||
|         value?: UIEventSource<string> | ||||
|     ) { | ||||
|         super(); | ||||
|         this.value = value ?? new UIEventSource<string>(undefined); | ||||
|         const self = this; | ||||
|         this.value.addCallbackAndRun(v => { | ||||
|             if(v === undefined){ | ||||
|                 return; | ||||
|             } | ||||
|             self.SetValue(v); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     InnerRender(): string { | ||||
|         return `<span id="${this.id}"><input type='color' id='color-${this.id}'></span>`; | ||||
|     } | ||||
|      | ||||
|     private SetValue(color: string){ | ||||
|         const field = document.getElementById("color-" + this.id); | ||||
|         if (field === undefined || field === null) { | ||||
|             return; | ||||
|         } | ||||
|         // @ts-ignore
 | ||||
|         field.value = color; | ||||
|     } | ||||
| 
 | ||||
|     protected InnerUpdate() { | ||||
|         const field = document.getElementById("color-" + this.id); | ||||
|         if (field === undefined || field === null) { | ||||
|             return; | ||||
|         } | ||||
|         const self = this; | ||||
|         field.oninput = () => { | ||||
|             const hex = field["value"]; | ||||
|             self.value.setData(hex); | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     GetValue(): UIEventSource<string> { | ||||
|         return this.value; | ||||
|     } | ||||
| 
 | ||||
|     IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false); | ||||
| 
 | ||||
|     IsValid(t: string): boolean { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -10,6 +10,7 @@ export class TextField extends InputElement<string> { | |||
|     private readonly _placeholder: UIElement; | ||||
|     public readonly IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false); | ||||
|     private readonly _htmlType: string; | ||||
|     private readonly _inputMode : string; | ||||
|     private readonly _textAreaRows: number; | ||||
| 
 | ||||
|     private readonly _isValid: (string,country) => boolean; | ||||
|  | @ -20,6 +21,7 @@ export class TextField extends InputElement<string> { | |||
|         value?: UIEventSource<string>, | ||||
|         textArea?: boolean, | ||||
|         htmlType?: string, | ||||
|         inputMode?: string, | ||||
|         label?: UIElement, | ||||
|         textAreaRows?: number, | ||||
|         isValid?: ((s: string, country?: () => string) => boolean) | ||||
|  | @ -36,6 +38,7 @@ export class TextField extends InputElement<string> { | |||
|         this._isValid = options.isValid ?? ((str, country) => true); | ||||
| 
 | ||||
|         this._placeholder = Translations.W(options.placeholder ?? ""); | ||||
|         this._inputMode = options.inputMode; | ||||
|         this.ListenTo(this._placeholder._source); | ||||
| 
 | ||||
|         this.onClick(() => { | ||||
|  | @ -72,11 +75,15 @@ export class TextField extends InputElement<string> { | |||
|         if (this._label != undefined) { | ||||
|             label = this._label.Render(); | ||||
|         } | ||||
|         let inputMode = "" | ||||
|         if(this._inputMode !== undefined){ | ||||
|             inputMode = `inputmode="${this._inputMode}" ` | ||||
|         } | ||||
|         return new Combine([ | ||||
|             `<span id="${this.id}">`, | ||||
|             `<form onSubmit='return false' class='form-text-field'>`, | ||||
|             label, | ||||
|             `<input type='${this._htmlType}' placeholder='${placeholder}' id='txt-${this.id}'/>`, | ||||
|             `<input type='${this._htmlType}' ${inputMode} placeholder='${placeholder}' id='txt-${this.id}'/>`, | ||||
|             `</form>`, | ||||
|             `</span>` | ||||
|         ]).Render(); | ||||
|  | @ -134,9 +141,6 @@ export class TextField extends InputElement<string> { | |||
|     } | ||||
| 
 | ||||
|     public SetCursorPosition(i: number) { | ||||
|         if(this._htmlType !== "text" && this._htmlType !== "area"){ | ||||
|             return; | ||||
|         } | ||||
|         const field = document.getElementById('txt-' + this.id); | ||||
|         if(field === undefined || field === null){ | ||||
|             return; | ||||
|  |  | |||
|  | @ -10,53 +10,36 @@ import CombinedInputElement from "./CombinedInputElement"; | |||
| import SimpleDatePicker from "./SimpleDatePicker"; | ||||
| import OpeningHoursInput from "../OpeningHours/OpeningHoursInput"; | ||||
| import DirectionInput from "./DirectionInput"; | ||||
| import ColorPicker from "./ColorPicker"; | ||||
| import {Utils} from "../../Utils"; | ||||
| 
 | ||||
| interface TextFieldDef { | ||||
|     name: string, | ||||
|     explanation: string, | ||||
|     isValid: ((s: string, country?:() => string) => boolean), | ||||
|     isValid: ((s: string, country?: () => string) => boolean), | ||||
|     reformat?: ((s: string, country?: () => string) => string), | ||||
|     inputHelper?: (value: UIEventSource<string>, options?: { | ||||
|         location: [number, number] | ||||
|     }) => InputElement<string>, | ||||
| 
 | ||||
|     inputmode?: string | ||||
| } | ||||
| 
 | ||||
| export default class ValidatedTextField { | ||||
| 
 | ||||
| 
 | ||||
|     private static tp(name: string, | ||||
|                       explanation: string, | ||||
|                       isValid?: ((s: string, country?: () => string) => boolean), | ||||
|                       reformat?: ((s: string, country?: () => string) => string), | ||||
|                       inputHelper?: (value: UIEventSource<string>, options?:{ | ||||
|                           location: [number, number] | ||||
|                       }) => InputElement<string>): TextFieldDef { | ||||
| 
 | ||||
|         if (isValid === undefined) { | ||||
|             isValid = () => true; | ||||
|         } | ||||
| 
 | ||||
|         if (reformat === undefined) { | ||||
|             reformat = (str, _) => str; | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         return { | ||||
|             name: name, | ||||
|             explanation: explanation, | ||||
|             isValid: isValid, | ||||
|             reformat: reformat, | ||||
|             inputHelper: inputHelper | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static tpList: TextFieldDef[] = [ | ||||
|         ValidatedTextField.tp( | ||||
|             "string", | ||||
|             "A basic string"), | ||||
|         ValidatedTextField.tp( | ||||
|             "text", | ||||
|             "A string, but allows input of longer strings more comfortably (a text area)"), | ||||
|             "A string, but allows input of longer strings more comfortably (a text area)", | ||||
|             undefined, | ||||
|             undefined, | ||||
|             undefined, | ||||
|             "text"), | ||||
|          | ||||
|         ValidatedTextField.tp( | ||||
|             "date", | ||||
|             "A date", | ||||
|  | @ -87,44 +70,63 @@ export default class ValidatedTextField { | |||
|             (str) => { | ||||
|                 str = "" + str; | ||||
|                 return str !== undefined && str.indexOf(".") < 0 && !isNaN(Number(str)) | ||||
|             }), | ||||
|             }, | ||||
|             undefined, | ||||
|             undefined, | ||||
|             "numeric"), | ||||
|         ValidatedTextField.tp( | ||||
|             "nat", | ||||
|             "A positive number or zero", | ||||
|             (str) => { | ||||
|                 str = "" + str; | ||||
|                 return str !== undefined && str.indexOf(".") < 0 && !isNaN(Number(str)) && Number(str) >= 0 | ||||
|             }), | ||||
|             }, | ||||
|             undefined, | ||||
|             undefined, | ||||
|             "numeric"), | ||||
|         ValidatedTextField.tp( | ||||
|             "pnat", | ||||
|             "A strict positive number", | ||||
|             (str) => { | ||||
|                 str = "" + str; | ||||
|                 return str !== undefined && str.indexOf(".") < 0 && !isNaN(Number(str)) && Number(str) > 0 | ||||
|             }), | ||||
|             }, | ||||
|             undefined, | ||||
|             undefined, | ||||
|             "numeric"), | ||||
|         ValidatedTextField.tp( | ||||
|             "direction", | ||||
|             "A geographical direction, in degrees. 0° is north, 90° is east, ... Will return a value between 0 (incl) and 360 (excl)", | ||||
|             (str) => { | ||||
|                 str = "" + str; | ||||
|                 return str !== undefined && str.indexOf(".") < 0 && !isNaN(Number(str)) && Number(str) >= 0 && Number(str) <= 360 | ||||
|             },str => str, | ||||
|             }, str => str, | ||||
|             (value) => { | ||||
|               return new DirectionInput(value); | ||||
|             } | ||||
|                 return new DirectionInput(value); | ||||
|             }, | ||||
|             "numeric" | ||||
|         ), | ||||
|         ValidatedTextField.tp( | ||||
|             "float", | ||||
|             "A decimal", | ||||
|             (str) => !isNaN(Number(str))), | ||||
|             (str) => !isNaN(Number(str)), | ||||
|             undefined, | ||||
|             undefined, | ||||
|             "decimal"), | ||||
|         ValidatedTextField.tp( | ||||
|             "pfloat", | ||||
|             "A positive decimal (incl zero)", | ||||
|             (str) => !isNaN(Number(str)) && Number(str) >= 0), | ||||
|             (str) => !isNaN(Number(str)) && Number(str) >= 0, | ||||
|             undefined, | ||||
|             undefined, | ||||
|             "decimal"), | ||||
|         ValidatedTextField.tp( | ||||
|             "email", | ||||
|             "An email adress", | ||||
|             (str) => EmailValidator.validate(str)), | ||||
|             (str) => EmailValidator.validate(str), | ||||
|             undefined, | ||||
|             undefined, | ||||
|             "email"), | ||||
|         ValidatedTextField.tp( | ||||
|             "url", | ||||
|             "A url", | ||||
|  | @ -135,18 +137,19 @@ export default class ValidatedTextField { | |||
|                 } catch (e) { | ||||
|                     return false; | ||||
|                 } | ||||
|             }, (str) => { | ||||
|             }, | ||||
|             (str) => { | ||||
|                 try { | ||||
|                     const url = new URL(str); | ||||
|                     const blacklistedTrackingParams = [ | ||||
|                         "fbclid",// Oh god, how I hate the fbclid. Let it burn, burn in hell!
 | ||||
|                         "gclid", | ||||
|                         "cmpid", "agid", "utm", "utm_source","utm_medium"] | ||||
|                         "cmpid", "agid", "utm", "utm_source", "utm_medium"] | ||||
|                     for (const dontLike of blacklistedTrackingParams) { | ||||
|                         url.searchParams.delete(dontLike) | ||||
|                     } | ||||
|                     let cleaned = url.toString(); | ||||
|                     if(cleaned.endsWith("/") && !str.endsWith("/")){ | ||||
|                     if (cleaned.endsWith("/") && !str.endsWith("/")) { | ||||
|                         // Do not add a trailing '/' if it wasn't typed originally
 | ||||
|                         cleaned = cleaned.substr(0, cleaned.length - 1) | ||||
|                     } | ||||
|  | @ -155,7 +158,9 @@ export default class ValidatedTextField { | |||
|                     console.error(e) | ||||
|                     return undefined; | ||||
|                 } | ||||
|             }), | ||||
|             }, | ||||
|             undefined, | ||||
|             "url"), | ||||
|         ValidatedTextField.tp( | ||||
|             "phone", | ||||
|             "A phone number", | ||||
|  | @ -165,26 +170,35 @@ export default class ValidatedTextField { | |||
|                 } | ||||
|                 return parsePhoneNumberFromString(str, (country())?.toUpperCase() as any)?.isValid() ?? false | ||||
|             }, | ||||
|             (str, country: () => string) => parsePhoneNumberFromString(str, (country())?.toUpperCase() as any).formatInternational() | ||||
|             (str, country: () => string) => parsePhoneNumberFromString(str, (country())?.toUpperCase() as any).formatInternational(), | ||||
|             undefined, | ||||
|             "tel" | ||||
|         ), | ||||
|         ValidatedTextField.tp( | ||||
|             "opening_hours", | ||||
|             "Has extra elements to easily input when a POI is opened", | ||||
|             (s, country) => true,  | ||||
|             () => true, | ||||
|             str => str, | ||||
|             (value) => { | ||||
|                 return new OpeningHoursInput(value); | ||||
|             } | ||||
|         ), | ||||
|         ValidatedTextField.tp( | ||||
|             "color", | ||||
|             "Shows a color picker", | ||||
|             () => true, | ||||
|             str => str, | ||||
|             (value) => { | ||||
|                 return new ColorPicker(value.map(color => { | ||||
|                     return Utils.ColourNameToHex(color ?? ""); | ||||
|                 }, [], str => Utils.HexToColourName(str))) | ||||
|             } | ||||
|         ) | ||||
|     ] | ||||
|      | ||||
|     private static allTypesDict(){ | ||||
|         const types = {}; | ||||
|         for (const tp of ValidatedTextField.tpList) { | ||||
|             types[tp.name] = tp; | ||||
|         } | ||||
|         return types; | ||||
|     } | ||||
|     /** | ||||
|      * {string (typename) --> TextFieldDef} | ||||
|      */ | ||||
|     public static AllTypes = ValidatedTextField.allTypesDict(); | ||||
| 
 | ||||
|     public static TypeDropdown(): DropDown<string> { | ||||
|         const values: { value: string, shown: string }[] = []; | ||||
|  | @ -195,15 +209,12 @@ export default class ValidatedTextField { | |||
|         return new DropDown<string>("", values) | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * {string (typename) --> TextFieldDef} | ||||
|      */ | ||||
|     public static AllTypes = ValidatedTextField.allTypesDict(); | ||||
| 
 | ||||
|     public static InputForType(type: string, options?: { | ||||
|         placeholder?: string | UIElement, | ||||
|         value?: UIEventSource<string>, | ||||
|         textArea?: boolean, | ||||
|         htmlType?: string, | ||||
|         textArea?:boolean, | ||||
|         inputMode?:string, | ||||
|         textAreaRows?: number, | ||||
|         isValid?: ((s: string, country: () => string) => boolean), | ||||
|         country?: () => string, | ||||
|  | @ -218,16 +229,16 @@ export default class ValidatedTextField { | |||
|         if (options.isValid) { | ||||
|             const optValid = options.isValid; | ||||
|             isValid = (str, country) => { | ||||
|                 if(str === undefined){ | ||||
|                 if (str === undefined) { | ||||
|                     return false; | ||||
|                 } | ||||
|                 return isValidTp(str, country ?? options.country) && optValid(str, country ?? options.country); | ||||
|             } | ||||
|         }else{ | ||||
|         } else { | ||||
|             isValid = isValidTp; | ||||
|         } | ||||
|         options.isValid = isValid; | ||||
| 
 | ||||
|         options.inputMode = tp.inputmode; | ||||
|         let input: InputElement<string> = new TextField(options); | ||||
|         if (tp.reformat) { | ||||
|             input.GetValue().addCallbackAndRun(str => { | ||||
|  | @ -240,7 +251,7 @@ export default class ValidatedTextField { | |||
|         } | ||||
| 
 | ||||
|         if (tp.inputHelper) { | ||||
|             input = new CombinedInputElement(input, tp.inputHelper(input.GetValue(),{ | ||||
|             input = new CombinedInputElement(input, tp.inputHelper(input.GetValue(), { | ||||
|                 location: options.location | ||||
|             })); | ||||
|         } | ||||
|  | @ -299,8 +310,6 @@ export default class ValidatedTextField { | |||
|         return new InputElementMap(textfield, isSame, fromString, toString); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     static Mapped<T>(fromString: (str) => T, toString: (T) => string, options?: { | ||||
|         placeholder?: string | UIElement, | ||||
|         type?: string, | ||||
|  | @ -323,4 +332,46 @@ export default class ValidatedTextField { | |||
|         ); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public static HelpText(): string { | ||||
|         const explanations = ValidatedTextField.tpList.map(type => ["## " + type.name, "", type.explanation].join("\n")).join("\n\n") | ||||
|         return "# Available types for text fields\n\nThe listed types here trigger a special input element. Use them in `tagrendering.freeform.type` of your tagrendering to activate them\n\n" + explanations | ||||
|     } | ||||
| 
 | ||||
|     private static tp(name: string, | ||||
|                       explanation: string, | ||||
|                       isValid?: ((s: string, country?: () => string) => boolean), | ||||
|                       reformat?: ((s: string, country?: () => string) => string), | ||||
|                       inputHelper?: (value: UIEventSource<string>, options?: { | ||||
|                           location: [number, number] | ||||
|                       }) => InputElement<string>, | ||||
|                       inputmode?: string): TextFieldDef { | ||||
| 
 | ||||
|         if (isValid === undefined) { | ||||
|             isValid = () => true; | ||||
|         } | ||||
| 
 | ||||
|         if (reformat === undefined) { | ||||
|             reformat = (str, _) => str; | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         return { | ||||
|             name: name, | ||||
|             explanation: explanation, | ||||
|             isValid: isValid, | ||||
|             reformat: reformat, | ||||
|             inputHelper: inputHelper, | ||||
|             inputmode: inputmode | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     private static allTypesDict() { | ||||
|         const types = {}; | ||||
|         for (const tp of ValidatedTextField.tpList) { | ||||
|             types[tp.name] = tp; | ||||
|         } | ||||
|         return types; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										65
									
								
								Utils.ts
									
										
									
									
									
								
							
							
						
						
									
										65
									
								
								Utils.ts
									
										
									
									
									
								
							|  | @ -1,6 +1,6 @@ | |||
| import * as $ from "jquery" | ||||
| import {type} from "os"; | ||||
| 
 | ||||
| import * as colors from "./assets/colors.json" | ||||
| export class Utils { | ||||
| 
 | ||||
|     /** | ||||
|  | @ -305,6 +305,69 @@ export class Utils { | |||
|         element.click(); | ||||
|     } | ||||
|      | ||||
|      | ||||
|     public static ColourNameToHex(color: string): string{ | ||||
|         return colors[color.toLowerCase()] ?? color; | ||||
|     } | ||||
|      | ||||
|     public static HexToColourName(hex : string): string{ | ||||
|         hex = hex.toLowerCase() | ||||
|         if(!hex.startsWith("#")){ | ||||
|             return hex; | ||||
|         } | ||||
|         const c = Utils.color(hex); | ||||
|          | ||||
|         let smallestDiff = Number.MAX_VALUE; | ||||
|         let bestColor = undefined; | ||||
|         for (const color in colors) { | ||||
|             if(!colors.hasOwnProperty(color)){ | ||||
|                 continue; | ||||
|             } | ||||
|             const foundhex = colors[color]; | ||||
|             if(typeof foundhex !== "string"){ | ||||
|                 continue | ||||
|             } | ||||
|             if(foundhex === hex){ | ||||
|                 return color | ||||
|             } | ||||
|             const diff = this.colorDiff(Utils.color(foundhex), c) | ||||
|             if(diff > 50){ | ||||
|                 continue; | ||||
|             } | ||||
|             if(diff < smallestDiff){ | ||||
|                 smallestDiff = diff; | ||||
|                 bestColor = color; | ||||
|             } | ||||
|         } | ||||
|         return bestColor ?? hex; | ||||
|     } | ||||
|      | ||||
|     private static colorDiff(c0 : {r: number, g: number, b: number}, c1: {r: number, g: number, b: number}){ | ||||
|         return Math.abs(c0.r - c1.r) + Math.abs(c0.g - c1.g) +Math.abs(c0.b - c1.b) ; | ||||
|     } | ||||
|      | ||||
|     private static color(hex: string) : {r: number, g: number, b: number}{ | ||||
|         if(hex.startsWith == undefined){ | ||||
|             console.trace("WUT?", hex) | ||||
|             throw "wut?" | ||||
|         } | ||||
|         if(!hex.startsWith("#")){ | ||||
|             return  undefined; | ||||
|         } | ||||
|         if(hex.length === 4){ | ||||
|          return { | ||||
|              r : parseInt(hex.substr(1, 1), 16), | ||||
|             g : parseInt(hex.substr(2, 1), 16), | ||||
|              b : parseInt(hex.substr(3, 1), 16), | ||||
|          }   | ||||
|         } | ||||
| 
 | ||||
|         return { | ||||
|             r : parseInt(hex.substr(1, 2), 16), | ||||
|             g : parseInt(hex.substr(3, 2), 16), | ||||
|             b : parseInt(hex.substr(5, 2), 16), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export interface TileRange{ | ||||
|  |  | |||
							
								
								
									
										150
									
								
								assets/colors.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								assets/colors.json
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,150 @@ | |||
| { | ||||
|   "aliceblue": "#f0f8ff", | ||||
|   "antiquewhite": "#faebd7", | ||||
|   "aqua": "#00ffff", | ||||
|   "aquamarine": "#7fffd4", | ||||
|   "azure": "#f0ffff", | ||||
|   "beige": "#f5f5dc", | ||||
|   "bisque": "#ffe4c4", | ||||
|   "black": "#000000", | ||||
|   "blanchedalmond": "#ffebcd", | ||||
|   "blue": "#0000ff", | ||||
|   "blueviolet": "#8a2be2", | ||||
|   "brown": "#a52a2a", | ||||
|   "burlywood": "#deb887", | ||||
|   "cadetblue": "#5f9ea0", | ||||
|   "chartreuse": "#7fff00", | ||||
|   "chocolate": "#d2691e", | ||||
|   "coral": "#ff7f50", | ||||
|   "cornflowerblue": "#6495ed", | ||||
|   "cornsilk": "#fff8dc", | ||||
|   "crimson": "#dc143c", | ||||
|   "cyan": "#00ffff", | ||||
|   "darkblue": "#00008b", | ||||
|   "darkcyan": "#008b8b", | ||||
|   "darkgoldenrod": "#b8860b", | ||||
|   "darkgray": "#a9a9a9", | ||||
|   "darkgrey": "#a9a9a9", | ||||
|   "darkgreen": "#006400", | ||||
|   "darkkhaki": "#bdb76b", | ||||
|   "darkmagenta": "#8b008b", | ||||
|   "darkolivegreen": "#556b2f", | ||||
|   "darkorange": "#ff8c00", | ||||
|   "darkorchid": "#9932cc", | ||||
|   "darkred": "#8b0000", | ||||
|   "darksalmon": "#e9967a", | ||||
|   "darkseagreen": "#8fbc8f", | ||||
|   "darkslateblue": "#483d8b", | ||||
|   "darkslategray": "#2f4f4f", | ||||
|   "darkslategrey": "#2f4f4f", | ||||
|   "darkturquoise": "#00ced1", | ||||
|   "darkviolet": "#9400d3", | ||||
|   "deeppink": "#ff1493", | ||||
|   "deepskyblue": "#00bfff", | ||||
|   "dimgray": "#696969", | ||||
|   "dimgrey": "#696969", | ||||
|   "dodgerblue": "#1e90ff", | ||||
|   "firebrick": "#b22222", | ||||
|   "floralwhite": "#fffaf0", | ||||
|   "forestgreen": "#228b22", | ||||
|   "fuchsia": "#ff00ff", | ||||
|   "gainsboro": "#dcdcdc", | ||||
|   "ghostwhite": "#f8f8ff", | ||||
|   "gold": "#ffd700", | ||||
|   "goldenrod": "#daa520", | ||||
|   "gray": "#808080", | ||||
|   "grey": "#808080", | ||||
|   "green": "#008000", | ||||
|   "greenyellow": "#adff2f", | ||||
|   "honeydew": "#f0fff0", | ||||
|   "hotpink": "#ff69b4", | ||||
|   "indianred": "#cd5c5c", | ||||
|   "indigo": "#4b0082", | ||||
|   "ivory": "#fffff0", | ||||
|   "khaki": "#f0e68c", | ||||
|   "lavender": "#e6e6fa", | ||||
|   "lavenderblush": "#fff0f5", | ||||
|   "lawngreen": "#7cfc00", | ||||
|   "lemonchiffon": "#fffacd", | ||||
|   "lightblue": "#add8e6", | ||||
|   "lightcoral": "#f08080", | ||||
|   "lightcyan": "#e0ffff", | ||||
|   "lightgoldenrodyellow": "#fafad2", | ||||
|   "lightgray": "#d3d3d3", | ||||
|   "lightgrey": "#d3d3d3", | ||||
|   "lightgreen": "#90ee90", | ||||
|   "lightpink": "#ffb6c1", | ||||
|   "lightsalmon": "#ffa07a", | ||||
|   "lightseagreen": "#20b2aa", | ||||
|   "lightskyblue": "#87cefa", | ||||
|   "lightslategray": "#778899", | ||||
|   "lightslategrey": "#778899", | ||||
|   "lightsteelblue": "#b0c4de", | ||||
|   "lightyellow": "#ffffe0", | ||||
|   "lime": "#00ff00", | ||||
|   "limegreen": "#32cd32", | ||||
|   "linen": "#faf0e6", | ||||
|   "magenta": "#ff00ff", | ||||
|   "maroon": "#800000", | ||||
|   "mediumaquamarine": "#66cdaa", | ||||
|   "mediumblue": "#0000cd", | ||||
|   "mediumorchid": "#ba55d3", | ||||
|   "mediumpurple": "#9370db", | ||||
|   "mediumseagreen": "#3cb371", | ||||
|   "mediumslateblue": "#7b68ee", | ||||
|   "mediumspringgreen": "#00fa9a", | ||||
|   "mediumturquoise": "#48d1cc", | ||||
|   "mediumvioletred": "#c71585", | ||||
|   "midnightblue": "#191970", | ||||
|   "mintcream": "#f5fffa", | ||||
|   "mistyrose": "#ffe4e1", | ||||
|   "moccasin": "#ffe4b5", | ||||
|   "navajowhite": "#ffdead", | ||||
|   "navy": "#000080", | ||||
|   "oldlace": "#fdf5e6", | ||||
|   "olive": "#808000", | ||||
|   "olivedrab": "#6b8e23", | ||||
|   "orange": "#ffa500", | ||||
|   "orangered": "#ff4500", | ||||
|   "orchid": "#da70d6", | ||||
|   "palegoldenrod": "#eee8aa", | ||||
|   "palegreen": "#98fb98", | ||||
|   "paleturquoise": "#afeeee", | ||||
|   "palevioletred": "#db7093", | ||||
|   "papayawhip": "#ffefd5", | ||||
|   "peachpuff": "#ffdab9", | ||||
|   "peru": "#cd853f", | ||||
|   "pink": "#ffc0cb", | ||||
|   "plum": "#dda0dd", | ||||
|   "powderblue": "#b0e0e6", | ||||
|   "purple": "#800080", | ||||
|   "rebeccapurple": "#663399", | ||||
|   "red": "#ff0000", | ||||
|   "rosybrown": "#bc8f8f", | ||||
|   "royalblue": "#4169e1", | ||||
|   "saddlebrown": "#8b4513", | ||||
|   "salmon": "#fa8072", | ||||
|   "sandybrown": "#f4a460", | ||||
|   "seagreen": "#2e8b57", | ||||
|   "seashell": "#fff5ee", | ||||
|   "sienna": "#a0522d", | ||||
|   "silver": "#c0c0c0", | ||||
|   "skyblue": "#87ceeb", | ||||
|   "slateblue": "#6a5acd", | ||||
|   "slategray": "#708090", | ||||
|   "slategrey": "#708090", | ||||
|   "snow": "#fffafa", | ||||
|   "springgreen": "#00ff7f", | ||||
|   "steelblue": "#4682b4", | ||||
|   "tan": "#d2b48c", | ||||
|   "teal": "#008080", | ||||
|   "thistle": "#d8bfd8", | ||||
|   "tomato": "#ff6347", | ||||
|   "turquoise": "#40e0d0", | ||||
|   "violet": "#ee82ee", | ||||
|   "wheat": "#f5deb3", | ||||
|   "white": "#ffffff", | ||||
|   "whitesmoke": "#f5f5f5", | ||||
|   "yellow": "#ffff00", | ||||
|   "yellowgreen": "#9acd32" | ||||
| } | ||||
|  | @ -6,6 +6,7 @@ import {UIElement} from "../UI/UIElement"; | |||
| import SimpleMetaTagger from "../Logic/SimpleMetaTagger"; | ||||
| import Combine from "../UI/Base/Combine"; | ||||
| import {ExtraFunction} from "../Logic/ExtraFunction"; | ||||
| import ValidatedTextField from "../UI/Input/ValidatedTextField"; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  | @ -19,6 +20,6 @@ function WriteFile(filename, html: UIElement) : void { | |||
| 
 | ||||
| WriteFile("./Docs/SpecialRenderings.md", SpecialVisualizations.HelpMessage) | ||||
| WriteFile("./Docs/CalculatedTags.md", new Combine([SimpleMetaTagger.HelpText(), ExtraFunction.HelpText()])) | ||||
| 
 | ||||
| writeFileSync("./Docs/SpecialInputElements", ValidatedTextField.HelpText()); | ||||
| console.log("Generated docs") | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										81
									
								
								test.html
									
										
									
									
									
								
							
							
						
						
									
										81
									
								
								test.html
									
										
									
									
									
								
							|  | @ -18,85 +18,8 @@ | |||
| <div id="maindiv">'maindiv' not attached</div> | ||||
| <div id="extradiv">'extradiv' not attached</div> | ||||
| 
 | ||||
| <script> | ||||
| 
 | ||||
|     const cacheElement = { | ||||
|         "freshness": "2021-04-21T09:50:28.000Z", | ||||
|         feature: | ||||
|             { | ||||
|                 "type": "Feature", | ||||
|                 "id": "way/912515518", | ||||
|                 "properties": { | ||||
|                     "id": "way/912515518", | ||||
|                     "name": "Speelbos De Reukens", | ||||
|                     "playground": "forest", | ||||
|                     "leisure": "playground", | ||||
|                     "operator": "The world!" | ||||
|                 }, | ||||
|                 "geometry": { | ||||
|                     "type": "Polygon", | ||||
|                     "coordinates": [ | ||||
|                         [ | ||||
|                             [ | ||||
|                                 4.378341436386108, | ||||
|                                 51.120101600003316 | ||||
|                             ], | ||||
|                             [ | ||||
|                                 4.378175139427185, | ||||
|                                 51.11954264284114 | ||||
|                             ], | ||||
|                             [ | ||||
|                                 4.3786633014678955, | ||||
|                                 51.119963544947566 | ||||
|                             ], | ||||
|                             [ | ||||
|                                 4.379317760467529, | ||||
|                                 51.119525806677146 | ||||
|                             ], | ||||
|                             [ | ||||
|                                 4.379017353057861, | ||||
|                                 51.11997027935011 | ||||
|                             ], | ||||
|                             [ | ||||
|                                 4.379714727401733, | ||||
|                                 51.12028679516189 | ||||
|                             ], | ||||
|                             [ | ||||
|                                 4.379006624221802, | ||||
|                                 51.120313732577664 | ||||
|                             ], | ||||
|                             [ | ||||
|                                 4.378706216812134, | ||||
|                                 51.120744729093836 | ||||
|                             ], | ||||
|                             [ | ||||
|                                 4.378384351730347, | ||||
|                                 51.120306998225196 | ||||
|                             ], | ||||
|                             [ | ||||
|                                 4.377686977386475, | ||||
|                                 51.120306998225196 | ||||
|                             ], | ||||
|                             [ | ||||
|                                 4.378341436386108, | ||||
|                                 51.120101600003316 | ||||
|                             ] | ||||
|                         ] | ||||
|                     ] | ||||
|                 } | ||||
|             } | ||||
|     } | ||||
| 
 | ||||
|     const cache = [cacheElement] | ||||
|     localStorage.setItem("cached-featuresspeelplekken", JSON.stringify(cache)) | ||||
| 
 | ||||
| 
 | ||||
| </script> | ||||
| 
 | ||||
| 
 | ||||
| <script src="./test.ts"></script> | ||||
| <iframe src="http://127.0.0.1:1234/index.html" height="500px" width="600px"></iframe> | ||||
| <iframe src="http://127.0.0.1:1234/index.html?layout=bookcases&fs-userbadge=false&fs-search=false&fs-welcome-message=false&fs-layers=false&fs-geolocation=false" | ||||
|         height="500px" width="600px"></iframe> | ||||
| 
 | ||||
| 
 | ||||
| </body> | ||||
| </html> | ||||
|  |  | |||
							
								
								
									
										5
									
								
								test.ts
									
										
									
									
									
								
							
							
						
						
									
										5
									
								
								test.ts
									
										
									
									
									
								
							|  | @ -1,4 +1,3 @@ | |||
| import ValidatedTextField from "./UI/Input/ValidatedTextField"; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| alert("Hello world!") | ||||
| ValidatedTextField.InputForType("phone").AttachTo("maindiv") | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue