Table of Contents:
[OpenBSD 4.0 Stable]
Postfixadmin is a web front end for managing a MySQL database, that is normally configured for use by your postfix installation.
This guide was tested on OpenBSD 4.0 release with postfixadmin 2.1.0 postfix 2.3.2 and dovecot 1.0rc15 and mysql 5.024a. Check with the release you are using whether you have the minimal software requirements before continuing.
If you have not already done so, please install and verify your mysql configuration with php support.
You can download postfixadmin from http://high5.net or grab the current source using svn, instructions at http://high5.net. If you can follow through the INSTALL.TXT file, go ahead and install from there. Otherwise, read that file first before continuing further.
For our purposes, we will assume that you have already unarchived the postfixadmin files available in the /var/www/htdocs/postfixadmin directory.
The simplest way to configure your mysql server is to use the DATABASE_MYSQL.TXT that holds the database schema as well as sample data for our postfixadmin.
We actually want to make some modifications to the file, as customisations for our own installation.
Obviously, the current release of postfixadmin is most likely to have the latest configuration of the database. For MySQL, the current schema is in the file DATABASE_MYSQL.TXT.
There are two basic changes I want to make for how I have installed postfixadmin:
You may not want to make the changes as I have made them, but may use this as the foundation for ensuring your set up is customised for your needs.
In our installation, I have decided to use different user account name for the postfix server, and a different database name. The standard release uses the database 'postfix' and a user account 'postfix' which I found confusing, so I've renamed the postfix user account to be 'postfixserver' and the database name will be 'mail.'
What we want to do is use the same installation files as postfixadmin with the following modifications.
The major changes we will want to make is shown in the following file fragment that begins at around line 25.
File Fragment: ./DATABASE_MYSQL.TXT
| 25 | # |
| 26 | # Postfix / MySQL |
| 27 | # |
| 28 | USE mysql; |
| 29 | # Postfix user & password |
| 30 | INSERT INTO user (Host, User, Password) VALUES ('localhost','postfix',password('postfix')); |
| 31 | INSERT INTO db (Host, Db, User, Select_priv) VALUES ('localhost','postfix','postfix','Y'); |
| 32 | # Postfix Admin user & password |
| 33 | INSERT INTO user (Host, User, Password) VALUES ('localhost','postfixadmin',password('postfixadmin')); |
| 34 | INSERT INTO db (Host, Db, User, Select_priv, Insert_priv, Update_priv, Delete_priv) VALUES ('localhost','postfix', 'postfixadmin', 'Y', 'Y', 'Y', 'Y'); |
| 35 | FLUSH PRIVILEGES; |
| 36 | GRANT USAGE ON postfix.* TO postfix@localhost; |
| 37 | GRANT SELECT, INSERT, DELETE, UPDATE ON postfix.* TO postfix@localhost; |
| 38 | GRANT USAGE ON postfix.* TO postfixadmin@localhost; |
| 39 | GRANT SELECT, INSERT, DELETE, UPDATE ON postfix.* TO postfixadmin@localhost; |
| 40 | CREATE DATABASE postfix; |
| 41 | USE postfix; |
After we make changes to the above file, we should have something like the below, with the changes highlighted in blue
Updated File Fragment: ./DATABASE_MYSQL.TXT
| 25 | # |
| 26 | # Postfix / MySQL |
| 27 | # |
| 28 | USE mysql; |
| 29 | # Postfix user & password |
| 30 | INSERT INTO user (Host, User, Password) VALUES ('localhost','postfixserver',password('postfixserver')); |
| 31 | INSERT INTO db (Host, Db, User, Select_priv) VALUES ('localhost','mail','postfixserver','Y'); |
| 32 | # Postfix Admin user & password |
| 33 | INSERT INTO user (Host, User, Password) VALUES ('localhost','postfixadmin',password('postfixadmin')); |
| 34 | INSERT INTO db (Host, Db, User, Select_priv, Insert_priv, Update_priv, Delete_priv) VALUES ('localhost','mail', 'postfixadmin', 'Y', 'Y', 'Y', 'Y'); |
| 35 | FLUSH PRIVILEGES; |
| 36 | GRANT USAGE ON mail.* TO postfixserver@localhost; |
| 37 | GRANT SELECT, INSERT, DELETE, UPDATE ON mail.* TO postfixserver@localhost; |
| 38 | GRANT USAGE ON mail.* TO postfixadmin@localhost; |
| 39 | GRANT SELECT, INSERT, DELETE, UPDATE ON mail.* TO postfixadmin@localhost; |
| 40 | CREATE DATABASE mail; |
| 41 | USE mail; |
Details of the changes we want to make are as follows:
The default superadmin user account can be somewhat confusing as you are literally requested to enter<EMAIL_ADDRESS>as the user account. This aesthetic change is to use 'admin' and allow setting the default password.
File Fragment: ./DATABASE_MYSQL.TXT
| 145 | # superadmin user & password (login<EMAIL_ADDRESS>password: admin) |
| 146 | INSERT INTO domain_admins (username, domain, active) VALUES ('admin@domain.tld','ALL','1'); |
| 147 | INSERT INTO admin (username, password, active) VALUES ('admin@domain.tld','$1$0fec9189$bgI6ncWrldPOsXnkUBIjl1','1'); |
We will change the default to something like the below
Updated File Fragment: ./DATABASE_MYSQL.TXT
| 145 | # superadmin user & password (login<EMAIL_ADDRESS>password: admin) |
| 146 | INSERT INTO domain_admins (username, domain, active) VALUES ('admin','ALL','1'); |
| 147 | INSERT INTO admin (username, password, active) VALUES ('admin','6dwLx9NTxhTjU','1'); |
Details of the changes we want to make are as follows:
Now that we've completed the modifications to our database instructions, we can create our database with the following instructions.
# mysql -u root -p < ./DATABASE_MYSQL.TXT
You should not get any error messages, and if you do you may need to check the file changes above for any syntax errors.
Otherwise, copy the DATABASE_MYSQL.TXT shown later in this documentation, and recreate the database after executing the below mysql commands to clear out your previous installation.
Note: You may inadvertently destroy something on your server, so only perform these on a test server unless you know what you are doing.
To remove the previous, or any part of the previous database instructions that may have been added to the database server:
Screen Session
$ mysql -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 25 to server version: 5.0.24a-log
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
mysql> drop database mail;
Query OK, 7 rows affected (0.05 sec)
mysql> drop database postfix;
ERROR 1008 (HY000): Can't drop database 'postfix'; database doesn't exist
mysql> use mysql;
Database changed
mysql> delete from user where user='postfix';
Query OK, 0 rows affected (0.00 sec)
mysql> delete from user where user='postfixadmin';
Query OK, 1 row affected (0.00 sec)
mysql> delete from user where user='postfixserver';
Query OK, 1 row affected (0.00 sec)
mysql> delete from db where user='postfix';
Query OK, 0 rows affected (0.01 sec)
mysql> delete from db where user='postfixserver';
Query OK, 1 row affected (0.00 sec)
mysql> delete from db where user='postfixadmin';
Query OK, 1 row affected (0.00 sec)
mysql> flush privileges;
Remember that if you've changed the user names, then you need to remember to use those changes in the above instructions.
Your database should now be appropriately created for use with postfix and postfixadmin.
There are two main changes you need to make with the files that are distributed with postfixadmin.
For security reasons, we will set the permissions for files in the postfixadmin directory. We can use a combination of the 'find' and 'chmod' programs to quickly achieve this
# cd /var/www/htdocs/postfixadmin
# find . -type f -exec chmod 640 "{}" ";"
If you observe some file permission problems after the above settings, then you may need to take a look at whether you also need to change the ownership of the files, as in the example below.
# find . -exec chown www:www "{}" ";"
postfixadmin's runtime behaviour is effected by initialisation settings in the config.inc.php file. Mostly, the changes are relevant to the users and passwords you've set in the above Database creation exercise, as well as issues relevant to your installation.
Things that I changed include:
File Fragment: /var/www/htdocs/postfixadmin/config.inc.php
$CONF['postfix_admin_url'] = 'http://PRIVATE_IP/postfixadmin/';
$CONF['postfix_admin_path'] = '/admin/';
$CONF['database_host'] = '<IP_ADDRESS>';
$CONF['database_name'] = 'mail';
$CONF['admin_email'] =<EMAIL_ADDRESS>$CONF['smtp_server'] = '<IP_ADDRESS>';
$CONF['encrypt'] = 'system';
$CONF['default_aliases'] = array (
'abuse' =><EMAIL_ADDRESS>'hostmaster' =><EMAIL_ADDRESS>'postmaster' =><EMAIL_ADDRESS>'webmaster' =><EMAIL_ADDRESS>);
$CONF['domain_path'] = 'YES';
$CONF['vacation_domain'] = 'autoreply.example.org';
$CONF['footer_text'] = 'Return to MYHOST';
$CONF['footer_link'] = 'http://PRIVATE_IP';
Documentation for configuring Postfix for use with this MySQL database, is described at this link.
Your postfixadmin installation should be installed and ready.
To start, open your browser to the url you specified in your configuration file, like:
$ lynx http://PRIVATE_IP/postfixadmin/
You can now use user 'admin' with password 'admin' for managing your virtual domains, or if you have changed the password in the above instructions, use that password.
Following is the modified DATABASE_MYSQL.TXT discussed above.
File: ./DATABASE_MYSQL.TXT
#
# Postfix Admin
# by Mischa Peters <mischa at high5 dot net>
# Copyright (c) 2002 - 2005 High5!
# Licensed under GPL for more info check GPL-LICENSE.TXT
#
# This is the complete MySQL database structure for Postfix Admin.
# If you are installing from scratch you can use this file otherwise you
# need to use the TABLE_CHANGES.TXT or TABLE_BACKUP_MX.TXT that comes with
Postfix Admin.
# You can find these in DOCUMENTS/
#
# There are 2 entries for a database user in the file.
# One you can use for Postfix and one for Postfix Admin.
#
# If you run this file twice (2x) you will get an error on the user creation in
MySQL.
# To go around this you can either comment the lines below "USE MySQL" until
"USE postfix".
# Or you can remove the users from the database and run it again.
#
# You can create the database from the shell with:
#
# mysql -u root [-p] < DATABASE_MYSQL.TXT
#
# Postfix / MySQL
#
USE mysql;
# Postfix user & password
INSERT INTO user (Host, User, Password) VALUES ('localhost','postfixserver',password('postfixserver'));
INSERT INTO db (Host, Db, User, Select_priv) VALUES ('localhost','mail','postfixserver','Y');
# Postfix Admin user & password
INSERT INTO user (Host, User, Password) VALUES ('localhost','postfixadmin',password('postfixadmin'));
INSERT INTO db (Host, Db, User, Select_priv, Insert_priv, Update_priv,
Delete_priv) VALUES ('localhost', 'mail', 'postfixadmin', 'Y', 'Y', 'Y', 'Y');
FLUSH PRIVILEGES;
GRANT USAGE ON mail.* TO postfixserver@localhost;
GRANT SELECT, INSERT, DELETE, UPDATE ON mail.* TO postfixserver@localhost;
GRANT USAGE ON mail.* TO postfixadmin@localhost;
GRANT SELECT, INSERT, DELETE, UPDATE ON mail.* TO postfixadmin@localhost;
CREATE DATABASE mail;
USE mail;
#
# Table structure for table admin
#
CREATE TABLE `admin` (
`username` varchar(255) NOT NULL default '',
`password` varchar(255) NOT NULL default '',
`created` datetime NOT NULL default '0000-00-00 00:00:00',
`modified` datetime NOT NULL default '0000-00-00 00:00:00',
`active` tinyint(1) NOT NULL default '1',
PRIMARY KEY (`username`),
KEY username (`username`)
) TYPE=MyISAM COMMENT='Postfix Admin - Virtual Admins';
#
# Table structure for table alias
#
CREATE TABLE `alias` (
`address` varchar(255) NOT NULL default '',
`goto` text NOT NULL,
`domain` varchar(255) NOT NULL default '',
`created` datetime NOT NULL default '0000-00-00 00:00:00',
`modified` datetime NOT NULL default '0000-00-00 00:00:00',
`active` tinyint(1) NOT NULL default '1',
PRIMARY KEY (`address`),
KEY address (`address`)
) TYPE=MyISAM COMMENT='Postfix Admin - Virtual Aliases';
#
# Table structure for table domain
#
CREATE TABLE `domain` (
`domain` varchar(255) NOT NULL default '',
`description` varchar(255) NOT NULL default '',
`aliases` int(10) NOT NULL default '0',
`mailboxes` int(10) NOT NULL default '0',
`maxquota` int(10) NOT NULL default '0',
`quota` int(10) NOT NULL default '0',
`transport` varchar(255) default NULL,
`backupmx` tinyint(1) NOT NULL default '0',
`created` datetime NOT NULL default '0000-00-00 00:00:00',
`modified` datetime NOT NULL default '0000-00-00 00:00:00',
`active` tinyint(1) NOT NULL default '1',
PRIMARY KEY (`domain`),
KEY domain (`domain`)
) TYPE=MyISAM COMMENT='Postfix Admin - Virtual Domains';
#
# Table structure for table domain_admins
#
CREATE TABLE `domain_admins` (
`username` varchar(255) NOT NULL default '',
`domain` varchar(255) NOT NULL default '',
`created` datetime NOT NULL default '0000-00-00 00:00:00',
`active` tinyint(1) NOT NULL default '1',
KEY username (`username`)
) TYPE=MyISAM COMMENT='Postfix Admin - Domain Admins';
#
# Table structure for table log
#
CREATE TABLE `log` (
`timestamp` datetime NOT NULL default '0000-00-00 00:00:00',
`username` varchar(255) NOT NULL default '',
`domain` varchar(255) NOT NULL default '',
`action` varchar(255) NOT NULL default '',
`data` varchar(255) NOT NULL default '',
KEY timestamp (`timestamp`)
) TYPE=MyISAM COMMENT='Postfix Admin - Log';
#
# Table structure for table mailbox
#
CREATE TABLE `mailbox` (
`username` varchar(255) NOT NULL default '',
`password` varchar(255) NOT NULL default '',
`name` varchar(255) NOT NULL default '',
`maildir` varchar(255) NOT NULL default '',
`quota` int(10) NOT NULL default '0',
`domain` varchar(255) NOT NULL default '',
`created` datetime NOT NULL default '0000-00-00 00:00:00',
`modified` datetime NOT NULL default '0000-00-00 00:00:00',
`active` tinyint(1) NOT NULL default '1',
PRIMARY KEY (`username`),
KEY username (`username`)
) TYPE=MyISAM COMMENT='Postfix Admin - Virtual Mailboxes';
#
# Table structure for table vacation
#
CREATE TABLE `vacation` (
`email` varchar(255) NOT NULL default '',
`subject` varchar(255) NOT NULL default '',
`body` text NOT NULL default '',
`cache` text NOT NULL default '',
`domain` varchar(255) NOT NULL default '',
`created` datetime NOT NULL default '0000-00-00 00:00:00',
`active` tinyint(1) NOT NULL default '1',
PRIMARY KEY (`email`),
KEY email (`email`)
) TYPE=MyISAM COMMENT='Postfix Admin - Virtual Vacation';
# superadmin user & password (login<EMAIL_ADDRESS>password: admin)
INSERT INTO domain_admins (username, domain, active) VALUES ('admin','ALL','1');
INSERT INTO admin (username, password, active) VALUES
('admin','6dwLx9NTxhTjU','1');
High5.net http://www.high5.net
Copyright (c) 2007 Samiuela LV Taufa. All Rights Reserved.
I reserve the right to be totally incorrect even at the best advice of betters.
You are permitted and encouraged to use this guide for fun or for profit as you see fit. If you republish this work in what-ever form, it would be nice (though not enforceable) to be credited.
|
postfixadmin - a web gui for administering Postfix/MySQL |
Copyright © 2007 NoMoa.COM All rights reserved.


