ajaxed/ folder.
Dear <<< name | unknown >>>,
We are happy you joined our website.
Greetings,
<<< sender | Your website team >>>
name and sender. Both have a default value which will be used if no value is available. Great! Thanks to iurisilvio as well.
Dear <<< customerName >>>,
Your ordered articles:
<<< block articles >>>
Name: <<< name >>>
Number :<<< nr | unknown >>>
<<< endblock articles >>>
Thank you for purchasing with us.
<%
set t = new TextTemplate
t.filename = "template.txt"
'create a block and add the data from a recordset
set b = new TextTemplateBlock
b.addRS(db.getRS("SELECT * FROM ...", empty))
'add the block to the template
t.add "articles", b
%>
<<< date | now() >>>. Default values if there is no code block, etc... okay that's gonna come in 2.2.lib.newRecordset()) which will create a new disconnected recordset. The alias is ["R"](). Let's see some examples:
<%
'creating new recordset
set rs = ["R"](array( _
array("firstname", "lastname", "age"), _
array("Lebron", "James", 20), _
array("Britney", "Spears") _
))
'ready for looping...
while not rs.eof
str.write(rs("firstname"))
rs.movenext
wend
'we can even skip columns
'age is skipped for the first record .. cause we do not know the age ;)
set rs = ["R"](array( _
array("firstname", "lastname", "age"), _
array("Barack", "Obama"), _
array("David", "Copperfield", 50) _
))
%>
Tip: Not sure if you know the other useful aliased functions:[],["D"]()and["O"](). If not then check out the API ... They are useful!
DataContainer. It allows us to add new items to a container instance easily. Here is a quick example:
<%
'dynamically expanding an array
set dc = (new DataContainer)(array())
'chained add
dc.add("one").add("two").add("three")
'dictionary data container
with (new DataContainer)(["D"](empty))
.add(array(1, "BWM"))
.add(array(2, "Ford"))
'updating the item with key 1
.add(array(1, "Toyota"))
end with
'recordsets
set dc = (new DataContainer)(["R"](array( _
array("brand", "ps"), _
array("BWM", 200)
)))
dc.add(array("Toyota", 100)).add(array("Mazda", 7))
%>
<!--#include virtual="/ajaxed/class_TestFixture/testFixture.asp"-->
<%
set tf = new TestFixture
tf.allEnvs = true
tf.run()
sub test_1()
tf.assert 1 = 1, "1 is not equal 1"
end sub
%>
Run this code — test_first.asp<!--#include virtual="/ajaxed/class_TestFixture/testFixture.asp"-->
<%
set tf = new TestFixture
tf.allEnvs = true
tf.run()
sub test_1()
tf.assert 1 = 2, "1 is not equal 1"
end sub
%>
Run this code — test_fail.aspAjaxedPage. The only exception is a test file because it contains an instance of TestFixture. For this reason the first line loads (includes) the TestFixture class and an instance named tf is created on the following line. Last but not least it is required to run the test with the run() method.
Note: By default all tests are only runnable on the DEV environment (security). To enable a test on all environments it's required to set theallEnvstotrue. All examples here will contain that setting.
TestFixture class comes with a lot of assertion methods which are only executeable within a test method. So we need to create a test method first. A test method has the following signature:
Test methods: a sub prefixed withtest_and followed by a continous number (starting with 1)
<%
sub test_1()
'your asserts
end sub
sub test_2()
'other asserts
end sub
'IMPORTANT: This test won't be called cause
'the test_3 is missing. Tests are not in
'continous order
sub test_4()
end sub
%>
setup() procedure into our test and it will be executed automatically before each test method.
test_. Prefixing allows you to identify your tests much quicker and additionally they will be available within your ajaxed console (under the "Test" tab).
Most important to remember when working with assertions is that the last parameter always contains a message which will be displayed if the assertion fails.
<!--#include virtual="/ajaxed/class_TestFixture/testFixture.asp"-->
<%
set tf = new TestFixture
tf.allEnvs = true
tf.run()
sub test_1()
with tf
'simple
.assert true, "Asserts truth"
.assertEqual true, true, "Asserts equality"
.assertNotEqual 1, 2, "Asserts not equality"
.assertInDelta 10, 15, 5, "Number with tolerance (delta)"
.assertInstanceOf "TestFixture", tf, "Asserts a type"
.assertNothing nothing, "Asserts that an object is nothing"
'advanced
.assertMatch "\d+", "12312", "Asserts to a given regex pattern"
.assertInFile "/ajaxed/ajaxed.asp", "include", "Asserts a string in a file"
.assertEqual array("x", "y"), array("x", "y"), "Asserts equality also on arrays"
'contains assertions
.assertHas array(1, 2, 3), 2, "Asserts containment"
.assertHasNot array(1, 2, 3), 5, "Asserts non existence"
end with
end sub
%>
Run this code — test_assertions.aspfail() method.
<!--#include virtual="/ajaxed/class_TestFixture/testFixture.asp"-->
<%
set tf = new TestFixture
tf.allEnvs = true
tf.run()
sub test_1()
if lib.env = "LIVE" then
tf.fail "i want the test to fail"
end if
end sub
%>
Run this code — test_manually_fail.aspassertResponse() which lets us check if a page contains a given response text (please refer to the API for a more detailed description). The following script tests some pages of the ajaxed.org page.
<!--#include virtual="/ajaxed/class_TestFixture/testFixture.asp"-->
<%
set tf = new TestFixture
tf.allEnvs = true
tf.run()
'testing our most important pages
sub test_1()
with tf
.assertResponse "/api/", empty, "API", "API has a problem"
.assertResponse "/", empty, "ajaxed", "Main page has a problem"
.assertResponse "/about/", empty, "michal", "About page has a problem"
.assertResponse "/download/", empty, "2.0", "Download page is not workin"
end with
end sub
%>
Run this code — test_response.aspTip: We strongly recommend creating a test for your asp files. That saves you the time to browse each page manually.]]>
recordLink property. It allows us to set a link which will be applied to each cell of each record. The best thing is that you can specify placeholders which will be replaced with a data value on each record iteration. In other words, you can create a record link with one line of code:table.recordLink = "modfify.asp?id={id}"
{id} will be replaced with the value of the column named id on each record iteration. Lets have a look at a working example:<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_datatable/datatable.asp"-->
<%
set table = new Datatable
set page = new AjaxedPage
page.draw()
sub init()
table.sql = "SELECT id, title, created_on FROM active_articles"
table.recordlink = "javascript:alert('The id is: {id} and ive been created on {created_on}')"
end sub
sub callback(a)
table.draw()
end sub
sub main()
table.draw()
end sub
%>Run this code — recordlink.asplink property of DatatableColumn and set it to false:<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_datatable/datatable.asp"-->
<%
set table = new Datatable
set page = new AjaxedPage
page.draw()
sub init()
table.sql = "SELECT * FROM active_articles"
table.recordlink = "javascript:alert('The id is: {id} and ive been created on {created_on}')"
'for that we need to add the columns manually
table.newColumn("id", "ID").link = false
table.newColumn "title", "Title"
table.newColumn "created_on", "Created"
end sub
sub callback(a)
table.draw()
end sub
sub main()
table.draw()
end sub
%>Run this code — recordlinkDisable.asp<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_datatable/datatable.asp"-->
<%
set table = new Datatable
set page = new AjaxedPage
page.draw()
sub init()
table.sql = "SELECT id, title, created_on FROM active_articles"
table.onRowCreated = "onRow"
end sub
sub onRow(dt)
'setting the link on the whole row.
'it will be placed into the TR tag directly
dt.row.attributes = "onclick=""alert('The id is: " & dt.data("id") & " and ive been created on " & dt.data("created_on") & "')"""
end sub
sub callback(a)
table.draw()
end sub
sub main()
table.draw()
end sub
%>Run this code — recordLinkOnRow.aspattributes property of each row for that.<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_datatable/datatable.asp"-->
<%
set table = new Datatable
set page = new AjaxedPage
page.draw()
sub init()
with table
.sql = "SELECT * FROM active_articles"
'we use the ID column to place the number into it
.newColumn("id", "").onCellCreated = "onID"
.newColumn "title", "Title"
.newColumn "created_on", "Created"
'turn on paging so you see how it affects numbering
.recsPerPage = 10
end with
end sub
function onID(dt)
'pass the row number and ignore the actual value (ID)
onID = dt.row.number
end function
sub callback(a)
table.draw()
end sub
sub main()
table.draw()
end sub
%>Run this code — numberedRecords.aspUpgrading from v2.0 is no problem at all. Just overwrite the ajaxed core and you"ll be happy with its new improvements.
<%
'before:
function onCell(dt)
onCell = dt.col.value
end function
'now (so fullsearch highlighting will work fine):
function onCell(dt)
onCell = dt.col.valueF
end function
%>
AJAXED_LOADINGTEXT initialization variable. Normally you set this within your config.asp file:
<%
AJAXED_LOADINGTEXT = "Please be patient..."
%>
ajaxLoadingIndicator. Have a look at this example which overrides the indicator message and applies a fancy style.<!--#include virtual="/ajaxed/ajaxed.asp"-->
<%
AJAXED_LOADINGTEXT = "You have to wait. You know that ;)"
set page = new AjaxedPage
page.draw()
sub pagePart_do()
lib.sleep(1)
str.write("Done! Do it again?")
end sub
sub main() %>
<style>
.ajaxLoadingIndicator {
font:16pt tahoma;
color:#fff;
background:#f00;
padding:1em 2em;
top:auto !important;
bottom:0px;
}
</style>
<button id="btn" type="button" onclick="ajaxed.callback('do', 'btn')">
Do somehthing
</button>
<% end sub %>Run this code — loading.aspImportant: The default indicator is ALWAYS positioned at the right top corner of your page with a fixed position. That means it will always stay at the top even if the user will scroll on your page. ajaxed sets the following css attributes automatically:top: 0px;
right: 0px;
position: fixed;
In case you want to position your default indicator somewhere else (means you want to change either the top, right or position attribute) you need to mark those attirbutes with!importantso ajaxed cannot overwrite them:right: 20px !important;
<!--#include virtual="/ajaxed/ajaxed.asp"-->
<%
set page = new AjaxedPage
page.draw()
sub callback(a)
lib.sleep(2)
end sub
sub main() %>
<script>
function doSomething() {
$('loading').show();
ajaxed.callback('do', function(r) {
alert('done!');
}, null, function(t) {
$('loading').hide();
})
}
</script>
<button type="button" onclick="doSomething()">
Do somehthing
</button>
<div id="loading" style="display:none">
<img src="http://www.tourmandu.com/img/uploading.gif" style="vertical-align:middle"/>
Please be patient...
</div>
<% end sub %>Run this code — customLoading.aspajaxed.callback. Before the actual call we show our custom indicator and hide it within the onComplete callback. Thats all. Styling is up to you.In case you only want to use your own custom indicators you can disable the default indicator by setting its message to an empty string and/or setting its style to display:none]]>active_articles which holds all the current articles and a category table which contains all our categories. Connecting them together is as easy as this..<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_dropdown/dropdown.asp"-->
<%
set page = new AjaxedPage
page.draw()
sub drawCategoryDD()
with new Dropdown
.datasource = "SELECT * FROM category ORDER BY catname"
'we use the category name as a value and as a caption
.dataTextField = "catname"
.dataValueField = "catname"
.name = "cat"
.commonFieldText = "-- category --"
'the "no category" item should have a blank value
'as by default its 0
.commonFieldValue = ""
.attributes = "onchange=""ajaxed.callback('articlesDD', 'articles')"""
.draw()
end with
end sub
sub pagePart_articlesDD()
with new Dropdown
set .datasource = db.getUnlockedRS( _
"SELECT * FROM active_articles WHERE category = '{0}' ORDER BY title", _
page.RF("cat") _
)
.dataTextField = "title"
.dataValueField = "id"
'we disable the articles if we have no selected category
.disabled = page.RFT("cat") = ""
.commonFieldText = "-- article --"
.name = "article"
.draw()
end with
end sub
sub main() %>
<form id="frm">
<div><% drawCategoryDD() %></div>
<div id="articles"><% pagePart_articlesDD() %></div>
</form>
<% end sub %>Run this code — dropdowns.aspDropdown class and rendered them onto our page. The second dropdown (the one with the articles) has been put into a pagepart (pagePart_articlesDD) so it can easily be called using our ajaxed pagepart technique. pagePart_articlesDD is the selection of all articles according to the selection of the category dropdown (see drawCategoryDD procedure). We grab the selected category from the post variables using page.RF("cat") and use it within our where clause. Last but not least we disable the articles dropdown if no category has been selected.Note: Both dropdowns are using different datasources. The category uses a string and the artciles dropdown uses a recordset. Why? The reason for that is that we want to use a parametrized SQL-query for the articles as we want to prevent SQL-Injection. You could solve that using string concatenation but that would be vulnerable.
In a perfectly normalized database you would rather use an ID for the category and parse the form variable usingpage.RFP()into a number.
attributes property of our category dropdown. There you find a javascript call to ajaxed.callback() which reload our pagepart (Reminder: as we have a form with the id frm all form fields are passed as post variables to the callback automatically).Dropdown class at this point. With only some modifications we are able to do some cool stuff. Lets do the following:<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_dropdown/dropdown.asp"-->
<%
set page = new AjaxedPage
page.draw()
sub drawCategoryDD()
with new Dropdown
.datasource = "SELECT * FROM category ORDER BY catname"
'we use the category name as a value and as a caption
.dataTextField = "catname"
.dataValueField = "catname"
.name = "cat"
.commonFieldText = "-- category --"
'the "no category" item should have a blank value
'as by default its 0
.commonFieldValue = ""
.attributes = "onchange=""ajaxed.callback('articlesDD', 'articles')"""
'create an event handler to highlight the news item
.onItemCreated = "onCategory"
.draw()
end with
end sub
sub onCategory(it)
'if the items value is news we make it bold and red
if it.value = "News" then it.style = "font-weight:bold;color:#f00"
end sub
sub pagePart_articlesDD()
with new Dropdown
set .datasource = db.getUnlockedRS( _
"SELECT * FROM active_articles WHERE category = '{0}' ORDER BY title", _
page.RF("cat") _
)
.dataTextField = "title"
.dataValueField = "id"
'we disable the articles if we have no selected category
.disabled = page.RFT("cat") = ""
.commonFieldText = "-- article --"
.name = "article"
.cssClass = "articles"
'enable convenient multiple selection
.multiple = true
.multipleSelectionType = DD_SELECTIONTYPE_MULTIPLE
'we need the height if multiple is turned on
.size = 80
.draw()
end with
end sub
sub main() %>
<style>
#articleSelection {
border:2px inset #ccc;
background:#eee;
padding:0.5em 1em;
width:300px;
}
.articles {
overflow:auto;
white-space:nowrap;
margin-top:1em;
}
</style>
<form id="frm">
<div id="articleSelection">
<div><% drawCategoryDD() %></div>
<div id="articles"><% pagePart_articlesDD() %></div>
<div>
</form>
<% end sub %>Run this code — dropdown_pimped.aspmultiple property. Just this change makes the dropdown render as a conventional multiple select tag, but we control the multiple selection with the multipleSelectionType property. This provides the ability to select multiple items using a checkbox and a single item using a radio button. Thats much more user friendly I guess. Play around with it - you will like it.Tip: You can even create dropdowns with a one-liner. Check the]]>getNew()method for that. You could use it as follows:
Renders a month dropdown with one line:(new Dropdown)(lib.range(1, 12, 1), "month", month(date())).toString()
<%
sub main()
'request an URL and store its response
r = lib.requestURL("GET", "http://www.ajaxed.org", empty, empty).responseText
end sub
%>
IServerXMLHTTPRequest. In case of any network or connection problems the return will be nothing. So a suggested real world implementation would look like the following...
<%
sub main()
'request as a post with a parameter "date" set to current date
'set the timeout to 5 seconds.
set r = lib.requestURL( _
"POST", "http://www.ajaxed.org", array("date", date()), _
array("timeout", 5)
)
if r is nothing then
lib.error("Could not request URL. It did not respond.")
elseif r.status <> 200 then
lib.error("Request successful but response not a success.")
else
'everthing fine .. do something with it
str.write(r.responseText)
end if
end sub
%>
Because the request is processed synchronously on server side its recommended to set a timeout and implement caching if possible. Page will load as long as the request lasts.
<!--#include virtual="/ajaxed/ajaxed.asp"-->
<%
set page = new AjaxedPage
page.draw()
'we are using ajaxed PageParts for that
sub pagePart_parse()
url = page.RFT("url")
if url = "" then lib.error("Please enter a valid URL.")
set r = lib.requestURL("GET", url, empty, array("timeout", 5))
if r is nothing then
lib.error("Could not request URL (timeout, ..). Are you sure it exists?")
elseif r.status <> 200 then
lib.error("URL responded with an unsuccessful status: " & r.status)
else
with new Regexp
'simple email pattern
.pattern = "[a-z0-9_.]+@[a-z0-9_.]+"
.global = true
.ignoreCase = true
set mails = .execute(r.responseText)
end with
if mails.count = 0 then str.write("No emails found at " & url)
for each mail in mails
str.write(mail & "<br/>")
next
end if
end sub
sub main() %>
<form id="frm" onsubmit="ajaxed.callback('parse', 'result');return false;">
<input type="text" name="url" value="http://www.">
<input type="submit" value="get all emails">
<div id="result"></div>
</form>
<% end sub %>Run this code — requestUrl.aspDebugging: If you have logging turned on within your setup then you will see a bunch of useful information which helps you debugging your requests. Just have a look at your log ;)]]>
ajaxed makes heavy use ofrequestURL()internally as well. e.g. When requesting pages on unit tests, when localizing the users country, ...
Datatable. All the examples here will connect to a sample database whose connectionstring is stored in a constant called DATA_CONNSTRING. So you will see a connection call to a different database than the default on. It should not confuse you because in your environment you can use your default configured connection. Furthermore we will with only one table called person which contains the columns id, firstname and lastname.<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_datatable/datatable.asp"-->
<%
set table = new Datatable
set page = new AjaxedPage
page.draw()
sub init()
db.open(DATA_CONNSTRING)
table.sql = "SELECT * FROM person"
end sub
sub callback(action)
table.draw()
end sub
sub main() %>
<% table.draw() %>
<% end sub %>Run this code — datatable.asptable. The instance has been defined globally because its necessary to access it during different steps of our page lifecycle. All your datatable settings must be done within the init() and your table must be drawn within the main() (to actually render the HTML at a given position) and within the callback() procedure (to process all requests its callback requests).sql property which is being used to populate the Datatable. Thats all the stuff you require to hook up your first one. They are a must!str.humanize()). Thats cool, but not always what we want. We might want to change order, hide some columns, etc. For that reason its possible to add the columns manually and as long as there is at least one column added manually the automatic detection is deactivated. newColumn() method is our friend in that case. Check that:<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_datatable/datatable.asp"-->
<%
set table = new Datatable
set page = new AjaxedPage
page.draw()
sub init()
db.open(DATA_CONNSTRING)
with table
.sql = "SELECT id, firstname, lastname, lastname + ', ' + firstname AS name FROM person"
.sort = "lastname, firstname"
.newColumn "name", "Name"
.newColumn "lastname", "Last"
.newColumn "firstname", "First"
end with
end sub
sub callback(action)
table.draw()
end sub
sub main() %>
<% table.draw() %>
<% end sub %>Run this code — columns.aspnewColumn() within our init() procedure. The first parameter is the name of the column as it appears in the SQL Query and the second parameter is caption which should be displayed as the column headline. Pro TIP: The second parameter can also be anarraywhich would result that the first field is the caption and the second a tooltip. E.g..newColumn "firstname", array("First", "Users Firstname")
name (which concatenates firstname and lastname) within the SQL query. Thus we were also able to add it as a column to the Datatable. Primary key columns: The first column is always being considered as the primary key column (which uniquely identifies each record). If you are missing that then you get an exception which tells you to set pkColumn property manually to the column which contains the primary key.sort property has been used to set the initial sorting of the data. You cannot use an ORDER BY clause within your SQL Query. Thats being added by the component itself. The sort property can contain any valid ORDER BY expressions though. Examples:
'valid sorting expressions
table.sort = "firstname DESC, lastname"
table.sort = "id, name, lastname"
newColumn methods returns an instance of a DatatableColumn. Thats great, because we can grab the column and customize it even more. Check the following example where we assign a css class to the id column to make it look gray in order to not attract too much attention. In addition we also styled the lastname column.<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_datatable/datatable.asp"-->
<%
set table = new Datatable
set page = new AjaxedPage
page.draw()
sub init()
db.open(DATA_CONNSTRING)
table.sql = "SELECT * FROM person"
'add id column and set CSS class
'quickes way (but only one property)
table.newColumn("id", "ID").cssClass = "light"
'add lastname column and set CSS class
'another way of assigning properties
with table.newColumn("lastname", "Name")
.cssClass = "name"
.help = "Some tooltip for the header"
end with
end sub
sub callback(action)
table.draw()
end sub
sub main() %>
<style>
.light {
color:#bbb;
}
.name {
color:#f00;
text-align:center;
font-weight:bold;
}
</style>
<% table.draw() %>
<% end sub %>Run this code — columnProperty.asp
table.sql = "SELECT id, firstname AS first, lastname AS last FROM person"
' would result in a datable with the following columns:
' id | first | last
table.sql = "SELECT id, '<strong>' + firstname + '</strong>' AS first FROM person"
When using HTML within your data be sure to setencodeHTMLtofalseon the respective column. Otherwise all HTML will be safely encoded by default.
DatatableRow) during runtime or you can do the same for a column. First should be used when you want to apply customizations on a per record basis and the latter is perfect when you want to deal with a column across all records. Lets take a deeper look at both of them.onRowCreated event is fired when the current row instance has been created but just before its been rendered. It has to be set on the datatable instance. At this stage you can modify any properties of the current row instance which is being held in the row property of your Datatable instance (check the API docs of DatatableRow for all its members). The following example will assign a css class called inactive to all persons whose ID is less than 10. Makes not much "real world" sense but should do it :)<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_datatable/datatable.asp"-->
<%
set table = new Datatable
set page = new AjaxedPage
page.draw()
sub init()
db.open(DATA_CONNSTRING)
table.sql = "SELECT * FROM person"
table.onRowCreated = "onRowHandler"
end sub
sub onRowHandler(dt)
if clng(dt.data("id")) < 10 then
dt.row.cssClass = "inactive"
end if
end sub
sub callback(action)
table.draw()
end sub
sub main() %>
<style>
.inactive td {
color:#ccc;
}
</style>
<% table.draw() %>
<% end sub %>Run this code — onRow.asponRowCreated is nothing more as a normal property. Just that the string it holds is the name of the procedure which will be called everytime a row has been created. The procedure has to accept one argument which is the Datatable instance itself. That makes it convenient to access all datatable properties during runtime and makes it even possible to reuse event handling procedures across more datatables. dt in that case) and its row property to get access to our current row instance. Also very interesting is the usage of the data property which always gives us access to underlying recordset. That allows us to read all the records data during runtime. Powerful!onCellCreated event of a Datatable column. Its fired when a given cell of that column has been created but not rendered yet. Thus you can change its properties according to any conditions you like. Lets check the following example which writes "less 10" if the ID is less than 10 and "greater 10" if the ID is greater instead of the ID itself. Additionally it should highlight all persons whose lastname starts with the letter "A".<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_datatable/datatable.asp"-->
<%
set table = new Datatable
set page = new AjaxedPage
page.draw()
sub init()
db.open(DATA_CONNSTRING)
table.sql = "SELECT * FROM person"
'quick way of attaching an event handling procedure
table.newColumn("id", "ID").onCellCreated = "onID"
'other way...
set c = table.newColumn("lastname", "Last")
c.onCellCreated = "onLastname"
end sub
'Important: its a function! onRowCreated was a sub!
function onID(dt)
onID = "less 10"
dt.col.cssClass = "inactive"
if dt.col > 10 then
onID = "greater 10"
dt.col.cssClass = ""
end if
end function
function onLastname(dt)
'important to pass through the original value
'as we dont change it
onLastname = dt.col.value
'note; we can use dt.col instead of dt.col.value
'as its the default property
if str.startsWith(dt.col, "A") then
dt.col.cssClass = "ePerson"
else
'important to clear it as it would remember
'it for the whole column
dt.col.cssClass = ""
end if
end function
sub callback(action)
table.draw()
end sub
sub main() %>
<style>
.inactive {
color:#ccc;
}
.ePerson {
color:#00f;
}
</style>
<% table.draw() %>
<% end sub %>Run this code — onCell.asponRowCreated apart from the fact that this time we need to define a function instead of a sub. Its because our cell event handlers need to return the actual value which will be rendered. The simplest handler would be just passing through the value:
<%
'same as if you would not use it. just pass through
function onFirstname(dt)
onFirstname = dt.col.value
end function
%>
onCellCreated event.
<%
function onLastname(dt)
onLastname = "<a href=""person.asp?id=" & dt.data("id") & """>" & dt.col & "</a>"
end function
%>
ajaxed.callback) to perform an action:
<%
function onAction(dt)
onAction = "<a href=""person.asp?id=" & dt.data("id") & """>edit</a>" & _
"<a href="javascript:void(0)" onclick=""doDelete(" & dt.data("id") & ")"">delete</a>"
end function
%>
css property (which takes a virtual path to a stylehseet file). If you specify it then yours is loaded instead of the standard one. Another option would be to just add your own stylesheet to the page which will override the standard settings. Thats not recommended though, as the standard appearance might change over time.td, th {
padding: 0.2em 1em;
font: 10pt arial;
width: 200px;
}
.axdDTControlsRow {
background:#bbb;
}
th, th * {
background:#000;
color:#fff;
}
.axdDTRowOdd td {
background:#eee;
}
.axdDTHighlight {
font-weight:bold;
color:#f00;
}
.sortedASC, .sortedDESC {
font-weight:bold;
}
tfoot tr {
background:#bbb;
color:#fff;
}
tbody tr:hover td {
background:#B6E2F3;
}
.pagingBar span {
padding:0.1em 0.3em;
}
.pagingBar .pCurrent {
font-weight:bold;
background:#000;
color:#fff;
}Run this code — style.csscss property.<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_datatable/datatable.asp"-->
<%
set table = new Datatable
set page = new AjaxedPage
page.draw()
sub init()
db.open(DATA_CONNSTRING)
table.sql = "SELECT * FROM person"
table.css = "style.css"
table.recsPerPage = 10
end sub
sub callback(action)
table.draw()
end sub
sub main() %>
<% table.draw() %>
<% end sub %>Run this code — pimped.aspShare your Theme: Please share your Datatable themes with others by posting to our Discussion Group. Its great to have some nice and good looking datatables around.
Debugging: Check your log files if you want to debug the Datatable. You will find the whole generated SQL which is being sent to the database when populating the table.]]>
AjaxedPage instance when working with AJAX. In that case you can set its debug property to true to get more information about the ajax life cycle. Thats especially useful when you experience problems during the ajaxed.callback call.
callback()), what parameters are being sent to it, which URL is being requested and what the response looks like. All debug information will pop up in JavaScript alert boxes.
<!--#include virtual="/ajaxed/ajaxed.asp"-->
<%
set page = new AjaxedPage
with
.debug = true
.draw()
end with
sub callback(a)
if a = "do" then page.return "done"
end sub
sub main() %>
<button type="button" onclick="ajaxed.callback('do', function(r) { alert(r)})">
clik me for debug info
</button>
<% end sub %>Run this code — debug.aspcallback() routine.
<%
sub callback(a)
lib.logger.debug "Action: " & a
if a = "some" then
lib.logger.debug "Went here"
else
lib.logger.error "Should not be here"
end if
end sub
%>
TIP: If you take a look at your logfiles you will recognize detailed tracing information about all the ajax calls. We highly recommended to set up logging.
str.write() within the callback() but its not possible as its never written back directly into the browser.
lib.error() or lib.throwError() method to alert some debug details. The first one stops the response, flushes it and write out the error message (useful for debugging or user friendly errors). The latter throws a real runtime error which can also be caught by error handlers.
<%
sub callback(a)
if a = "some" then
a = 1 + 2
'can be useful for debugging
lib.error("a = " & a)
else
lib.throwError("Invalid call.")
end if
end sub
%>
ajaxed.callback to fail and thus the JavaScript calback function is not called. Thats good cause it might produce errors as the callback failed already.]]>request.form (for POST parameters) collection. Thats great but ajaxed takes it a step further.
request.querystring is really really long and results in unreadable code)AjaxedPage class. All methods starting with RF are referring to POST parameters (RF stands for request.form). On the other hand all methods starting with QS are referring to GET parameters (QS stands for querystring). From today on you should use them instead of the native ones ;) Just have a look at the length of the methodname. Its much shorter.
AjaxedPage you will have access to methods for POST and GET parameters.
Important: The same applies when working with ajax. You can access POST and GET parameters within thecallback()using the same methods.
AjaxedPage starting with RF. They always contain at least one parameter which holds the name of the field (parameter) your are requesting. Examples:
<%
'we need a page instance in order to get
'access to the post parameters
set p = new AjaxedPage
p.draw()
sub main()
'-- 1. common reading
'old style
someVal = request.form("paramName")
'new style
someVal = p.RF("paramName")
'-- 2. parsing into a given datatype in one go
'old style (fails if a parameter is not an int)
someVal = cint(request.form("paramName"))
'new style
someVal = p.RFP("paramName", 0)
someBool = p.RFP("paramName", true)
'-- 3. trimming
'old style
someVal = trim(request.form("paramName"))
'new style
someVal = p.RFT("paramName")
'-- 4. getting an array (if more value have the same name)
'old style
set val = request.form(name)
someVal = array()
redim preserve someVal(val.count - 1)
for i = 0 to uBound(someVal)
someVal(i) = val(i + 1)
next
RFA = arr
'new style
someVal = p.RFA("paramName")
'-- 5. Checking existence
'old style
someVal = request.form("paramName") <> ""
'new style
someVal = p.RFHas("paramName")
end sub
%>
RFP which parses a posted value into a desired datatype. What does that actually mean to us? That means that it tries to parse the parameter into the datatype of your fallback variable. If it fails then the fallback variable is passed through. In that way you can prevent malicous input. You could e.g. use that value directly within a SQL query:
<%
'this is safe because the ID parameter will be
'parsed into an integer for sure
set RS = db.getRS("SELECT * FROM users WHERE id = " p.RFP("id", 0), empty)
%>
Note: When you reqiure a floating point number then be sure to pass 0.0 as the fallback value.
AsRFP()usesstr.parseinternally, you should refer to its documentation if you require more details about the parsing behavoir.
RFHas(). It checks if a given parameter contains a non empty string. This is very useful when dealing with e.g. checkboxes. The following example ticks a checkbox when the page contains a posted value named foo:
<input type="checkbox" checked="<%= lib.iif(p.RFHas("foo"), "checked", "") %>"/>
TIP: Use theisPostback()method if you want to check if the page is a postback (has been requested using POST).
QS.
<%
'we need a page instance in order to get
'access to the post parameters
set p = new AjaxedPage
p.draw()
sub main()
'-- 1. common reading
'old style
someVal = request.querystring("paramName")
'new style
someVal = p.QS("paramName")
'-- 2. parsing into a given datatype in one go
'old style (fails if a parameter is not an int)
someVal = cint(request.querystring("paramName"))
'new style
someVal = p.QSP("paramName", 0)
someBool = p.QSP("paramName", true)
'-- 3. trimming
'old style
someVal = trim(request.querystring("paramName"))
'new style
someVal = p.QST("paramName")
end sub
%>
page.asp which can take a parameter speed:
<%
set p = new AjaxedPage
p.draw()
sub main()
'tries to parse it into a floating point number
'page can be called with ...
' page.asp?speed=2.2 => would pass 2.2
' page.asp?speed=xy => would pass 0.0
' page.asp?speed=1 => would pass 1.0
' page.asp => would pass 0.0
speed = page.RFP("speed", 0.0)
end sub
%>
response.write. When working with ajaxed we try to avoid its usage as we have several helper methods which makes life easier and are shorter to write:
<%
set p = new AjaxedPage
p.draw
sub main()
'old style
response.write("hello world")
'new style
str.write("hello world")
'or
page.write("hello world")
'old
response.write("hello world" & vbnewline)
'new
str.writeln("hello world")
'or
page.writeln("hello world")
'old
response.end()
'new
str.end()
'old
response.write("some debug")
response.end()
'new
str.writeend("some debug")
'old
name = "Jack"
response.write("My name is " & name & ".")
'new
str.writef("My name is {0}.", name)
'more parameters
str.writef("My name is {0} and i am {1}.", array(name, 18))
'old
response.write(server.HTMLEncode("some <html>"))
'new
str.HTMLEncode("some <html>")
'or quicker by using the default method
str("some <html>")
end sub
%>
writef() method which allows you to format a string and write it our directly to the response. Thats a time saver :)
Please refer to the]]>str.format()method if you need more details about the formatting ofstr.writef().
Important (preventing XSS attacks): Be sure to usestr.HTMLEncode()whenever you display user input within your page. You can use the short versionstr()as well
ajaxed.callback function.changes.txt for the details. Read some details about the highlights below. getRS() and getUnlockedRS() methods. Those replace the prior methods getRecordset() and getUnlockedRecordset(). Although all are still supported its recommended to use the new ones. They give you the ability of parametrized SQL queries. In other words - everthing is done to prevent SQL injection. The names are also shorter :) check the changes:
<%
'before
set RS = db.getRecordset("SELECT * FROM user WHERE id = " & str.sqlSafe(id))
'now
set RS = db.getRS("SELECT * FROM user WHERE id = {0}", id)
%>
update() or insert() all the text fields are trimmed to the maximum allowed length. This mean no errors anymore if the input is too long as allowed by the database field.insertOrUpdate() method which allows you to insert a record if it does not exist yet and update it if its already there. With update() you can also perform batch updates now.pagePart_ and use the name as the action parameter within ajaxed.callback:<!--#include virtual="/ajaxed/ajaxed.asp"-->
<%
set page = new AjaxedPage
page.draw()
sub main() %>
<button type="button" onclick="ajaxed.callback('one', 'content')">content 1</button>
<button type="button" onclick="ajaxed.callback('two', 'content')">content 2</button>
<div id="content"></div>
<% end sub %>
<% sub pagepart_one() %>
the first content
<% end sub %>
<% sub pagepart_two() %>
some other content from server side
<% end sub %>Run this code — pageparts.aspDatatable. It lets you grab data from the database and display it in a tabular form. Including sorting, paging, searching, etc. All fully ajaxed! Perfect for backends. We love SQL and we use it. That's why the Datatable is focused on SQL queries as datasource.<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_datatable/datatable.asp"-->
<!--#include virtual="/ajaxed/class_dropdown/dropdown.asp"-->
<%
set table = new Datatable
set page = new AjaxedPage
page.draw()
sub init()
db.open(DATA_CONNSTRING)
table.sql = "SELECT * FROM person"
table.recsPerPage = 10
end sub
sub callback(action)
table.draw()
end sub
sub main() %>
<% table.draw() %>
<% end sub %>Run this code — datatable.aspDataContainer. This class is loaded by default. Here is a short demonstration:<!--#include virtual="/ajaxed/ajaxed.asp"-->
<%
set page = new AjaxedPage
page.draw()
sub main()
'create a new datacontainer for an array
set data = (new DataContainer)(array("first", "second", "Second", "third"))
'write some details about it
str.writef "First Element: {0} <br/>", data.first
str.writef "Last Element: {0} <br/>", data.last
str.writef "Count: {0} items <br/>", data.count
'get only unique elements
for each el in data.unique(false)
str.write(el & "<br/>")
next
end sub
%>Run this code — datacontainer.aspDataContainer can be used with a Dictionary or Recordset as well. Even simple arrays are supported. You just don't care anymore whats the underlying data strucuture.Validator provides you the base for any kind of validation. Use it to validate your models or use it directly in your views.<!--#include virtual="/ajaxed/ajaxed.asp"-->
<!--#include virtual="/ajaxed/class_validator/validator.asp"-->
<%
set page = new AjaxedPage
page.draw()
sub callback(a)
if a = "save" then
'create new validator
set v = new Validator
'if the name is empty add an error
if page.RFT("name") = "" then v.add "name", "Name required"
'if age is not a number or less than 1 then error
if page.RFP("age", 0) < 1 then v.add "age", "Age invalid"
'thanks to ajaxed we can return the whole
'object. it implements the reflect() method
page.return v
end if
end sub
sub main() %>
<script>
function save() {
ajaxed.callback('save', function(r) {
//easy access to the validator properties.
if (!r.valid) return $('errors').update(r.summary);
alert('Successully saved. No errors :)');
})
}
</script>
<form id="frm" onsubmit="save();return false;">
<ul id="errors"></ul>
Your Name:
<input type="text" name="name"/>
<br/>
Your Age:
<input type="text" name="age"/>
<br/>
<input type="submit" value="save"/>
</form>
<% end sub %>Run this code — validator.aspLocalization class offers information about the client and its settings. An instance is available by default through the local variable. Here is one example which locates you (it should show the country of your ISP):<!--#include virtual="/ajaxed/ajaxed.asp"-->
<%
set page = new AjaxedPage
page.draw()
sub main()
'get the countrycode with timeout of 5 seconds
countryCode = local.locateClient(5, empty)
if isEmpty(countryCode) then str.writeEnd("Service unavailable")
if countryCode = "XX" then str.writeEnd("Location unknown")
str.writef "You are located in '{0}', arent you?", countryCode
end sub
%>Run this code — local.aspDropdown class. It's optimized for performance and allows you to get rid of the spaghetti code involved with creating dropdowns. Check a simple example:<!--#include virtual="/ajaxed/ajaxed.asp"-->
<!--#include virtual="/ajaxed/class_dropdown/dropdown.asp"-->
<%
set page = new AjaxedPage
page.draw()
sub drawDD()
with new Dropdown
'you can also use SQL, Dictionary, ...
.datasource = array("Jack Johnson", "John Meyer")
.name = "myDD"
.multiple = page.QS("t") <> ""
if .multiple then .size = 2
if page.QS("t") = "multipleS" then
.multipleSelectionType = DD_SELECTIONTYPE_SINGLE
.size = 50
elseif page.QS("t") = "multipleM" then
.multipleSelectionType = DD_SELECTIONTYPE_MULTIPLE
.size = 50
end if
.draw()
end with
end sub
sub main() %>
<a href="?t=">classic</a>
<br/>
<a href="?t=multiple">multiple classic</a>
<br/>
<a href="?t=multipleS">multiple single selection</a>
<br/>
<a href="?t=multipleM">multiple multi selection</a>
<br/>
<% drawDD() %>
<% end sub %>Run this code — dropdown.aspStringBuilder class which takes advantage of the available .net String Builder (System.IO.StringWriter). Yes, we are gettnig help from .net. Thats great! So whenever you are working with a lot of data consider using the StringBuilder (e.g. Datatable makes heavy use of it already as it usually deals with large sets of data):<!--#include virtual="/ajaxed/ajaxed.asp"-->
<%
set page = new AjaxedPage
page.draw()
sub main()
'create new instance
set o = new StringBuilder
for i = 0 to 10000
'append some value
o("Hello world")
next
'output the concatenated string
str.write(o.toString())
end sub
%>Run this code — stringbuilder.aspAjaxedPage provides you a property called headerFooter. This makes it possible to define a page header and footer explictly. Programatically. This could be a "master page" for all your page now:
<!--#include virtual="/ajaxed/ajaxed.asp"-->
<%
with new AjaxedPage
.headerFooter = array("pageHeader", "pageFooter")
.draw()
end with
%>
<% sub pageHeader() %>
<html><body>
<% end sub %>
<% sub pageFooter() %>
</html></body>
<% end sub %>
TestFixture is the ability to check the response of any page of your application. You can quickly write tests to check if all pages are working correctly (at least are accessible and do not throw an error). Write a test like this:
<!--#include virtual="/ajaxed/class_testFixture/testFixture.asp"-->
<%
set tf = new TestFixture
tf.run
sub test_1
tf.assertResponse "/somePage.asp", empty, "<h3>.*?</h3>", "Page seem not to work"
end sub
%>
rReplace() replaces all occurrences according to a regex pattern wheras matching() only checks if a string matches it. The idea for the latter comes from Ruby on Rails =~ operator ;)
<%
'checks if numbers exist in a given string
isNumber = str.matching("2", "^\d$", true)
'removes all numbers from a string
noNumbers = str.rReplace("abcd2efg4", "\d", "", true)
'Note: the last parameter has to do with
'case sensivity. check the API for it.
%>
<%
'get values from forms auto-trimmed
name = page.RFT("name")
'get from querystring, form and parse immediately
'before
id = str.parse(page.RF("id"), 0)
id = str.parse(page.QS("id"), 0)
'now
id = page.RFP("id", 0)
id = page.QSP("id", 0)
'combine str.write and str.format
'before
str.write str.format("say {0}", "hello")
'now
str.writef "say {0}", "hello"
'str.format can be used without array too (if only one arg)
'before
str.format "say {0}", array(10)
'now
str.format "say {0}", 10
'StringOperations default function HTML encodes string
'dont forget to encode all your user input
'to prevent attacks such as XSS
str.write(str("<please encode me>"))
%>
If you are starting to use ajaxed, please refer to the beginners tutorials first. This article requires advanced ajaxed and JavaScript knowledge.
ajaxed.callback has to be used if you want to use ajaxed Page Parts or want to call server side functions. It's the communication node between client side and server side.
ajaxed.callback(action, onReturn, [params, onCompleted, url])
pagepart_yourActionName then the procedure is being called directly. Otherwise callback is executed and the action is passed as the argument.request.form collection on server side.callback procedure unless it finds a procedure with the same name and prefixed with pagepart_. In this case it calls the procedure directly and returns its response back to the client
<%
sub callback(a)
if a = "one" then
'NEVER REACHED! because there is a
'sub called pagePart_one
page.return "foo"
elseif a = "two" then
'will be reached
page.return "foo"
end if
end sub
sub pagePart_one
str.write("<strong>some html response</strong>")
end sub
%>
<script>
ajaxed.callback("one", function(r) {
//do something
});
ajaxed.callback("two", function(r) {
//do something
});
</script>
onReturn parameter will be invoked once the callback has been finished successfully. It's NEVER called if something fails as opposed to the onComplete function.
AjaxedPage.return() or AjaxedPage.returnValue(). In case of a page part call it contains a string which holds the XHTML produced by the page part.
<%
sub callback(a)
if a = "actionOne" then
page.return "wow!"
end if
end sub
sub pagePart_partOne %>
<strong>hi</strong>
<% end sub %>
<script>
//you can create functions and reuse them
function myHandler(r) {
alert(r);
}
//will alert "<strong>hi</strong>"
ajaxed.callback('partOne', myHandler);
//will alert "wow!"
ajaxed.callback('actionOne', myHandler);
//you can write the function inline as well
//will alert "wow!"
ajaxed.callback('actionOne', function(r) {
alert(r)
});
</script>
<% sub pagePart_partOne %>
<strong>hi</strong>
<% end sub %>
<script>
ajaxed.callback('partOne', 'destination');
//same, but not rapid :)
ajaxed.callback('partOne', function(r) {
$('destination').update(r)
});
</script>
<div id="destination"></div>
frm. If there is one it grabs all its form fields and passes those values as parameters to your action. If ther is no form called frm then no parameters are being sent unless you specify them manually. You can always provide the parameters by yourself regardless of an existing form or not. Those parameters must be provided as a Hash then (name value pairs in JSON notation). All parameters are available via the request.form collection on the server side afterwards.
<script>
//#1
//will automatically pass the param1 value
//to the server side (because the page contains
//a form with the ID 'frm'
ajaxed.callback('check', function(r) {
//do something
});
//#2
//provide parameters manually
ajaxed.callback('check', function(r) {
//do something
}, {someParam1: "foo", anotherParam: "foo"});
//#3
//take parameter from form and add an own (1st)
ajaxed.callback('check', function(r) {
//do something
}, {pName: $F("pID"), anotherParam: "foo"});
//#4
//take parameter from form and add an own
//automatically grab all fields from the form
ajaxed.callback('check', function(r) {
//do something
}, $("frm").serialize(true).set(anotherParam, "foo"));
</script>
<form id="frm">
<input type="text" id="pID" name="pName" value="foo"/>
</form>
<%
'our server side handler
sub callback(a)
if a <> "check" then exit sub
'will contain "foo" when calling with #1, #2, #4
'will contain no value when caling with #3
pName = page.RF("pname")
'will contain value only when calling with #3
someParam1 = page.RF("someParam1")
'will always contain "foo" unless on #1 call
anotherParam = page.RF("anotherParam")
end sub
%>
All this applies to page parts and callbacks.
Remember: The fields NAME is being used as the parameter name.
Not the fields ID (see in example #3)!
<!--#include virtual="/ajaxed/ajaxed.asp"-->
<%
set page = new AjaxedPage
page.draw()
sub main() %>
<div id="output"><% pagePart_one %></div>
<form id="frm" onsubmit="ajaxed.callback('one', 'output'); return false">
<input type="text" name="value">
<input type="submit" value="send">
</form>
<% end sub %>
<% sub pagePart_one %>
You typed: <%= page.RF("value") %>
<% end sub %>Run this code — pagepart_param.asponCompleted parameter can hold a reference to a JavaScript function which is invoked after the callback has completed (its being passed the XMLHttpRequest object). Very useful to do clean up work. Check the following example. It demonstrated how to disable a button on callback and enable it back again when it finished.
<%
sub callback(a)
if a = "myAction" then page.return "something"
end sub
%>
<script>
function doFoo(sender) {
//first we disable our element which invokes
//the function. So he cannot call it again as
//as its already running
Field.disable(sender);
ajaxed.callback('myAction', function(r) {
//do something
}, null, function(t) {
//when the callback completes we enable it back
Field.enable(sender)
})
}
</script>
<button type="button" onclick="doFoo()">Do</button>
You have to place the enable code into the onComplete as it would never enable the button when the action failed.
Important: You need to specify the URL parameter when using ajaxed.callback on a default page of a folder. This is due to a bug within IIS. It does not return the default page when being called (with POST) without the filename.
E.g. Lets say /pages/default.asp is the default page of the /pages/ folder. In that case the ajaxed.callback wont work with only on /pages/
You need to specifydefault.aspas the url here. Don't forget the querystring if required.
<%
sub callback(a)
'gets the total sales for a given year
if a = "getTotalSales" then
'calculate the total sales somehow
page.return getTotals(page.RFP("yr", 0))
end if
end sub
%>
<script>
ajaxed.callback('getTotalSales', function(r) {
$('sales').update(r);
}, {yr: <%= year(now) %>>);
</script>
Sales this year:
<div id="sales"></div>
) added, because docs looked a little messed up sometimes.
<!--#include virtual="/ajaxed/ajaxed.asp"-->
<!--#include virtual="/ajaxed/class_email/email.asp"-->
<%
set page = new AjaxedPage
page.draw()
sub main()
with new Email
.addRecipient "to", "[email protected]", "Some recipient"
.sendersName = "ajaxed.org"
.sendersEmail = "[email protected]"
.mailserver = "smtp.yourserver.com"
.subject = "Hello world!"
.body = "ajaxed says hello"
.send()
end with
end sub
%>
Email class with the include statement first. After that we are able to use the class. The property names and methods say the rest. But thats a bit lot of code if we want to send emails rapidly within our app. For that reason we can override all the common settings of Email in our config.asp (afterwards they are used for every new email instance):
<%
AJAXED_EMAIL_SENDER = "[email protected]"
AJAXED_EMAIL_SENDER_NAME = "ajaxed.org"
AJAXED_MAILSERVER = "smtp.yourserver.com"
AJAXED_EMAIL_DISPATCH = false
%>
AJAXED_EMAIL_DISPATCH. This setting prevents the email from actually being dispatched. The whole process of email sending is simulated but in the end the email is not sent. Thats useful during the development process. The emails seem to be sent, but acutally they are not :) You can see them in your log though. Anyway, now we can send an email with only that code:
<!--#include virtual="/ajaxed/ajaxed.asp"-->
<!--#include virtual="/ajaxed/class_email/email.asp"-->
<%
set page = new AjaxedPage
page.draw()
sub main()
with new Email
.addRecipient "to", "[email protected]", "Some recipient"
.subject = "Hello world!"
.body = "ajaxed says hello"
if not .send() then str.write("Could not send: " & .errorMsg)
end with
end sub
%>
send() method returns true if mailing was successful. Thus in this case we demonstrated even some error handling ;) AJAXED_EMAIL_DISPATCH to falseAJAXED_EMAIL_ALLTO config variable. This allows you to receive all emails sent within your system./ajaxedLogs/ folder) to see details about the sending process.
<%
response.write("something")
response.end
%>
config.asp. Set the AJAXED_LOGLEVEL to 1 to log all kinds of messages.Important: The logger logs by default into theNow browse any of your application pages. After that check the/ajaxedLogs/directory. This folder requires write permission for the IUSR.
/ajaxedLogs/dev.log file (in case of live environment the file is named live.log). It should contain log messages about your previous page requests. It logs details about page requests, sent form parameters, querystring details, ajax callback, used SQL queries, user information, etc. This information is brought by the library itself, but how to log messages by yourself?
<!--#include virtual="/ajaxed/ajaxed.asp"-->
<%
set page = new AjaxedPage
page.draw()
sub main()
lib.logger.info "page called"
'we write the querystring into the logger as a debug message
lib.logger.debug page.QS(empty)
end sub
%>
Logger instance lib.logger. It can be used everywhere within your code. If the logger is disabled you don't need to remove those calls. The logger just does not log the messages ;) If you call this page you should see those messages in your logfile now.AJAXED_LOG_COLORIZE to false or (the recommended choice) you download (win users) a shell which supports colorization. We recommend Cygwin.tail -f dev.log
tail -f command. This allows you to see changes of the log immeditely. You productivity will rise dramatically!str.writeend to write out something to the response. The response stops afterwards. However, we recommend using lib.logger.debug instead. Especially if you work with AJAX callback the writeend approach won't work.]]>DEV (default) and LIVE. You can set the environment in your config.asp using the AJAXED_ENVIRONMENT variable:
'live environment
AJAXED_ENVIRONMENT = "LIVE"
Library class provides some properties for the environment setting:
<%
'gets the current environment
currentEnvironment = lib.env
'checks if it is development
isDevelopment = lib.dev
'checks if it is live (production)
isLive = lib.live
%>
footer.asp.
<% if lib.live then %>
<script>
//google analytics javascript
</script>
<% end if %>
<!--#include virtual="/ajaxed/ajaxed.asp"-->
<%
set page = new AjaxedPage
page.draw()
sub main() %>
<button type="button" onclick="ajaxed.callback('one', 'content')">content 1</button>
<button type="button" onclick="ajaxed.callback('two', 'content')">content 2</button>
<div id="content"></div>
<% end sub %>
<% sub pagepart_one() %>
the first content
<% end sub %>
<% sub pagepart_two() %>
some other content from server side
<% end sub %>Run this code — pageparts.aspajaxed.callback() JavaScript function. This is the main (and currently only one) AJAX function within ajaxed. Please remember it, its the most important function for the use of AJAX.click event with the ajaxed.callback() function. The first argument holds the name of the page part (one and two) and the second refers to the target element (which will receive the content). On the server side we created two procedures. One for each page part: pagepart_one and pagepart_two. Thats all! As you see the only thing you need to do on server side is to prefix your page part procedure with pagepart_.<!--#include virtual="/ajaxed/ajaxed.asp"-->
<%
set page = new AjaxedPage
page.draw()
sub callback(action)
if action = "returnChar" then
page.return "A"
elseif action = "returnFloat" then
page.return 2.5
end if
end sub
sub main() %>
<script>
function returned(result) {
$('result').update(result);
}
</script>
<button type="button"
onclick="ajaxed.callback('returnChar', returned)">
return A
</button>
<button type="button"
onclick="ajaxed.callback('returnFloat', returned)">
return 2.5
</button>
<div id="result"></div>
<% end sub %>Run this code — callback.aspajaxed.callback() function is our go-to-guy. We used it again :) This time the first parameter tells the server which function (also called action) needs to be executed. The second parameter holds a JavaScript callback function which receives the return values from the server side (in this case we named it returned). Whatever you return on server side this function will get it. callback() procedure on server side to handle our client requests. This procedure requires one parameter which holds the actual action. You can see it in the example: sub callback(action). Within the callback procedure we use page.return() to return the value to the client side. This method can return (almost) everything which is available within VBScript. E.g. You can even return an array: page.return array(1, 2, 3) and it can be used as an array within your JavaScript. For more details about the different datatypes please read the documentation of AjaxedPage.return() method or a more technically detailed version at Generate JSON from asp datatypes.AjaxedPage. With that knowledge we can code our first page:<!--#include virtual="/ajaxed/ajaxed.asp"-->
<%
set page = new AjaxedPage
page.draw()
sub main()
str.write("Hello world!")
end sub
%>Run this code — hello-world.aspAjaxedPage which is drawn afterwards - draw() method.main. Our entry point contains only one line which prints out Hello world!. str.write() is the ajaxed version of response.write. Get used to it :) The thing to remember here is that the output of your main procedure will be surrounded with the page header and page footer.<!--#include virtual="/ajaxed/ajaxed.asp"-->
<%
set page = new AjaxedPage
page.title = "A page title"
page.draw()
sub main()
content()
end sub
sub content %>
The page is located here:<br/>
<%= page.getLocation("virtual", true) %>
<br/><br/>
The environment is: <%= lib.env %>
<br/><br/>
ajaxed version: <%= lib.version %>
<% end sub %>Run this code — page.aspcontent procedure within the main. The content is our view and the rest (main and the other procedures) is the page controller. The view consists mostly of markup thats why it is not surrounded with propellers whereas the rest of our code has propellers. Brings a bit of MVC feeling :)]]>config.asp and configure your connection string. The constant called AJAXED_CONNSTRING must be set to a valid connection string. Some are already suggested and can be used directly - just uncomment.Don't forget to change the password, user and database within the predefined connection strings (for more connection strings check http://www.connectionstrings.com/). If your database is running on a different machine than the webserver then you need to change the server adress as well.
Database class. ajaxed offers you an ready-to-use instance of that class called db. Before using it you need to either open the connection manually using the db.openDefault() method or let it do automatically by your pages. You can set the DBConnection property of your page to true. This opens and closes the database connection automatically.
<!--#include virtual="/ajaxed/ajaxed.asp"-->
<%
set page = new AjaxedPage
page.DBConnection = true
page.draw()
sub main()
'getting a read-only recordset
set RS = db.getRS("SELECT * FROM table", empty)
'getting an updateable recordset
set RS = db.getUnlockedRS("SELECT * FROM table", empty)
'using parameters
set RS = db.getRS("SELECT * FROM table WHERE id = {0}", 10)
'getting a single value
age = db.getScalar("SELECT MAX(age) FROM users", 0)
end sub
%>
db.open() method with a connection string of your desired database. After that, all methods of the Database class are executed against the "new" connected database. The database connection is closed automatically at the end of page processing. Database and have both Databases opened in seperate variables.config.asp:header.asp:footer.asp:
AJAXED_LOADINGTEXT = "Hold on. I am working..."
html, body and it may contain custom markup which is needed by your application.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title><%= title %></title>
<% ajaxedHeader(array()) %>
</head>
<body>
title variable and the second is the ajaxedHeader(array()) call. The first one is easy :) It holds the page title. The second is a request to load the necessary libraries for ajaxed to work (javascript libraries). If you need to know the details please refer to the API. For now remember: This call is a MUST!
<div><%= lib.env %></div>
</body>
</html>
ajaxed is fully flexible on structuring your pages. You can create different headers for different pages. You can even move the location of those files. Refer to the API for more details.]]>
/ajaxed/
/ajaxedConfig/
http://yourhost.com/ajaxed/demo/ and try the demo. If it works you are ready for development.http://yourhost.com/ajaxed/console/ which will show you an own ajaxed console. Just in case you need something to play with :)In case of upgrading to a newer version you should only override the]]>ajaxedfolder. Otherwise your config gets lost.
/ajaxed/console/ on your server. You'll find there a lot of useful stuff like a test runner, current configuration, documentation creator, regular expression evaluator, etc. Check a demo of the console here.config.asp) has been moved to an own folder which is located in the webroot /ajaxedConfig/config.asp. Seperating the configuration from the library makes the update process easier now. You can overwrite the ajaxed library without any thoughts...header.asp and footer.asp in your ajaxedConfig folder. Those files are automatically loaded within your pages. This change brought some additional features as wellajaxedPage.defaultStructure property lets you load your page with a default header and footer (provided by the library itself). This is useful if you quickly need a standard structure and don't really care about the detailsajaxedPage.title gives you the possibility to assign a title to your page which may be used in the headerajaxedPage.plain property allows to render a page completely plain which means it renders only those things which are defined in the page itself. (no header and footer). You will need this for your XHRlib.exec lets you call any function only if it exists. This is used within the header to allow page specific head elements. Whenever you want anything to appear in your header just add a header() sub to your page and place the stuff there. e.g. especially javascripts, styles
<% sub header() %>
<style>
/* some styles */
</style>
<% page.loadJSFile "someJSFile.js" %>
<% end sub %>
header.asp in your config to see whats going on there. And don't forget it's up to you what you bung in there. You can add as many lib.exec calls as you want.LIVE (production) and DEV (development). The config variable AJAXED_ENVIRONMENT sets the current environment. This enhancement allows us ...lib.env, lib.DEV and lib.LIVE are helpers to check which environment is running.ajaxedPage.onlyDev to true in order to prevent calling it on the LIVE environment. E.g. whole ajaxed console and tests are only available on DEV env yet.TestFixture class lets you create unit tests for your own applications. It comes with a lot of different asserts (as commonly known in other unit testing frameworks) and is very quickly to set up. After you have written your tests you can run them directly from the ajaxed console. Tests for the library itself are located there as well. Here is an example of how you write a simple test (test file must start with test_):
<!--#include virtual="/ajaxed/class_testFixture/testFixture.asp"-->
<%
set tf = new TestFixture
tf.run()
'create test methods starting with test_
'followed by an increasing number
sub test_1()
tf.assertEqual "x", "x", "x should be x"
tf.assert 1 = 1, "one should be definitely one"
tf.assertInstanceOf "testFixture", tf, "this test should be a TestFixture"
tf.assertInDelta 1, 1.1, 0.1, "equality with a delta"
tf.assertHas array(1, 2, 3), 2, "the array should contain 2"
end sub
%>
Documentor and can be found within your ajaxed console. It is also used for the generation of the actual ajaxed documentation which got a small face lift as well. Read How to document to know more about documenting your code (This manual can be found in your ajaxed console). Example of a method documentation:
<%
'' @DESCRIPTION: gets all users for a given country
'' @PARAM: country [string]: specified country. e.g. AT
'' @RETURN: [recordset]
function getUsersBy(country)
end function
%>
<% hash = (new MD5).hash("mypassword") %>
<%
'get the number of records of a table
numberOfRecords = db.count("tablename", empty)
'get number of records with condition
numberOfRecords = db.count("tablename", "active = 1")
'insert a record into a table (returns the ID)
ID = db.insert("tablename", array("firstname", "jack", "lastname", "johnson"))
'updating a record
db.update "tablename", array("firstname", "Johnny"), ID
'works as well with condition
db.update "tablename", array("firstname", "Johnny"), "firstname = 'Jack'"
'delete a record with a condition
db.delete "tablename", "active = 0"
'delete with an ID
db.delete "tablename", 10
'toggle the value of bit fields.
db.toggle "tablename", "active", "country = 'AT'"
'or with ID
db.toggle "tablename", "active", 10
%>
WHERE clause).db.getRecordset().
<%
'example of reading some feed
set r = new RSS
r.url = "http://domain.com/feed"
r.load()
%>
TextTemplate. Very useful if you send emails within your applications. Create an template for the content of your email and parse it using TextTemplate. The ajaxed console provides a simple management of templates. For more details about TextTemplate read the article Template component for classic ASP or check the ajaxed documentation. Simple example
<%
set t = new TextTemplate
t.filename = "mytemplate.template"
t.add "name", "jack johnson"
'your email object
'i use the first line of the template as the subject of my emails
mail.subject = t.getFirstLine()
mail.body = t.getAllButFirstLine()
%>
Cache class. This class has been around for while as well and a details article is available here.init() must not exist necessarily anymore. If it exists it will be called. if not then not ;) Thanks to the new lib.exec() and lib.getFunction(). Check the reference for more details.str.matching checks a string against a given regular expression pattern. Is much more readable then creating the regex object over and over again.main() in your page. Makes it easier to know whats going on...lib.contains is a useful function which lets us check if a given value exists in a given data structure. Currently supported data structures are array and dictionary. Usage is easy: lib.contains(array("yes", "no"), "maybe")changes.txt or directly in the SVNconfig.asp into the new /ajaxedConfig/ folder. Last but not least create a header.asp and footer.asp in that folder. Put all your html, head and body tags there. You can use the existing ones and adopt them to your needs. Last but not least change AJAXED_ENVIRONMENT to your needed environment.