Friday November 10th, 2017 Terry Riegel
Building XML/JSON
#
# File Formats
# JSON
# XML
#
# We rely on a global tag_filetype to determine output text format
# if tag_filetype is ambiguous then default to json
# - if tag_filetype='XML' then filetype='XML' else filetype='JSON' /if
/#
The api library is used for generating responses to api requests.
It focuses on building a json or an xml string to be delivered via webpush()
Build nodes by adding name/value pairs with outvar=appendNode(outvar,name,value) then render final out using push_nodes(outvar)
For an example suppose we wanted to create a xml or json object to describe the properties of a file
a=fileinfo('/logo.jpg')
name size last modify kind area
a ==> /logo.jpg 1950 09/10/05 12:43:00 FILE PUBLIC
This can be accomplished by creating 5 nodes using appendnode()
info='ERROR'
info=appendNode(info,'name',a[1,1])
info=appendNode(info,'size',a[2,1])
info=appendNode(info,'modify',a[3,1])
info=appendNode(info,'kind',a[4,1])
info=appendNode(info,'area',a[5,1])
out=push_nodes(info)
Notice push_nodes() actually delivers the rendered json or XML pen here
The above code will render the variable out as either JSON or XML, uses global tag_filetype to determine output format.
JSON
{
name : "/logo.png",
size : 1048,
modify : "2017-10-05T12:43",
kind : "FILE",
area : "PUBLIC"
}
XML
<document>
<name>
/logo.png
</name>
<size>
1048
</size>
<modify>
10/05/17 12:43:00
</modify>
<kind>
FILE
</kind>
<area>
PUBLIC
</area>
</document>
You can also send out a table like this
table='ERROR'
table=appendTableNode(table,'fileinfo',a)
out=push_nodes(table)
JSON
[
"/logo.png",
1048,
"10/05/17 12:43:00",
"FILE",
"PUBLIC"
]
XML
<fileinfo cols="5" rows="1">
<row>
<col>
/logo.png
</col>
<col>
1048
</col>
<col>
10/05/17 12:43:00
</col>
<col>
FILE
</col>
<col>
PUBLIC
</col>
</row>
</fileinfo>
and finally you can nest and create hierarchical trees.
Suppose you want to deliver the json as name values and also as a table you can do it this way...
info='ERROR'
info=appendNode(info,'name',a[1,1])
info=appendNode(info,'size',a[2,1])
info=appendNode(info,'modify',a[3,1])
info=appendNode(info,'kind',a[4,1])
info=appendNode(info,'area',a[5,1])
out='ERROR'
out=appendNode(out,'properties',info)
out=appendTableNode(out,'fileinfo',a)
out=push_nodes(out)
JSON
{
properties : {
name : "/logo.png",
size : 1048,
modify : "2017-10-05T12:43",
kind : "FILE",
area : "PUBLIC"
},
fileinfo : [
"/logo.png",
1048,
"10/05/17 12:43:00",
"FILE",
"PUBLIC"
]
}
XML
<document>
<properties>
<name>
/logo.png
</name>
<size>
1048
</size>
<modify>
10/05/17 12:43:00
</modify>
<kind>
FILE
</kind>
<area>
PUBLIC
</area>
</properties>
<fileinfo cols="5" rows="1">
<row>
<col>
/logo.png
</col>
<col>
1048
</col>
<col>
10/05/17 12:43:00
</col>
<col>
FILE
</col>
<col>
PUBLIC
</col>
</row>
</fileinfo>
</document>
And one final note, you could use XML attributes to represent the data as well
attribs='ERROR'
attribs=setvalue(attribs,'size',a[2,1])
attribs=setvalue(attribs,'modify',a[3,1])
attribs=setvalue(attribs,'kind',a[4,1])
attribs=setvalue(attribs,'area',a[5,1])
out='ERROR'
out=appendNodeAttribs(out,'file',a[1,1],attribs)
out=push_nodes(out)
XML
<file size="1048" modify="10/05/17 12:43:00" kind="FILE" area="PUBLIC">
/logo.png
</file>
JSON
{
@size : 1048,
@modify : 2017-10-05T12:43,
@kind : FILE,
@area : PUBLIC,
file : /logo.png
}
end.
Thursday January 7th, 2016 Terry Riegel
Comma bug with system() tag
Here is some code to test/demonstrate the bug
Updated with a better solution
I have included a page that will demonstrate the bug, and also offer a workaround. The workaround is system specific, it assumes Linux as the underlying platform and also assumes the system folder is set and set to /.
The idea of the workaround it to postprep() the command, send it, unpostprep it, and finally execute it. See: this page for the unpostprep part of the solution.
The Workaround
function system_with_commas(cmd) do
return system(^bin/echo ^+postprep(cmd)+^ | sed "s@+@ @g;s@%@\\\\x@g" | xargs -0 printf "%b" | /bin/bash^) /return
/function
Demonstrate the Problem
<<
function system_with_commas(cmd) do
return system(^bin/echo ^+postprep(cmd)+^ | sed "s@+@ @g;s@%@\\\\x@g" | xargs -0 printf "%b" | /bin/bash^) /return
/function
>>
<html>
<head>
<title>Comma Bug</title>
</head>
<body>
<h1>System tag Comma Bug</h1>
<p>When submitting a command using the system tag it seems to be eating commas from the input. On this installation the system folder is /.
<hr>
<pre><<system('bin/echo -n "Hello,1,2,3,4, World"')>></pre>
<hr>
<pre><<system('bin/echo -n "Hello,1,2,3,4, World"')>></pre>
<hr>
<p>Typing the same command from the command line yields the following...
<hr>
<pre>Hello,1,2,3,4, World</pre>
<hr>
<h2>Is the bug fixed?</h2>
NATIVE:<br><<system('bin/echo -n "Hello,1,2,3,4, World"')>>
<hr>
SYSCALL.SH:<br><<system(^usr/bin/syscall.sh ^+postprep('/bin/echo -n "Hello,1,2,3,4, World"'))>>
<hr>
WITH_COMMAS:<br><<system_with_commas('/bin/echo -n "Hello,1,2,3,4, World"')>>
</body>
</html>
To fix change any system calls to this...
Be sure to include the function at the top of the example in your code somewhere.
BEFORE: temp=system(command)
AFTER: temp=system_with_commas(command))
This alternate solution is not recommended. It is here for historical reasons.
Alternate Solution
Older Solution Requiring SSH access to the underlying server
Here is a shell script that can be used to get around the bug
#!/bin/bash
urldecode() {
# urldecode <string>
local url_encoded="${1//+/ }"
printf '%b' "${url_encoded//%/\x}"
}
cmd=$(urldecode $1)
eval $cmd
Be sure to save it to /usr/bin/syscall.sh and chmod 755 /usr/bin/syscall.sh
To fix change any system calls to this...
BEFORE: temp=system(command)
AFTER: temp=system(^usr/bin/syscall.sh ^+postprep('/'+command))
Also I have reported the bug over here...
Saturday October 3rd, 2015 Terry Riegel
Responsive Images
Responsive Images for Every Browser on the Planet
my contribution to the conversation
its basically the <picture> tag for older browsers
Resize your browser, rotate your phone to see the various images load as the environment changes.
The new HTML5 picture tag requires us to abandon older browsers to get responsive images. But all of the pieces are in place already for those browsers. Here is how to do it.
This approach uses the existing tag with the src attribute specially designed for the browser to ignore.
<img src="/0#/path/to/image.jpg">
This approach requires a 1 pixel gif on the server at the root level named 0.
Here is what we get with our responsive images
Similar syntax to the existing <img> tag Serves the correct image to Mobile, Desktop, and Retina for landscape or portrait. Uses a wrapper that allows for Mobile/Desktop styling similar to the way bootstrap does it. For the most part this does not rely on any type of browser sniffing*. * This demo sniffs for firefox broswers and uses the /0# syntax and also sniffs for IE9 and comments out the proprietary filters that are only for IE678.
All of the images on this page are generated (server side) from the same image. The correct image is served up based on the Screen size, Retina capabilities, and device orientation.
The markup for each images is as follows M100 would indicate 100% on mobile, D70 indicates 70% on Desktop.
Notice the strange src in the <img> tag. The strange syntax is designed so that the client never makes a request for the source. Then the src is chosen with a css selector to determine the actual src depending on the device.
...
<div class="image center M100 D70">
<img src="/0#/apps/test/test.jpg">
</div>
...
<div class="image right M50 D30">
<img src="/0#/apps/test/test.jpg">
</div>
...
<div class="image left M70 D30">
<img src="/0#/apps/test/test.jpg">
</div>
...
The path to the image can be a real path or a meta path to the image on the server. The src path is used in the CSS declaration to actually decide what images will be delivered based on screen size, orientation and retina capabilities.
The syntax allows for delivery of the correct content and hinted display for mobile or desktop. It could easily be extended for additional breakpoints
Art Direction means it will send the correct image server for portrait and landscape devices. Consequently all the problems the picture element was created for are solved with this approach. If you look at the syntax for the picture element its syntax relies heavily on CSS. The system I am proposing actually just uses CSS with inline <style> tags for defining the context of the images.
The images are actually loaded as background images and using an IE filter for IE678.
Supported Browsers. (haven't found one where it doesn't work)
- Internet Explorer - All versions from 6 on up (versions 6,7,8 require respond.js and also for 6 it requires Dean Edwards IE7.js)
- Chrome - All versions
- Safari - All Versions
- Opera - All versions
- Firefox - All versions from 4 and up
- Mobile Safari - All versions
- Android - All Versions
The <style> tag looks like this for each image.
<style>
/* IE 6,7,8 Image */
img[src*="/apps/test/test.jpg"] {
filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/apps/test/test.jpg?for=ie678',sizingMethod='scale');
-ms-filter: "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/apps/test/test.jpg?for=ie678',sizingMethod='scale')";
padding-bottom:56.25%;
}
/* Mobile Image Portrait */ @media screen and (orientation:portrait) and (max-width: 980px) {
img[src*="/apps/test/test.jpg"] {
background-image:url('/apps/test/test.jpg?for=mobile-portrait');
padding-bottom:100%;
}}
/* Mobile Image Landscape */ @media screen and (orientation:landscape) and (max-width: 980px) {
img[src*="/apps/test/test.jpg"] {
background-image:url('/apps/test/test.jpg?for=mobile-landscape');
padding-bottom:56.25%;
}}
/* Retina Mobile Image Portrait */ @media screen and (orientation:portrait) and (-webkit-min-device-pixel-ratio: 2), only screen and (orientation:portrait) and (min-resolution: 192dpi) {
img[src*="/apps/test/test.jpg"] {
background-image:url('/XPATH/apps/test/test.jpg?for=mobile-retina-portrait');
padding-bottom:100%;
}}
/* Retina Mobile Image Landscape */ @media screen and (orientation:landscape) and (-webkit-min-device-pixel-ratio: 2), only screen and (orientation:landscape) and (min-resolution: 192dpi) {
img[src*="/apps/test/test.jpg"] {
background-image:url('/apps/test/test.jpg?for=mobile-retina-landscape');
padding-bottom:56.25%;
}}
/* Desktop Image Portrait */ @media screen and (orientation:portrait) and (min-width: 981px) {
img[src*="/apps/test/test.jpg"] {
background-image:url('/apps/test/test.jpg?for=desktop-portrait');
padding-bottom:100%;
}}
/* Desktop Image Landscape */ @media screen and (orientation:landscape) and (min-width: 981px) {
img[src*="/apps/test/test.jpg"] {
background-image:url('/apps/test/test.jpg?for=desktop-landscape');
padding-bottom:56.25%;
}}
/* Retina Desktop Image Portrait */
@media screen and (orientation:portrait) and (-webkit-min-device-pixel-ratio: 2) and (min-width: 981px),screen and (orientation:portrait) and (min-resolution: 192dpi) and (min-width: 981px) {
img[src*="/apps/test/test.jpg"] {
background-image:url('/apps/test/test.jpg?for=desktop-retina-portrait');
padding-bottom:100%;
}}
/* Retina Desktop Image Landscape */ @media screen and (orientation:landscape) and (-webkit-min-device-pixel-ratio: 2) and (min-width: 981px),screen and (orientation:landscape) and (min-resolution: 192dpi) and (min-width: 981px) {
img[src*="/apps/test/test.jpg"] {
background-image:url('/apps/test/test.jpg?for=desktop-retina-landscape');
padding-bottom:56.25%;
}}
</style>
Reference this Stackoverflow Question regarding the img src syntax.
Wednesday April 8th, 2015 Terry Riegel
Dynamic Script Loader
dynamically loaded javascript
I was starting to get a lot of jQuery plugins on WebBlocks.co the CMS that runs learnhtmlos.com. They were all needed but only in certain contexts. I found these 3 articles useful. I rolled them into my own safe code execution script.
Start by specifying a global Javascript variable like jQuery.colorbox and its url and the code you want to run. If the url is already loaded it will execute the code, if not it will load url then execute the code.
The ridiculous case of adding a script element
Check if a jQuery plugin is loaded
The script
runSafe=function(name,url,callback){
if(typeof window[name] != 'undefined'){
callback;
} else {
var js = document.createElement('script');
js.src = url;
js.onload = js.onreadystatechange = callback;
var first = document.getElementsByTagName('script')[0];
first.parentNode.insertBefore(js, first);
}
}
Usage
runSafe('jQuery.colorbox', '/apps/scripts/colorbox.min.js',function(){
// do something with the colorbox plugin
});
Wednesday March 18th, 2015 Terry Riegel
middleUTF()
UTF and HTML/OS
I decided to roll my own UTF-8 string compatability.
So if you have a string of text that you know is UTF-8 encoded you can get its length and also parse it just like regular text.
Step 1. | Step 2. | Step 3. |
---|---|---|
Prepare: temp=asUTF(text) | Perform string manipulations using the alternative tags length_(temp), middle_(temp) etc (notice the underscore)... | Convert back to string text=asText(temp) |
For Example this page...
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>UTF8 Demo</title>
</head>
<body>
<<
msg="Everybody is using emoticons (πππ¨π) These days"
display "msg="+msg+'<br>' /display
display "Length(msg)="+length(msg)+'<br>' /display
msg2=asUTF(msg)
display "Length_(msg2)="+length_(msg2)+'<br>' /display
msg_middle=middle_(msg2,31,31)
display '31st character is "'+asText(msg_middle)+'"' /display
>>
</body>
</html>
Would display this output...
msg=Everybody is using emoticons (πππ¨π) These days
Length(msg)=58
Length_(msg2)=46
31st character is "π"
Here are the Functions
function asUTF(text) locals x,s,st,i,a do
x=1
st='ERROR'
while x<length(text)+1 do
i=getascii(middle(text,x,x))
if i<192 then s=1
elif i<224 then s=2
elif i<240 then s=3
elif i<248 then s=4
elif i<252 then s=5
elif i<254 then s=6
/if
a='ERROR'
a=middle(text,x,x+(s-1))
if st='ERROR' then st=a else st=merge(st,a) /if
x=x+s
/while
return st /return
/function
function middle_(text_UTF,s,e) do
return gettable(text_UTF,s,e,1,1) /return
/function
function length_(text_UTF) do
return cols(text_UTF) /return
/function
function left_(text_UTF,i) do
return gettable(text_UTF,1,i,1,1) /return
/function
function right_(text_UTF,i) locals l do
l=cols(text_utf)
return gettable(text_UTF,l-i+1,l,1,1) /return
/function
function reverse_(text_UTF,i) do
return reversecols(text_UTF) /return
/function
function concat_(text1_UTF,text2_UTF) do
return merge(text1_UTF,text2_UTF) /return
/function
function asText(text_UTF) locals text,x do
text=''
for name=x value=1 to cols(text_UTF) do
text=text+text_UTF[x,1]
/for
return text /return
/function
Saturday February 21st, 2015 Terry Riegel
Overlay Managment
Semicolon URL notation with HTML/OS
Sometimes it is desirable to use the semicolon notation to reuse overlay's. Instead of duplicate overlays we can instead opt to use semicolon notation.
This allows you to reference a single overlay from multiple pages.
The problem
The main issue with doing it this way is the "page" value gets lost.
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<a href="master.overlay;doit">Run doit Overlay</a><hr>
</body>
</html>
master.overlay
<<goto error(400)>>
<<overlay doit
goto page
>>
The Solution
To overcome that you can use the following coding pattern.
index.html
<<
expand file="/system/clearimage/DLL.lib" /expand
>>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<a href="<<page>>">RELOAD</a><br>
<a href="<<pagelink('master.overlay;doit','qwerty','Terry')>>">doit</a><hr>
<<page>><br>
<<page_>><br>
<<page_()>><br>
<<pagefromdoit>><br>
<<qwerty>><<qwerty='ERROR'>>
</body>
</html>
master.overlay
<<goto error(400)
#
# This is a pattern for master overlay's
#
# To call a master overlay use the following syntax
#
# <a href="<<pagelink1('master.overlay;doit')>>">Run Do It</a>
# <a href="<<pagelink('master.overlay;doit',varname,varvalue)>>">Run Do It</a>
#
# execute get_() to initialize each overlay
#
# Use page_ in place of page for goto's
# tag can also be used for goto's
#
# notice the goto error(400) that will be invoked if you forget and do a goto page
/#
>>
<<overlay doit get_()
pagefromdoit='Welcome: '+qwerty+' '+random(1000,9999)+' '+page_+' '+page+' '+page_()+' '+tag
goto page_
>>
Thats it for now.
Tuesday February 3rd, 2015 Terry Riegel
Layout TAG
LAYOUT()
A new way to use layout
The layout tag is usually used to take an Aestiva table and create some html markup with it. For example
mystuff
+-----------------------+
| Dog | Cat | Mouse |
|-------+-------+-------|
| Blue | Green | Red |
+-----------------------+
Could be displayed as a table like this using layout()
<table>
<<layout(mystuff,'<tr><td>',[1],'</td><td>',[2],'</td><td>',[3],'</td></tr>')>>
</table>
Dog | Cat | Mouse |
Blue | Green | Red |
But...
There is another very useful way to use layout
It can be used to extract columns from a variable and place them into a new variable. For example using the variable mystuff we could extract the third and the first column in that order using layout.
newvar=layout(mystuff,[3],[1])
Which would yield newvar...
newvar
+---------------+
| Mouse | Dog |
|-------+-------|
| Red | Blue |
+---------------+
Thursday November 27th, 2014 Terry Riegel
Programming Patterns
Separation of concerns
Programming usually follows this pattern.
- Get it working - At this stage we are just trying to get things working, we are trying to prove our concept and show that it can work. It is easy to use sloppy programming techniques in the name of expediency and in an effort to just get the thing working.
- Extend its functionality - At this stage we have a basic working system and we decide to add additional features to extend its usefulness. This stage can often times reveal problems with the style of coding we used in step 1.
I promise to never...
After doing a project or two we usually come to the realization that we need to enforce more rigor into our programming style. This can be a huge win for step 2 and a huge loss for step 1. Sometimes the inertia necessary to start a project is thwarted by past projects that became unmanageable.
This post aims to offer some help in programming styles that will not be so overly burdensome to stifle the creative process we all love in step 1 and yet also foster a more manageable more maintainable code base for future extensibility.
Separation of concerns
So What do we mean when we talk about Separation of concerns. This is a huge topic. We could be talking about keeping your javascript/html/css separate. The Separation I am going to address is the Separation of your data from its presentation.
Lets start with a simple example. Suppose we have a database that stores
name,address,city,state,friendstatus
We might built a piece of code to display all my friends
Get It Working
<<
friends=dbsearch('frienddb','FRIENDSTATUS="y"',1,1000)
>>
<html>
<h1>My Friends</h1>
<table>
<tr><td>Name</td><td>City</td></tr>
<<
for name=friends rowname=x do
display '<tr><td>'+x[2]+'</td><td>'+x[3]+'</td></tr>'
/for
>>
</table>
</html>
This code is basically step 1. It does exactly what we want it to do but it doesn't extend very well. So we might want to encapsulate it all into a single monlithic function and call it our friend function or our friend widget
Extend its functionality
<<
function friends(db,title,allorjustfriends) locals friends,x,text,thesearch do
if allorjustfriends='all' then thesearch='' else thesearch='Y' /if
friends=dbsearch(db,'FRIENDSTATUS="'+thesearch+'"',1,1000)
text='<h1>'+title+'</h1>'+lf+
'<table>'+lf+
' <tr><td>Name</td><td>City</td></tr>'+lf+
for name=friends rowname=x do
text=text+'<tr><td>'+x[2]+'</td><td>'+x[3]+'</td></tr>'
/for
text=text+'</table>'
return text /return
/function
>>
<html>
<<friends('frienddb','My Friends','JUSTFRIENDS')>>
</html>
I have followed this exact pattern more times that I can count. The only problem is that it is completely wrong and doesn't actually make my code any more extensible. All it really did is create a black box that I can use on other pages, but that black box is to rigid.
So the temptation is to try to tack on extensibility to a wrong pattern. I might add additional parameters to send to my function etc.
The problem is this pattern will not extend and is destined to be relegated to code that once was cool but now is a pain to maintain.
So the fundamental flaw here is the failure to separate concerns. There are at least 2 concerns going on.
- We Get Data
- We Present Data
So we should really be separating our code into...
- Code that gets data
- and Code that presents data
And this pattern can be expressed in several ways. We can decide to go with functions or we could decide to go with pages. Both are perfectly fine.
Here is a page approach...
index.html
<<
#
# Begin GET DATA
#
# First we have all the logic to get data
# notice there IS NO HTML here
# If you feel the need to add HTML here then STOP and
# think it through, make sure you are keeping your
# concerns separate
/#
if mypref='showall' then
thedata=getfriends('all')
else
thedata=getfriends('justfriends')
/if
#
# End GET DATA
/#
#
# Begin Presentation
/#
if mystyle='modern' then
goto 'modern.html'
else
goto 'traditional.html'
/if
#
# End Presentation
/#
>>
modern.html
<<
# This page expects the following data
# THEDATA - A Six column table record,name,address,city,state,friendstatus
/#
>>
<html>
<<
for name=thedata rowname=x do
display x[1]+'~'+x[2]+'<br>' /display
/for
>>
</html>
traditional.html
<<
# This page expects the following data
# THEDATA - A Six column table record,name,address,city,state,friendstatus
/#
>>
<html>
<<
for name=thedata rowname=x do
display x[1]+'|'+x[2]+'<br>' /display
/for
>>
</html>
Saturday November 22nd, 2014 Terry Riegel
Parsing URL's
Parsing URL's and file paths
ramblings about maintainable code
So today I wrote this ugly little piece of code
<<
reverse(chopleft(replace(reverse(replaceall(
getenv('DOCUMENT_ROOT')+'/'
,'//','/')),'/',''),'/'))
>>
The task I am trying to accomplish is I want to take DOCUMENT_ROOT and remove the last part of the path, I wanted a one liner and I discovered that DOCUMENT_ROOT may not always have a trailing slash. So that added to the complexity of the one liner.
- Add Trailing /
- Replace all // with / (in case adding it wasn't needed)
- Reverse the result
- Remove the first /
- Chop everything to the left of the first /
- reverse again
These...
/home/docs/public_html
/home/docs/public_html/
Would produce...
/home/docs/
Done!
So It would be nice to have a URL/File Path parsing system. Perhaps something like...
pathdice('/1/2/3','/.../-1') ==> /1/2
pathdice('/1/2/3/','/.../-1') ==> /1/2/
Are there libraries like this for other Languages? If so how do they tackle the problem?
The issue I am trying to solve is I want to have maintainable code, and a one liner like that is almost impossible to recognize the original intent of the one writing the code, so it is not maintainable.
If instead I had some generalized file path library and I used it then the intent of my code would be clear to the person looking it at some point in the future.
The code as written does give a few clues into my intent. First since I am asking for document_root I can see I am expecting to be dealing with a file path. And since I am using a forward slash then I understand the file paths not to have back slashes in them.
But beyond that the code isn't clear at all as to intent.
In the absence of any type of library heavy comments are probably the next best thing. Of course comments can be get out of sync with the actual code.
So would my original code be better written...
<<
reverse(chopleft(replace(reverse(replaceall(
getenv('DOCUMENT_ROOT')+'/'
,'//','/')),'/',''),'/'))
#
# 1. Add Trailing /
# 2. Replace all // with / (in case adding it wasn't needed)
# 3. Reverse the result
# 4. Remove the first /
# 5. Chop everything to the left of the first /
# 6. reverse again
/#
>>
Friday November 21st, 2014 Chris Underwood
Live Weather Reports
Current or Historical Weather
Have you ever wanted to show current or historical weather reports on your HTML/OS based web pages? If the answer is "Yes" you should continue reading because this post will show you how to access the Weather Underground API and display the XML formatted weather results in your page.
Before you begin coding you'll need to sign up for a free account and obtain your own API identity string:
http://www.wunderground.com/weather/api/
In the sample code below I am going to replace my identity string with a random value, but this will need to be changed to your own identity in order for it to work.
I have found it convenient to encode this feature as a FUNCTION that takes two parameters, date and zip code, as follows:
FUNCTION WeatherBox( d, z ) LOCALS h, wid, df, os, wux, flag DO
# Parameters:
d = The current date as MM/DD/YYYY
z = Zip Code as #####
/#
# Header value used for NETWEB command /#
h = 'Accept: text/html, application/xhtml+xml, */*
Accept-Language: en-US
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; EIE10;ENUSWOL
Connection: close
Accept-Encoding: identity'
flag = 'OK'
wid = '12345ABCDE67890F1' # Change this to your IDENTITY STRING /#
IF LENGTH( z ) !=5 OR ISINTEGER( z ) = 'FALSE' THEN
flag = 'Weather N/A - Invalid Zip Code: "' + z + '"'
ELIF LENGTH( d ) !=10 OR ISDATE( d ) = 'FALSE' THEN
flag = 'Weather N/A - Invalid Date: "' + d + '"'
ELSE
# Date Formatted for how Weather Underground wants it /#
df = CONCAT( CONCAT( RIGHT( d, 4 ), LEFT( d, 2 )), MIDDLE( d, 4, 5 ))
# If you use the Clear Image Library, replace line above with:
df = MOMENT( d, 'YYYYMMDD' )
/#
wux = NETWEB(
'http://api.wunderground.com/api/' + wid + '/history_' +
df + '/pws:0/q/' + z + '.xml', '50000', '10', 'GET', '', h )
IF LOCATE( wux, '<error>' ) OR wux = '' THEN
flag = XMLGETVALUE( wux, '<response><error><description>' )
ELSE
# Truncate returned XML to only the <summary> area, speeds string
processing and simplifies further references below
/#
wux = XMLGET( wux, '<response><history><dailysummary><summary>' )
/IF
/IF
IF flag !='OK' THEN # Something went wrong, so send back the error /#
os = '<p>Error Message: ' + flag + '</p>'
ELSE
w_mint = XMLGETVALUE( wux, '<summary><mintempi>' )
w_meant = XMLGETVALUE( wux, '<summary><meantempi>' )
w_maxt = XMLGETVALUE( wux, '<summary><maxtempi>' )
w_minw = XMLGETVALUE( wux, '<summary><minwspdi>' )
IF w_minw='' THEN w_minw = 'n/a' /IF
w_meanw = XMLGETVALUE( wux, '<summary><meanwindspdi>' )
IF w_meanw='' THEN w_meanw = 'n/a' /IF
w_maxw = XMLGETVALUE( wux, '<summary><maxwspdi>' )
IF w_maxw='' THEN w_maxw = 'n/a' /IF
w_minv = XMLGETVALUE( wux, '<summary><minvisi>' )
w_meanv = XMLGETVALUE( wux, '<summary><meanvisi>' )
w_maxv = XMLGETVALUE( wux, '<summary><maxvisi>' )
w_fog = XMLGETVALUE( wux, '<summary><fog>' )
IF w_fog = 0 OR w_fog = '' THEN w_fog = 'No' ELSE w_fog = 'Yes' /IF
w_rain = XMLGETVALUE( wux, '<summary><rain>' )
IF w_rain = 0 OR w_rain = '' THEN w_rain = 'No' ELSE w_rain = 'Yes' /IF
w_snow = XMLGETVALUE( wux, '<summary><snow>' )
IF w_snow = 0 OR w_snow = '' THEN w_snow = 'No' ELSE w_snow = 'Yes' /IF
os = '<dl id="col1">
<dt>Min Temp:</dt><dd>' + w_mint + ' °F</dd>
<dt>Avg Temp:</dt><dd>' + w_meant + ' °F</dd>
<dt>Max Temp:</dt><dd>' + w_maxt + ' °F</dd>
</dl>
<dl id="col2">
<dt>Min Wind:</dt><dd>' + w_minw + ' MPH</dd>
<dt>Avg Wind:</dt><dd>' + w_meanw + ' MPH</dd>
<dt>Max Wind:</dt><dd>' + w_maxw + ' MPH</dd>
</dl>
<dl id="col1">
<dt>Min Vis:</dt><dd>' + w_minv + ' Mi</dd>
<dt>Avg Vis:</dt><dd>' + w_meanv + ' Mi</dd>
<dt>Max Vis:</dt><dd>' + w_maxv + ' Mi</dd>
</dl>
<dl id="col2">
<dt>Rain:</dt><dd>' + w_rain + '</dd>
<dt>Snow:</dt><dd>' + w_snow + '</dd>
<dt>Fog:</dt><dd>' + w_fog + '</dd>
</dl>'
/IF
RETURN os /RETURN
/FUNCTION
The last piece of code where the value "os" is being set up as a formatted string with <dl> tags should be adjusted to give you back the values however you'd like to display them, such as a definition list (shown), table or a formatted sentence.
Note also that you really should declare the various w_* variables as LOCALS in the FUNCTION declaration, as I have omitted this for the sake of readability.
So in summary, let's say I wanted to get the weather on the current date in the middle of Las Vegas, I would make a call like this:
<<
DISPLAY WeatherBox( GETDATE(NOW,"NORMAL"), '89101' ) /DISPLAY
>>
Thursday November 20th, 2014 Terry Riegel
Timezone to UTC
More Fun with Dates and Time
It seems my work has had a lot to do with dates and times lately. I found myself in need of a function that would convert a Timezone name to its equivalant UTC+n UTC-n format. So here it is...
function TimeZoneToUTC(timezonename) locals text,t,x do
text="Pacific/Midway,-11:00,Midway"+lf+"Pacific/Niue,-11:00,Niue"+lf+"Pacific/Pago_Pago,-11:00,Pago Pago"+lf+"Pacific/Honolulu,-10:00,Hawaii Time"+lf+"Pacific/Rarotonga,-10:00,Rarotonga"+lf+"Pacific/Tahiti,-10:00,Tahiti"+lf+"Pacific/Marquesas,-09:30,Marquesas"+lf+"America/Anchorage,-09:00,Alaska Time"+lf+"Pacific/Gambier,-09:00,Gambier"+lf+"America/Los_Angeles,-08:00,Pacific Time"+lf+"America/Tijuana,-08:00,Pacific Time - Tijuana"+lf+"America/Vancouver,-08:00,Pacific Time - Vancouver"+lf+"America/Whitehorse,-08:00,Pacific Time - Whitehorse"+lf+"Pacific/Pitcairn,-08:00,Pitcairn"+lf+"America/Dawson_Creek,-07:00,Mountain Time - Dawson Creek"+lf+"America/Denver,-07:00,Mountain Time"+lf+"America/Edmonton,-07:00,Mountain Time - Edmonton"+lf+"America/Hermosillo,-07:00,Mountain Time - Hermosillo"+lf+"America/Mazatlan,-07:00,Mountain Time - Chihuahua^, Mazatlan"+lf+"America/Phoenix,-07:00,Mountain Time - Arizona"+lf+"America/Yellowknife,-07:00,Mountain Time - Yellowknife"+lf+"America/Belize,-06:00,Belize"+lf+"America/Chicago,-06:00,Central Time"+lf+"America/Costa_Rica,-06:00,Costa Rica"+lf+"America/El_Salvador,-06:00,El Salvador"+lf+"America/Guatemala,-06:00,Guatemala"+lf+"America/Managua,-06:00,Managua"+lf+"America/Mexico_City,-06:00,Central Time - Mexico City"+lf+"America/Regina,-06:00,Central Time - Regina"+lf+"America/Tegucigalpa,-06:00,Central Time - Tegucigalpa"+lf+"America/Winnipeg,-06:00,Central Time - Winnipeg"+lf+"Pacific/Easter,-06:00,Easter Island"+lf+"Pacific/Galapagos,-06:00,Galapagos"+lf+"America/Bogota,-05:00,Bogota"+lf+"America/Cayman,-05:00,Cayman"+lf+"America/Grand_Turk,-05:00,Grand Turk"+lf+"America/Guayaquil,-05:00,Guayaquil"+lf+"America/Havana,-05:00,Havana"+lf+"America/Iqaluit,-05:00,Eastern Time - Iqaluit"+lf+"America/Jamaica,-05:00,Jamaica"+lf+"America/Lima,-05:00,Lima"+lf+"America/Montreal,-05:00,Eastern Time - Montreal"+lf+"America/Nassau,-05:00,Nassau"+lf+"America/New_York,-05:00,Eastern Time"+lf+"America/Panama,-05:00,Panama"+lf+"America/Port-au-Prince,-05:00,Port-au-Prince"+lf+"America/Rio_Branco,-05:00,Rio Branco"+lf+"America/Toronto,-05:00,Eastern Time - Toronto"+lf+"America/Caracas,-04:30,Caracas"+lf+"America/Antigua,-04:00,Antigua"+lf+"America/Asuncion,-04:00,Asuncion"+lf+"America/Barbados,-04:00,Barbados"+lf+"America/Boa_Vista,-04:00,Boa Vista"+lf+"America/Campo_Grande,-04:00,Campo Grande"+lf+"America/Cuiaba,-04:00,Cuiaba"+lf+"America/Curacao,-04:00,Curacao"+lf+"America/Guyana,-04:00,Guyana"+lf+"America/Halifax,-04:00,Atlantic Time - Halifax"+lf+"America/La_Paz,-04:00,La Paz"+lf+"America/Manaus,-04:00,Manaus"+lf+"America/Martinique,-04:00,Martinique"+lf+"America/Port_of_Spain,-04:00,Port of Spain"+lf+"America/Porto_Velho,-04:00,Porto Velho"+lf+"America/Puerto_Rico,-04:00,Puerto Rico"+lf+"America/Santiago,-04:00,Santiago"+lf+"America/Santo_Domingo,-04:00,Santo Domingo"+lf+"America/Thule,-04:00,Thule"+lf+"Antarctica/Palmer,-04:00,Palmer"+lf+"Atlantic/Bermuda,-04:00,Bermuda"+lf+"America/St_Johns,-03:30,Newfoundland Time - St. Johns"+lf+"America/Araguaina,-03:00,Araguaina"+lf+"America/Argentina/Buenos_Aires,-03:00,Buenos Aires"+lf+"America/Bahia,-03:00,Salvador"+lf+"America/Belem,-03:00,Belem"+lf+"America/Cayenne,-03:00,Cayenne"+lf+"America/Fortaleza,-03:00,Fortaleza"+lf+"America/Godthab,-03:00,Godthab"+lf+"America/Maceio,-03:00,Maceio"+lf+"America/Miquelon,-03:00,Miquelon"+lf+"America/Montevideo,-03:00,Montevideo"+lf+"America/Paramaribo,-03:00,Paramaribo"+lf+"America/Recife,-03:00,Recife"+lf+"America/Sao_Paulo,-03:00,Sao Paulo"+lf+"Antarctica/Rothera,-03:00,Rothera"+lf+"Atlantic/Stanley,-03:00,Stanley"+lf+"America/Noronha,-02:00,Noronha"+lf+"Atlantic/South_Georgia,-02:00,South Georgia"+lf+"America/Scoresbysund,-01:00,Scoresbysund"+lf+"Atlantic/Azores,-01:00,Azores"+lf+"Atlantic/Cape_Verde,-01:00,Cape Verde"+lf+"Africa/Abidjan,+00:00,Abidjan"+lf+"Africa/Accra,+00:00,Accra"+lf+"Africa/Bamako,+00:00,Bamako"+lf+"Africa/Banjul,+00:00,Banjul"+lf+"Africa/Bissau,+00:00,Bissau"+lf+"Africa/Casablanca,+00:00,Casablanca"+lf+"Africa/Conakry,+00:00,Conakry"+lf+"Africa/Dakar,+00:00,Dakar"+lf+"Africa/El_Aaiun,+00:00,El Aaiun"+lf+"Africa/Freetown,+00:00,Freetown"+lf+"Africa/Lome,+00:00,Lome"+lf+"Africa/Monrovia,+00:00,Monrovia"+lf+"Africa/Nouakchott,+00:00,Nouakchott"+lf+"Africa/Ouagadougou,+00:00,Ouagadougou"+lf+"Africa/Sao_Tome,+00:00,Sao Tome"+lf+"America/Danmarkshavn,+00:00,Danmarkshavn"+lf+"Atlantic/Canary,+00:00,Canary Islands"+lf+"Atlantic/Faroe,+00:00,Faeroe"+lf+"Atlantic/Reykjavik,+00:00,Reykjavik"+lf+"Atlantic/St_Helena,+00:00,St Helena"+lf+"Etc/GMT,+00:00,GMT (no daylight saving)"+lf+"Europe/Dublin,+00:00,Dublin"+lf+"Europe/Lisbon,+00:00,Lisbon"+lf+"Europe/London,+00:00,London"+lf+"Africa/Algiers,+01:00,Algiers"+lf+"Africa/Bangui,+01:00,Bangui"+lf+"Africa/Brazzaville,+01:00,Brazzaville"+lf+"Africa/Ceuta,+01:00,Ceuta"+lf+"Africa/Douala,+01:00,Douala"+lf+"Africa/Kinshasa,+01:00,Kinshasa"+lf+"Africa/Lagos,+01:00,Lagos"+lf+"Africa/Libreville,+01:00,Libreville"+lf+"Africa/Luanda,+01:00,Luanda"+lf+"Africa/Malabo,+01:00,Malabo"+lf+"Africa/Ndjamena,+01:00,Ndjamena"+lf+"Africa/Niamey,+01:00,Niamey"+lf+"Africa/Porto-Novo,+01:00,Porto-Novo"+lf+"Africa/Tunis,+01:00,Tunis"+lf+"Africa/Windhoek,+01:00,Windhoek"+lf+"Europe/Amsterdam,+01:00,Amsterdam"+lf+"Europe/Andorra,+01:00,Andorra"+lf+"Europe/Belgrade,+01:00,Central European Time - Belgrade"+lf+"Europe/Berlin,+01:00,Berlin"+lf+"Europe/Brussels,+01:00,Brussels"+lf+"Europe/Budapest,+01:00,Budapest"+lf+"Europe/Copenhagen,+01:00,Copenhagen"+lf+"Europe/Gibraltar,+01:00,Gibraltar"+lf+"Europe/Luxembourg,+01:00,Luxembourg"+lf+"Europe/Madrid,+01:00,Madrid"+lf+"Europe/Malta,+01:00,Malta"+lf+"Europe/Monaco,+01:00,Monaco"+lf+"Europe/Oslo,+01:00,Oslo"+lf+"Europe/Paris,+01:00,Paris"+lf+"Europe/Prague,+01:00,Central European Time - Prague"+lf+"Europe/Rome,+01:00,Rome"+lf+"Europe/Stockholm,+01:00,Stockholm"+lf+"Europe/Tirane,+01:00,Tirane"+lf+"Europe/Vienna,+01:00,Vienna"+lf+"Europe/Warsaw,+01:00,Warsaw"+lf+"Europe/Zurich,+01:00,Zurich"+lf+"Africa/Blantyre,+02:00,Blantyre"+lf+"Africa/Bujumbura,+02:00,Bujumbura"+lf+"Africa/Cairo,+02:00,Cairo"+lf+"Africa/Gaborone,+02:00,Gaborone"+lf+"Africa/Harare,+02:00,Harare"+lf+"Africa/Johannesburg,+02:00,Johannesburg"+lf+"Africa/Kigali,+02:00,Kigali"+lf+"Africa/Lubumbashi,+02:00,Lubumbashi"+lf+"Africa/Lusaka,+02:00,Lusaka"+lf+"Africa/Maputo,+02:00,Maputo"+lf+"Africa/Maseru,+02:00,Maseru"+lf+"Africa/Mbabane,+02:00,Mbabane"+lf+"Africa/Tripoli,+02:00,Tripoli"+lf+"Asia/Amman,+02:00,Amman"+lf+"Asia/Beirut,+02:00,Beirut"+lf+"Asia/Damascus,+02:00,Damascus"+lf+"Asia/Gaza,+02:00,Gaza"+lf+"Asia/Jerusalem,+02:00,Jerusalem"+lf+"Asia/Nicosia,+02:00,Nicosia"+lf+"Europe/Athens,+02:00,Athens"+lf+"Europe/Bucharest,+02:00,Bucharest"+lf+"Europe/Chisinau,+02:00,Chisinau"+lf+"Europe/Helsinki,+02:00,Helsinki"+lf+"Europe/Istanbul,+02:00,Istanbul"+lf+"Europe/Kiev,+02:00,Kiev"+lf+"Europe/Riga,+02:00,Riga"+lf+"Europe/Sofia,+02:00,Sofia"+lf+"Europe/Tallinn,+02:00,Tallinn"+lf+"Europe/Vilnius,+02:00,Vilnius"+lf+"Africa/Addis_Ababa,+03:00,Addis Ababa"+lf+"Africa/Asmara,+03:00,Asmera"+lf+"Africa/Dar_es_Salaam,+03:00,Dar es Salaam"+lf+"Africa/Djibouti,+03:00,Djibouti"+lf+"Africa/Kampala,+03:00,Kampala"+lf+"Africa/Khartoum,+03:00,Khartoum"+lf+"Africa/Mogadishu,+03:00,Mogadishu"+lf+"Africa/Nairobi,+03:00,Nairobi"+lf+"Antarctica/Syowa,+03:00,Syowa"+lf+"Asia/Aden,+03:00,Aden"+lf+"Asia/Baghdad,+03:00,Baghdad"+lf+"Asia/Bahrain,+03:00,Bahrain"+lf+"Asia/Kuwait,+03:00,Kuwait"+lf+"Asia/Qatar,+03:00,Qatar"+lf+"Asia/Riyadh,+03:00,Riyadh"+lf+"Europe/Kaliningrad,+03:00,Moscow-01 - Kaliningrad"+lf+"Europe/Minsk,+03:00,Minsk"+lf+"Indian/Antananarivo,+03:00,Antananarivo"+lf+"Indian/Comoro,+03:00,Comoro"+lf+"Indian/Mayotte,+03:00,Mayotte"+lf+"Asia/Tehran,+03:30,Tehran"+lf+"Asia/Baku,+04:00,Baku"+lf+"Asia/Dubai,+04:00,Dubai"+lf+"Asia/Muscat,+04:00,Muscat"+lf+"Asia/Tbilisi,+04:00,Tbilisi"+lf+"Asia/Yerevan,+04:00,Yerevan"+lf+"Europe/Moscow,+04:00,Moscow+00"+lf+"Europe/Samara,+04:00,Moscow+00 - Samara"+lf+"Indian/Mahe,+04:00,Mahe"+lf+"Indian/Mauritius,+04:00,Mauritius"+lf+"Indian/Reunion,+04:00,Reunion"+lf+"Asia/Kabul,+04:30,Kabul"+lf+"Antarctica/Mawson,+05:00,Mawson"+lf+"Asia/Aqtau,+05:00,Aqtau"+lf+"Asia/Aqtobe,+05:00,Aqtobe"+lf+"Asia/Ashgabat,+05:00,Ashgabat"+lf+"Asia/Dushanbe,+05:00,Dushanbe"+lf+"Asia/Karachi,+05:00,Karachi"+lf+"Asia/Tashkent,+05:00,Tashkent"+lf+"Indian/Kerguelen,+05:00,Kerguelen"+lf+"Indian/Maldives,+05:00,Maldives"+lf+"Asia/Calcutta,+05:30,India Standard Time"+lf+"Asia/Colombo,+05:30,Colombo"+lf+"Asia/Katmandu,+05:45,Katmandu"+lf+"Antarctica/Vostok,+06:00,Vostok"+lf+"Asia/Almaty,+06:00,Almaty"+lf+"Asia/Bishkek,+06:00,Bishkek"+lf+"Asia/Dhaka,+06:00,Dhaka"+lf+"Asia/Thimphu,+06:00,Thimphu"+lf+"Asia/Yekaterinburg,+06:00,Moscow+02 - Yekaterinburg"+lf+"Indian/Chagos,+06:00,Chagos"+lf+"Asia/Rangoon,+06:30,Rangoon"+lf+"Indian/Cocos,+06:30,Cocos"+lf+"Antarctica/Davis,+07:00,Davis"+lf+"Asia/Bangkok,+07:00,Bangkok"+lf+"Asia/Hovd,+07:00,Hovd"+lf+"Asia/Jakarta,+07:00,Jakarta"+lf+"Asia/Omsk,+07:00,Moscow+03 - Omsk^, Novosibirsk"+lf+"Asia/Phnom_Penh,+07:00,Phnom Penh"+lf+"Asia/Saigon,+07:00,Hanoi"+lf+"Asia/Vientiane,+07:00,Vientiane"+lf+"Indian/Christmas,+07:00,Christmas"+lf+"Antarctica/Casey,+08:00,Casey"+lf+"Asia/Brunei,+08:00,Brunei"+lf+"Asia/Choibalsan,+08:00,Choibalsan"+lf+"Asia/Hong_Kong,+08:00,Hong Kong"+lf+"Asia/Krasnoyarsk,+08:00,Moscow+04 - Krasnoyarsk"+lf+"Asia/Kuala_Lumpur,+08:00,Kuala Lumpur"+lf+"Asia/Macau,+08:00,Macau"+lf+"Asia/Makassar,+08:00,Makassar"+lf+"Asia/Manila,+08:00,Manila"+lf+"Asia/Shanghai,+08:00,China Time - Beijing"+lf+"Asia/Singapore,+08:00,Singapore"+lf+"Asia/Taipei,+08:00,Taipei"+lf+"Asia/Ulaanbaatar,+08:00,Ulaanbaatar"+lf+"Australia/Perth,+08:00,Western Time - Perth"+lf+"Asia/Dili,+09:00,Dili"+lf+"Asia/Irkutsk,+09:00,Moscow+05 - Irkutsk"+lf+"Asia/Jayapura,+09:00,Jayapura"+lf+"Asia/Pyongyang,+09:00,Pyongyang"+lf+"Asia/Seoul,+09:00,Seoul"+lf+"Asia/Tokyo,+09:00,Tokyo"+lf+"Pacific/Palau,+09:00,Palau"+lf+"Australia/Adelaide,+09:30,Central Time - Adelaide"+lf+"Australia/Darwin,+09:30,Central Time - Darwin"+lf+"Antarctica/DumontDUrville,+10:00,Dumont D'Urville"+lf+"Asia/Yakutsk,+10:00,Moscow+06 - Yakutsk"+lf+"Australia/Brisbane,+10:00,Eastern Time - Brisbane"+lf+"Australia/Hobart,+10:00,Eastern Time - Hobart"+lf+"Australia/Sydney,+10:00,Eastern Time - Melbourne^, Sydney"+lf+"Pacific/Chuuk,+10:00,Truk"+lf+"Pacific/Guam,+10:00,Guam"+lf+"Pacific/Port_Moresby,+10:00,Port Moresby"+lf+"Pacific/Saipan,+10:00,Saipan"+lf+"Asia/Vladivostok,+11:00,Moscow+07 - Yuzhno-Sakhalinsk"+lf+"Pacific/Efate,+11:00,Efate"+lf+"Pacific/Guadalcanal,+11:00,Guadalcanal"+lf+"Pacific/Kosrae,+11:00,Kosrae"+lf+"Pacific/Noumea,+11:00,Noumea"+lf+"Pacific/Pohnpei,+11:00,Ponape"+lf+"Pacific/Norfolk,+11:30,Norfolk"+lf+"Asia/Kamchatka,+12:00,Moscow+08 - Petropavlovsk-Kamchatskiy"+lf+"Asia/Magadan,+12:00,Moscow+08 - Magadan"+lf+"Pacific/Auckland,+12:00,Auckland"+lf+"Pacific/Fiji,+12:00,Fiji"+lf+"Pacific/Funafuti,+12:00,Funafuti"+lf+"Pacific/Kwajalein,+12:00,Kwajalein"+lf+"Pacific/Majuro,+12:00,Majuro"+lf+"Pacific/Nauru,+12:00,Nauru"+lf+"Pacific/Tarawa,+12:00,Tarawa"+lf+"Pacific/Wake,+12:00,Wake"+lf+"Pacific/Wallis,+12:00,Wallis"+lf+"Pacific/Apia,+13:00,Apia"+lf+"Pacific/Enderbury,+13:00,Enderbury"+lf+"Pacific/Fakaofo,+13:00,Fakaofo"+lf+"Pacific/Tongatapu,+13:00,Tongatapu"+lf+"Pacific/Kiritimati,+14:00,Kiritimati"
t=csvtotable467(text)
for name=t rowname=x do
if timezonename=x[1] then return x[2] /return /if
/for
return 'ERROR' /return
/function
Example Code:
TimeZoneToUTC('Pacific/Wallis')
Results
+12:00
Wednesday November 19th, 2014 Chris Underwood
Prevent Double Submits
Disable Form Buttons on Submit
Sometimes a web server may be slow to responsd from a form submit, thus giving a user the opportunity and motive to press the button again. Rarely does this produce good results, so I use this technique to prevent the situation.
When any submit button is pressed the entire page is covered with a translucent <DIV> that slightly dims the content and prevents any of the on-screen controls from being touched during the wait for a server response. Simple, clean and effective.
Note that it does rely on JQUERY, so do take care to put a reference to this in your page header <SCRIPT> area.
Place this piece of <STYLE> in your page, I typically have it in my page header function:
<style>
.FreezePaneOff {
visibility: hidden;
display: none;
position: absolute;
top: -100px;
left: -100px;
}
.FreezePaneOn {
position: absolute;
top: 0px;
left: 0px;
visibility: visible;
display: block;
width: 100%;
height: 100%;
z-index: 999;
filter:alpha(opacity=15);
-moz-opacity: 0.25;
padding-top: 20%;
background: rgb(110, 110, 110);
background: rgba(110, 110, 110, .2);
}
</style>
Place this <DIV> and <SCRIPT> after all of your page output, I typically make it the last thing in my page footer function:
<div align="center" id="FreezePane" class="FreezePaneOff"></div>
<script type="text/javascript">
$('input:submit').click(function FreezeScreen(){
scroll(0,0);
var outerPane = document.getElementById("FreezePane");
if (outerPane) outerPane.className = "FreezePaneOn";
})
</script>
This technique performs well in virtually every browser and in situations ranging from an instant response to a long delay.
Wednesday November 19th, 2014 Terry Riegel
Force HTTPS
Requiring HTTPS on all pages
How To require that all pages be served with HTTPS instead of HTTP. The best way is to prevent any and all HTTP requests. This may not be possible and would require configuring your HTTP server to reject any requests on port 80.
You can however make sure that all of your HTML/OS pages stop delivery of anything not requested over HTTPS.
First edit your htmlos.conf file and look for a line with ServiceType in it
ServiceType https
Next, you will need to protect any potential point of entry, like /system/login.html and /system/rs.html also on any startlinks you might have. Make sure all of these pages have this snip of code at the top of them. Your code might look something like thisβ¦
<<
if getenv('HTTPS')='ERROR' then
# Force HTTPS /#
goto 'https://'+domainname+getenv('REQUEST_URI')
/if
>>
Placing that little snip of code at the start of all your HTML/OS pages will prevent them from being delivered over HTTP.
To verify your code is working take any page and change the https://... to http://... and load it. If it loads over HTTP then you have a problem and need to make sure you have the conditional on the page.
To protect /system/rs.html and /system/login.html do the following
First copy /system/rs.html to /system/rsAESTIVA.html and /system/login.html to /system/loginAESTIVA.html
Next create a new /system/rs.html and add the following code to it...
<<
if getenv('HTTPS')='ERROR' then
# Force HTTPS /#
goto 'https://'+domainname+getenv('REQUEST_URI')
/if
goto '/system/rs.html'
>>
And lastly Next create a new /system/login.html and add the following code to it...
<<
if getenv('HTTPS')='ERROR' then
# Force HTTPS /#
goto 'https://'+domainname+getenv('REQUEST_URI')
/if
goto '/system/login.html'
>>
Wednesday November 19th, 2014 Terry Riegel
UTC Time
How to get UTC+0 / GMT
This seems like a very simple question with a simple answer.
NOW
A simple use of NOW will only return the time and date portion of what the underlying server is reporting. For example if you were to type date from the command line on your server you would get something like this...
$ date
Wed Nov 19 11:18:56 PST 2014
And then if you run NOW in your html/os code you get something like this...
11/19/2014 11:18
Notice the timezone is no where to be seen. And to complicate things if you use the Aestiva Control Panel to modify your time to say add 120 minutes to the server time then the Aestiva Time will return...
11/19/2014 13:18
And once again the concept of which timezone this represents is lost.
Thankfully newer versions of HTML/OS include a nice function called UTCOFFSET(now) in the example listed above UTCOFFSET(now) would return -0800, but it would return that regardless of any time modifications you might have made with the time control panel.
So in order to get the UTC+0 time on your server you can use the function ADDMINUTES() to subtract the UTCOFFSET times 60 but then you will also have to subtract the minutes offset you may have entered into your Time Control Panel.
Here is a function that encapsulates these two arithmetic operations to give you the true UTC+0 or GMT date and time.
function nowUTC() locals u,s,timeconf do
copy file="/system/conf/time.conf" ts=',' to timeconf /copy
if timeconf[1,1]='ERROR' then timeconf[1,1]=0 /if
if timeconf[1,1]='' then timeconf[1,1]=0 /if
u=utcoffset(now)
s=left(u,1)
u=middle(u,2,3)
u=u+0
if s='-' then u=u*-1 /if
return addminutes(now,(u*-60)-timeconf[1,1]) /return
/function
Let me know in the comments if this works for you and if not why.
Monday November 17th, 2014 Terry Riegel
Welcome
LearnHTMLOS.com
We hope to make this a resource for new and experienced HTML/OS programmers to gather to learn new coding techniques and also to share and help each other with issues that may arise while building their web application with HTML/OS