View in browser
Email Title
Content goes here. Resize your browser to see this email template get responsive!


MetaStore
Version: ${project.version}
Date: ${datestamp}
This is the core module for using metastore via REST.
It provides the basic functionality of MASi which is usable for
all communities without any changes. For special/additional functionality
extensions may be available. (e.g.: Fuzzy search/Facets, provenance,...)
Link to REST documentation
Documentation of Metastore REST API


| Analyze a commit position range (?): | # Iterations: |
| Suspect Type | Suspect Info | Feedback | ||
|---|---|---|---|---|
| Culprit | {{culprit.commit_position or culprit.git_hash}} | {% else %}Suspected Build | {{ suspected_flake.build_number }} | {% endif %}Correct Incorrect |
| When | Who | Suspect Type | Suspect Info | Result | Analysis Version Number | ||
|---|---|---|---|---|---|---|---|
| {{ triage_record.triaged_time }} | {{ triage_record.user_name }} | {% if triage_record.suspect_info.culprit_revision %}Culprit | {{triage_record.suspect_info.culprit_commit_position or triage_record.suspect_info.culprit_revision}} | {% else %}Suspected Build | {{ triage_record.suspect_info.build_number }} | {% endif %}{{ triage_record.triage_result }} | {{ triage_record.version_number }} |


{% extends "account/base.html" %} {% block nav_style %} {% endblock nav_style %} {% block content %} Your password has been reset You can give it a try by signing into your account. {% endblock %}
You can give it a try by signing into your account.


public interface Cataloger extends Remote
ServiceCataloger
.| Modifier and Type | Method and Description |
|---|---|
Boolean |
deleteContext(String providerName,
String serviceType,
String methodName)
Delete the dataContext from the provider specified for the interface and
method
|
Context |
exertService(String providerName,
Class serviceType,
String methodName,
Context theContext)
Create an exertion on the provider specified, sending the dataContext to the
method specified.
|
Context |
getContext(String providerName,
String serviceType,
String methodName)
Get the dataContext from the provider specified for the interface and method
requested
|
Context |
getContexts(Class provider,
String method)
Returns the template dataContext with which the provider is registered.
|
String[] |
getGroups()
Get the groups Cataloger is registered in.
|
String[] |
getInterfaceList(String serviceType)
Returns a String array of the interfaces defined by the given provider
name.
|
LookupLocator[] |
getLL()
Gets the LookupLocator from Cataloger
|
String[] |
getMethodsList(String providerName,
String serviceType)
Returns the methods defined on the interface of the provider specified.
|
String[] |
getProviderList()
Returns a String array of the providers currently on the network.
|
HashMap |
getProviderMethods()
Returns a hash map with a key as service interface (those interfaces
which are placed under package sorcer.provider or sorcer.core.provider)
and its value is a list of interface's method names.
|
String[] |
getSavedContextList(String providerName,
String serviceType)
Get a list of the contexts currently stored on the provider.
|
String |
getServiceInfo()
Returns service information about this provider.
|
ServiceTemplate |
getTemplate()
Get a template for a Service, this is used create a local lookup service
listener
|
Provider |
lookup(Class... primaryInterfaces)
Returns a SORCER service provider identified by its primary service type.
|
Provider |
lookup(ServiceID sid)
Returns a SORCER service provider identified by its service ID.
|
Object |
lookup(ServiceTemplate tmpl)
Returns the service Provider from an item matching the template, or null
if there is no match.
|
ServiceMatches |
lookup(ServiceTemplate tmpl,
int maxMatches)
Returns at most maxMatches items matching the template, plus the total
number of items that match the template.
|
Provider |
lookup(String providerName,
Class... primaryInterfaces)
* Returns a SORCER service provider identified by its primary service
type and the provider's name/
|
ServiceItem |
lookupItem(String providerName,
Class... serviceTypes)
* Returns a JINI ServiceItem containing SORCER service provider
identified by its primary service type and the provider's name/
|
Boolean |
saveContext(String providerName,
String serviceType,
String methodName,
Context theContext)
Saves the dataContext to the provider specified for the interface and method
|
Provider lookup(Class... primaryInterfaces) throws RemoteException
primaryInterfaces
- - the interface of a SORCER providerRemoteException
Provider lookup(String providerName, Class... primaryInterfaces) throws RemoteException
providerName
- - a provider name, a friendly provider's ID.primaryInterfaces
- - interface of a SORCER providerRemoteException
ServiceItem lookupItem(String providerName, Class... serviceTypes) throws RemoteException
providerName
- - a provider name, a friendy provider's ID.primaryInterface
- - the interface of a SORCER providerRemoteException
Provider lookup(ServiceID sid) throws RemoteException
sid
- - provider's IDRemoteException
Object lookup(ServiceTemplate tmpl) throws RemoteException
tmpl
- - template to matchRemoteException
ServiceMatches lookup(ServiceTemplate tmpl, int maxMatches) throws RemoteException
tmpl
- - template to matchmaxMatches
- RemoteException
HashMap getProviderMethods() throws RemoteException
RemoteException
String[] getProviderList() throws RemoteException
RemoteException
String[] getInterfaceList(String serviceType) throws RemoteException
serviceType
- Service type of the currently selected providerRemoteException
String[] getMethodsList(String providerName, String serviceType) throws RemoteException
providerName
- String of the currently selected providerserviceType
- the currently selected interfaceRemoteException
LookupLocator[] getLL() throws RemoteException
RemoteException
String[] getGroups() throws RemoteException
RemoteException
ServiceTemplate getTemplate() throws RemoteException
RemoteException
Context getContext(String providerName, String serviceType, String methodName) throws RemoteException
providerName
- String of the currently selected providerserviceType
- the currently selected interfacemethodName
- String of the currently selected method nameRemoteException
Boolean saveContext(String providerName, String serviceType, String methodName, Context theContext) throws RemoteException
providerName
- String of the currently selected providerserviceType
- the currently selected interfacemethodName
- String of the currently selected method nametheContext
- Context to be storedRemoteException
Boolean deleteContext(String providerName, String serviceType, String methodName) throws RemoteException
providerName
- String of the currently selected providerserviceType
- the currently selected interfacemethodName
- String of the currently selected method nameRemoteException
String[] getSavedContextList(String providerName, String serviceType) throws RemoteException
providerName
- String of the currently selected providerserviceType
- the currently selected interfaceRemoteException
Context exertService(String providerName, Class serviceType, String methodName, Context theContext) throws RemoteException
providerName
- String of the currently selected providerserviceType
- the currently selected interfacemethodName
- String of the currently selected method nameRemoteException
Context getContexts(Class provider, String method) throws RemoteException
provider
- - provider namemethod
- - a method nameRemoteException
String getServiceInfo() throws RemoteException
RemoteException
Copyright © 2013 Sorcersoft.com S.A.. All Rights Reserved.


