/*
   (c) 2009 Creative Development LLC. All rights reserved.
                 http://www.ecwid.com/
*/

function fixPNG(element, sizingMethod)
{
  if(!sizingMethod) sizingMethod = 'scale';

  if (/MSIE (5\.5|6).+Win/.test(navigator.userAgent))
  {
    var src;
	
    if (element.tagName=='IMG')
    {
      if (/\.png$/.test(element.src))
      {
        src = element.src;
        if (/siteUrl/.test("$siteUrl")) element.src = "blank.gif";
        else element.src = "$siteUrl/blank.gif";
      }
    }
    else
    {
      src = element.currentStyle.backgroundImage.match(/url\("(.+\.png)"\)/i);
      if (src)
      {
        src = src[1];
        element.runtimeStyle.backgroundImage="none";
      }
    }
    if (src) element.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + "',sizingMethod='"+sizingMethod+"')";
  }
}
function fixPNG_crop(element) {
    fixPNG(element, "crop");
}

window.xnextFixedMode = true;
if (document.compatMode == "BackCompat" && /MSIE .+Win/.test(navigator.userAgent) || /MSIE (5\.5|6).+Win/.test(navigator.userAgent) && !/MSIE [78]/.test(navigator.userAgent)) {
	window.xnextFixedMode = false;
}
function xnextScrollTop() {
  return document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop;
}

function xnextScrollLeft() {
  return document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft;
}

function xnextClientWidth() {
  return document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.clientWidth;
}

function xnextClientHeight() {
  return document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight;
}

function xnextStickTop(minicart) {
  minicart.style.bottom = "auto";
  if (xnextFixedMode) minicart.style.top = "0px";
  else minicart.style.setExpression("top", "( ignoreMe = xnextScrollTop() ) + 'px'");
}

function xnextStickBottom(minicart) {
  var h = minicart.offsetHeight+xnextParseSize(minicart.style.marginTop)+xnextParseSize(minicart.style.marginBottom);
  if (xnextFixedMode) { minicart.style.top = "auto"; minicart.style.bottom = "0px"; }
  else { 
    minicart.style.bottom = "auto"; 
    minicart.style.setExpression("top", "( ignoreMe = xnextScrollTop() + xnextClientHeight() - "+h+" ) + 'px'");
  }
}

function xnextStickLeft(minicart) {
  minicart.style.right = "auto";
  if (xnextFixedMode) minicart.style.left = "0px";
  else {
    minicart.style.setExpression("left", "( ignoreMe = xnextScrollLeft() ) + 'px'");
  }
}

function xnextParseSize(size) {
  if (!size || size == "") return 0;
  return eval(size.replace("px", ""));
}

function xnextStickRight(minicart) {
  var w = minicart.offsetWidth+xnextParseSize(minicart.style.marginLeft)+xnextParseSize(minicart.style.marginRight);
  if (xnextFixedMode) { minicart.style.left="auto"; minicart.style.right = "0px"; }
  else { 
    minicart.style.right = "auto";
    minicart.style.setExpression("left", "( ignoreMe = xnextScrollLeft() + xnextClientWidth() - "+w+" ) + 'px'");
  }
}
//
//Copyright (c) 2008 Paul Duncan (paul@pablotron.org)
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in
//all copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//THE SOFTWARE.
//

/**
* Persist - top-level namespace for Persist library.
*/
Persist = (function() {
var VERSION = '0.1.0', P, B, esc, init, empty, ec;

// easycookie 0.2.1 (pre-minified)
// (see http://pablotron.org/software/easy_cookie/)
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;i<KEYS.length;i++){key=KEYS[i];if(val=opt[key])
r.push(key+'='+val);}
if(opt.secure)
r.push('secure');return r.join('; ');}
var alive=function(){var k='__EC_TEST__',v=new Date();v=v.toGMTString();this.set(k,v);this.enabled=(this.remove(k)==v);return this.enabled;}
me={set:function(key,val){var opt=(arguments.length>2)?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;i<keys.length;i++)
if(opt[keys[i]])
cfg[keys[i]]=opt[keys[i]];var r=cookify(key,val,cfg);doc.cookie=r;return val;},has:function(key){key=esc(key);var c=doc.cookie,ofs=c.indexOf(key+'='),len=ofs+key.length+1,sub=c.substring(0,key.length);return((!ofs&&key!=sub)||ofs<0)?false:true;},get:function(key){key=esc(key);var c=doc.cookie,ofs=c.indexOf(key+'='),len=ofs+key.length+1,sub=c.substring(0,key.length),end;if((!ofs&&key!=sub)||ofs<0)
return null;end=c.indexOf(';',len);if(end<0)
end=c.length;return un(c.substring(len,end));},remove:function(k){var r=me.get(k),opt={expires:EPOCH};doc.cookie=cookify(k,'',opt);return r;},keys:function(){var c=doc.cookie,ps=c.split('; '),i,p,r=[];for(i=0;i<ps.length;i++){p=ps[i].split('=');r.push(un(p[0]));}
return r;},all:function(){var c=doc.cookie,ps=c.split('; '),i,p,r=[];for(i=0;i<ps.length;i++){p=ps[i].split('=');r.push([un(p[0]),un(p[1])]);}
return r;},version:'0.2.1',enabled:false};me.enabled=alive.call(me);return me;}());

