diff --git a/zkwebui/WEB-INF/src/metainfo/zk/lang-addon.xml b/zkwebui/WEB-INF/src/metainfo/zk/lang-addon.xml
index a5e48881f3..4619a69ce6 100644
--- a/zkwebui/WEB-INF/src/metainfo/zk/lang-addon.xml
+++ b/zkwebui/WEB-INF/src/metainfo/zk/lang-addon.xml
@@ -24,5 +24,7 @@ Copyright (C) 2007 Ashley G Ramdass (ADempiere WebUI).
-
+
+
+
diff --git a/zkwebui/WEB-INF/src/org/adempiere/webui/AdempiereWebUI.java b/zkwebui/WEB-INF/src/org/adempiere/webui/AdempiereWebUI.java
index ffe8a71d43..fa4b81eae9 100644
--- a/zkwebui/WEB-INF/src/org/adempiere/webui/AdempiereWebUI.java
+++ b/zkwebui/WEB-INF/src/org/adempiere/webui/AdempiereWebUI.java
@@ -25,15 +25,19 @@ import javax.servlet.http.HttpSession;
import org.adempiere.webui.apps.AEnv;
import org.adempiere.webui.component.DrillCommand;
+import org.adempiere.webui.component.TokenCommand;
import org.adempiere.webui.component.ZoomCommand;
import org.adempiere.webui.desktop.DefaultDesktop;
import org.adempiere.webui.desktop.IDesktop;
+import org.adempiere.webui.event.TokenEvent;
import org.adempiere.webui.session.SessionManager;
import org.adempiere.webui.theme.ThemeManager;
+import org.adempiere.webui.util.BrowserToken;
import org.adempiere.webui.util.UserPreference;
import org.compiere.model.MRole;
import org.compiere.model.MSession;
import org.compiere.model.MSysConfig;
+import org.compiere.model.MUser;
import org.compiere.util.CLogger;
import org.compiere.util.Env;
import org.compiere.util.Language;
@@ -160,7 +164,7 @@ public class AdempiereWebUI extends Window implements EventListener, IWebClient
Session currSess = Executions.getCurrent().getDesktop().getSession();
HttpSession httpSess = (HttpSession) currSess.getNativeSession();
- MSession.get (ctx, currSess.getRemoteAddr(),
+ MSession mSession = MSession.get (ctx, currSess.getRemoteAddr(),
currSess.getRemoteHost(), httpSess.getId() );
//enable full interface, relook into this when doing preference
@@ -239,6 +243,16 @@ public class AdempiereWebUI extends Window implements EventListener, IWebClient
ExecutionCarryOver eco = new ExecutionCarryOver(this.getPage().getDesktop());
currSess.setAttribute("execution.carryover", eco);
}
+
+ if ("Y".equalsIgnoreCase(Env.getContext(ctx, BrowserToken.REMEMBER_ME)))
+ {
+ MUser user = MUser.get(ctx);
+ BrowserToken.save(mSession, user);
+ }
+ else
+ {
+ BrowserToken.remove();
+ }
}
private void createDesktop()
@@ -326,5 +340,6 @@ public class AdempiereWebUI extends Window implements EventListener, IWebClient
new ZoomCommand("onZoom", Command.IGNORE_OLD_EQUIV);
new DrillCommand("onDrillAcross", Command.IGNORE_OLD_EQUIV);
new DrillCommand("onDrillDown", Command.IGNORE_OLD_EQUIV);
+ new TokenCommand(TokenEvent.ON_USER_TOKEN, Command.IGNORE_OLD_EQUIV);
}
}
diff --git a/zkwebui/WEB-INF/src/org/adempiere/webui/component/TokenCommand.java b/zkwebui/WEB-INF/src/org/adempiere/webui/component/TokenCommand.java
new file mode 100644
index 0000000000..a5531e33cd
--- /dev/null
+++ b/zkwebui/WEB-INF/src/org/adempiere/webui/component/TokenCommand.java
@@ -0,0 +1,50 @@
+/******************************************************************************
+ * Copyright (C) 2009 Low Heng Sin *
+ * Copyright (C) 2009 Idalica Corporation *
+ * This program is free software; you can redistribute it and/or modify it *
+ * under the terms version 2 of the GNU General Public License as published *
+ * by the Free Software Foundation. This program is distributed in the hope *
+ * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied *
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
+ * See the GNU General Public License for more details. *
+ * You should have received a copy of the GNU General Public License along *
+ * with this program; if not, write to the Free Software Foundation, Inc., *
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
+ *****************************************************************************/
+package org.adempiere.webui.component;
+
+import org.adempiere.webui.event.TokenEvent;
+import org.zkoss.lang.Objects;
+import org.zkoss.zk.au.AuRequest;
+import org.zkoss.zk.au.Command;
+import org.zkoss.zk.mesg.MZk;
+import org.zkoss.zk.ui.Component;
+import org.zkoss.zk.ui.UiException;
+import org.zkoss.zk.ui.event.Events;
+
+/**
+ * Command class to handle user authentication token event
+ * @author hengsin
+ *
+ */
+public class TokenCommand extends Command {
+
+ public TokenCommand(String id, int flags) {
+ super(id, flags);
+ }
+
+ @Override
+ protected void process(AuRequest request) {
+ final String[] data = request.getData();
+
+ final Component comp = request.getComponent();
+ if (comp == null)
+ throw new UiException(MZk.ILLEGAL_REQUEST_COMPONENT_REQUIRED, this);
+
+ if (data == null || data.length < 2)
+ throw new UiException(MZk.ILLEGAL_REQUEST_WRONG_DATA, new Object[] {
+ Objects.toString(data), this });
+
+ Events.postEvent(new TokenEvent(getId(), comp, data));
+ }
+}
diff --git a/zkwebui/WEB-INF/src/org/adempiere/webui/event/TokenEvent.java b/zkwebui/WEB-INF/src/org/adempiere/webui/event/TokenEvent.java
new file mode 100644
index 0000000000..de046cc48c
--- /dev/null
+++ b/zkwebui/WEB-INF/src/org/adempiere/webui/event/TokenEvent.java
@@ -0,0 +1,38 @@
+/******************************************************************************
+ * Copyright (C) 2009 Low Heng Sin *
+ * Copyright (C) 2009 Idalica Corporation *
+ * This program is free software; you can redistribute it and/or modify it *
+ * under the terms version 2 of the GNU General Public License as published *
+ * by the Free Software Foundation. This program is distributed in the hope *
+ * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied *
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
+ * See the GNU General Public License for more details. *
+ * You should have received a copy of the GNU General Public License along *
+ * with this program; if not, write to the Free Software Foundation, Inc., *
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
+ *****************************************************************************/
+package org.adempiere.webui.event;
+
+import org.zkoss.zk.ui.Component;
+import org.zkoss.zk.ui.event.Event;
+
+/**
+ * Event for user authentication token
+ * @author hengsin
+ *
+ */
+public class TokenEvent extends Event {
+
+ /** on loading of user token **/
+ public final static String ON_USER_TOKEN = "onUserToken";
+
+ /**
+ * @param name
+ * @param target
+ * @param data
+ */
+ public TokenEvent(String name, Component target, Object data) {
+ super(name, target, data);
+ }
+
+}
diff --git a/zkwebui/WEB-INF/src/org/adempiere/webui/panel/AbstractADWindowPanel.java b/zkwebui/WEB-INF/src/org/adempiere/webui/panel/AbstractADWindowPanel.java
index 54c9943481..bb6bf476ac 100644
--- a/zkwebui/WEB-INF/src/org/adempiere/webui/panel/AbstractADWindowPanel.java
+++ b/zkwebui/WEB-INF/src/org/adempiere/webui/panel/AbstractADWindowPanel.java
@@ -1347,8 +1347,7 @@ public abstract class AbstractADWindowPanel extends AbstractUIPart implements To
String msg = CLogger.retrieveErrorString(null);
if (msg != null)
{
- FDialog.error(curWindowNo, parent, null, msg);
- statusBar.setStatusLine(Msg.getMsg(Env.getCtx(), "SaveIgnored"), true, false);
+ statusBar.setStatusLine(Msg.getMsg(Env.getCtx(), msg), true, true);
}
//other error will be catch in the dataStatusChanged event
}
diff --git a/zkwebui/WEB-INF/src/org/adempiere/webui/panel/LoginPanel.java b/zkwebui/WEB-INF/src/org/adempiere/webui/panel/LoginPanel.java
index 254c35dddb..23bf8efb94 100644
--- a/zkwebui/WEB-INF/src/org/adempiere/webui/panel/LoginPanel.java
+++ b/zkwebui/WEB-INF/src/org/adempiere/webui/panel/LoginPanel.java
@@ -34,13 +34,17 @@ import org.adempiere.webui.component.ConfirmPanel;
import org.adempiere.webui.component.Label;
import org.adempiere.webui.component.Textbox;
import org.adempiere.webui.component.Window;
+import org.adempiere.webui.event.TokenEvent;
import org.adempiere.webui.exception.ApplicationException;
import org.adempiere.webui.session.SessionManager;
import org.adempiere.webui.theme.ITheme;
import org.adempiere.webui.theme.ThemeManager;
+import org.adempiere.webui.util.BrowserToken;
import org.adempiere.webui.util.UserPreference;
import org.adempiere.webui.window.LoginWindow;
import org.compiere.Adempiere;
+import org.compiere.model.MSession;
+import org.compiere.model.MUser;
import org.compiere.util.DB;
import org.compiere.util.Env;
import org.compiere.util.KeyNamePair;
@@ -63,6 +67,7 @@ import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.util.Clients;
+import org.zkoss.zul.Checkbox;
import org.zkoss.zul.Comboitem;
import org.zkoss.zul.Image;
@@ -75,11 +80,11 @@ import org.zkoss.zul.Image;
* @date July 18, 2007
*/
public class LoginPanel extends Window implements EventListener
-{
+{
/**
- *
+ *
*/
- private static final long serialVersionUID = -2243984359460922023L;
+ private static final long serialVersionUID = 3992171368813030624L;
private static final String RESOURCE = "org.compiere.apps.ALoginRes";
private ResourceBundle res = ResourceBundle.getBundle(RESOURCE);
@@ -91,6 +96,7 @@ public class LoginPanel extends Window implements EventListener
private Textbox txtPassword;
private Combobox lstLanguage;
private LoginWindow wndLogin;
+ private Checkbox chkRememberMe;
public LoginPanel(Properties ctx, LoginWindow loginWindow)
{
@@ -102,6 +108,8 @@ public class LoginPanel extends Window implements EventListener
AuFocus auf = new AuFocus(txtUserId);
Clients.response(auf);
+
+ BrowserToken.load(this.getUuid());
}
private void init()
@@ -167,6 +175,18 @@ public class LoginPanel extends Window implements EventListener
tr.appendChild(td);
td.appendChild(lstLanguage);
+ tr = new Tr();
+ tr.setId("rowRememberMe");
+ table.appendChild(tr);
+ td = new Td();
+ tr.appendChild(td);
+ td.setSclass(ITheme.LOGIN_LABEL_CLASS);
+ td.appendChild(new Label(""));
+ td = new Td();
+ td.setSclass(ITheme.LOGIN_FIELD_CLASS);
+ tr.appendChild(td);
+ td.appendChild(chkRememberMe);
+
div = new Div();
div.setSclass(ITheme.LOGIN_BOX_FOOTER_CLASS);
ConfirmPanel pnlButtons = new ConfirmPanel(false);
@@ -176,6 +196,34 @@ public class LoginPanel extends Window implements EventListener
pnlButtons.getButton(ConfirmPanel.A_OK).setSclass(ITheme.LOGIN_BUTTON_CLASS);
div.appendChild(pnlButtons);
this.appendChild(div);
+
+ this.addEventListener(TokenEvent.ON_USER_TOKEN, new EventListener() {
+
+ @Override
+ public void onEvent(Event event) throws Exception {
+ String[] data = (String[]) event.getData();
+ int AD_Session_ID = Integer.parseInt(data[0]);
+ MSession session = new MSession(Env.getCtx(), AD_Session_ID, null);
+ if (session.get_ID() == AD_Session_ID)
+ {
+ int AD_User_ID = session.getCreatedBy();
+ MUser user = MUser.get(Env.getCtx(), AD_User_ID);
+ if (user != null && user.get_ID() == AD_User_ID)
+ {
+ String token = data[1];
+ if (BrowserToken.validateToken(session, user, token))
+ {
+ txtUserId.setValue(user.getName());
+ onUserIdChange();
+ txtPassword.setValue(token);
+ txtPassword.setAttribute("user.token.hash", token);
+ txtPassword.setAttribute("user.token.sid", AD_Session_ID);
+ chkRememberMe.setChecked(true);
+ }
+ }
+ }
+ }
+ });
}
private void initComponents()
@@ -203,7 +251,7 @@ public class LoginPanel extends Window implements EventListener
txtPassword.setId("txtPassword");
txtPassword.setType("password");
txtPassword.setCols(25);
- txtPassword.setMaxlength(40);
+// txtPassword.setMaxlength(40);
txtPassword.setWidth("220px");
lstLanguage = new Combobox();
@@ -233,6 +281,9 @@ public class LoginPanel extends Window implements EventListener
break;
}
}
+
+ //TODO: localization
+ chkRememberMe = new Checkbox("Remember Me");
}
public void onEvent(Event event)
@@ -255,32 +306,36 @@ public class LoginPanel extends Window implements EventListener
{
if(eventComp.getId().equals(txtUserId.getId()))
{
- String userId = txtUserId.getValue();
- if(userId != null && userId.length() > 0)
- {
- int AD_User_ID = DB.getSQLValue(null, "SELECT AD_User_ID FROM AD_User WHERE Name = ?", userId);
- if(AD_User_ID > 0)
- {
- // Elaine 2009/02/06 Load preference from AD_Preference
- UserPreference userPreference = SessionManager.getSessionApplication().loadUserPreference(AD_User_ID);
- String initDefault = userPreference.getProperty(UserPreference.P_LANGUAGE);
- for(int i = 0; i < lstLanguage.getItemCount(); i++)
- {
- Comboitem li = lstLanguage.getItemAtIndex(i);
- if(li.getLabel().equals(initDefault))
- {
- lstLanguage.setSelectedIndex(i);
- languageChanged(li.getLabel()); // Elaine 2009/04/17 language changed
- break;
- }
- }
- }
- }
+ onUserIdChange();
}
}
//
}
+ private void onUserIdChange() {
+ String userId = txtUserId.getValue();
+ if(userId != null && userId.length() > 0)
+ {
+ int AD_User_ID = DB.getSQLValue(null, "SELECT AD_User_ID FROM AD_User WHERE Name = ?", userId);
+ if(AD_User_ID > 0)
+ {
+ // Elaine 2009/02/06 Load preference from AD_Preference
+ UserPreference userPreference = SessionManager.getSessionApplication().loadUserPreference(AD_User_ID);
+ String initDefault = userPreference.getProperty(UserPreference.P_LANGUAGE);
+ for(int i = 0; i < lstLanguage.getItemCount(); i++)
+ {
+ Comboitem li = lstLanguage.getItemAtIndex(i);
+ if(li.getLabel().equals(initDefault))
+ {
+ lstLanguage.setSelectedIndex(i);
+ languageChanged(li.getLabel()); // Elaine 2009/04/17 language changed
+ break;
+ }
+ }
+ }
+ }
+ }
+
private void languageChanged(String langName)
{
Language language = findLanguage(langName);
@@ -309,6 +364,24 @@ public class LoginPanel extends Window implements EventListener
Login login = new Login(ctx);
String userId = txtUserId.getValue();
String userPassword = txtPassword.getValue();
+
+ //check is token
+ String token = (String) txtPassword.getAttribute("user.token.hash");
+ if (token != null && token.equals(userPassword))
+ {
+ userPassword = "";
+ int AD_Session_ID = (Integer)txtPassword.getAttribute("user.token.sid");
+ MSession session = new MSession(Env.getCtx(), AD_Session_ID, null);
+ if (session.get_ID() == AD_Session_ID)
+ {
+ MUser user = MUser.get(Env.getCtx(), session.getCreatedBy());
+ if (BrowserToken.validateToken(session, user, token))
+ {
+ userPassword = user.getPassword();
+ }
+ }
+ }
+
KeyNamePair rolesKNPairs[] = login.getRoles(userId, userPassword);
if(rolesKNPairs == null || rolesKNPairs.length == 0)
throw new WrongValueException("User Id or Password invalid!!!");
@@ -348,6 +421,8 @@ public class LoginPanel extends Window implements EventListener
Session currSess = Executions.getCurrent().getDesktop().getSession();
currSess.setAttribute("Check_AD_User_ID", Env.getAD_User_ID(ctx));
// End of temporary code for [ adempiere-ZK Web Client-2832968 ] User context lost?
+
+ Env.setContext(ctx, BrowserToken.REMEMBER_ME, chkRememberMe.isChecked());
}
}
diff --git a/zkwebui/WEB-INF/src/org/adempiere/webui/util/BrowserToken.java b/zkwebui/WEB-INF/src/org/adempiere/webui/util/BrowserToken.java
new file mode 100644
index 0000000000..4205a83129
--- /dev/null
+++ b/zkwebui/WEB-INF/src/org/adempiere/webui/util/BrowserToken.java
@@ -0,0 +1,136 @@
+/******************************************************************************
+ * Copyright (C) 2009 Low Heng Sin *
+ * Copyright (C) 2009 Idalica Corporation *
+ * This program is free software; you can redistribute it and/or modify it *
+ * under the terms version 2 of the GNU General Public License as published *
+ * by the Free Software Foundation. This program is distributed in the hope *
+ * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied *
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
+ * See the GNU General Public License for more details. *
+ * You should have received a copy of the GNU General Public License along *
+ * with this program; if not, write to the Free Software Foundation, Inc., *
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
+ *****************************************************************************/
+package org.adempiere.webui.util;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.logging.Level;
+
+import org.compiere.Adempiere;
+import org.compiere.model.MSession;
+import org.compiere.model.MUser;
+import org.compiere.util.CLogger;
+import org.zkoss.zk.au.out.AuScript;
+import org.zkoss.zk.ui.util.Clients;
+
+import sun.misc.BASE64Encoder;
+
+/**
+ * class to manage browser token for auto authentication
+ * @author hengsin
+ *
+ */
+public final class BrowserToken {
+
+ private final static CLogger log = CLogger.getCLogger(BrowserToken.class);
+
+ private BrowserToken() {}
+
+ public final static String REMEMBER_ME = "Login.RememberMe";
+ /**
+ * save session and user as client side token for future auto login
+ * @param session
+ * @param user
+ */
+ public static void save(MSession session, MUser user) {
+ try
+ {
+ String home = getHomeToken();
+ String hash = getPasswordHash(session, user);
+ String script = "adempiere.saveUserToken('" + home + "', '" + hash + "', '" + session.getAD_Session_ID() + "');";
+ AuScript aus = new AuScript(null, script);
+ Clients.response("saveUserToken", aus);
+ }
+ catch (Exception e)
+ {
+ log.log(Level.WARNING, e.getLocalizedMessage(), e);
+ }
+ }
+
+ /**
+ * remove client side token for auto login
+ */
+ public static void remove() {
+ try {
+ String home = getHomeToken();
+ String script = "adempiere.removeUserToken('" + home + "');";
+ AuScript aus = new AuScript(null, script);
+ Clients.response("removeUserToken", aus);
+ } catch (Exception e) {
+ log.log(Level.WARNING, e.getLocalizedMessage(), e);
+ }
+ }
+
+ /**
+ * load stored client side token for auto login
+ * @param cmpid
+ */
+ public static void load(String cmpid) {
+ //remember me
+ try
+ {
+ String home = getHomeToken();
+ String script = "adempiere.findUserToken('" + cmpid + "', '" + home + "');";
+ AuScript aus = new AuScript(null, script);
+ Clients.response("findUserToken", aus);
+ }
+ catch (Exception e)
+ {
+ log.log(Level.WARNING, e.getLocalizedMessage(), e);
+ }
+ }
+
+ /**
+ * validate a stored client side token is valid
+ * @param session
+ * @param user
+ * @param token
+ * @return true if token is valid
+ */
+ public static boolean validateToken(MSession session, MUser user, String token) {
+ try
+ {
+ String hash = getPasswordHash(session, user);
+ return hash.equals(token);
+ }
+ catch (Exception e)
+ {
+ log.log(Level.WARNING, e.getLocalizedMessage(), e);
+ }
+ return false;
+ }
+
+ private static String getHomeToken() throws UnsupportedEncodingException {
+ String home = Adempiere.getAdempiereHome();
+ BASE64Encoder encoder = new BASE64Encoder();
+ home = encoder.encode(home.getBytes("UTF-8"));
+ home = URLEncoder.encode(home, "UTF-8");
+ return home;
+ }
+
+ private static String getPasswordHash(MSession session, MUser user) throws UnsupportedEncodingException, NoSuchAlgorithmException {
+ MessageDigest digest = MessageDigest.getInstance("SHA-512");
+ BASE64Encoder encoder = new BASE64Encoder();
+ digest.reset();
+ digest.update(session.getWebSession().getBytes("UTF-8"));
+ String password = user.getPassword();
+ byte[] input = digest.digest(password.getBytes("UTF-8"));
+ String hash = encoder.encode(input);
+ hash = URLEncoder.encode(hash, "UTF-8");
+
+ return hash;
+ }
+}
diff --git a/zkwebui/js/persist-min.js b/zkwebui/js/persist-min.js
new file mode 100644
index 0000000000..5a46b42e3f
--- /dev/null
+++ b/zkwebui/js/persist-min.js
@@ -0,0 +1,68 @@
+
+(function(){if(window.google&&google.gears)
+return;var F=null;if(typeof GearsFactory!='undefined'){F=new GearsFactory();}else{try{F=new ActiveXObject('Gears.Factory');if(F.getBuildInfo().indexOf('ie_mobile')!=-1)
+F.privateSetGlobalObject(this);}catch(e){if((typeof navigator.mimeTypes!='undefined')&&navigator.mimeTypes["application/x-googlegears"]){F=document.createElement("object");F.style.display="none";F.width=0;F.height=0;F.type="application/x-googlegears";document.documentElement.appendChild(F);}}}
+if(!F)
+return;if(!window.google)
+google={};if(!google.gears)
+google.gears={factory:F};})();Persist=(function(){var VERSION='0.2.0',P,B,esc,init,empty,ec;ec=(function(){var EPOCH='Thu, 01-Jan-1970 00:00:01 GMT',RATIO=1000*60*60*24,KEYS=['expires','path','domain'],esc=escape,un=unescape,doc=document,me;var get_now=function(){var r=new Date();r.setTime(r.getTime());return r;}
+var cookify=function(c_key,c_val){var i,key,val,r=[],opt=(arguments.length>2)?arguments[2]:{};r.push(esc(c_key)+'='+esc(c_val));for(i=0;i2)?arguments[2]:{},now=get_now(),expire_at,cfg={};if(opt.expires){opt.expires*=RATIO;cfg.expires=new Date(now.getTime()+opt.expires);cfg.expires=cfg.expires.toGMTString();}
+var keys=['path','domain','secure'];for(i=0;i0)
+fn.call(scope,true,r.rows.item(0)['v']);else
+fn.call(scope,false,null);});});},set:function(key,val,fn,scope){var rm_sql=C.sql.remove,sql=C.sql.set;this.transaction(function(t){t.executeSql(rm_sql,[key],function(){t.executeSql(sql,[key,val],function(t,r){if(fn)
+fn.call(scope||this,true,val);});});});return val;},remove:function(key,fn,scope){var get_sql=C.sql.get;sql=C.sql.remove;this.transaction(function(t){if(fn){t.executeSql(get_sql,[key],function(t,r){if(r.rows.length>0){var val=r.rows.item(0)['v'];t.executeSql(sql,[key],function(t,r){fn.call(scope||this,true,val);});}else{fn.call(scope||this,false,null);}});}else{t.executeSql(sql,[key]);}});}}},globalstorage:{size:5*1024*1024,test:function(){return window.globalStorage?true:false;},methods:{key:function(key){return esc(this.name)+esc(key);},init:function(){alert('domain = '+this.o.domain);this.store=globalStorage[this.o.domain];},get:function(key,fn,scope){key=this.key(key);if(fn)
+fn.call(scope||this,true,this.store.getItem(key));},set:function(key,val,fn,scope){key=this.key(key);this.store.setItem(key,val);if(fn)
+fn.call(scope||this,true,val);},remove:function(key,fn,scope){var val;key=this.key(key);val=this.store[key];this.store.removeItem(key);if(fn)
+fn.call(scope||this,(val!==null),val);}}},localstorage:{size:-1,test:function(){return window.localStorage?true:false;},methods:{key:function(key){return esc(this.name)+esc(key);},init:function(){this.store=localStorage;},get:function(key,fn,scope){key=this.key(key);if(fn)
+fn.call(scope||this,true,this.store.getItem(key));},set:function(key,val,fn,scope){key=this.key(key);this.store.setItem(key,val);if(fn)
+fn.call(scope||this,true,val);},remove:function(key,fn,scope){var val;key=this.key(key);val=this.getItem(key);this.store.removeItem(key);if(fn)
+fn.call(scope||this,(val!==null),val);}}},ie:{prefix:'_persist_data-',size:64*1024,test:function(){return window.ActiveXObject?true:false;},make_userdata:function(id){var el=document.createElement('div');el.id=id;el.style.display='none';el.addBehavior('#default#userdata');document.body.appendChild(el);return el;},methods:{init:function(){var id=B.ie.prefix+esc(this.name);this.el=B.ie.make_userdata(id);if(this.o.defer)
+this.load();},get:function(key,fn,scope){var val;key=esc(key);if(!this.o.defer)
+this.load();val=this.el.getAttribute(key);if(fn)
+fn.call(scope||this,val?true:false,val);},set:function(key,val,fn,scope){key=esc(key);this.el.setAttribute(key,val);if(!this.o.defer)
+this.save();if(fn)
+fn.call(scope||this,true,val);},remove:function(key,fn,scope){var val;key=esc(key);if(!this.o.defer)
+this.load();val=this.el.getAttribute(key);this.el.removeAttribute(key);if(!this.o.defer)
+this.save();if(fn)
+fn.call(scope||this,val?true:false,val);},load:function(){this.el.load(esc(this.name));},save:function(){this.el.save(esc(this.name));}}},cookie:{delim:':',size:4000,test:function(){return P.Cookie.enabled?true:false;},methods:{key:function(key){return this.name+B.cookie.delim+key;},get:function(key,fn,scope){var val;key=this.key(key);val=ec.get(key);if(fn)
+fn.call(scope||this,val!=null,val);},set:function(key,val,fn,scope){key=this.key(key);ec.set(key,val,this.o);if(fn)
+fn.call(scope||this,true,val);},remove:function(key,fn,scope){var val;key=this.key(key);val=ec.remove(key)
+if(fn)
+fn.call(scope||this,val!=null,val);}}},flash:{test:function(){if(typeof deconcept=="undefined"||!deconcept||!deconcept.SWFObjectUtil)
+return false;var major=deconcept.SWFObjectUtil.getPlayerVersion().major;return(major>=8)?true:false;},methods:{init:function(){if(!B.flash.el){var o,key,el,cfg=C.flash;el=document.createElement('div');el.id=cfg.div_id;document.body.appendChild(el);o=new deconcept.SWFObject(this.o.swf_path||cfg.path,cfg.id,cfg.size.w,cfg.size.h,'8');for(key in cfg.args)
+o.addVariable(key,cfg.args[key]);o.write(el);B.flash.el=document.getElementById(cfg.id);}
+this.el=B.flash.el;},get:function(key,fn,scope){var val;key=esc(key);val=this.el.get(this.name,key);if(fn)
+fn.call(scope||this,val!==null,val);},set:function(key,val,fn,scope){var old_val;key=esc(key);old_val=this.el.set(this.name,key,val);if(fn)
+fn.call(scope||this,true,val);},remove:function(key,fn,scope){var val;key=esc(key);val=this.el.remove(this.name,key);if(fn)
+fn.call(scope||this,true,val);}}}};var init=function(){var i,l,b,key,fns=C.methods,keys=C.search_order;for(i=0,l=fns.length;i