Provides different information such as file URL, its mime type, percent complete, received bytes etc. Also it allows registering the DownloadEvent to receive download update events for this particular download item.
More...
|
| void | Pause () |
| | Pauses this download. Will have no effect when the download is already paused. More...
|
| |
| void | Resume () |
| | Resumes this download. Will have no effect when the download is not paused. More...
|
| |
| void | Cancel () |
| | Cancels this download. Will have no effect when the download is already cancelled. More...
|
| |
|
| string | URL [get] |
| | Gets URL of this download. More...
|
| |
| bool | Paused [get] |
| | Indicates whether this download is paused. Use this method in the download event to find out whether download is paused or not. Return true when this download is paused. More...
|
| |
| bool | Canceled [get] |
| | Indicates whether this download is canceled. Use this method in the download event to find out whether download is canceled or not. Return true when this download is canceled. More...
|
| |
| bool | Completed [get] |
| | Indicates whether this download is completed. Use this method in the DownloadEvent event to find out whether download is completed or not. More...
|
| |
| string | MimeType [get] |
| | Gets the MIME type of this download. More...
|
| |
| int | ID [get] |
| | Gets unique identifier of this download. More...
|
| |
| long | CurrentSpeed [get] |
| | Gets simple speed estimate in bytes/s. More...
|
| |
| int | PercentComplete [get] |
| | Gets rough percent complete. -1 means we don't know it, because we didn't receive a total size. More...
|
| |
| long | TotalBytes [get] |
| | Gets the total size of a file in bytes. More...
|
| |
| long | ReceivedBytes [get] |
| | Gets the number or received (downloaded) bytes. The received bytes must be always less that total bytes. More...
|
| |
| string | DestinationFile [get, set] |
| | Gets or sets destination file that indicates where the file will be downloaded. More...
|
| |
| DownloadInterruptReason | DownloadInterruptReason [get] |
| | Gets the possible download interrupt reason. More...
|
| |
|
| DownloadEventHandler | DownloadEvent |
| | An event that indicates that download update for this download instance. More...
|
| |
Provides different information such as file URL, its mime type, percent complete, received bytes etc. Also it allows registering the DownloadEvent to receive download update events for this particular download item.
| void DotNetBrowser.DownloadItem.Cancel |
( |
| ) |
|
Cancels this download. Will have no effect when the download is already cancelled.
| void DotNetBrowser.DownloadItem.Pause |
( |
| ) |
|
Pauses this download. Will have no effect when the download is already paused.
| void DotNetBrowser.DownloadItem.Resume |
( |
| ) |
|
Resumes this download. Will have no effect when the download is not paused.
| bool DotNetBrowser.DownloadItem.Canceled |
|
get |
Indicates whether this download is canceled. Use this method in the download event to find out whether download is canceled or not. Return true when this download is canceled.
| bool DotNetBrowser.DownloadItem.Completed |
|
get |
Indicates whether this download is completed. Use this method in the DownloadEvent event to find out whether download is completed or not.
TRUE when this download is completed.
| long DotNetBrowser.DownloadItem.CurrentSpeed |
|
get |
Gets simple speed estimate in bytes/s.
| string DotNetBrowser.DownloadItem.DestinationFile |
|
getset |
Gets or sets destination file that indicates where the file will be downloaded.
Gets the possible download interrupt reason.
| int DotNetBrowser.DownloadItem.ID |
|
get |
Gets unique identifier of this download.
| string DotNetBrowser.DownloadItem.MimeType |
|
get |
Gets the MIME type of this download.
| bool DotNetBrowser.DownloadItem.Paused |
|
get |
Indicates whether this download is paused. Use this method in the download event to find out whether download is paused or not. Return true when this download is paused.
| int DotNetBrowser.DownloadItem.PercentComplete |
|
get |
Gets rough percent complete. -1 means we don't know it, because we didn't receive a total size.
| long DotNetBrowser.DownloadItem.ReceivedBytes |
|
get |
Gets the number or received (downloaded) bytes. The received bytes must be always less that total bytes.
| long DotNetBrowser.DownloadItem.TotalBytes |
|
get |
Gets the total size of a file in bytes.
| string DotNetBrowser.DownloadItem.URL |
|
get |
Gets URL of this download.
| DownloadEventHandler DotNetBrowser.DownloadItem.DownloadEvent |
An event that indicates that download update for this download instance.


Projected SIP returns for various
time durations. [ @rate ]
Duration
SIP Amount (₹)
Future Value (₹)
SIP Calculator
SIP Calculator is a tool which is used to make a Systematic Investment Plan.
Using this tool you can calculate the amount of gain you will get if you invest a certain amount of money
periodically in a mutual fund.  Using these three inputs from the user, it generates
the amount (maturity amount) and the gain that you can expect in return from your mutual fund investments
after all the regular payments. Moreover, it also shows you the extended model of your investment
SIP stands for Systematic Investment Planning. This is an effective way of compounding money over the years.
This service is generally offered by the Mutual Fund companies. One has to invest even a small amount of
money periodically according to his/her goal of what they want to achieve. It is a passive approach to earn
compound money over the years without knowing anything about the market.
How to use SIP Calculator for better profits - Mutual fund companies take the money from the
investors and
invest their money into the market. Customer does not have to care about the market fluctuations. These
investments are generally best suited for long term investments, which helps the customer to earn maximum
Compound Interest. NEED TO WRITE MORE, write about how you can get maximum profit from sip scheme, like take
some figures like you should Invest atleast 5000 per month for 20-25 years so that you can get better
return. Discuss about what expected return rate you should consider, discuss according to present scenerio,
what the situation right now in 2020, mention 2020 whereever possible, write about inflation , how inflation
affect your SIP INVESTMENT, why you should always consider inflation while using SIP calculator, what
inflation rate you should select according to 2020.
Benefits of SIP -
Investor does not have to know very much about the market technicality.
One can invest according to their own pace and according to their own earnings.
One can skip or alter a monthly cycle anytime they need to do so.
Better returns than many other ways of saving money like Fixed Deposits, Recurring Deposits etc.
Why you should invest in SIP rather than opting for any other investment plan -
SIP is a best option to start investing for someone who want to invest in certain scheme which can give him
better return after a fixed interval of time as SIP pays off high returns in the long term with the
guarantee of substantial growth in investments and involves fewer market risks as compared to other equity
investments. There are several other factors which into play while we talk about why SIP is a better option
then any other investment. Let’s have a look at them:
Less term and condition : With SIP investment the term and condition are not that complex as
compare to
other. Here you simply select a mutual fund scheme with the fixed amount that you want to invest per
month
or per quarter, for a particular period of time. You can choose whether you want to pay manually every
month or quarter or you can automate this process by auto debit the fixed amount from your bank.
Suitable for Middle class people: The SIP investment is more popular among all classes of
people because
you can invest any amount from 500 per month to any amount you want.
Skippable Monthly Payment: You can skip the payment of any month, you want. You can pay the
amount with
the payment of next month.
Penalty on discontinuing the plan: You can stop your plan anytime you want. There are no
penalties
for discontinuing the plan.


Featured
Submit a complaint
', 'body': 'Can’t fix a problem with a financial product or service? Submit a complaint. We’ll work to get you a response from the company.
', 'links': [ { 'text': 'Submit your complaint', 'url': '/complaint/' }, { 'text': 'Browse public complaints about companies', 'url': '/complaintdatabase/' } ] } ) }}Get answers to money questions
', 'body': 'Financial matters can be complicated. Get the facts you need to make choices about money, credit scores, mortgages, and more.
', 'links': [ { 'text': 'Find answers to common questions', 'url': '/ask-cfpb/' } ] } ) }}Reach your financial goals
', 'body': 'For the big financial decisions in your life, we offer free tools and resources to help you reach your goals.
', 'links': [ { 'text': 'Paying for College', 'url': '/paying-for-college/' }, { 'text': 'Buying a House', 'url': '/owning-a-home/' }, { 'text': 'Planning for Retirement', 'url': '/retirement/before-you-claim/' } ] } ) }}Standing up for you
We hold companies accountable for illegal practices
$13.2 billion in relief
to consumers from our enforcement actions
35 million+ consumers
will receive relief because of our actions
We listen to consumers and make their voices heard
1.9 million+ complaints
we’ve handled from consumers
97% of consumers
get timely replies when we send their complaints to companies


14 November 1997
Disease Outbreak Reported
The cholera outbreak which started in August 1997 is continuing but is still limited to Maputo. The number of cases has significantly increased in the last six weeks bringing the cumulative totals to 1330 cases and 41 deaths but the case fatality rate was lower in the last two weeks at 1.8%. The Ministry of Health has increased educational activities through the mass media and house-to-house visits. Efforts are also being concentrated on improvement of case management. Health authorities in other provinces are being advised to be prepared in case of eventual spread.


--- layout: default title: Home | Power-Up Games ---
So far there is not much on this site. go to google instead!


Registration Form
First-Name :
Last-Name :
Birth-of-Date:
Gender :
Contact :
User-id:
Password:
Re-password:


Emit 'foo'
Emit 'bar'
Emit 'baz' and, due to propagation, 'baz.wrapper'.
Emit 'yak' and allow default navigation to #yak.
Emit 'yak', 'foo' and 'bar' events.
This is a checkbox.
This is a radio button.
Emit 'floog' but clicking elsewhere in this div should *not* produce an 'unhandled' event.
Test an anchor tag with a div inside it.
Test an anchor tag with a div inside it emitting a 'bloop'.


Can u Spend it all???
Mansion
£8,000,000
House
£500,000
Bugatti
£3,000,000
Big Mac
£5
Macbook
£1,999
PS4
£250
Private Jet
£50,000,000
Yacht
£10,000,000
Rolex
£9,000
Jack Daniels
£20
Guitar
£150
Tesla
£81,000


Develop Across All Platforms
Learn one way to build applications with Angular and reuse your code and abilities to build apps for any deployment target. For web, mobile web, native mobile and native desktop.
Speed & Performance
Achieve the maximum speed possible on the Web Platform today, and take it further, via Web Workers and server-side rendering.
Angular puts you in control over scalability. Meet huge data requirements by building data models on RxJS, Immutable.js or another push-model.
Incredible Tooling
Build features quickly with simple, declarative templates. Extend the template language with your own components and use a wide array of existing components. Get immediate Angular-specific help and feedback with nearly every IDE and editor. All this comes together so you can focus on building amazing apps rather than trying to make the code work.
Loved by Millions
From prototype through global deployment, Angular delivers the productivity and scalable infrastructure that supports Google's largest applications.


ID: text
Name: text
Diagnosis: text
HRN: text
HRN(blackened): text
Phone: text
Attending Doctor: text
Attending Nurse: text
Room Number: text
Obligation: text


Government Gateway account created
User ID:<PHONE_NUMBER>54
You have created a Government Gateway account. You can now register for anti-money laundering supervision.
ContinueYou have created a Government Gateway account. You can now register for anti-money laundering supervision.
Continue


If you're reading this, it means you were able to start the http and/or https server(s). I've been running these examples on the Nodify.US Node.JS Development Kickstarter Appliance which uses a fixed range of local IP addresses. If you're doing the same, you can click on either of the two links to see this page served via HTTP or HTTPS.
If you're running this example on a different host, you'll likely have to modify the URLs in the links above.
Also, if you click on the HTTPS link above, your browser should give you a dire warning that you're about to click on a site whose URL doesn't match the name in the certificate. This is because the certificate we distribute with this package is completely insecure. Please don't try to use it on a production system (or any system with sensitve data.)
The easiest way to create your own key and certificate is to use the OpenSSL command line utility to generate the appropriate key and CSR (Certificate Signature Request) files. You then pick a Certifying Authority to sign the public key associated with the private key you generated and follow their process to retrieve a server certificate.