// empty function
empty = function() { };

// escape spaces in name
esc = function(str) {
 return 'PS' + str.replace(/_/g, '__').replace(/ /g, '_s');
};



C = {
 /* 
  * Backend search order.
  * 
  * Note that the search order is significant; the backends are
  * listed in order of capacity, and many browsers
  * support multiple backends, so changing the search order could
  * result in a browser choosing a less capable backend.
  * 
  */ 
 search_order: [
   // TODO: air
   'localstorage',
   'gears',
   'whatwg_db', 
   'globalstorage', 
   'ie', 
   'flash',
   'cookie'
 ],

 // valid name regular expression
 name_re: /^[a-z][a-z0-9_ -]+$/i,

 // list of backend methods
 methods: [
   'init', 
   'get', 
   'set', 
   'remove', 
   'load', 
   'save'
   // TODO: clear method?
 ],

 // sql for db backends (gears and db)
 sql: {
   version:  '1', // db schema version

   create:   "CREATE TABLE IF NOT EXISTS persist_data (k TEXT UNIQUE NOT NULL PRIMARY KEY, v TEXT NOT NULL)",
   get:      "SELECT v FROM persist_data WHERE k = ?",
   set:      "INSERT INTO persist_data(k, v) VALUES (?, ?)",
   remove:   "DELETE FROM persist_data WHERE k = ?" 
 },

 // default flash configuration
 flash: {
   // ID of wrapper element
   div_id:   '_persist_flash_wrap',

   // id of flash object/embed
   id:       '_persist_flash',

   // default path to flash object
   path: 'persist.swf',
   size: { w:1, h:1 },

   // arguments passed to flash object
   args: {
     autostart: true
   }
 } 
};