For last few weeks I've been spending time on accelerating Zk-STARK friendly Rescue Prime Hash Function so that large number of hash computations can be offloaded into accelerators i.e. OpenCL backed CPU, GPGPU etc. As current implementation of Rescue Prime Hash function doesn't lend itself well for data parallel acceleration, I've majorly concentrated in running multiple instances of Rescue Prime Hash function on independent and non-overlapping input. I can simply say, given N-many independent inputs Rescue Prime Hash is computed N-many times, producing N-many digests, in-parallel. Though with this individual computation of Rescue Prime Hash doesn't end up being faster, but total throughput definitely improves. During this process of accelerating Rescue Prime Hash function, I also explored possibility of vectorizing Rescue Permutation steps using both OpenCL vector intrinsics and nightly Rust's portable-simd feature. But today I'm not going to be talking about Rescue Prime Hash function and its implementation itself, instead one application of that --- Merkle Tree Construction using Rescue Prime Hash function. Some other day I'll take up Rescue Prime Hash function; for now if interested you should be looking into following references. Note, all following implementations I'm linking are using F(264 - 232 + 1) as prime field to operate on.
I'd like to clarify that for this work I'm assuming I've access to OpenCL accelerated Rescue Prime Hash function merge(digest_0, digest_1), which takes two Rescue Prime digests and produces single Rescue Prime digest. For reference you should check out this OpenCL kernel, which exactly does that. With this, I can say following Merkle Tree construction technique can be generalised to other cryptographic hash functions offering acceptable security and collision resistance, where two hash digests of respective hash function can be supplied as input and it produces another hash digest as output. Say for example in Blake3, two hash digests can be flattened into a byte array which is supplied to Blake3 hasher for producing digest. For this work I'm going to use aforementioned prime field F(264 - 232 + 1), so each element of prime field can be represented in native 64-bit unsigned integer i.e. uint64_t. Input to Rescue Prime hash's merge function is 512-bit which is interpreted as 8 prime field elements or more specifically two Rescue Prime hash digests placed next to each other, each of width 256-bit ( read 4 prime field elements ). Output of Rescue Prime hash function i.e. digest is of width 4 field elements, which takes 256-bit to represent.
Let's dive into Merkle Tree construction.
In following explanation, I design one function which takes N-many Rescue Prime Digests, which are interpreted as leaves of Merkle Tree and as output that function produces all intermediate nodes of Merkle tree. These intermediate nodes are nothing but Rescue Prime digests of two immediate children nodes. I consider perfectly balanced binary Merkle Tree i.e. each non-leaf node has exactly two children and all leaves are living at same depth. Naturally N can only be power of 2. That means with N-many leaves provided, I should construct all intermediate nodes of a Merkle Tree with log2N levels or in other words Merkle tree of height (log2N + 1). So there should be (N - 1)-many intermediate nodes to compute of Merkle Tree with total (2N - 1) nodes. Let's take an example for better understanding.
In above diagram, I've a binary Merkle Tree with 8 leaves. Each consequtive pair of leaves
are merged ( read hashed ) together for obtaining deepest level of intermediate nodes i.e. intermediates living just above leaves.
Similarly those intermediate nodes are paired together for hashing and obtained digests will live on intermediate node
layer just above it. This procedure continues until we reach root node. If interested you can dig deep into Merkle Tree construction,
a starting point can be this.
Notice how tree nodes are numbered, which are nothing but indices of intermediate and leaf nodes in a 1D container array.
Here I'm assuming both input digests ( read tree leaves ) and output intermediate node digests are kept in same array.
Now assume that as input following array is provided, which are considered as leaves of Merkle Tree.
Output should have (N - 1)-many intermediate node digests.
Output should be placed in an one dimensional array of length N such that 1st element ( i.e. at index zero ) is empty
( which is why there's no node tagged with index 0 in above diagram ),
2nd element ( Merkle root at index 1 ) is obtained by hashing elements at index 2 and 3 ( in ordered fashion ). Similarly element at index 2 of output array
is computed by hashing elements at index 4 and 5. This means element at index i of output array is computed by hashing elements
at index (2 * i) and (2 * i + 1), generally speaking. This is perfectly applicable only when
all nodes of Merkle Tree ( including input leaves ) are placed in same contiguous memory allocation.
And this is applicable, with small change, when input ( Merkle Tree leaves ) and output are placed in
different arrays.
As I'll go with later, I've to start filling output array backwards.
First I'll pair consequtive elements in input array of length N and compute Rescue Prime Hash ( read merge(digest_0, digest_1) function )
for each pair. This should give me (N >> 1)-many intermediate nodes, who live just above leaf nodes.
In next round of computation I'll calculate Rescue Prime hashes on intermediate nodes computed during previous round. I'll pair consequtive intermediate nodes and compute Rescue Prime merge function on them for getting (N >> 2)-many new intermediate nodes. After completion of this round, I've ((N >> 1) + (N >> 2)) -many intermediate node digests.
Notice only 2nd element of output array is non-computed still, which is root of Merkle tree we're computing. Root of tree is nothing but digest of two immediate subtrees. It can be computed as below. This way I've computed ((N >> 1) + (N >> 2) + (N >> 3)) -many intermediate nodes of Merkle Tree, in log2N rounds.
When N is large, say 224, it's beneficial to compute each round of Merkle Tree construction in parallel.
There'll be 24 rounds and during each round given that all intermediate nodes of previous round are already computed
and available for read-only access, it's easy to just pair consequtive intermediate nodes computed during previous round
and apply Rescue Prime merge function on them, which produces intermediate nodes to be used
during next round.
For N-many leaves provided as input to one OpenCL kernel, in log2N rounds
total (N - 1) -many intermediate nodes to be computed. Each of these rounds can safely run in-parallel
but round k is data dependent on round (k - 1). Intermediate nodes computed during round k are used as input during
dispatch of round (k + 1). That means OpenCL event based dispatched kernel dependency management is required so that compute
dependency graph can be inferred by OpenCL runtime and no data race happens.
In OpenCL, I'll pass input array of length N as an OpenCL buffer ( read cl_mem )
to kernel, which computes intermediate nodes of Merkle Tree. Output array which is also passed as an OpenCL buffer,
is also of length N, where first element will be kept empty and it'll never be touched ( read accessed ) by any dispatched kernels.
Though output buffer is of length N, all (N - 1) -many intermediate nodes can't be computed in a single
dispatch. Total log2N -many dispatches will be required.
During first dispatch only (N >> 1) -many intermediate nodes to be computed, which are living just above leaf nodes.
This time input buffer of length N is accessed in read-only mode and last (N >> 1)-many places of output buffer
is accessed in write-only mode. This is the only kernel dispatch round when input buffer is used, in all upcoming kernel
dispatch rounds, non-overlapping regions of output buffer will be used for both input and output purposes.
Note, size of output buffer region used for input is always twice the size of subbuffer used
for writing intermediate nodes computed during that round. It can be trivially figured out due to the fact
that each call to merge function merges two chidren nodes into single parent node.
I'll use OpenCL subbuffers for accessing only subsection of output
buffer required during certain kernel dispatch round. For one kernel dispatch round, other than first one, output buffer will be splitted into
two non-overlapping ( though living next to each other ) subbuffers, where one is input ( accessed with read-only flag ) and another one is output ( accessed with write-only flag ) for that round.
In next kernel dispatch round, previous round's write-only subbuffer region is accessed in read-only mode
and appropriate region of output buffer is accessed in write-only mode for placing computed intermediate nodes.
Let me take a diagrammatic example for better demonstration purposes.
I allocate two OpenCL buffers on target compute device, one for storing input digests, representing leaves of tree
and another one for storing all computed intermediate digests of Merkle Tree. These two buffers are supplied to Merkle Tree construction kernel.
There will be other inputs required which are hash function specific and I'm skipping them now. To be more specific in Rescue Prime hash function
it needs to have read-only access to MDS matrix, Round Key constants, to be applied on hash state during Rescue Permutation rounds.
I'm going to demonstrate construction of Merkle Tree with 32 leaves, so there'll be 5 rounds i.e. 5 kernel dispatches will be required. And these 5 dispatched kernels
are to be executed in-order due to inherent data dependency in hierarchical structure of binary Merkle Tree.
In very first round of kernel dispatch, original input buffer is used as input ( read-only mode ) and marked half of output buffer is taken as subbuffer ( write-only mode ) and used for writing output of that round, which will be input for next round.
In second round of kernel dispatch, original output buffer of length N ( = 32, here ) is splitted into two non-overlapping regions i.e. subbuffers. Previous round's output subbuffer region is accessed in read-only mode this time and marked portion of output buffer is accessed as write-only subbuffer where this round's computed digests ( read intermediate nodes ) are written, which are going to be accessed in read-only mode during next kernel dispatch round.
In third round, 4 intermediate nodes are computed by reading from subbuffer which represents 8 intermediate nodes ( computed during last round ), living just below this round's computed intermediate nodes.
In this round, 3rd round's output subbuffer region is accessed in read-only mode for computing 2 intermediate nodes, which are root nodes of immediate subtrees of Merkle Root. Note, Merkle Root is yet to be computed and placed at index 1 of output buffer, which is why it's shown as empty in following diagram.
And finally in round 5 of kernel dispatch, root of Merkle Tree is computed by accessing two intermediate nodes, computed during round 4, which are Merkle Root's immediate subtree roots.
With this our OpenCL kernel has computed all intermediate nodes ( 31 in number ) of Merkle Tree with 32 leaves, in 5 consequtive rounds, where each round safely executed in parallel.
This is time to clarify how much memory does it actually consume to store 32 abstractly
represented cells in input/ output buffer. Each cell is abstract representation of one Rescue Prime digest which is
256-bit in width or in other words 4 contiguous prime field elements, where each element can be represented using u64.
Total of 32 * 256 -bit = 1024 bytes of memory is allocated for each of input and output buffer. Now remember, I had to
split original output buffer into non-overlapping subbuffer regions for read-only and write-only access during several kernel dispatch
rounds, which is fine as long as subbuffer's origin ( in terms of bytes ) is properly aligned with
OpenCL device's base address alignment requirement. For more info you should look at Khronos OpenCL man page
here and search for error code
CL_MISALIGNED_SUB_BUFFER_OFFSET.
For querying what is OpenCL device memory base address alignment requirement, I use
this function.
If you've clinfo tool installed, that can also be used for checking all available device base address alignment requirements like below.
We're interested in Alignment of base address field, but there're two of them because two devices are available in my system. As I'm planning to run this kernel on Nvidia Tesla V100 GPGPU, which is enlisted first when clinfo is run, I'll just consider following from above output.
This means I can't create a subbuffer from whole output buffer when origin of subbuffer is not aligned to 512 bytes. To be more specific, I can't create one subbuffer which originates at some byte index that can't be evenly divided by 512. Let's now go back to previous demonstration, where I constructed a Merkle Tree from 32 leaves, and explore deeply to ensure that all kernel dispatches will execute as envisioned.
Original input/ output buffer are having origin at byte index 0 and size of 1024 bytes. In round 1, whole input buffer is used in read-only mode and half of output buffer is used for writing computed intermediate nodes.
In second round, previous round's output subbuffer is used as input subbuffer and
this round's input subbuffer halves in size; details below.
And here it all breaks ! Looks like I can't create subbuffer region in second round, because
output subbuffer of second round is not properly aligned with Nvidia Tesla V100 base memory address alignment
requirement ( because 256 % 512 != 0 )
That means, as I was planning to dispatch kernel 5 times with different non-overlapping subbuffer regions,
to be used as input/ output for that round, I can't do that. And as an alternative I've to compute some intermediate
nodes sequentially. I showed I have to stop after round 1 kernel dispatch in above case and there're still 15
more intermediate nodes to compute. I write one kernel where only one OpenCL work-item computes all remaining
intermediate nodes at tip of Merkle Tree by iteratively going through them in order. In order to avoid device-to-host data transfer
when whole Merkle Tree is not yet constructed, I have to use sequential implementation of kernel. For different OpenCL devices,
different memory alignment requirement may exist, so portable host-side orchestrator code can't hard-code how many of intermediate nodes
at tip of Merkle Tree to be computed sequentially. I suggest you take a look at my implementation of that, which I'm going to link at end.
An optimization experiment --- one can attempt to do data transfer to host and compute remaining intermediate nodes in parallel ( as I primarily proposed )
on host itself where memory base address alignment issues are absent because pointer arithmetics can be comfortably used
for accessing subregions of whole array, allocated on host for holding all intermediate nodes ( either computed/ to be computed ).
This is the kernel
I wrote for sequentially computing tip of Merkle Tree.
Finally let's talk numbers.
My vectorized OpenCL Rescue Prime Hash implementation empowers Merkle Tree construction, when run on data center grade Nvidia Tesla V100 GPGPU, I see following numbers. For sake of reproducibility of claimed benchmark, I provide all necessary implementations here. Major portion of code I wrote for parallelizing Merkle Tree construction is orchestration of several kernel dispatch rounds on host; if interested you should check here.
In coming days, I'd like to explain implementation details of SIMD powered Rescue Prime Hash function.
Have a great time !


Search
Retro and Modern Sylvanian Families Collectibles
Home
Collection
Other
Blog
eBay
Forums
Links
News
Contact
Back
Periwinkle Milk Rabbit Family & Sweetpea Rabbit Family
Epoch Japan
Periwinkle Milk Rabbit Family
Father at Home
Mother at Home
Cute Dress Girls
Milk Rabbit Baby with Mini House
Banana Babies
Epoch UK
Periwinkle Milk Rabbit Family
Periwinkle Milk Rabbits - Blue Box
Periwinkle Milk Rabbit Baby (Blue)
Baby Sleigh Ride
Country Dentist
School Friends
Ballerina Friends
Flair UK
Periwinkle Rabbit Grandparents
Periwinkle Rabbit Family
School Sports Day
Calico Critters
Sweetpea Rabbit Family
Britney's Ballet Recital
Country Nurse Set
Doctor Checkup Set
Apple & Jake's Ride 'n Play
School Friends
Town Series UK
Flower Gifts Playset
Blind Bags
Shopping Series
Book
We Love Sylvanian Families Book & Milk Rabbit


time |
10:00 AM |
short date |
January 11 |
abbreviated short date |
Jan 11 |
long date |
April 23, 2010 |
abbreviated long date |
Apr 23, 2010 |
short date and time |
February 5, 8:00 AM |
abbreviated short date and time |
Feb 5, 8:00 AM |
long date and time |
February 8, 2016, 1:00 PM |
abbreviated long date and time |
Feb 8, 2016, 1:00 PM |
day of week and time |
Monday, 7:30 AM |
abbreviated day of week and time |
Mon, 7:30 AM |
day of week, short date and time |
Monday, March 15, 3:25 PM |
abbreviated day of week, abbreviated short date and time |
Mon, Mar 15, 3:25 PM |
For dates/times associated with user generated data (e.g., notes, documents), use the smart format
, which changes relative to the current date.
If the date is in the past or future on the current day, format as time only |
10:00 AM |
If the date is in the past or future within the current calendar year, format as short date |
January 11 |
If the date is in the past or future outside the current calendar year, format as long date |
April 23, 2010 |
| For reminders or appointments, show both time and date | Jun 8, 8:00 AM Today, 8:00 AM |
| Separate ranges with an en-dash without a space | Jun 15–Jun 16 Dec 20, 2014–Jan 2, 2015 |
| If a time range shares a common AM/PM, append only on the end of the range | 10:00–11:00 AM 10:00 AM–12:30 PM |
In some limited circumstances these are still acceptable to use (e.g. legacy or internal), but please ensure you are using the current standardized date format above for publically accessible applications.
condensed long date |
MM/DD/YYYY, DD/MM/YYYY, or YYYY/MM/DD |
The condensed version of the long date format has 3 variations:
MM/DD/YYYY
(05/12/2015)DD/MM/YYYY
(12/05/2015)YYYY/MM/DD
(2015/12/05)MM/DD/YYYY
is the format most used in the US, whereas DD/MM/YYYY
is used most outside the US. Depending on the date, the format, and the geographical location or customs of the user, a date has the potential for being misinterpreted if the format is not what the user is expecting. The only non-ambiguous format is YYYY/MM/DD
.
Depending on your target audience, determine which format makes the most sense for your application.


HOME
PRODUCTS
PRODUCTS
Incorporating Artificial Intelligience to improve the lives of others
Prescription
Chatbot
Hospital Search
Comming Soon


Home
News
Rockets
Teams
About Us
Sponsors
Contact
Join Us
Nova Rocket Team
Discovering the Unknown
Contacts
Send us an email:
<EMAIL_ADDRESS>Or send us a message through the form below:
First Name
Last Name
email
Country
Portugal
Spain
France
United Kingdom
Europe
USA
Canada
South America
Asia
Russia
Africa
Australia
Antarctica
North pole
Subject
Where to find us


Transforming your data with Ruby and ActiveWarehouse-ETL
Thibaut Barrère from LoGeek
This talk will show how to use ActiveWarehouse-ETL to extract, transform and move your data around in Ruby, and what other people do in real life with this toolkit.
1# Beginner guide to ActiveWarehouse-ETL
- Gem presentation (concepts etc)
- Beginner examples of data processing
2# Real-life examples all in Ruby:
- Extracting data from a CRM to build a metrics dimensional dashboard (business intelligence)
- Extracting and geocoding places before publishing a map on the web
- Adapting data for consumption by COBOL systems
- Cleaning and extending the CRM
- Extracting only what changed (aka Change Data Capture)
Thibaut Barrère is a Ruby freelancer since 2005, doing data processing on a regular basis since then too.
He is also bootstrapping a Rails-based SaaS product WiseCashHQ.


Move using WASD keys
Shoot with LMB
Your ship can take 3 points of damage before you are vulnerable to enemy fire! Your shields will recharge after 3 seconds as long as you don't take any additional damage.
Fire a Rocket with V. The rocket will target and home in on the player closest to your cursor. You can store up to 2 rockets at a time. Rockets take 3 seconds to recharge. Rockets pierce shields.
Destroying large and medium asteroids will occassionally drop invulnerability powerups. Press SPACE to use. Lasts 2 seconds.
Good luck out there!
(C) Jesse Yue 2017


PasswordField is similar to TextField apart from the ability to set datatype. PasswordField is intended to work with text and string entity attributes only.
PasswordField
datatype


Java annotation processors to generate simple, safe and consistent value objects. Do not repeat yourself, try Immutables, the most comprehensive tool in this field!
Get started! Read guide... v2.9.2import org.immutables.value.Value;
// Define abstract value type
@Value.Immutable
public interface ValueObject {
String name();
List<Integer> counts();
Optional<String> description();
}
// Use generated immutable implementation
ValueObject valueObject =
ImmutableValueObject.builder()
.name("My value")
.addCounts(1)
.addCounts(2)
.build();
// Or you can configure different @Value.Style
@Value.Immutable
abstract class AbstractItem {
abstract String getName();
abstract Set<String> getTags();
abstract Optional<String> getDescription();
}
// Use generated value object
Item namelessItem = Item.builder()
.setName("Nameless")
.addTags("important", "relevant")
.setDescription("Description provided")
.build();
Item namedValue = namelessItem.withName("Named");
With Immutables you can generate state of the art immutable objects and builders. Type-safe, null-safe and thread-safe, with no boilerplate. Generate builders for immutable objects and even plain static factory methods.
Just add jar to classpath and use. No required runtime dependencies! Guava is supported, but not required. Works out-of-the box with build tools, integrates into IDEs. Get started!
Lazy, derived and optional attributes. Comprehensive support for collections as attributes, including Guava collections. Generic parameters. Copy with structural sharing. Singleton and interned instances… and more!
Immutables has much higher standards for code readability than other generators. Generated APIs are carefully tuned for the best power-to-weight ratio. No ugly named identifiers. Implementation code is clear, efficient, and have corresponding javadocs. Take a look
Adapt Immutables to your code conventions and tastes. Customize get
, set
, with
or whatever prefixes. Describe styles as reusable meta-annotations. Generate as much or as little as you need, expose or hide generated classes. Go rogue with Encodings to implement custom attribute types!
Immutables are serialization ready, including JSON and its binary forms. Jackson and Gson libraries are supported. Gson is integrated using generated TypeAdapters which use no reflection. JSON guide. MongoDB repositories also included.
Generate typesafe query and repository DSL to access different datasources.


Country Singer Jordan Davis & Wife Kristen Welcome First Child -- See the Adorable Pic! (Exclusive)
by Zach SeemayerCountry star Jordan Davis and his wife Kristen welcomed their first child, a daughter named Eloise Larkin, on Sunday, ET has exclusively learned.
"We are overjoyed with the addition of our baby girl Eloise Larkin Davis," the couple tells ET. "Momma and baby are healthy as can be and we have a lot to be thankful for this holiday season."
Eloise was born in Nashville, Tennessee and weighed in at 8 lbs., and measured 19.75 inches.
Just last week, ET spoke to the "Slow Dance In A Parking Lot" singer on the red carpet at the 2019 CMA Awards where he shared his emotions on becoming a first-time father.
"There’s a little bit of nerves," Davis revealed. "I mean I feel like we’re ready as can be to be parents, I’m just ready to meet the guy or girl."
"If we’re blessed enough to have another child, I want to find out what it is," Davis joked. "Because I’m tired of calling it 'the baby' or 'it.' I want to know 'him' or 'her.'"
On the red carpet, Davis also revealed the baby's name was not drawn from a musical inspiration, but it's more of a "family thing."
It's been a huge month for Davis, both with the super exciting addition to his adorable family and in regards to his music. Just days ago, the singer released the new song "Cool Anymore," which he recorded with Julia Michaels.
Davis also opened up to ET at the CMAs about working with Michaels, explaining that he's wanted to "collab with Julia for about four years now."
"She's just such a great artist," Davis shared. "We sent [the song] over to her, told her to make it her own, and hopefully she would want to sing on it. And [then] I saw her at the CMT Awards and she told me that she wanted to sing on it, and I played it cool when I was in front of her and then I walked off and kinda freaked out."
"I'm just happy the world's hearing it," he added. "I love the message of it and super excited to see what it does."
Congrats to Davis and his wife on the new addition to their happy family!


Active ingredient (in each dose)
Polyethylene Glycol 3350, 17 g (cap filled to line)
Uses
- •
- relieves occasional constipation (irregularity)
- •
- generally produces a bowel movement in 1 to 3 days
Warnings
Allergy alert: Do not use if you are allergic to polyethylene glycol
Do not use
if you have kidney disease, except under the advice and supervision of a doctor
Ask a doctor before use if you have
- •
- nausea, vomiting or abdominal pain
- •
- a sudden change in bowel habits that lasts over 2 weeks
- •
- irritable bowel syndrome
Ask a doctor or pharmacist before use if you are
taking a prescription drug
When using this product
you may have loose, watery, more frequent stools
Stop use and ask a doctor if
- •
- you have rectal bleeding or your nausea, bloating, cramping or abdominal pain gets worse. These may be signs of a serious condition.
- •
- you get diarrhea
- •
- you need to use a laxative for longer than 1 week
If pregnant or breast-feeding,
ask a health professional before use.
Keep out of reach of children.
In case of overdose, get medical help or contact a Poison Control Center right away. (1-800-222-1222)
Directions (Bottle Only)
- •
- do not take more than directed unless advised by your doctor
- •
- the bottle top is a measuring cap marked to contain 17 grams of powder when filled to the indicated line (white section in cap)
- •
- adults and children 17 years of age and older:
- •
- fill to top of white section in cap which is marked to indicate the correct dose (17 g)
- •
- stir and dissolve in any 4 to 8 ounces of beverage (cold, hot or room temperature) then drink
- •
- use once a day
- •
- use no more than 7 days
- •
- children 16 years of age or under: ask a doctor
Other information
- •
- store at 20°-25°C (68°-77°F)
- •
- tamper-evident: do not use if printed foil seal under cap is missing, open or broken
Inactive ingredients
none
Questions or comments?
1-800-632-6900
Principal Display Panel
COMPARE TO the active ingredient of MIRLAX®
See back panel
GentleLAX®
Polyethylene Glycol 3350
Powder for Solution, Osmotic Laxative
Relieves Occasional Constipation / Irregularity
Softens Stool
Unflavored Powder
Grit Free
NET WT 8.3 OZ (238g)
14 ONCE-DAILY DOSES


given an array of sorted values and a value to find, return the first index value that is less than or equal to the target.
if the array values are inverted (e.g. index 1 is the largest value), set inverted = .true.
if indirect_indices specified, use as indirect indices into data_array, with these indices giving the sorted order. return index will be the direct index into the data_array. to also return the index into the indirect array, specify the_indirect_index in the arg list.
note that you cannot specify both inverted and indirect.
my_status values: 0 = good return -1 = value_to_find is below the smallest value 1 = value_to_find is above largest value
94 = invalid indirect index values 95 = cannot specify the_indirect_index and not indirect_indices(:) 96 = cannot specify both indirect and inverted 97 = empty input data array 98 = interval values are inverted 99 = unknown error
bad output values use MISSING_I and MISSING_R8
usage example: you have a long array of unsorted numbers and you want to find the index of a given value.
! do this only once call index_sort(unsorted_array, index_array, sizeof(unsorted_array))
call find_first_occurrence(sizeof(unsorted_array), unsorted_array, value_to_find, & this_index, istat, indirect_indices = index_array) if (istat /= 0) return if (value_to_find == unsorted_array(this_index)) then print , 'found ', value_to_find, ' in array at index ', this_index else print , 'did not find exact match in array' print , 'largest value still less than ', value_to_find, ' in array at index ', this_index print , 'is value ', unsorted_array(this_index) endif
FIXME - do we need an integer version of this? (i think yes) possibly also a character version for arrays of strings. C++ overloading would be nice sometimes.
if the input is a single value, test it and return if the value to find is too small or too large. also check for bad indirect index values.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| integer, | intent(in) | :: | nitems | |||
| real(kind=r8), | intent(in) | :: | data_array(nitems) | |||
| real(kind=r8), | intent(in) | :: | value_to_find | |||
| integer, | intent(out) | :: | the_index | |||
| integer, | intent(out) | :: | my_status | |||
| logical, | intent(in), | optional | :: | inverted | ||
| integer, | intent(in), | optional | :: | indirect_indices(nitems) | ||
| integer, | intent(out), | optional | :: | the_indirect_index |


DataWorker is designed to manage large datasets client-side, using modern web technologies such as Web Workers and WebSockets. DataWorker allows users to perform expensive operations (such as sorting, joining, searching, filtering, grouping, and much more) in a multi-threaded environment to maintain responsiveness. Data can be provided locally as an array, or remotely via AJAX or WebSockets. DataWorker has no dependencies on external libraries, but works well with others.
Not all browsers fully support all of the provided features (such as using a WebSocket from within a Web Worker) because some of the technology is quite new, so fallbacks are in place to make sure data can still be accessed across the most amount of devices possible.
To get started, you will need to include one of the distribution files in the dist/
folder: dataworker.js
for development/testing, and dataworker.min.js
for production. For example:
<script src="dist/dataworker.min.js"></script>
DataWorker takes an array of records to construct the initial dataset. The first record defines column properties.
var dataset = [ [ { name: "column_a", title: "Column A", aggType: "max", sortType: "alpha" }, { name: "column_b", title: "Column B", aggType: "max", sortType: "alpha" }, { name: "column_c", title: "Column C", aggType: "min", sortType: "alpha" } ], [ "apple", "violin", "music" ], [ "cat", "tissue", "dog" ], [ "banana", "piano", "gum" ], [ "gummy", "power", "star" ] ]; var dw = new DataWorker(dataset);
It is also possible to just supply column names:
var dataset = [ [ "column_a", "column_b", "column_c" ], [ "apple", "violin", "music" ], [ "cat", "tissue", "dog" ], [ "banana", "piano", "gum" ], [ "gummy", "power", "star" ] ]; var dw = new DataWorker(dataset);
If only column names are supplied, the following default column properties are used:
title
: columnName
aggType
: "max"sortType
: "alpha"Additionally, you should give DataWorker an error handler (by default DataWorker uses console.error
) using the onError
method or by adding it as a property on the dataset object:
dataset.onError = function (msg) { alert(msg) }; new DataWorker(dataset);
or
var dw = new DataWorker(dataset).onError(function (errorMsg) { alert(errorMsg); });
DataWorker can be provided with an error handler:
dw.onError(function (msg) { alert(msg); });
By default, errors are printed to the console.
Alternatively, DataWorker can stream data from a WebSocket server:
var dw = new DataWorker({ datasource : "ws://<IP_ADDRESS>:8888", authenticate : "{}", request : "{\"cmd\":\"requestDataset\"}" });
datasource
should be the URL of the websocket server.
authenticate
is optional. If present, will be the first message sent to the server upon connecting.
request
is sent after authenticate
.
After the request is sent, DataWorker will expect a reply in the form of a JSON object with expectedNumRows
and columns
properties such as:
{ expectedNumRows : 10, columns : [ "column_a", "column_b", "column_c" ] }
expectedNumRows
should be the number of rows to expect in that dataset.
columns
should be the columns of the expected dataset. They can be provided as simple column names or as hashes with custom column properties.
Note DataWorker cannot proceed without first knowing the columns and the total number of expected rows. That being said, these two properties may be transmitted separately. For example:
// First message: { "expectedNumRows": 10 } // Second message: { columns: [ "column_a", "column_b", "column_c" ] }
Afterwards, DataWorker will expect arrays of dataset rows from the server. These rows will be appended to the dataset. The reply will be similar to the following:
[ [ "apple", "violin", "music" ], [ "cat", "tissue", "dog" ], [ "banana", "piano", "gum" ], [ "gummy", "power", "star" ] ]
The onReceiveColumns
callback will be called when columns and the expected number of rows are received from the Websocket server. This callback can be set with the onReceiveColumns
method:
dw.onReceiveColumns(function () { alert("Columns have been received!"); });
Alternatively, this callback can be passed into the constructor:
var dw = new DataWorker({ datasource : "ws://<IP_ADDRESS>:8888", authenticate : "{}", request : "{\"cmd\":\"requestDataset\"}", onReceiveColumns : function (numRows) { alert("Columns have been received!"); } });
The onReceiveRows
callback will be called whenever rows are received from the Websocket server. This callback can be set with the onReceiveRows
method:
dw.onReceiveRows(function (numRows) { alert("Received " + numRows + " rows."); });
Alternatively, this callback can be passed into the constructor:
var dw = new DataWorker({ datasource : "ws://<IP_ADDRESS>:8888", authenticate : "{}", request : "{\"cmd\":\"requestDataset\"}", onReceiveRows : function (numRows) { alert(numRows + " rows received."); } });
If not set, this callback defaults to doing nothing.
The onAllRowsReceived
callback is called when all expected rows have been received from the Websocket server. This callback can be set with the onAllRowsReceived
method:
dw.onAllRowsReceived(function () { alert("All rows have been received!"); });
Alternatively, this callback can be passed into the constructor:
var dw = new DataWorker({ datasource : "ws://<IP_ADDRESS>:8888", authenticate : "{}", request : "{\"cmd\":\"requestDataset\"}", onReceiveRows : function (numRows) { alert(numRows + " rows received."); } onAllRowsReceived : function () { alert("All rows have been received!"); } });
If not set, this callback does nothing.
If WebSockets are not available, data can be received via AJAX as well:
var dw = new DataWorker({ datasource : "http://<IP_ADDRESS>:8888", request : "?this=is;a=query;string", onAllRowsReceived : function () { alert("all rows have been received!"); } });
datasource
should be the base url to send GET requests to.
request
should be the query string to append to the base url. The prepended question mark is optional; if it is missing, one will automatically be inserted. Optionally, request
could be a javascript object or a JSON string; DataWorker will convert this into a query string.
If multiple data sources exist, it is possible to provide fallbacks when instantiating DataWorker. This can be useful if you have a server down for maintenance or your WebSocket service is not running. Simply provide the possible sources as an array to datasource
in priority order:
var dw = new DataWorker({ request : { cmd: "requestDataset" }, datasource : [ "http://example.com", "ws://<IP_ADDRESS>:8888", "http://<IP_ADDRESS>:9999" ] });
The sources will be tried in order. If they all fail, DataWorker will throw an error.
For finer-grain control, authentications can be supplied on a per-datasource basis:
var dw = new DataWorker({ request : "\"cmd\":\"requestDataset\"}", datasource : [ { source : "ws://<IP_ADDRESS>:8888", authenticate : "zxcv" }, { source : "http://<IP_ADDRESS>/data-origin", authenticate : "asdf" } ] });
A new dataset can be requested using the requestDataset
method:
// For WebSockets or AJAX: dw.requestDataset("{\"cmd\":\"requestDataset\"}"); // For AJAX only: dw.requestDataset("query=string");
The newly-requested dataset will completely replace the previously-requested dataset.
A new dataset can be requested and appended to the current dataset using the requestDatasetForAppend
method:
// For WebSockets or AJAX: dw.requestDatasetForAppend("{\"cmd\":\"requestDataset\"}"); // For AJAX only: dw.requestDatasetForAppend("query=string");
Note that the newly-requested dataset should have the same columns as the previously-requested dataset.
Ongoing requests can be cancelled with the cancelOnoingRequests
method:
dw.cancelOngoingRequests();
When requestDataset
is called, ongoing requests are automatically cancelled (requestDatasetForAppend
does not cancel ongoing requests).
When connected to a Websocket server, DataWorker will send the cancelRequestsCmd
message signalling the server to cancel the previous requests if such a message has been provided:
var dw = new DataWorker({ datasource: { source : "ws://<IP_ADDRESS>:8888", cancelRequestsCmd : "CANCEL", cancelRequestsAck : "ACK_CANCEL" } });
If cancelRequestsAck
has been provided, DataWorker will wait for the server to acknowledge with a reply that matches cancelRequestsAck
exactly before proceeding.
DataWorker can pass messages to and from the remote server via the postMessage
method and triggers:
dw.onTrigger(function (reply) { alert(reply); }).postMessage("MESSAGE TO SERVER");
In the above example, the string "MESSAGE TO SERVER" is sent to the server, and any reply will by shown as an alert. Server replies that are triggers should be stringified JSON objects that look like this:
{ trigger: true, msg: "REPLY FROM SERVER" }
The onTrigger
callback can be passed into the constructor as well:
var dw = new DataWorker({ datasource : "ws://<IP_ADDRESS>:8888", authenticate : "{}", onTrigger: function (reply) { alert(reply) } });
Web Workers are enabled by default, to get the most performance out of DataWorker. Without any extra configuration, a Blob will be used to create the Web Worker. However, some browsers do not support remote data from within a Web Worker created in that fashion, though they work fine when created using a standard URL. DataWorker will detect these errors and look for a file named dw-helper.js
in the same folder as dw.js
. THe method used to determine the source path does not work well in some circumstances, for example when a module loader like RequireJS. To provide alternative sources, extra options can be provided when instantiating DataWorker.
If workerSource
is provided, it will be used first. If that source fails to create a Web Worker, or an error occurs on connection to the datasource, a Blob will be created next to try again. If the Blob fails, then backupWorkerSource
will be used next. If that fails, or wasn't provided, DataWorker will make it's best guess as to the location of dw-helper.js
. When all else fails, DataWorker will revert to a single-threaded environment. Finally, an error will be thrown if a connection still cannot be made and no local dataset is provided.
new DataWorker({ datasource: "http://example.com/dataset.json", workerSource: "/path/to/dw-helper.js", backupWorkerSource: "/other/path/to/dw-helper.js" });
There is an initial cost to creating a Web Worker, and they are meant to stay alive as long as possible. This can be important for avoiding memory leaks in single page applications. Rather than terminating a Web Worker, DataWorkerHelper is designed to idle when not in use and be reused when needed. To clean up an instance of DataWorker, simply call finish(optionalCallback)
on the instance. If a callback is included it will be called once the Web Worker has responded that it is ready to be reused. Simply creating a new DataWorker will reuse that Web Worker.
Web Workers are still an experimental technology, and may have some issues. If you prefer to avoid using Web Workers at all, you may pass in a flag to force it to be single-threaded.
new DataWorker({ datasource: "http://example.com/dataset.json", forceSingleThread: true });
A DataWorker dataset can be deep-copied by calling clone
:
dw.clone(function (newD) { newDataset = newD; });
Note that any streaming datasources are not copied as part of the cloning process.
DataWorker has a concept of child rows. Child rows are rows that are a subset of another row, and may be used, for example, to provide more detail. The functionality is currently limited to a small subset of functions. Where use of child rows is not explicitly defined, they are treated as if they are distinct rows that have been added to the dataset. These are the currently supported functions:
DataWorker can use differing values for display and dataset operations. This enables you to include HTML formatting for cell values but not have to consider them when actually working with the dataset. To take advantage of this feature, simply pass the cell value an object with the display
and raw
properties defined:
var dataset = [ [ "column_a", "column_b", "column_c" ], [ { display: "apple", raw: "apple" }, "violin", "music" ], [ "cat", "tissue", "dog" ], [ "banana", "piano", "gum" ] ]; var d = new DataWorker(dataset); d.getRows(function (rows) { // rows is [ // [ "apple" "violin", "music" ], // [ "cat", "tissue", "dog" ], // [ "banana", "piano", "gum" ] // ] }); d.applyFilter(/^apple^/, "column_a"); // Operates on the raw value d.getRows(function (rows) { // rows is [ // [ "apple" "violin", "music" ], // ] });
The display
value will be returned by the methods that allow access to the dataset, while the methods that manipulate the dataset will operate on the raw
value. Also note that the two methods of specifying cell values can be mixed.
Column properties can be altered after DataWorker is instantiated.
To change the name of column_a
to column_a1
:
dw.alterColumnName("column_a", "column_a1");
To change the title of column_a
to Things I Love
:
dw.alterColumnTitle("column_a", "Things I Love");
To change the aggregate type of column_a
to min
:
dw.alterColumnAggregateType("column_a", "min");
Valid aggregate types are:
max
min
sum
To change the sort type of column_a
to num
:
dw.alterColumnSortType("column_a", "num");
Valid sort types are:
alpha
num
Alternatively, you may pass in a sort function that takes two arguments (a, b)
and returns -1
for a < b
, 1
for a > b
or 0
for a == b
.
To prepend a_
to all column names:
dw.prependColumnNames("a_");
The append
method is used to concatenate two datasets together. The following appends dataset2
to dataset1
:
var dataset1 = [ [ "column_a", "column_b", "column_c" ], [ "apple", "violin", "music" ], [ "cat", "tissue", "dog" ], [ "banana", "piano", "gum" ], ]; var dataset2 = [ [ "column_a", "column_b", "column_c" ], [ "gummy", "power", "apple" ], [ "car", "screen", "phone" ], [ "sign", "bagel", "chips" ] ]; var dw = new DataWorker(dataset1); dw.append(dataset2);
Alternatively, you may also append a DataWorker dataset:
var dataset1 = [ [ "column_a", "column_b", "column_c" ], [ "apple", "violin", "music" ], [ "cat", "tissue", "dog" ], [ "banana", "piano", "gum" ] ]; var dataset2 = [ [ "column_a", "column_b", "column_c" ], [ "gummy", "power", "apple" ], [ "car", "screen", "phone" ], [ "sign", "bagel", "chips" ] ]; var d1 = new DataWorker(dataset1); var d2 = new DataWorker(dataset2); d1.append(d2);
Note that column names must match up; an error will be thrown otherwise.
The applyFilter
method is used to filter out rows that do not contain the specified regex. The following filters out any row that does not contain the word "apple":
var dataset = [ [ "column_a", "column_b", "column_c" ], [ "apple", "red", "fuji" ], [ "apple", "green", "granny smith" ], [ "apple", "yellow", "golden delicious" ], [ "banana", "green", "unripe" ], [ "banana", "yellow", "ripe" ], [ "banana", "brown", "beyond ripe" ], [ "banana", "black", "rotten/frozen" ] ], dw = new DataWorker(dataset); dw.applyFilter(/\bapple\b/) /* This results in the following rows: [ "apple", "red", "fuji" ], [ "apple", "green", "granny smith" ], [ "apple", "yellow", "golden delicious" ] */
You may also filter only on certain columns:
dw.applyFilter(/\bapple\b/, "column_a", "column_b");
Note that the following also works (and results in the exact same dataset):
dw.applyFilter(/\bapple\b/, [ "column_a", "column_b" ]);
You may also use the complex syntax, in which all filters provided must find a match for the row to be visible. The complex filters can take any of the following arguments:
columns
(array of strings or single string): Columns on which to filter (defaults to all columns)column
(single string): Single column on which to filter (defaults to all columns) (note: if both columns
and column
are defined, the latter will be used)matchAll
(boolean): If this flag is set to true
then all columns provided must match the filter for the row to stay visible. By default, if any of the columns match the row will stay visibleaccentInsensitive
(boolean): If this flag is set to true
then characters with accent marks will be treated as normal ascii character (e.g., applé matches both applé and apple).regex
(string or RegExp): Columns must match this regular expression (if a string is provided it will be converted to a RegExp)!regex
(string or RegExp): Columns must not match this regular expression (if a string is provided it will be converted to a RegExp)eq
(value): Columns must equal (==) this valuene
(value): Columns must not equal (!=) this valuegte
(value): Columns must be greater than or equal to (>=) this valuegt
(value): Columns must be greater than (>) this valuelte
(value): Columns must be lesser than or equal to (<=) this valuelt
(value): Columns must be lesser than (<) this valueFor example:
dw.applyFilter( { column : "column_a", eq : "apple" }, { column : "column_b", regex : /yellow/, "!regex" : /blue/ }, { columns : [ "column_a", "column_b" ], gte: "apple", lt: "zebra" } ); /* This results in the single row: [ "apple", "yellow", "golden delicious" ] */
The filter can be cleared by calling the clearFilters
method:
dw.clearFilters();
Filters stack on top of each other:
dw.applyFilter(/banana/); /* This results in the following rows: [ "banana", "green", "unripe" ], [ "banana", "yellow", "ripe" ], [ "banana", "brown", "beyond ripe" ], [ "banana", "black", "rotten/frozen" ] */ dw.applyFilter(/yellow/); /* The results are further filtered down to one row: [ "banana", "yellow", "ripe" ], */ dw.clearFilters().applyFilter(/yellow/); /* Old filters are cleared and a new one is applied: [ "apple", "yellow", "golden delicious" ], [ "banana", "yellow", "ripe" ], */
To permanently remove rows from a dataset with a filter, use the filter
method:
dw.filter(/\bapple\b/)
Similar to filters, the search
method filters the currently visible dataset. The difference is that this will not modify the dataset. The simple form takes a callback and a search term, which can be either a regular expression, a string, or an array of complex filters.
dw.search(function (results) { console.log(results); }, /apple/);
It's also possible to pass in extra, optional arguments as an object. The valid options are:
columns
(array of strings or single string): Columns to be searched and returned (defaults to all columns)searchOn
(array of strings or single string): Columns to be searched (defaults to columns
, which defaults to all columns)returnColumns
(array of strings or single string): Columns to be returned (defaults to columns
, which defaults to all columns)sortOn
(array of strings or single string): Rows will be sorted on this/these columns, which can be invisible columnslimit
(integer): Maximum number of rows to return. Data will be sorted before limiting resultsfromRow
(integer): Indicates the zero-based row number of the result set to start returning. This can be used, for example, in conjunction with limit
to get paged results.allRows
(boolean): Allows the search to include hidden rows. Defaults to false
.getDistinct
(boolean): Only returns rows that are unique from each other. Defaults to false
.For example:
dw.search(function (results) { console.log(results); }, /a.*le/i, { columns: [ "column_a", "column_c" ], sortOn: "-column_b", fromRow: 6, limit: 3 });
Similar to grouping in SQL, the group
method allows you to group rows together.
dw.group("column_a");
You may also group by multiple rows:
dw.group("column_a", "column_b");
Note that the following is also valid:
dw.group([ "column_a", "column_b" ]);
Rows with the same value for the specified column(s) will be combined; the column property aggType
determines how values for non-specified columns are combined.
DataWorker also supports joining via the join
method. It can inner join, left outer join, or right outer join.
The following inner joins d1
with d2
on column_a
from d1
and column_d
from d2
:
d1.join(d2, "column_a", "column_d");
The following left outer joins d1
with d2
on column_a
from d1
and column_d
from d2
:
d1.join(d2, "column_a", "column_d", "left");
The following right outer joins d1
with d2
on column_a
from d1
and column_d
from d2
:
d1.join(d2, "column_a", "column_d", "right");
Joins can also be performed on multiple columns:
d1.join(d2, [ "column_a", "column_b" ], [ "column_d", "column_e" ]);
The applyLimit
method limits the amount of visible rows in the dataset. The following allows only the first 10 rows in the dataset to be visible:
dw.applyLimit(10);
The limit can be cleared by calling the clearFilters
method:
dw.clearFilters();
To permanently remove rows from a dataset with a limit, use the limit
method:
dw.limit(10)
You may completely delete columns from a dataset with the removeColumns
method.
dw.removeColumns("column_a", "column_b");
Note that the following is also valid:
dw.removeColumns([ "column_a", "column_b" ]);
Instead of permanently deleting the columns, you may also temporarily hide them from view using the hideColumns
method:
dw.hideColumns("column_a", "column_b");
Note that the following is also valid:
dw.hideColumns([ "column_a", "column_b" ]);
Hidden columns can be shown with the showColumns
method:
dw.showColumns("column_a", "column_b");
Note that the following is also valid:
dw.showColumns([ "column_a", "column_b" ]);
The hideColumns
and showColumns
methods may also take a regex as an argument. Any column name matching the regex will be hidden/shown, respectively.
dw.hideColumns(/^column_[ab]$/i);
All columns can be hidden with the hideAllColumns
method:
dw.hideAllColumns();
All hidden columns can be revealed with the showAllColumns
method:
dw.showAllColumns();
Alternatively, you may retrieve all columns (visible AND non-visible) by using the getAllColumns
method:
dw.getAllColumns(function (columns) { allColumns = columns; });
If you want to completely clear the dataset so that you can add new data while leaving any custom handlers intact, you may call clearDataset
.
dw.clearDataset();
This will remove references to all columns and rows. The function takes no parameters.
The following sorts the dataset on column_a
:
dw.sort("column_a")
To reverse sort, prepend the column name with a -
:
dw.sort("-column_a")
You may also sort on multiple columns:
dw.sort("column_a", "-column_b");
In this case, the sort will fallback to column_b
if the contents of column_a
are equal.
Note that the following does the same thing:
dw.sort([ "column_a", "-column_b" ]);
When child rows exist in the dataset, parents and children are kept together. The dataset will be sorted first by the parent, then by the children, using the same column. Using the example from Add Child Rows, dw.sort("-numbers");
will produce the following dataset:
[ [ "xyz", 789 ], [ "xyz", 789 ], [ "abc", 123 ], [ "abc", 579 ], [ "abc", 456 ], [ "def", 0 ] ]
addChildRows
. The columns must be the same as the original dataset as in the following example:
var dataset = [ [ "letters", "numbers" ], [ "abc", 579 ], [ "def", 0 ], [ "xyz", 789 ] ], childRows = [ [ "abc", 123 ], [ "abc", 456 ], [ "xyz", 789 ] ], dw = DataWorker(dataset);
The call to addChildRows
expects a column that will be used to determine to which row the children belong. It also expects a dataset of child rows.
You may either pass in an array of rows:
dw.addChildRows(childRows, "letters");
or you may pass in another DataWorker object:
var d2 = DataWorker([[ "letters", "numbers" ]].concat(childRows), "letters"); dw.addChildRows(d2);
In either case, the result passed into the callback for getRows
will be the same:
[ [ "abc", 123 ], [ "abc", 456 ], [ "abc", 579 ], [ "def", 0 ], [ "xyz", 789 ], [ "xyz", 789 ] ]
The default visibility for a child row depends on its parent. If the parent row was set to hidden then the child row will still be added to the parent, but will be hidden as well.
If a parent row cannot be found for the children, those child rows will be ignored. If multiple parent rows exist for a given child row, the result is undefined.
Visible dataset rows can be retrieved for use via the getRows
method.
If called with just the callback function, getRows
will get all rows. The next two arguments are the start and end of the range. If unspecified, they are the start and end of the dataset. These arguments are 0-based, so a dataset with 15 rows will have rows 0 - 14. If a number larger than the last row is used DataWorker will simply return anything in the range up to (and including) the last row.
var dataset = [ [ "column_a", "column_b", "column_c" ], [ "apple", "violin", "music" ], [ "cat", "tissue", "dog" ], [ "banana", "piano", "gum" ], [ "gummy", "power", "star" ] ]; var dw = new DataWorker(dataset); var records; dw.getRows(function (result) { records = result; });
The following is the contents of records
:
[ [ "apple", "violin", "music" ], [ "cat", "tissue", "dog" ], [ "banana", "piano", "gum" ], [ "gummy", "power", "star" ] ]
If you would only like certain columns, you may provide those columns after the range of rows, either as an array or as extra arguments. All of the following calls to getRows are valid examples:
var callback = function (rows) { /* Do something */ }; dw.getRows(callback); dw.getRows(callback, 5); dw.getRows(callback, undefined, 10); dw.getRows(callback, 5, 10); dw.getRows(callback, 5, 10, "column_a", "column_b"); dw.getRows(callback, 5, 10, [ "column_a", "column_b" ]); dw.getRows(callback, undefined, undefined, [ "column_a", "column_b" ]);
The getHashedRows
function makes it possible to get records as a hash with the column names instead of as a simple array. It works exactly the same as getRows
but returns the data in a different format. In the following example
var dataset = [ [ "column_a", "column_b", "column_c" ], [ "apple", "violin", "music" ], [ "cat", "tissue", "dog" ], [ "banana", "piano", "gum" ], [ "gummy", "power", "star" ] ]; var dw = new DataWorker(dataset); var records; dw.getHashedRows(function (result) { records = result; });
the contents of records
will be:
[ { "column_a": "apple", "column_b": "violin", "column_c": "music" }, { "column_a": "cat", "column_b": "tissue", "column_c": "dog" }, { "column_a": "banana", "column_b": "piano", "column_c": "gum" }, { "column_a": "gummy", "column_b": "power", "column_c": "star" } ]
The getColumns
method is used to get the visible columns of the dataset:
dw.getColumns(function (columns) { visibleColumns = columns; });
Use getAllColumns
to retrieve both visible and non-visible columns.
Visible columns may be retrieved simultaneously with visible records with getColumnsAndRecords
.
dw.getColumnsAndRecords(function (columns, records) { // Do something. });
Columns will be given as a dictionary with the columnName
as the key and its properties (also in a dictionary) as the value.
Records will be returned the same as in getRows
.
The getNumberOfRecords
returns the number of visible rows currently in the dataset:
dw.getNumberOfRecords(function (num) { numberOfRows = num; });
Note that for streaming datasets, this value will be the current number of rows it has (and not the total number of rows expected). Use getExpectedNumberOfRecords
to determine the total number of rows in a streaming dataset.
The getExpectedNumberOfRecords
method returns the expected number of records in a streaming dataset.
dw.getExpectedNumberOfRecords(function (num) { expectedNumberOfRows = num; });
Distinct rows can be retrieved using getDistinctConsecutiveRows
. The function takes a callback
and columnName
as its parameters:
function doSomethingInteresting(records) { } dw.getDistinctConsecutiveRows(doSomethingInteresting, "column_a");
The value passed into the callback is an array of records. Each record contains three values: value
of the column, startRow
of that value, and endRow
of that value.
If values are repeated again later, but not consecutively, another record will exist in the results. For example, with the following dataset:
[ [ "column_a", "column_b" ] [ "abc", "123" ], [ "abc", "456" ], [ "abc", "789" ], [ "def", "123" ], [ "ghi", "123" ], [ "ghi", "456" ], [ "def", "456" ], [ "def", "789" ] ]
the function getDistinctConsecutiveRows
will pass the following records
into the callback as a sole parameter if "column_a" were passed in as columnName
:
[ [ "abc", 0, 2 ], [ "def", 3, 3 ], [ "ghi", 4, 5 ], [ "def", 6, 7 ] ]
but if "column_b" were passed in as columnName
then the results would be:
[ [ "123", 0, 0 ], [ "456", 1, 1 ], [ "789", 2, 2 ], [ "123", 3, 4 ], [ "456", 5, 6 ], [ "789", 7, 7 ] ]
The number of records returned will always be equal to or less than the number of records in the original dataset.
Pagination eases incremental access of the records. The following sets DataWorker to display 10 rows per page
dw.paginate(10);
Now you may grab the next 10 rows using the getNextPage
method:
var next10Rows, currentPage; dw.getNextPage(function (result, pageNumber) { next10Rows = result; currentPage = pageNumber; });
The callback will provide the requested rows as well as the current page number.
The pagination system will not let you change to a page outside of the dataset. Grabbing the next page when you're on the last page will still return the last page.
The previous 10 rows can be grabbed using the getPreviousPage
method:
var previous10Rows, currentPage; dw.getPreviousPage(function (result, pageNumber) { previous10Rows = result; currentPage = pageNumber; });
Grabbing a previous page from the 1st page will simply return the 1st page again.
You may jump to a specific page using the getPage
method. The following grabs page 4:
var page, currentPage; dw.getPage(function (result, pageNumber) { page = result; currentPage = pageNumber; // pageNumber == 4 }, 4);
Note that this also sets your current page to the page you grab.
The previous three functions (getNextPage
, getPreviousPage
, and getPage
) can all take extra arguments defining which columns to return. This will allow you to get a reduced dataset or specify columns that would normally be hidden. The columns can be defined as additional names, or as an array of names. The following are both valid:
var callback = function (result, pageNumber) { /* Do something */ }; dw.getNextPage(callback, "column_a", "column_b"); dw.getNextPage(callback, [ "column_b", "column_c" ]);
Use the setPage
method to set a new current page. Attempting to set the page to 0 or a negative number will set the page to page 1. Setting to a page past the max number of current pages will set the page to the last page. The following sets your current page to page 4:
dw.setPage(4);
Use the getNumberOfPages
method to get the total number of pages in the dataset with the current pagination. Note that this is also the same as the last page in the dataset.
var lastPage; dw.getNumberOfPages(function (totalNumberOfPages) { lastPage = totalNumberOfPages; });
DataWorker can partition its dataset into multiple smaller datasets. The partitioned datasets can be retrieved afterwards using their partition key(s). The following partitions the dataset by the contents of column_a
and uses the getPartitioned
method to push each partition onto the partitioned
array:
var partitioned = []; dw.partition("column_a"); dw.getPartitionKeys(function (keys) { keys.forEach(function (key) { dw.getPartitioned(function (result) { partitioned.push(result); }, key); }); });
They getPartitioned
method returns an array of records.
You may also partition by multiple keys:
dw.partition("column_a", "column_b");
The following does the same thing:
dw.partition([ "column_a", "column_b" ]);
You may also sort partitions with the sortPartition
method:
dw.sortPartition(partitionKey, columnsToSortOn);
The render
method allows you to pass DataWorker a function to render the dataset. When render
is called without arguments, DataWorker will call the rendering function that the user passed in, or do nothing if the user has not set a rendering function.
dw.render(function () { /* code for rendering the dataset */ }); /* Make some changes to the dataset. */ dw.render(); // Renders the new dataset according to the user-defined function.
To generate the distribution files, we use the node module grunt
. If you would like to play around with the source and create your own distribution files, you must first have node.js and NPM installed on your machine. From the DataWorker's root directory, run the command npm install
in order to get the latest dev dependencies for the package. Then type grunt dist
to generate the distribution files. You may also type grunt watch
instead, which will automatically generate new distribution files whenever you change one of the source files.