// built-in backends
B = {
 // gears db backend (webkit, Safari 3.1+)
 // (src: http://code.google.com/apis/gears/api_database.html)
 gears: {
   // no known limit
   size:   -1,

   test: function() {
     // test for gears
     return (window.google && window.google.gears && google.gears.factory.getPermission()) ? true : false;
   },

   methods: {
     transaction: function(fn) {
       var db = this.db;

       // begin transaction
       try {
       db.execute('BEGIN').close();

       // call callback fn
       fn.call(this, db);

       // commit changes
       db.execute('COMMIT').close();
       } catch (err) {
    	   db.remove(); // cleanup and retry
    	   this.init();
           db.execute('BEGIN').close();
           fn.call(this, db);
           db.execute('COMMIT').close();
       }
     },

     init: function() {
       var db;

       // create database handle (TODO: add schema version?)
       db = this.db = google.gears.factory.create('beta.database');

       // open database
       // from gears ref:
       //
       // Currently the name, if supplied and of length greater than
       // zero, must consist only of visible ASCII characters
       // excluding the following characters:
       //
       //   / \ : * ? " < > | ; ,
       db.open(esc(this.name));

       // create table
       db.execute(C.sql.create).close();
     },

     get: function(key, fn, scope) {
       var r, sql = C.sql.get;

       // if callback isn't defined, then return
       if (!fn)
         return;

       // begin transaction
       this.transaction(function (t) {
         // exec query
         r = t.execute(sql, [key]);

         // call callback
         if (r.isValidRow())
           fn.call(scope || this, true, r.field(0));
         else
           fn.call(scope || this, false, null);

         // close result set
         r.close();
       });
     },

     set: function(key, val, fn, scope) {
       var rm_sql = C.sql.remove,
           sql    = C.sql.set, r;

       // begin set transaction
       this.transaction(function(t) {
         // exec remove query
         t.execute(rm_sql, [key]).close();

         // exec set query
         t.execute(sql, [key, val]).close();
         
         // run callback
         if (fn)
           fn.call(scope || this, true, val);
       });
     },

     // begin remove transaction
     remove: function(key, fn, scope) {
       var get_sql = C.sql.get;
           sql = C.sql.remove,
           r, val;

       this.transaction(function(t) {
         // if a callback was defined, then get the old
         // value before removing it
         if (fn) {
           // exec get query
           r = t.execute(get_sql, [key]);

           if (r.isValidRow()) {
             // key exists, get value 
             val = r.field(0);

             // exec remove query
             t.execute(sql, [key]).close();

             // exec callback
             fn.call(scope || this, true, val);
           } else {
             // key does not exist, exec callback
             fn.call(scope || this, false, null);
           }

           // close result set
           r.close();
         } else {
           // no callback was defined, so just remove the
           // data without checking the old value

           // exec remove query
           t.execute(sql, [key]).close();
         }
       });
     } 
   }
 }, 

 // whatwg db backend (webkit, Safari 3.1+)
 // (src: whatwg and http://webkit.org/misc/DatabaseExample.html)
 whatwg_db: {
   size:   200 * 1024,

   test: function() {
     var name = 'PersistJS Test', 
         desc = 'Persistent database test.';

     // test for openDatabase
     if (!window.openDatabase)
       return false;

     // make sure openDatabase works
     // XXX: will this leak a db handle and/or waste space?
     if (!window.openDatabase(name, C.sql.version, desc, B.whatwg_db.size))
       return false;

     return true;
   },

   methods: {
     transaction: function(fn) {
       if (!this.db_created) {
         var sql = C.sql.create;

         this.db.transaction(function(t) {
           // create table
           t.executeSql(sql, [], function() {
             this.db_created = true;
           });
         }, empty); // trap exception
       } 

       this.db.transaction(fn);
     },

     init: function() {
       var desc, size; 
       
       // init description and size
       desc = this.o.about || "Persistent storage for " + this.name;
       size = this.o.size || B.whatwg_db.size;

       // create database handle
       this.db = openDatabase(this.name, C.sql.version, desc, size);
     },

     get: function(key, fn, scope) {
       var sql = C.sql.get;

       // if callback isn't defined, then return
       if (!fn)
         return;

       // get callback scope
       scope = scope || this;

       // begin transaction
       this.transaction(function (t) {
         t.executeSql(sql, [key], function(t, r) {
           if (r.rows.length > 0)
             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;

       // begin set transaction
       this.transaction(function(t) {
         // exec remove query
         t.executeSql(rm_sql, [key], function() {
           // exec set query
           t.executeSql(sql, [key, val], function(t, r) {
             // run callback
             if (fn)
               fn.call(scope || this, true, val);
           });
         });
       });

       return val;
     },

     // begin remove transaction
     remove: function(key, fn, scope) {
       var get_sql = C.sql.get;
           sql = C.sql.remove;

       this.transaction(function(t) {
         // if a callback was defined, then get the old
         // value before removing it
         if (fn) {
           // exec get query
           t.executeSql(get_sql, [key], function(t, r) {
             if (r.rows.length > 0) {
               // key exists, get value 
               var val = r.rows.item(0)['v'];

               // exec remove query
               t.executeSql(sql, [key], function(t, r) {
                 // exec callback
                 fn.call(scope || this, true, val);
               });
             } else {
               // key does not exist, exec callback
               fn.call(scope || this, false, null);
             }
           });
         } else {
           // no callback was defined, so just remove the
           // data without checking the old value

           // exec remove query
           t.executeSql(sql, [key]);
         }
       });
     } 
   }
 }, 
 
 // globalstorage backend (globalStorage, FF2+, IE8+)
 // (src: http://developer.mozilla.org/en/docs/DOM:Storage#globalStorage)
 //
 // TODO: test to see if IE8 uses object literal semantics or
 // getItem/setItem/removeItem semantics
 globalstorage: {
   // (5 meg limit, src: http://ejohn.org/blog/dom-storage-answers/)
   size: 5 * 1024 * 1024,

   test: function() {
     try {
       return (window.globalStorage && window.globalStorage[location.hostname])? true : false;
     } catch(e) {
       if(window.console) window.console.log(e);
       return false;
     }
   },

   methods: {
     key: function(key) {
       return esc(this.name) + esc(key);
     },

     init: function() {
       this.store = globalStorage[this.o.domain];
     },

     get: function(key, fn, scope) {
       // expand key
       key = this.key(key);

       if (fn)
         fn.call(scope || this, true, this.store.getItem(key));
     },

     set: function(key, val, fn, scope) {
       // expand key
       key = this.key(key);

       // set value
       this.store.setItem(key, val);

       if (fn)
         fn.call(scope || this, true, val);
     },

     remove: function(key, fn, scope) {
       var val;

       // expand key
       key = this.key(key);

       // get value
       val = this.store[key];

       // delete value
       this.store.removeItem(key);

       if (fn)
         fn.call(scope || this, (val !== null), val);
     } 
   }
 }, 
 
 // localstorage backend (globalStorage, FF2+, IE8+)
 // (src: http://www.whatwg.org/specs/web-apps/current-work/#the-localstorage)
 localstorage: {
   // (unknown?)
   size: -1,

   test: function() {
     try {
       return window.localStorage ? true : false;
     } catch(e) {
       if(window.console) window.console.log(e);
       return false;
     }
   },

   methods: {
     key: function(key) {
       return esc(this.name) + esc(key);
     },

     init: function() {
       this.store = localStorage;
     },

     get: function(key, fn, scope) {
       // expand key
       key = this.key(key);

       if (fn)
         fn.call(scope || this, true, this.store.getItem(key));
     },

     set: function(key, val, fn, scope) {
       // expand key
       key = this.key(key);

       // remove value beforehands to make it work on iPad
       // see http://www.ecwid.com/bt/view.php?id=3776
       this.store.removeItem(key);

       // set value
       this.store.setItem(key, val);

       if (fn)
         fn.call(scope || this, true, val);
     },

     remove: function(key, fn, scope) {
       var val;

       // expand key
       key = this.key(key);

       // get value
       val = this.getItem(key);

       // delete value
       this.store.removeItem(key);

       if (fn)
         fn.call(scope || this, (val !== null), val);
     } 
   }
 }, 
 
 // IE backend
 ie: {
   prefix:   '_persist_data-',
   // style:    'display:none; behavior:url(#default#userdata);',

   // 64k limit
   size:     64 * 1024,

   test: function() {
     // make sure we're dealing with IE
     // (src: http://javariet.dk/shared/browser_dom.htm)
     return window.ActiveXObject ? true : false;
   },

   make_userdata: function(id) {
     var el = document.createElement('div');

     // set element properties
     el.id = id;
     el.style.display = 'none';
     el.addBehavior('#default#userData');

     // append element to body
     document.body.appendChild(el);

     // return element
     return el;
   },

   methods: {
     init: function() {
       var id = B.ie.prefix + esc(this.name);

       // save element
       this.el = B.ie.make_userdata(id);

       // load data
       if (this.o.defer)
         this.load();
     },

     get: function(key, fn, scope) {
       var val;

       // expand key
       key = esc(key);

       // load data
       if (!this.o.defer)
         this.load();

       // get value
       val = this.el.getAttribute(key);

       // call fn
       if (fn)
         fn.call(scope || this, val ? true : false, val);
     },

     set: function(key, val, fn, scope) {
       // expand key
       key = esc(key);
       
       // set attribute
       this.el.setAttribute(key, val);

       // save data
       if (!this.o.defer)
         this.save();

       // call fn
       if (fn)
         fn.call(scope || this, true, val);
     },

     load: function() {
	   try {
	      this.el.load(esc(this.name));
	   } catch(e) {
		  // workaround for http://www.ecwid.com/bt/view.php?id=4098
		  if(window.console) window.console.log("Could not load user data: "+e);
		  this.el.save(esc(this.name));
	   }
     },

     save: function() {
       this.el.save(esc(this.name));
     }
   }
 },

 // cookie backend
 // uses easycookie: http://pablotron.org/software/easy_cookie/
 cookie: {
   delim: ':',

   // 4k limit (low-ball this limit to handle browser weirdness, and 
   // so we don't hose session cookies)
   size: 4000,

   test: function() {
     // XXX: use easycookie to test if cookies are enabled
     return P.Cookie.enabled ? true : false;
   },

   methods: {
     key: function(key) {
       return this.name + B.cookie.delim + key;
     },

     get: function(key, fn, scope) {
       // expand key 
       key = this.key(key);

       // get value
       val = ec.get(key);

       // call fn
       if (fn)
         fn.call(scope || this, val != null, val);
     },

     set: function(key, val, fn, scope) {
       // expand key 
       key = this.key(key);

       // save value
       ec.set(key, val, this.o);

       // call fn
       if (fn)
         fn.call(scope || this, true, val);
     },

     remove: function(key, val, fn, scope) {
       var val;

       // expand key 
       key = this.key(key);

       // remove cookie
       val = ec.remove(key)

       // call fn
       if (fn)
         fn.call(scope || this, val != null, val);
     } 
   }
 },

 // flash backend (requires flash 8 or newer)
 // http://kb.adobe.com/selfservice/viewContent.do?externalId=tn_16194&sliceId=1
 // http://livedocs.adobe.com/flash/8/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00002200.html
 flash: {
   test: function() {
     // TODO: better flash detection
     if (!window.SWFObject || !deconcept || !deconcept.SWFObjectUtil)
       return false;

     // get the major version
     var major = deconcept.SWFObjectUtil.getPlayerVersion().major;

     // check flash version (require 8.0 or newer)
     return (major >= 8) ? true : false;
   },

   methods: {
     init: function() {
       if (!B.flash.el) {
         var o, key, el, cfg = C.flash;

         // create wrapper element
         el = document.createElement('div');
         el.id = cfg.div_id;

         // FIXME: hide flash element
         // el.style.display = 'none';

         // append element to body
         document.body.appendChild(el);

         // create new swf object
         o = new SWFObject(this.o.swf_path || cfg.path, cfg.id, cfg.size.w, cfg.size.h, '8');

         // set parameters
         for (key in cfg.args)
           o.addVariable(key, cfg.args[key]);

         // write flash object
         o.write(el);

         // save flash element
         B.flash.el = document.getElementById(cfg.id);
       }

       // use singleton flash element
       this.el = B.flash.el;
     },

     get: function(key, fn, scope) {
       var val;

       // escape key
       key = esc(key);

       // get value
       val = this.el.get(this.name, key);

       // call handler
       if (fn)
         fn.call(scope || this, val !== null, val);
     },

     set: function(key, val, fn, scope) {
       var old_val;

       // escape key
       key = esc(key);

       // set value
       old_val = this.el.set(this.name, key, val);

       // call handler
       if (fn)
         fn.call(scope || this, true, val);
     },

     remove: function(key, fn, scope) {
       var val;

       // get key
       key = esc(key);

       // remove old value
       val = this.el.remove(this.name, key);

       // call handler
       if (fn)
         fn.call(scope || this, true, val);
     }
   }
 }
};

// init function
var init = function() {
 var i, l, b, key, fns = C.methods, keys = C.search_order;

 // set all functions to the empty function
 for (i = 0, l = fns.length; i < l; i++) 
   P.Store.prototype[fns[i]] = empty;

 // clear type and size
 P.type = null;
 P.size = -1;

 // loop over all backends and test for each one
 for (i = 0, l = keys.length; !P.type && i < l; i++) {
   b = B[keys[i]];

   // test for backend
   if (b.test()) {
     // found backend, save type and size
     P.type = keys[i];
     P.size = b.size;

     // extend store prototype with backend methods
     for (key in b.methods)
       P.Store.prototype[key] = b.methods[key];
   }
 }

 // mark library as initialized
 P._init = true;
};

// create top-level namespace
P = {
 // version of persist library
 VERSION: VERSION,

 // backend type and size limit
 type: null,
 size: 0,

 // expose init function
 // init: init,

 add: function(o) {
   // add to backend hash
   B[o.id] = o;

   // add backend to front of search order
   C.search_order = [o.id].concat(C.search_order);

   // re-initialize library
   init();
 },

 remove: function(id) {
   var ofs = C.search_order.indexOf(id);
   if (ofs < 0)
     return;

   // remove from search order
   C.search_order.splice(ofs, 1);

   // delete from lut
   delete B[id];

   // re-initialize library
   init();
 },

 // expose easycookie API
 Cookie: ec,

 // store API
 Store: function(name, o) {
   // verify name
   if (!C.name_re.exec(name))
     throw new Error("Invalid name");

   // XXX: should we lazy-load type?
   // if (!P._init)
   //   init();

   if (!P.type)
     throw new Error("No suitable storage found");

   o = o || {};
   this.name = name;

   // get domain (XXX: does this localdomain fix work?)
   o.domain = o.domain || location.hostname || 'localhost.localdomain';

   this.o = o;

   // expires in 2 years
   o.expires = o.expires || 365 * 2;

   // set path to root
   o.path = o.path || '/';

   // call init function
   this.init();
 } 
};

// init persist
init();

// return top-level namespace
return P;
})();

