/** * The One Time Offer Script. * * Copyright Green Web Services Inc. 2007 */ // {{{ library // {{{ Functions // {{{ Matt Kruse Date functions var MONTH_NAMES=new Array('January','February','March','April','May','June','July','August','September','October','November','December','Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');var DAY_NAMES=new Array('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sun','Mon','Tue','Wed','Thu','Fri','Sat'); function LZ(x){return(x<0||x>9?"":"0")+x} function isDate(val,format){var date=getDateFromFormat(val,format);if(date==0){return false;}return true;} function compareDates(date1,dateformat1,date2,dateformat2){var d1=getDateFromFormat(date1,dateformat1);var d2=getDateFromFormat(date2,dateformat2);if(d1==0 || d2==0){return -1;}else if(d1 > d2){return 1;}return 0;} function formatDate(date,format){format=format+"";var result="";var i_format=0;var c="";var token="";var y=date.getYear()+"";var M=date.getMonth()+1;var d=date.getDate();var E=date.getDay();var H=date.getHours();var m=date.getMinutes();var s=date.getSeconds();var yyyy,yy,MMM,MM,dd,hh,h,mm,ss,ampm,HH,H,KK,K,kk,k;var value=new Object();if(y.length < 4){y=""+(y-0+1900);}value["y"]=""+y;value["yyyy"]=y;value["yy"]=y.substring(2,4);value["M"]=M;value["MM"]=LZ(M);value["MMM"]=MONTH_NAMES[M-1];value["NNN"]=MONTH_NAMES[M+11];value["d"]=d;value["dd"]=LZ(d);value["E"]=DAY_NAMES[E+7];value["EE"]=DAY_NAMES[E];value["H"]=H;value["HH"]=LZ(H);if(H==0){value["h"]=12;}else if(H>12){value["h"]=H-12;}else{value["h"]=H;}value["hh"]=LZ(value["h"]);if(H>11){value["K"]=H-12;}else{value["K"]=H;}value["k"]=H+1;value["KK"]=LZ(value["K"]);value["kk"]=LZ(value["k"]);if(H > 11){value["a"]="PM";}else{value["a"]="AM";}value["m"]=m;value["mm"]=LZ(m);value["s"]=s;value["ss"]=LZ(s);while(i_format < format.length){c=format.charAt(i_format);token="";while((format.charAt(i_format)==c) &&(i_format < format.length)){token += format.charAt(i_format++);}if(value[token] != null){result=result + value[token];}else{result=result + token;}}return result;} function _isInteger(val){var digits="1234567890";for(var i=0;i < val.length;i++){if(digits.indexOf(val.charAt(i))==-1){return false;}}return true;} function _getInt(str,i,minlength,maxlength){for(var x=maxlength;x>=minlength;x--){var token=str.substring(i,i+x);if(token.length < minlength){return null;}if(_isInteger(token)){return token;}}return null;} function getDateFromFormat(val,format){val=val+"";format=format+"";var i_val=0;var i_format=0;var c="";var token="";var token2="";var x,y;var now=new Date();var year=now.getYear();var month=now.getMonth()+1;var date=1;var hh=now.getHours();var mm=now.getMinutes();var ss=now.getSeconds();var ampm="";while(i_format < format.length){c=format.charAt(i_format);token="";while((format.charAt(i_format)==c) &&(i_format < format.length)){token += format.charAt(i_format++);}if(token=="yyyy" || token=="yy" || token=="y"){if(token=="yyyy"){x=4;y=4;}if(token=="yy"){x=2;y=2;}if(token=="y"){x=2;y=4;}year=_getInt(val,i_val,x,y);if(year==null){return 0;}i_val += year.length;if(year.length==2){if(year > 70){year=1900+(year-0);}else{year=2000+(year-0);}} }else if(token=="MMM"||token=="NNN"){month=0;for(var i=0;i11)){month=i+1;if(month>12){month -= 12;}i_val += month_name.length;break;}} }if((month < 1)||(month>12)){return 0;}}else if(token=="EE"||token=="E"){for(var i=0;i12)){return 0;}i_val+=month.length;}else if(token=="dd"||token=="d"){date=_getInt(val,i_val,token.length,2);if(date==null||(date<1)||(date>31)){return 0;}i_val+=date.length;}else if(token=="hh"||token=="h"){hh=_getInt(val,i_val,token.length,2);if(hh==null||(hh<1)||(hh>12)){return 0;}i_val+=hh.length;}else if(token=="HH"||token=="H"){hh=_getInt(val,i_val,token.length,2);if(hh==null||(hh<0)||(hh>23)){return 0;}i_val+=hh.length;}else if(token=="KK"||token=="K"){hh=_getInt(val,i_val,token.length,2);if(hh==null||(hh<0)||(hh>11)){return 0;}i_val+=hh.length;}else if(token=="kk"||token=="k"){hh=_getInt(val,i_val,token.length,2);if(hh==null||(hh<1)||(hh>24)){return 0;}i_val+=hh.length;hh--;}else if(token=="mm"||token=="m"){mm=_getInt(val,i_val,token.length,2);if(mm==null||(mm<0)||(mm>59)){return 0;}i_val+=mm.length;}else if(token=="ss"||token=="s"){ss=_getInt(val,i_val,token.length,2);if(ss==null||(ss<0)||(ss>59)){return 0;}i_val+=ss.length;}else if(token=="a"){if(val.substring(i_val,i_val+2).toLowerCase()=="am"){ampm="AM";}else if(val.substring(i_val,i_val+2).toLowerCase()=="pm"){ampm="PM";}else{return 0;}i_val+=2;}else{if(val.substring(i_val,i_val+token.length)!=token){return 0;}else{i_val+=token.length;}} }if(i_val != val.length){return 0;}if(month==2){if( ((year%4==0)&&(year%100 != 0) ) ||(year%400==0) ){if(date > 29){return 0;}}else{if(date > 28){return 0;}} }if((month==4)||(month==6)||(month==9)||(month==11)){if(date > 30){return 0;}}if(hh<12 && ampm=="PM"){hh=hh-0+12;}else if(hh>11 && ampm=="AM"){hh-=12;}var newdate=new Date(year,month-1,date,hh,mm,ss);return newdate.getTime();} function parseDate(val){var preferEuro=(arguments.length==2)?arguments[1]:false;generalFormats=new Array('y-M-d','MMM d, y','MMM d,y','y-MMM-d','d-MMM-y','MMM d');monthFirst=new Array('M/d/y','M-d-y','M.d.y','MMM-d','M/d','M-d');dateFirst =new Array('d/M/y','d-M-y','d.M.y','d-MMM','d/M','d-M');var checkList=new Array('generalFormats',preferEuro?'dateFirst':'monthFirst',preferEuro?'monthFirst':'dateFirst');var d=null;for(var i=0;i 0) for(i = -1, l = r.length; ++i < l;){ for(s = r[i], r[i] = ""; s.length > m; j = c ? m : (j = s.substr(0, m).match(/\S*$/)).input.length - j[0].length || j.input.length + (j = s.substr(m).match(/^\S*/)).input.length + j[0].length, r[i] += s.substr(0, j) + ((s = s.substr(j)).length ? b : "") ); r[i] += s; } return r.join("\n"); }; // }}} // {{{ String.trim functions String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g,""); } String.prototype.ltrim = function() { return this.replace(/^\s+/,""); } String.prototype.rtrim = function() { return this.replace(/\s+$/,""); } // }}} // }}} // {{{ YUI UTILITIES // Note that these files have been included from the YAHOO YUI library. // To enable the vim fold utility you will have to run the following // command after you include a new version of the files // ** this line here to balance out the braces below {{{ // :.,+118s/}}}/}} }/g // where you are sitting on the first line of the included section and // 118 is the number of lines you sucked in. // {{{ yahoo-dom-event.js /* Copyright (c) 2006, Yahoo! Inc. All rights reserved.Code licensed under the BSD License:http://developer.yahoo.net/yui/license.txt version: 0.12.1 */ if(typeof YAHOO=="undefined"){var YAHOO={};} YAHOO.namespace=function(){var a=arguments,o=null,i,j,d;for(i=0;i-1),isSafari=(ua.indexOf('safari')>-1),isGecko=(!isOpera&&!isSafari&&ua.indexOf('gecko')>-1),isIE=(!isOpera&&ua.indexOf('msie')>-1);var patterns={HYPHEN:/(-[a-z])/i};var toCamel=function(property){if(!patterns.HYPHEN.test(property)){return property;} if(propertyCache[property]){return propertyCache[property];} while(patterns.HYPHEN.exec(property)){property=property.replace(RegExp.$1,RegExp.$1.substr(1).toUpperCase());} propertyCache[property]=property;return property;};if(document.defaultView&&document.defaultView.getComputedStyle){getStyle=function(el,property){var value=null;var computed=document.defaultView.getComputedStyle(el,'');if(computed){value=computed[toCamel(property)];} return el.style[property]||value;};}else if(document.documentElement.currentStyle&&isIE){getStyle=function(el,property){switch(toCamel(property)){case'opacity':var val=100;try{val=el.filters['DXImageTransform.Microsoft.Alpha'].opacity;}catch(e){try{val=el.filters('alpha').opacity;}catch(e){}} return val/100;break;default:var value=el.currentStyle?el.currentStyle[property]:null;return(el.style[property]||value);}};}else{getStyle=function(el,property){return el.style[property];};} if(isIE){setStyle=function(el,property,val){switch(property){case'opacity':if(typeof el.style.filter=='string'){el.style.filter='alpha(opacity='+val*100+')';if(!el.currentStyle||!el.currentStyle.hasLayout){el.style.zoom=1;}} break;default:el.style[property]=val;}};}else{setStyle=function(el,property,val){el.style[property]=val;};} YAHOO.util.Dom={get:function(el){if(!el){return null;} if(typeof el!='string'&&!(el instanceof Array)){return el;} if(typeof el=='string'){return document.getElementById(el);} else{var collection=[];for(var i=0,len=el.length;i=this.left&®ion.right<=this.right&®ion.top>=this.top&®ion.bottom<=this.bottom);};YAHOO.util.Region.prototype.getArea=function(){return((this.bottom-this.top)*(this.right-this.left));};YAHOO.util.Region.prototype.intersect=function(region){var t=Math.max(this.top,region.top);var r=Math.min(this.right,region.right);var b=Math.min(this.bottom,region.bottom);var l=Math.max(this.left,region.left);if(b>=t&&r>=l){return new YAHOO.util.Region(t,r,b,l);}else{return null;}};YAHOO.util.Region.prototype.union=function(region){var t=Math.min(this.top,region.top);var r=Math.max(this.right,region.right);var b=Math.max(this.bottom,region.bottom);var l=Math.min(this.left,region.left);return new YAHOO.util.Region(t,r,b,l);};YAHOO.util.Region.prototype.toString=function(){return("Region {"+"top: "+this.top+", right: "+this.right+", bottom: "+this.bottom+", left: "+this.left+"}");};YAHOO.util.Region.getRegion=function(el){var p=YAHOO.util.Dom.getXY(el);var t=p[1];var r=p[0]+el.offsetWidth;var b=p[1]+el.offsetHeight;var l=p[0];return new YAHOO.util.Region(t,r,b,l);};YAHOO.util.Point=function(x,y){if(x instanceof Array){y=x[1];x=x[0];} this.x=this.right=this.left=this[0]=x;this.y=this.top=this.bottom=this[1]=y;};YAHOO.util.Point.prototype=new YAHOO.util.Region(); YAHOO.util.CustomEvent=function(type,oScope,silent,signature){this.type=type;this.scope=oScope||window;this.silent=silent;this.signature=signature||YAHOO.util.CustomEvent.LIST;this.subscribers=[];if(!this.silent){} var onsubscribeType="_YUICEOnSubscribe";if(type!==onsubscribeType){this.subscribeEvent=new YAHOO.util.CustomEvent(onsubscribeType,this,true);}};YAHOO.util.CustomEvent.LIST=0;YAHOO.util.CustomEvent.FLAT=1;YAHOO.util.CustomEvent.prototype={subscribe:function(fn,obj,override){if(this.subscribeEvent){this.subscribeEvent.fire(fn,obj,override);} this.subscribers.push(new YAHOO.util.Subscriber(fn,obj,override));},unsubscribe:function(fn,obj){var found=false;for(var i=0,len=this.subscribers.length;i0){param=args[0];} ret=s.fn.call(scope,param,s.obj);}else{ret=s.fn.call(scope,this.type,args,s.obj);} if(false===ret){if(!this.silent){} return false;}} } return true;},unsubscribeAll:function(){for(var i=0,len=this.subscribers.length;i=0){cacheItem=listeners[index];} if(!el||!cacheItem){return false;} if(this.useLegacyEvent(el,sType)){var legacyIndex=this.getLegacyIndex(el,sType);var llist=legacyHandlers[legacyIndex];if(llist){for(i=0,len=llist.length;i0);} var notAvail=[];for(var i=0,len=onAvailStack.length;i0){for(var i=0,len=listeners.length;i0){j=listeners.length;while(j){index=j-1;l=listeners[index];if(l){EU.removeListener(l[EU.EL],l[EU.TYPE],l[EU.FN],index);} j=j-1;} l=null;EU.clearCache();} for(i=0,len=legacyEvents.length;i=200&&httpStatus<300){try {responseObject=this.createResponseObject(o,callback.argument);if(callback.success){if(!callback.scope){callback.success(responseObject);} else{callback.success.apply(callback.scope,[responseObject]);}} } catch(e){}} else{try {switch(httpStatus){case 12002:case 12029:case 12030:case 12031:case 12152:case 13030:responseObject=this.createExceptionObject(o.tId,callback.argument,(isAbort?isAbort:false));if(callback.failure){if(!callback.scope){callback.failure(responseObject);} else{callback.failure.apply(callback.scope,[responseObject]);}} break;default:responseObject=this.createResponseObject(o,callback.argument);if(callback.failure){if(!callback.scope){callback.failure(responseObject);} else{callback.failure.apply(callback.scope,[responseObject]);}} }} catch(e){}} this.releaseObject(o);responseObject=null;},createResponseObject:function(o,callbackArg) {var obj={};var headerObj={};try {var headerStr=o.conn.getAllResponseHeaders();var header=headerStr.split('\n');for(var i=0;i');if(typeof secureUri=='boolean'){io.src='javascript:false';} else if(typeof secureURI=='string'){io.src=secureUri;}} else{var io=document.createElement('iframe');io.id=frameId;io.name=frameId;} io.style.position='absolute';io.style.top='-1000px';io.style.left='-1000px';document.body.appendChild(io);},appendPostData:function(postData) {var formElements=[];var postMessage=postData.split('&');for(var i=0;i0){try {for(var i=0;i 0) { // if it is an array of items we will pass each to this // method for processing for (var i = 0, l = element.length; i < l; i++) { // This could return an array of items... elems = elems.concat(this.findElements(element[i])); } } else { // we will assume that it is a DOM object already elems.push(element); } return elems; }; // }}} // {{{ getElements // Will try and find the element on the page that has the given name. // This will return an array of elements found. // You can pass in an array of element IDs and they will all get added // into one array to be returned. GWS.Utility.prototype.getElements = function(element) { var els = this.findElements(element); if (els) { // need to check if these elements are configured properly. var el = null; var n = null; for (var i = 0, l = els.length; i < l; i++) { el = els[i]; n = el.getAttribute('name'); if (!el.id || !n || el.id != n) { var l = el.id ? el.id : n; GWS.util.error('Element "' + l + '" is incorrect, it needs to have' + ' both the ID and NAME attributes and they need to be' + ' to be equal to "' + l + '" for the OTO script to work.'); return false; } // if it does not have valid attributes } // for all elements } return els; }; // }}} // {{{ aliases GWS.bt = GWS.Utility.boolTest; GWS.ct = GWS.Utility.configStringTest; GWS.util = new GWS.Utility(); // }}} // GWS.Utility }}} // {{{ OneTimeOffer // {{{ constructor GWS.SalesBullyUI = function(projID) { this.projID = projID; // Set up the defaults this.setShadow(); // Client configuration parameters. The defaults are in the shadows. // These are the public properties. this.key = ''; this.check = ''; this.debug = ''; this.clear = ''; this.and = ''; this.remaining = []; this.timer = {}; this.schedule = {}; this.stock = {}; this.official = {}; this.identifier = {}; this.bonusConfig = {}; // The rest of these are used for calculations / state this.returning = false; this.startTime = ''; this.acceptSet = false; this.timerDiff = 0; this.actionOptions = [ ['decline', 'declined.html', ''], ['accept', 'accepted.html', ''] ]; this.ident = { mode : '', success : false, message : '', action : '', actionVar : 'GWS.TAKE_ACTION', clearVar : 'GWS.CLEAR_ACTION', requestType : 'POST' }; // The following collections are used for storing all the elements that // are to be modified when some interesting event happens. // used for calculating the schedule display units this.displays = { timer : new GWS.DisplayCollection('datetime'), schedule : new GWS.DisplayCollection('datetime'), scheduleStart : new GWS.DisplayCollection('datetime'), scheduleEnd : new GWS.DisplayCollection('datetime'), scheduleLength : new GWS.DisplayCollection('datetime'), stock : new GWS.DisplayCollection('number'), price : new GWS.DisplayCollection('string'), skid : new GWS.DisplayCollection('number') }; // We need to cache the values for the displays until we run and the DOM is // loaded. this.stockCache = []; this.priceCache = []; this.skidCache = []; this.timerCache = []; this.scheduleCache = []; // Bonus data this.bonusCache = []; this.bonusElementCache =[]; this.bonusDisplayCache =[]; this.bonusCollection = null; // Timer data, for the heartbeat this.timerData = { startTime : 0, // when to calculate their visit time from accumulatedTime : 0, // how much time they have accumulated heartbeat : 0, // when the last heartbeat was contact : 0, // next time to contact the server request : undefined // the request to the server }; // for setting the clock this.officialData = { referenceTime : -1, // the latest time sample from the server aggregateDiffs : 0, // the total difference of all samples timeChecks : 0, // the number of samples we have averageDiff : 0, // the average of all the samples oSource : '', // where to poll for time samples requestType : 'GET',// what do we really want? warned : false // whether or not the user has already been warned }; this.saveEvent = new YAHOO.util.CustomEvent('actionSaved', this); }; // GWS.SalesBullyUI }}} // {{{ getName GWS.SalesBullyUI.prototype.getName = function() { return 'OneTimeOffer'; }; // }}} // {{{ setShadow // The default settings for all configuration variables. If a variable is // not set by the user then its equivalent will be looked for here. GWS.SalesBullyUI.prototype.setShadow = function() { this.shadow = {}; var sh = this.shadow; sh.key = 'offer'; sh.check = 'yes'; sh.debug = 'no'; sh.clear = ''; sh.remaining = ['', 'days', 'hours', 'minutes', 'seconds', '', 'day', 'hour', 'minute', 'second']; sh.and = 'and'; sh.timer = { seconds : 0, element : 'timer', format : 'clock', track : 'yes', override : 'yes', heartSource : 'heartbeat.php', endURL : 'expired.html' }; sh.schedule = { start : 'no', end : 'no', beforeURL : '', afterURL : '', timerFormat : 'clock', dateFormat : 'EE, MMM d, h:mma', endSeconds : -1, offset : 0 }; sh.stock = { source : 'no', destination : '', element : '', en : 1, display : 'stockDisplay', timeoutSeconds : 10 }; sh.official = { sourceURL : '', type : 'header', element : '', pattern : '', en : 1 }; sh.identifier = { requestType : 'POST', data : 'gws-oto-data.php', use : 'no' }; sh.bonusConfig = { timeout : 15, display : 'block', nodisplay : 'none', visible : 'yes' }; }; // setShadow }}} // {{{ gC // This function will retrieve a configuration value. It will first check // if there is a user set value and if it exists that gets returned, // otherwise the default will be returned from the shadows. // // This method will accept a string or an array of strings. If it is an // array then those will get turned into accessors for depth. // // The method accesses the variables by use of an eval, strings will be // created of the array index format. GWS.SalesBullyUI.prototype.gC = function(conf) { var a = ''; if (conf instanceof Array) { for (var i = 0; i < conf.length; i++) { a += "['" + conf[i] + "']"; } } else { // we are just going to assume it is a single string a += "['" + conf + "']"; } var b = eval('this' + a); var s = eval('this.shadow' + a); var ret = false; if (!b || (b instanceof Array && b.length == 0)) { b = s; // even if b is not set, that just means this is a variable that does // not necessarily have a default, or that default is blank. } else if (!(typeof b == 'string')) { // we will assume this is an object and we have to merge with the shadow YAHOO.augment(b, s); } return b; }; // gC }}} // {{{ Run GWS.SalesBullyUI.prototype.run = function() { GWS.util.setMode(true, GWS.bt(this.check), GWS.bt(this.debug)); this.getOfficialTime(); this.setupDisplays(); this.doSchedule(); this.checkCurrentInformation(); this.doStock(); //this.checkPage(); this.doTimer(); }; // run }}} // {{{ Displays // {{{ setupDisplays GWS.SalesBullyUI.prototype.setupDisplays = function() { var ch = GWS.bt(this.gC('check')); if (this.remaining && ( !(this.remaining instanceof Array) || this.remaining.length != 10) ) { GWS.util.warning('There has been a value set for oto.remaining but it is not correct. Using the default instead.'); this.remaining = undefined; // will use the shadow instead } this.setTimerDisplay(); // turn off warnings because there is no guarantee that any of the // default elements will be there. GWS.util.setMode(true, false, false); this.timerDisplay('remaining', 'timerRemaining'); this.timerDisplay('rounded', 'timerRounded'); this.timerDisplay('clock', 'timerClock'); this.timerDisplay('seconds', 'timerSeconds'); this.setTimerDisplay(); GWS.util.setMode(true, ch, false); this.setStockDisplay(); GWS.util.setMode(true, false, false); this.stockDisplay('sold', 'stockSold'); this.stockDisplay('sentence', 'stockSentence','There are #!# available.', 'There is only #!# left!'); this.stockDisplay('remaining', 'stockRemaining'); this.stockDisplay('total', 'stockTotal'); this.setStockDisplay(); GWS.util.setMode(true, ch, false); this.setPriceDisplay(); this.setSkidDisplay(); this.setScheduleDisplay(); GWS.util.setMode(true, false, false); this.scheduleDisplay('end', 'scheduleEnd'); this.scheduleDisplay('start', 'scheduleStart'); this.scheduleDisplay('length', 'scheduleLength'); this.scheduleDisplay('clock', 'scheduleClock'); this.scheduleDisplay('remaining', 'scheduleRemaining'); this.scheduleDisplay('rounded', 'scheduleRounded'); this.scheduleDisplay('seconds', 'scheduleSeconds'); this.setScheduleDisplay(); GWS.util.setMode(true, ch, false); this.setBonuses(); this.setAddBonus(); this.setBonusDisplay(); for (var i in this.displays) { this.displays[i].setUnits(this.gC('remaining'), this.gC('and')); } }; // }}} // {{{ stockDisplay GWS.SalesBullyUI.prototype.stockDisplay = function(label, element, sent1, sent2) { this.stockCache.push([label, element, sent1, sent2]); }; // }}} // {{{ setStockDisplay GWS.SalesBullyUI.prototype.setStockDisplay = function() { var sc; var dat = null; var label, element, sent1, sent2; while (sc = this.stockCache.shift()) { dat = null; label = sc[0]; element = sc[1]; sent1 = sc[2]; sent2 = sc[3]; if (element == 'sentence' && (!sent1 || !sent2)) { GWS.util.warning('A call to configure the stock quantity with a sentence has been' + 'made, but there are no sentences supplied.'); } else if(sent1 || sent2) { dat = [sent1, sent2]; } this.displays.stock.createDisplay(label, element, dat); } }; // }}} // {{{ priceDisplay GWS.SalesBullyUI.prototype.priceDisplay = function(label, element) { this.priceCache.push([label, element]); }; // }}} // {{{ setPriceDisplay GWS.SalesBullyUI.prototype.setPriceDisplay = function() { var sc; var dat = null; var label, element; while (sc = this.priceCache.shift()) { dat = null; label = sc[0]; element = sc[1]; this.displays.price.createDisplay(label, element, false); } }; // }}} // {{{ skidDisplay GWS.SalesBullyUI.prototype.skidDisplay = function(label, element) { this.skidCache.push([label, element]); }; // }}} // {{{ setSkidDisplay GWS.SalesBullyUI.prototype.setSkidDisplay = function() { var sc; var dat = null; var label, element; while (sc = this.skidCache.shift()) { dat = null; label = sc[0]; element = sc[1]; this.displays.skid.createDisplay(label, element, false); } }; // }}} // {{{ timerDisplay GWS.SalesBullyUI.prototype.timerDisplay = function(label, element, data) { this.timerCache.push([label, element, data]); }; // }}} // {{{ setTimerDisplay GWS.SalesBullyUI.prototype.setTimerDisplay = function() { var td; while (td = this.timerCache.shift()) { this.displays.timer.createDisplay(td[0], td[1], td[2]); } }; // }}} // {{{ scheduleDisplay GWS.SalesBullyUI.prototype.scheduleDisplay = function(label, element, data) { this.scheduleCache.push([label, element, data]); }; // }}} // {{{ setScheduleDisplay GWS.SalesBullyUI.prototype.setScheduleDisplay = function() { var sc; var disp; while(sc = this.scheduleCache.shift()) { switch (sc[0]) { case 'start': disp = this.displays.scheduleStart; break; case 'end': disp = this.displays.scheduleEnd; break; case 'length': disp = this.displays.scheduleLength; break; default: disp = this.displays.schedule; break; } disp.createDisplay(sc[0], sc[1], sc[2]); } }; // }}} // {{{ bonusDisplay GWS.SalesBullyUI.prototype.bonusDisplay = function(label, type, element, data) { this.bonusDisplayCache.push([label, type, element, data]); }; // }}} // {{{ setBonusDisplay GWS.SalesBullyUI.prototype.setBonusDisplay = function () { var bc; while (bc = this.bonusDisplayCache.shift()) { // call bonus createDisplay based on the label saved previously this.bonusCollection.createDisplay(bc[0], bc[1], bc[2], bc[3]); } }; // }}} //}}} // {{{ getTimerNow GWS.SalesBullyUI.prototype.getTimerNow = function() { var rightnow = 0; if (GWS.ct(this.gC(['official', 'sourceURL']))) { var nt = new Date().getTime(); if (this.officialData.averageDiff) { rightnow = nt + this.officialData.averageDiff; } else { return false; } } else { rightnow = new Date().getTime(); } return rightnow; } // }}} // {{{ getTimerMS GWS.SalesBullyUI.prototype.getTimerMS = function() { var rightnow = this.getTimerNow(); var d = 0; var st; // the start time to use if (this.gC(['timer', 'track']) == 'pause') { st = this.timerData.startTime; } else { st = new Date(this.startTime).getTime(); } d = Math.max(0, (st + (this.gC(['timer', 'seconds'])*1000)) - rightnow); return d; }; // getTimerMS }}} // {{{ timerInterval GWS.SalesBullyUI.prototype.timerInterval = function() { var t = this.getTimerMS(); if (t === false) { return; } this.displays.timer.refresh(t); if (this.bonusCollection) { this.bonusCollection.refresh(t); } if (t < 1) { var endUrl = this.gC(['timer', 'endURL']); if (endUrl) { this.goNow(endUrl); } else { this.goNow(0); } } }; // timerInterval }}} // {{{ heartbeatInterval GWS.SalesBullyUI.prototype.heartbeatInterval = function() { var start = this.timerData.startTime; var last = this.timerData.heartbeat; var now = this.getTimerNow(); var tso = this.gC(['timer', 'heartSource']); this.timerData.heartbeat = now; if (now - last < 10000) { // less than 10 seconds this.timerData.accumulatedTime += now - start; } else { this.timerData.startTime = now - this.timerData.accumulatedTime; } if (this.timerData.contact < now) { this.timerData.contact = now + 20000; // we should contact it again in 20 seconds var callback = { success : this.processHeartbeat, failure : this.failedConnection, scope : this }; now = new Date(now); now = now.getFullYear() + '/' + now.getMonth() + '/' + now.getDate() + ' ' + now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds(); var args = this.projectVariable + '=' + this.projID + '&' + this.heartbeatVariable + '=' + now; this.timerData.request = YAHOO.util.Connect.asyncRequest('POST', tso, callback, args); } }; // }}} // {{{ processHeartbeat GWS.SalesBullyUI.prototype.processHeartbeat = function(response) { var t = response.responseText; var now = this.getTimerNow(); var acc = parseInt(t.trim()); if (isNaN(acc)) { GWS.util.warning('The response from ' + this.gC(['timer', 'heartSource']) + ' should be positive integer ' + 'but "' + a + '" was received. Please check the value for ' + 'oto.stock section of the config and that the file pointed ' + 'at is returning the appropriate text.'); } acc = acc * 1000; if (acc > this.timerData.accumulatedTime) { this.timerData.accumulatedTime = acc; this.timerData.startTime = now - acc; } }; // }}} // {{{ getArguments GWS.SalesBullyUI.prototype.getArguments = function() { var myKey = this.gC('key'); var args = location.search; var st = args.indexOf(myKey + '='); var en = args.length; var a = ''; if (st != -1) { st += myKey.length + 1; // +1 for the '=' } if (args.indexOf('&', st) != -1) { en = args.indexOf('&', st); } if (st != -1) { a = args.substring(st, en); } return a; }; // getArguments }}} // {{{ checkIdentifier GWS.SalesBullyUI.prototype.checkIdentifier = function() { var aid = undefined; if (!GWS.bt(this.gC(['identifier', 'use']))) { return aid; } var iden = this.ident; if (!iden.mode) { // fine, nothing has happened yet return aid; } if (!iden.success) { if (GWS.bt(this.gC('check'))) { // Note that this is returning an error from a lower level to the user. alert(iden.message); } return false; // this is a different state then undefined } switch(iden.mode) { case 'hasActed': // drop through case 'noRecord': aid = this.processActionString(iden.action); break; default: // we have something wrong, probably that the conversation between // php and JS has messed up somehow. GWS.util.warning('There has been an issue communicating with the OTO-Identifier application.'); break; } return aid; }; // checkIdentifier }}} // {{{ checkCurrentInformation // Here we retrieve the current information and then see if there is // anything we need to do with it. GWS.SalesBullyUI.prototype.checkCurrentInformation = function() { // first we need to check if the user is clearing their data var testIdent = undefined; var args = this.getArguments(); // These retrieve information from the storage mechanisms. Which, // by side effect includes the startTime. // // Note that the order is important because the Identifier may overwrite // values set by the cookies. testIdent = this.checkIdentifier(); }; // checkCurrentInformation }}} // {{{ doClear GWS.SalesBullyUI.prototype.doClear = function (args) { if (!args || args != this.gC('clear')) { return false; } // clear out some data that may be set. this.startTime = ''; this.returning = false; this.ident.action = ''; this.ident.message = ''; this.ident.success = true; // we have "truly" cleared out data. // Clear the cookie out if (GWS.bt(this.gC('useCookies'))) { document.cookie = this.gC('cookie') + '='; } // Also need to clean out the saved value from the DB // this is only used in debug, so its a little sloppy if (GWS.bt(this.gC(['identifier', 'use']))) { YAHOO.util.Connect.asyncRequest('POST', this.gC(['identifier', 'data']), null, this.ident.clearVar + '=true'); } return true; } // doClear }}} // {{{ saveAction GWS.SalesBullyUI.prototype.saveAction = function (doit) { var action = doit ? doit : ''; // {{{ save timer? if (GWS.bt(this.gC(['timer', 'track'])) && this.startTime) { action += this.actionDivider + this.startTime; } // }}} if (!action) { return; } // {{{ using cookies? if (GWS.bt(this.gC('useCookies'))) { var ck = this.gC('cookie') + '=' + action + '; max-age=' + this.cookieExpiry; document.cookie = ck; } // }}} // {{{ using identifier? if (GWS.bt(this.gC(['identifier', 'use']))) { var identCallback = { success : this.saveIdentOkay, failure : this.saveIdentBad, scope : this }; var identArg = this.ident.actionVar + '=' + action; var saveU = this.gC(['identifier', 'data']); if (this.gC(['identifier', 'requestType']).search(/^GET$/i) != -1) { YAHOO.util.Connect.asyncRequest('GET', saveU + '?' + identArg, identCallback); } else if (this.gC(['identifier', 'requestType']).search(/^POST$/i) != -1) { YAHOO.util.Connect.asyncRequest('POST', saveU, identCallback, identArg); } else { GWS.util.warning('The request type for the identifier needs to be' + 'either GET or POST. Identifier functionality will not be used.'); this.identifier.use = 'no'; } } // }}} else { // since we aren't communicating with a server we can just fire this right away this.saveEvent.fire(); } }; // saveAction }}} // {{{ identOkay GWS.SalesBullyUI.prototype.saveIdentOkay = function(obj) { var a = '(' + obj.responseText + ')'; var res = eval(a); if (!res.success) { GWS.util.warning('Unable to save Identifier information, received the following message:' + res.message); return false; } this.saveEvent.fire(); return true; }; // identOkay }}} // {{{ identBad GWS.SalesBullyUI.prototype.saveIdentBad = function(obj) { GWS.util.warning('There was an error in trying to connect to the Identifier to save the current state information. Please confirm its config.'); }; // identBad }}} // {{{ accept // The first accept action set by the client will replace the action // in position 1. After that they get added to the end of the // actionOptions array. GWS.SalesBullyUI.prototype.accept = function(label, url, returnURL) { if (this.acceptSet) { this.setAction(label, url, returnURL); } else { this.setAction(label, url, returnURL, 1); this.acceptSet = true; } }; // accept }}} // {{{ decline // The decline action always goes into the zeroth position of the // actionOptions. GWS.SalesBullyUI.prototype.decline = function(label, url, returnURL) { this.setAction(label, url, returnURL, 0); }; // decline }}} // {{{ setAction // Adds an action to those avaialable. GWS.SalesBullyUI.prototype.setAction = function(label, url, returnURL, pos) { if (label == this.gC('visitAction')) { GWS.util.warning('Actions with the same name as the oto.visitAction parameter' + 'cannot be added. Ignoring action with label "' + label + '".'); return; } returnURL = returnURL || ''; // See if the label already exists var t = this.findAction(label); if ((!pos && pos !== 0) && t) { // if so then overwrite it pos = t; } else if (!pos && pos !== 0) { // otherwise we justappend pos = this.actionOptions.length; } this.actionOptions[pos] = [label, url, returnURL]; }; // }}} // {{{ findAction // Finds the AID to go with a particular action string. // // Returns AID, which will be undefined if it is not found. // // If an action string has something in it and it does not match with any // saved labels then a warning is issued. Program should continue to // function though. GWS.SalesBullyUI.prototype.findAction = function(arg) { var aid = undefined; if (!arg) { return aid; } var opts = this.actionOptions; if (arg && arg.length > 0) { for (var i = 0, l = opts.length; i < l; i++) { if (opts[i][0] == arg) { aid = i; break; } } } return aid; }; // findAction }}} // {{{ getScheduleMS GWS.SalesBullyUI.prototype.getScheduleMS = function() { var end = this.gC(['schedule', 'endSeconds']); var now = this.getNow(); if (!now) { return false; } return (end - now); }; // getScheduleMS }}} // {{{ goNow // This will redirect the user immediately to another URL rather than the // current page, it does this without saving any information about this // user. GWS.SalesBullyUI.prototype.goNow = function(where) { var url = ''; var aid = parseInt(where); if (isNaN(aid)) { // we will assume this is a string url = where; } else { // it should be an ID url = this.getActionByAid(aid, true)[1]; } if (!url) { GWS.util.error('The application has tried to access aid: "' + where + '" and cannot find it.'); url = this.getActionByAid(0, true)[1]; } window.location.replace(url); }; // goNow }}} // {{{ getActionByAid // Returns the appropriate URL given the index for the action and // the state of the returning flag. GWS.SalesBullyUI.prototype.getActionByAid = function(aid, withReturn) { var id = parseInt(aid); if((!id && id !== 0) || isNaN(id) || id < 0 || id > this.actionOptions.length) { return undefined; } var loc = this.actionOptions[id]; var ret = [loc[0], loc[1]]; if (withReturn && loc[2]) { ret[1] = loc[2]; } return ret; }; // getActionByAid }}} // {{{ sendThem GWS.SalesBullyUI.prototype.sendThem = function(which) { var ch = parseInt(which); if (isNaN(ch)) { ch = this.findAction(which); } var a = this.getActionByAid(ch, false); var val = a[0]; var url = a[1]; this.saveEvent.subscribe(this.sendThemListener, url, this); this.saveAction(val); }; // sendThem }}} // {{{ sendThemListener GWS.SalesBullyUI.prototype.sendThemListener = function(eType, eArg, eObj) { window.location.replace(eObj); }; // sendThemListener }}} // {{{ checkPage GWS.SalesBullyUI.prototype.checkPage = function() { if (!GWS.bt(this.gC('check'))) { return; } var anchors = document.getElementsByTagName('a'); if (anchors.length <= 0) { GWS.util.warning('The script cannot find any anchor tags. The user needs to be' + 'able to accept the offer.'); return false; } var acceptTest = []; var declineTest = false; var base = this.key + '='; var a = null; var att = null; for (var i = 0; i < anchors.length; i++) { a = anchors[i]; att = a.getAttributeNode('href'); if (att != null && att.value) { for (var j = 0; j < this.actionOptions.length; j++) { if (att.value.indexOf(base + this.actionOptions[j][0]) != -1) { acceptTest[j] = true; } } if (att.value.indexOf(base + this.declineData.value) != -1) { declineTest = true; } if (this.acceptTest(acceptTest) && this.declineTest) { break; } } } // for all anchors if (!(this.acceptTest(acceptTest) && declineTest)) { var a = 'It appears that there are no links in your page that' + 'have the accept and/or decline values. This *may* be okay' + 'as the test is only looking at HREF attributes of A elements.' + 'Please examine your HTML to be sure. The following should' + 'probably appear somewhere:' + "\n"; for (var i = 0; i < this.actionOptions.length; i++) { if (!acceptTest[i]) { a += location.pathname + '?' + this.key + '=' + this.actionOptions[i][0]; } if (!declineTest) { a += location.pathname + '?' + this.key + '=' + this.declineData.value; } } GWS.util.warning(a); } }; // checkPage }}} // {{{ acceptTest GWS.SalesBullyUI.prototype.acceptTest = function(results) { if (!results || results.length != this.actionOptions.length) { return false; } for (var i = 0; i < results.length; i++) { if (!results[i]) { return false; } } return true; }; // acceptTest }}} // {{{ getOfficialTime GWS.SalesBullyUI.prototype.getOfficialTime = function() { var ofd = this.officialData; var os = this.gC(['official', 'sourceURL']); var ot = this.gC(['official', 'type']); var callback; if (!os || os.search(/^no$/i) == -1) { if (os == '') { os = window.location.pathname; } // if os is Blank ofd.oSource = os; if (!ot || ot == '' || ot == 'header') { ofd.requestType = 'HEAD'; } callback = { success : this.setOfficial, failure : this.noOfficial, scope : this }; YAHOO.util.Connect.asyncRequest('GET', os, callback, null); } }; // }}} // {{{ doSchedule GWS.SalesBullyUI.prototype.doSchedule = function() { var ss = GWS.ct(this.gC(['schedule', 'start'])); var se = GWS.ct(this.gC(['schedule', 'end'])); if (!se && !ss) { return; } var starting = false; var start = -1; var ending = false; var end = -1; if (ss) { start = Date.parse(ss); this.schedule.startSeconds = start; starting = true; } if (se) { end = Date.parse(se); this.schedule.endSeconds = end; ending = true; } // Perform sanity checks if (ending && !end) { GWS.util.warning('The END schedule end has been set to something other than "no" but it cannot be understood as a date and time. Please ' + 'change it to "YYYY/Mon/DD HH:MM". Currently it is ' + 'oto.schedule.end = \'' + this.schedule.end + '\''); return false; } if (starting && !start) { GWS.util.warning('The START schedule end has been set to something other than "no" ' + 'but it cannot be understood as a date and time. Please ' + 'change it to "YYYY/Mon/DD HH:MM". Currently it is ' + 'oto.schedule.start = \'' + this.schedule.start + '\''); return false; } if (starting && !this.schedule.beforeURL) { GWS.util.warning('The START schedule end has been set to something other than "no" ' + 'but there is no URL configured for oto.schedule.beforeURL ' + 'we need to send them somewhere before it starts.'); return false; } if (ending && !this.schedule.afterURL) { GWS.util.warning('The END schedule end has been set to something other than "no" ' + 'but there is no URL configured for oto.schedule.afterURL ' + 'we need to send them somewhere after it ends. This will' + 'default to the decline page.'); return false; } // swap them if someone has entered in the values backwards if (starting && ending && (end < start)) { this.schedule.startSeconds = end; this.schedule.endSeconds = start; start = end; end = this.schedule.endSeconds; } var now = this.getNow(); var sau = this.gC(['schedule', 'afterURL']); if (now) { if (starting && now < start) { this.goNow(this.gC(['schedule', 'beforeURL'])); return true; } else if (ending && now >= end) { if (sau) { this.goNow(sau); } else { this.goNow(0); // this.declineData.url); } return true; } } if (starting) { this.displays.scheduleStart.refresh(start); } if (ending) { this.displays.scheduleEnd.refresh(end); } if (starting && ending) { this.displays.scheduleLength.refresh((end - start)/1000); } if (ending) { var selfless = this; window.setInterval( function() { var t = selfless.getScheduleMS(); if (t !== false) { if (t <= 0) { if (sau) { selfless.goNow(sau); } else { selfless.goNow(0); } } selfless.displays.schedule.refresh(t); } }, 500); } // if this is going to end at some point }; // doSchedule }}} // {{{ doStock GWS.SalesBullyUI.prototype.doStock = function () { if (!GWS.ct(this.gC(['stock', 'sourceURL']))) { return; } var qen = this.gC(['stock', 'en']); var qto = this.gC(['stock', 'timeoutSeconds']); var qel = this.gC(['stock', 'element']); var qso = this.gC(['stock', 'sourceURL']); var en = parseInt(qen); var to = parseInt(qto); if (qel && qel.indexOf('<') != -1) { GWS.util.warning('The stock elment has been set to an invalid value ' + 'you only need to have the letters that make up the name ' + 'of the element tag. It is currently set to: ' + 'oto.stock.element = \'' + qel); return false; } if (!to) { to = 30; } en--; // because humans work from 1, but we are zero-ish here to = Math.max(to, 2); to = Math.min(to, 300); to *= 1000; // milliseconds var callback = { success : this.processStock, failure : this.failedConnection, scope : this }; var c = YAHOO.util.Connect.asyncRequest('GET', qso, callback, null); window.setInterval( function() { c = YAHOO.util.Connect.asyncRequest('GET', qso, callback, null); }, to); }; // doStock }}} // {{{ doTimer GWS.SalesBullyUI.prototype.doTimer = function() { // {{{ check: start time // If there is not a startTime set then its about time we set it. // // If we are tracking the user's time across different requests and they // have a previous time we need to use that for our start time rather // than the default (which was set on class instantiation. if (!this.startTime) { this.startTime = new Date().toString(); if (GWS.bt(this.gC(['timer', 'track']))) { this.saveAction(); } } // }}} // {{{ setup: heartbeat var hs = GWS.ct(this.gC(['timer', 'heartSource'])); if (hs) { if (!this.timerData.startTime) { this.timerData.startTime = new Date().getTime(); this.timerData.heartbeat = this.timerData.startTime; } now = this.getTimerNow(); now = now ? now : new Date(); if (now) { var callback = { success : this.processHeartbeat, failure : this.failedConnection, scope : this }; now = new Date(now); now = now.getFullYear() + '/' + now.getMonth() + '/' + now.getDate() + ' ' + now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds(); var args = this.projectVariable + '=' + this.projID + '&' + this.heartbeatVariable + '=' + now; this.timerData.request = YAHOO.util.Connect.asyncRequest('POST', hs, callback, args); } selfless = this; window.setInterval(function () {selfless.heartbeatInterval();}, 30000); } // }}} var timerSecs = parseInt(this.gC(['timer', 'seconds'])); if (isNaN(timerSecs)) { GWS.util.warning('An incorrect value has been supplied for oto.timer.seconds, it needs' + 'to be an integer number of seconds, or zero to disable. It is currently' + 'set to: ' + timerSecs); } else if (timerSecs != 0 && timerSecs <10) { GWS.util.warning('There is a very low value for oto.timer.seconds this is probably' + 'incorrect. It is: ' + timerSecs + ' seconds.'); } else if (timerSecs > (240 * 60)) { GWS.util.warning('The value for oto.timer.seconds is very high. This is probably' + 'incorrect, the value is: ' + timerSecs + ' seconds.'); } // it could still be invalid or zero, either way we don't want to do anything // with it. if (timerSecs) { var t = this.getTimerMS(); if (t !== false && t <= 0) { var endUrl = this.gC(['timer', 'endURL']); if (endUrl) { this.goNow(endUrl); } else { this.goNow(0); } } var selfless = this; window.setInterval(function () {selfless.timerInterval();}, 1000); this.timerDiff = timerSecs * 1000; } // if there is a counter set /* if (this.scheduleDiff != 0 || this.timerDiff != 0) { var l = Math.min(this.scheduleDiff, this.timerDiff); document.write("\n"); } */ }; // }}} // {{{ processStock GWS.SalesBullyUI.prototype.processStock = function(response) { var stock = this.gC('stock'); var qen = this.gC(['stock', 'en']); var qel = GWS.ct(stock.element); var qde = this.gC(['stock', 'destination']); var qso = this.gC(['stock', 'sourceURL']); var a = -1; if (qel) { var t = response.responseXML; var els = t.getElementsByTagName(qel); if (!els || els.length < stock.en+1) { GWS.util.warning('In looking for the stock source element ' + "'" + stock.element + "' it was not able to be found. This " + 'could be because there it is not there, or there are not ' + 'at least ' + (stock.en+1) + ' of them, as set in oto.stock.en.'); return false; } a = els[stock.en].firstChild.nodeValue; } else { a = response.responseText; } var b = a.trim(); var n = 0; // The number that have been sold var p = 0; // The price that this one should sell at var r = 0; // The number of stock left at this price (skid) // Perhaps there is a price attached to this quantity? if (b.indexOf(':') != -1) { var sss = b.split(':'); n = parseInt(sss[0]); p = sss[1]; if (b.length > 2) { r = parseInt(sss[2]); } } else { n = parseInt(a.trim()); } if ( (!n && n !== 0) || (n < 0)) { GWS.util.warning('The response from ' + stock.source + ' should be positive integer ' + 'but "' + a + '" was received. Please check the value for ' + 'oto.stock section of the config and that the file pointed ' + 'at is returning the appropriate text.'); return false; } var tot = parseInt(stock.startAmount); // Did the user give us something sensical? if (isNaN(tot)) { GWS.util.warning('The set value for the total stock to be sold was not a valid integer.'); return false; } if (tot > n) { this.displays.stock.refresh(tot-n, tot); this.displays.price.refresh(p); this.displays.skid.refresh(r); } else { if (stock.destination) { u = stock.destination; } this.goNow(u); } return true; }; // processStock }}} // {{{ failedConnection GWS.SalesBullyUI.prototype.failedConnection = function() { GWS.util.warning('There has been a failure trying to connect to ' + '"' + this.gC(['stock', 'sourceURL']) + '". The return code was' + response.statusText); return false; }; // failedConnection }}} // {{{ noOfficial GWS.SalesBullyUI.prototype.noOfficial = function(obj) { // NOP }; // noOfficial }}} // {{{ getDateField GWS.SalesBullyUI.prototype.getDateField = function(obj) { var os = this.gC(['official', 'sourceURL']); var ot = this.gC(['official', 'type']); var oe = this.gC(['official', 'element']); var on = this.gC(['official', 'en']); var od = this.gC(['official', 'display']); var dat = false; switch (ot) { case 'xml': if (!oe) { GWS.util.warning('The OTO program is set to get an XML file for the official time, but' + 'does not have any element set to look up the time in.'); } else { var roo = obj.responseXML; var elems = roo.getElementsByTagName(oe); if (elems && elems.length >= on) { dat = elems[on - 1].firstChild.nodeValue; // the user thinks in ordinals } else if (elems) { dat = elems[0].firstChild.nodeValue; } } break; case 'text': dat = obj.responseText; break; case 'match': var pat = this.gC(['official', 'pattern']); if (!pat) { GWS.util.warning('The OTO program is set to get the official time according to a text' + 'match but the pattern is not set.'); } if (!on || on <1) { on =1; GWS.util.warning('The OTO program was configured with an invalid value for oto.official.en' + 'It should be a positive integer.'); } var ind = pat.indexOf('#!DATE!#'); if (ind == -1) { GWS.util.warning('The OTO pattern is set, but it does not contain #!DATE!#.'); } else { var find = /#!DATE!#/i; pat = pat.replace(find, '(.*)'); find = /#!NL!#/i; pat.replace(find, '$'); pat = new RegExp(pat, 'gi'); dat = obj.responseText; var howMany = this.official.en ? this.official.en-1 : 0; var matchResult; while((matchResult = pat.exec(dat)) != null) { if (howMany == 0) { dat = matchResult[1]; // we are looking for the contents of the brackets break; } howMany--; } if (howMany != 0) { GWS.util.warning('The date could not be found, there were not enough matches for your pattern' + 'in the oto.official.sourceURL to meet your oto.official.en sotting.'); dat = false; } } // we have found an appropriate match pattern break; default: dat = obj.getResponseHeader['Date']; break; } // switch on type return dat; }; // getDateField }}} // {{{ setOfficial GWS.SalesBullyUI.prototype.setOfficial = function(obj) { var n = new Date().getTime(); // do this right at the top to get a time // as close as possible to the server time var dateText = this.getDateField(obj); var d = Date.parse(dateText); var offd = this.officialData; if (d) { if (d != offd.referenceTime) { offd.referenceTime = d; offd.aggregateDiffs += n-d; offd.timeChecks++; // the negative one is because we want to apply this difference offd.averageDiff = -1 * offd.aggregateDiffs / offd.timeChecks; if (offd.timeChecks < 5) { selfless = this; var callback = { success : this.setOfficial, failure : this.noOfficial, scope : this }; window.setTimeout(function() { YAHOO.util.Connect.asyncRequest('GET', offd.oSource, callback, null); }, 2500); } // if we don't have enough samples } // if the received time is different from the reference time (we have a new sample) } // if we have a valid date string else { if (!this.officialData.warned) { this.officialData.warned = true; GWS.util.warning('The date text found in looking for an official date was "' + dateText + '" which ' + 'was not able to be parsed into an recognizeable date. Please check your settings'+ "\n" + 'and those of your oto.official.sourceURL.'); } } }; // setOfficial }}} // {{{ getNow GWS.SalesBullyUI.prototype.getNow = function() { if (GWS.ct(this.gC(['official', 'sourceURL']))) { var off = this.gC(['schedule', 'offset']) * 1000; var nt = new Date().getTime(); if (this.officialData.averageDiff) { return nt + this.officialData.averageDiff + off; } else { return false; } } else { return (new Date().getTime()) + off; } }; // getNow }}} // {{{ submitForm GWS.SalesBullyUI.prototype.submitForm = function(form) { var whichAccept = this.actionOptions[0][0]; if (form.gwsAccept) { var passed = false; for (var i = 0; i < this.actionOptions.length; i++) { if (this.actionOptions[i][0] == form.gwsAccept.value) { passed = true; break; } } if (!passed) { GWS.util.warning('The submitForm method has been called with the gwsAccept hidden element' + '"gwsAccept" but it does not match any of the accept options set.' + 'setting the accept to the default.'); // NOP - already set at top } else { whichAccept = form.gwsAccept.value; } } // if they have set the gwsAccept link this.saveEvent.subscribe(this.submitFormListener, form, this); this.saveAction(whichAccept); }; // submitForm }}} // {{{ submitFormListener GWS.SalesBullyUI.prototype.submitFormListener = function(eType, eArg, eObj) { eObj.submit(); // eObj is the form }; // submitFormListener }}} // {{{ config GWS.SalesBullyUI.prototype.config = function(retrieve) { if (!retrieve) { if (GWS.bt(this.check)) { GWS.util.warning(''); } } }; // config }}} // {{{ bonus // This accepts configuration information from our client, there is just too many // options for this one though. GWS.SalesBullyUI.prototype.bonus = function(label, type, element, start, end, urlSuffix, visible, invisible, previs) { // XXX check for validity of data var data = { "label" : label, "bonusType" : type, "element" : element, "start" : start, "end" : end, "urlSuffix" : urlSuffix, "visible" : visible, "invisible" : invisible, "previs" : previs }; this.bonusCache.push(data); }; // }}} // {{{ setBonuses GWS.SalesBullyUI.prototype.setBonuses = function() { var b = null; var bc = this.bonusCache; var coll = null; if (!bc || !bc.length) { // no bonuses return true; } if (!this.bonusCollection) { coll = this.bonusCollection = new GWS.BonusCollection(); } while (b = bc.shift()) { coll.createBonus(b); } this.bonusCollection.setConfig(this.gC('bonusConfig')); }; // }}} // {{{ addBonus GWS.SalesBullyUI.prototype.addBonus = function(label, element) { this.bonusElementCache.push([label, element]); }; // }}} // {{{ setAddBonus GWS.SalesBullyUI.prototype.setAddBonus = function(label, element) { if (this.bonusElementCache.length) { if (!this.bonusCollection) { GWS.util.warning('An attempt has been made to add an element to a bonus when' + 'no bonuses have been configured.'); return false; } else { var b = null; while(b = this.bonusElementCache.shift()) { this.bonusCollection.addElement(b); } } } }; // }}} // {{{ bonusXML GWS.SalesBullyUI.prototype.bonusXML = function(label, element, en) { // XXX }; // }}} // }}} // {{{ Displays // A "Display" is really just an element on the page that will have its contents // changed to whatever is appropriate. In many cases there might be only one // element that needs to be altered, but there are many different formating and // data options that need to be enabled. // {{{ GWS.Display // Groups a number of elements together and updates them all at once using the // data and formatter set by the DisplayCollection. // // Note: Should probably not be used outside of the DisplayCollection context. // {{{ constructor GWS.Display = function(displayCollection) { this.dc = displayCollection; // the displayCollection owner this.elems = []; // an array of the DOM objects this.extraData = {}; // stores additional information about a particular element this.formatter = null; }; // }}} // {{{ getName GWS.Display.prototype.getName = function() { return 'Display:'; } // }}} // {{{ setFormatter GWS.Display.prototype.setFormatter = function(form) { this.formatter = form; }; // }}} // {{{ add GWS.Display.prototype.add = function(element, data) { this.addElement(element); if (data) { if (!(data instanceof Array)) { data = [data]; } this.extraData[element] = data; } }; // }}} // {{{ addElement GWS.Display.prototype.addElement = function(element) { var els = GWS.util.getElements(element); this.addElems(els); }; // }}} // {{{ addElems GWS.Display.prototype.addElems = function(els) { if (!els) { GWS.util.error('There are no elements to add. This should not be called with nothing to add.'); } if (els.length) { for (var i = 0, l = els.length; i < l; i++) { this.elems.push(els[i]); } } else { this.elems.push(els); } }; // }}} // {{{ refresh GWS.Display.prototype.refresh = function(ref, data) { var d = undefined; var el = null; for (var l in this.elems) { el = this.elems[l]; if (el && el.id) { if (this.extraData && this.extraData[el.id]) { d = this.extraData[el.id]; } else if (data) { d = data; } else { d = undefined; } el.innerHTML = this.formatter.call(this.dc, ref, d); } } }; // }}} // {{{ setUnits GWS.Display.prototype.setUnits = function(un) { this.units = un; }; // }}} // Display }}} // {{{ DisplayCollection // Provides an interface for having many displays that all use the same piece // of data for updating (or just setting) their value to. // {{{ constructor GWS.DisplayCollection = function(rt) { this.timeBased = false; this.timeWarning = false; this.displays = {}; this.units = []; this.dateFormat = 'EE, MMM d, h:mma'; this.reportType = null; this.source = undefined; // where to get the value from, this should be a function // For the formatRounded function. this.roundedUnits = [ (48 * 60 * 60), (2 * 60 * 60), (5 * 60)]; switch(rt) { case 'datetime': this.timeBased = true; // drop through case 'number': case 'string': this.reportType = rt; break; default: GWS.util.warning('An attempt has been made to to create a DisplayCollection with a reportType of "' + rt + '" which is invalid. Setting to type string.'); this.reportType = 'string'; break; } }; // }}} // {{{ getName GWS.DisplayCollection.prototype.getName = function() { return 'DisplayCollection:'; } // }}} // {{{ createDisplay GWS.DisplayCollection.prototype.createDisplay = function(reportStyle, element, data) { if (!this.displays[reportStyle]) { var formatter = this.getFormatter(reportStyle); if (!formatter) { return false; } var d = new GWS.Display(this); if (!d) { GWS.util.error('Attempting to create a Display of type "'+ type +'" which is invalid.', this); return false; } d.setFormatter(formatter); if (this.units.length) { d.setUnits(this.units); } this.displays[reportStyle] = d; } this.displays[reportStyle].add(element, data); }; // }}} // {{{ refresh GWS.DisplayCollection.prototype.refresh = function(ref, data) { // if we have already had problems parsing this one then don't continue. if (this.timeWarning) { return false; } if (!ref && ref !== 0) { ref = this.checkSource(); } if (this.timeBased) { ref = this.checkForSeconds(ref); if (ref === false) { GWS.util.warning('Reference data given for a time based output is not a number of seconds or a date that can be parsed. No display will happen.'); this.timeWarning = true; return false; } } for (var www in this.displays) { this.displays[www].refresh(ref, data); } }; // }}} // {{{ getFormatter GWS.DisplayCollection.prototype.getFormatter = function(style) { var rt = this.reportType; var formatter = false; if (rt == 'datetime') { switch (style) { case 'end': case 'start': case 'full': formatter = this.formatDate; break; case 'remaining': formatter = this.formatRemaining; break; case 'rounded': formatter = this.formatRounded; break; case 'seconds': formatter = this.formatSeconds; break; case 'length': case 'clock': default: formatter = this.formatClock; break; } } else if (rt == 'number') { switch (style) { case 'sentence': formatter = this.formatSentence; break; case 'total': formatter = this.formatTotal; break; case 'sold': formatter = this.formatSold; break; case 'remaining': default: formatter = this.formatEcho; break; } } else if (rt == 'string') { formatter = this.formatEcho; } else { GWS.util.error('Runtime has reached a (hopefully) impossible location in the getFormatter method.'); } return formatter; }; // }}} // {{{ formatTotal GWS.DisplayCollection.prototype.formatTotal = function(ref, data) { if (!data) { return ''; } // the data for the quantity is the starting number return data; }; // }}} // {{{ formatSold GWS.DisplayCollection.prototype.formatSold = function(ref, data) { if (!data || ref > data) { return ''; } return (data - ref); }; // }}} // {{{ formatClock GWS.DisplayCollection.prototype.formatClock = function(val, data) { val = Math.floor(val/1000); // convert from ms to s var a = ''; var h = Math.floor(val / 3600); var m = Math.floor( (val % 3600) / 60); var s = Math.max(val - (h*3600) - (m*60), 0); a = LZ(h) + ':' + LZ(m) + ':' + LZ(s); return a; }; // }}} // {{{ formatRounded GWS.DisplayCollection.prototype.formatRounded = function(val, data) { val = Math.floor(val/1000); // convert from ms to s var u = 0; var d = data ? data[0] : this.roundedUnits[0]; var h = data ? data[1] : this.roundedUnits[1]; var m = data ? data[2] : this.roundedUnits[2]; if (val > d) { val = Math.floor(val / 86400); u = 1; } else if (val > h) { val = Math.floor(val / 3600); u = 2; } else if (val > m) { val = Math.floor(val / 60); u = 3; } else { u = 4; } if (val == 1) { u += 5; } val = val + ' ' + this.units[u]; return val; }; // }}} // {{{ formatRemaining GWS.DisplayCollection.prototype.formatRemaining = function(val, data) { val = Math.floor(val/1000); // convert from ms to s var u = data ? data : this.units; var num = 0; var segments = [86400, 3600, 60]; // number of seconds is each segment var a = ''; for (var i = 0, l = segments.length; i < l; i++) { num = Math.floor(val / segments[i]); if (num) { if (!a) { // if this is the first one then we need to prepend the lead in string if (num == 1) { a = u[5]; } else { a = u[0] } } // add in the value a += ' ' + num + ' '; // add on the segment string if (num == 1) { a += u[i + 6]; // singulars start at position 6 } else { a += u[i + 1]; // plurals start at position 1 } // decrement the amount we have accounted for val -= num * segments[i]; } // if there were any of this segment } // for each segment // We always display seconds val = Math.floor(Math.max(val, 0)); // just in case something strange happened if (!a) { // its possible there was nothing before... if (val == 1) { a = u[5]; } else { a = u[0] } } else { // or we need an "and" a += ' ' + u[10]; } a += ' ' + val + ' '; if (val == 1) { a += u[9]; } else { a += u[4]; } return a; }; // }}} // {{{ formatDate GWS.DisplayCollection.prototype.formatDate = function(ref, data) { var fmt = ''; if (data) { fmt = data[0]; } else { fmt = this.dateFormat; } return formatDate(new Date(ref), fmt); }; // }}} // {{{ formatSentence GWS.DisplayCollection.prototype.formatSentence = function(val, data) { if (!data) { return ''; } var a = ''; var plural = data[0]; var single = data[1]; var find = /#!#/g; if (val == 1 && single) { a = single.replace(find, val); } else { a = plural.replace(find, val); } return a; }; // }}} // {{{ formatSeconds GWS.DisplayCollection.prototype.formatSeconds = function(ref, data) { return Math.floor(ref / 1000); }; // }}} // {{{ formatEcho GWS.DisplayCollection.prototype.formatEcho = function(ref, data) { return ref; }; // }}} // {{{ setUnits GWS.DisplayCollection.prototype.setUnits = function(un, an) { this.units = un; this.units.push(an); for (var i in this.displays) { this.displays[i].setUnits(un); } }; // }}} // {{{ checkForSeconds GWS.DisplayCollection.prototype.checkForSeconds = function(secs) { var ret = parseInt(secs); if (isNaN(ret)) { ret = Date.parse(secs); if (!ret) { ret = false; } } return ret; }; // }}} // {{{ setDateFormat GWS.DisplayCollection.prototype.setDateFormat = function(vvv) { this.dateFormat = vvv; }; // }}} // DisplayCollection }}} // Displays }}} // {{{ Bonuses // Each bonus is a section of the page (a div usually) that is displayed for a // specific period of time. It has a start and end, and during that time it // will have displays that are actively updated. // {{{ Bonus // {{{ constructor GWS.Bonus = function(bt) { this.config = {}; this.elems = []; this.start = ''; this.end = ''; this.urlSuffix = ''; this.visible = ''; this.invisible = ''; this.previs = ''; this.reportType = ''; this.bonusType = bt; switch (bt) { case 'quantity': this.reportType = 'number'; break; case 'timer': case 'calendar': this.reportType = 'datetime'; break; default: this.reportType = 'echo'; break; } this.startDisplays = new GWS.DisplayCollection(this.reportType); this.lengthDisplays = new GWS.DisplayCollection(this.reportType); this.endDisplays = new GWS.DisplayCollection(this.reportType); this.countdownDisplays = new GWS.DisplayCollection(this.reportType); }; // }}} // {{{ edit GWS.Bonus.prototype.edit = function(obj) { var oe = this.element; // so we can check against it shortly // XXX we need to confirm each of these settings to be reasonable // based on tye and anything else we can think of. for (var i in obj) { this[i] = obj[i]; } // We only find it if we have not found it before if (this.element && (oe !== this.element)) { this.elems = this.elems.concat(GWS.util.getElements(this.element)); } }; // }}} // {{{ refresh GWS.Bonus.prototype.refresh = function(reference, data) { var value = undefined; if (!reference && reference !== 0) { reference = this.checkSource(); } if (!reference && reference !== 0) { return; } switch (this.testReference(reference)) { case -1: // before the element should be shown this.previsElements(); break; case 0: // The element should be shown now this.showElements(); this.countdownDisplays.refresh(reference, data); break; case 1: // After the element should be shown this.hideElements(); break; default: // if we don't know then we should hide it this.hideElements(); break; } }; // }}} // {{{ testReference GWS.Bonus.prototype.testReference = function(r) { if (!r) { return false; } var test = false; switch (this.type) { case 'quantity': // drop through case 'timer': var n = parseInt(r); if (isNaN(n)) { this.warning('We are using a integer based bonus that has received an invalid reference of "' + r + '".'); test = false; } else if (this.start && n > this.start) { test = -1; } else if (this.end && n < this.end) { test = 1; } else { test = 0; } break; case 'calendar': var d = Date.parse(r); var s = this.start ? Date.parse(this.start) : ''; var e = this.end ? Date.parse(this.end) : ''; if (!d) { this.warning('The bonus is using a calendar based method and the passed in reference is not recognizable as a date. It is "' + r + '".'); test = false; } else if (s && d < s) { test = -1; } else if (e && d > e) { test = 1; } else { test = 0; } break; case 'echo': // drop through default: test = 0; break; } return test; }; // }}} // {{{ showElements GWS.Bonus.prototype.showElements = function () { var e = null; var st = null; var cf = this.config; var d = GWS.ct(cf.display); var v = GWS.ct(cf.visibility); for (var i = 0, l = this.elems.length; i < l; i++) { e = this.elems[i]; st = e.style; if (this.visible) { e.className = this.visible; } else { st.display = d ? d : 'block'; if (v) { st.visibility = 'visible'; } } } }; // }}} // {{{ previsElements GWS.Bonus.prototype.previsElements = function() { var e = null; var st = null; var cf = this.config; var d = GWS.ct(cf.nodisplay); var v = GWS.ct(cf.visibility); for (var i = 0, l = this.elems.length; i < l; i++) { e = this.elems[i]; st = e.style; if (this.previs) { e.className = this.previs; } else { st.display = d ? d : 'none'; if (v) { st.visibility = 'hidden'; } } } }; // }}} // {{{ hideElements GWS.Bonus.prototype.hideElements = function() { var e = null; var st = null; var cf = this.config; var d = GWS.ct(cf.nodisplay); var v = GWS.ct(cf.visibility); for (var i = 0, l = this.elems.length; i < l; i++) { e = this.elems[i]; st = e.style; if (this.invisible) { e.className = this.invisible; } else { st.display = d ? d : 'none'; if (v) { st.visibility = 'hidden'; } } } }; // }}} // {{{ setConfig GWS.Bonus.prototype.setConfig = function(data) { this.config = data; }; // }}} // {{{ createDisplay GWS.Bonus.prototype.createDisplay = function(rt, element, data) { var dc = null; switch (rt) { case 'start': dc = this.startDisplays; break; case 'end': dc = this.endDisplays; break; case 'length': dc = this.lengthDisplays; break; default: dc = this.countdownDisplays; break; } return dc.createDisplay(rt, element, data); } // }}} // GWS.Bonus }}} // {{{ BonusCollection // {{{ constructor GWS.BonusCollection = function() { this.bonuses = {}; this.configData = {}; }; // }}} // {{{ createBonus GWS.BonusCollection.prototype.createBonus = function(obj) { var l = obj.label; var bs = this.bonuses; var b = null; if (!bs[l]) { obj = this.checkBonusConfig(obj); b = bs[l] = new GWS.Bonus(obj.bonusType); } b.edit(obj); if (this.configData) { b.setConfig(this.configData); } }; // }}} // {{{ checkBonusConfig GWS.BonusCollection.prototype.checkBonusConfig = function(obj) { var bt= obj.bonusType; var l = obj.label; var s = obj.start; var e = obj.end; var bonusReference = ''; // the "bonusType" may actually be a reference to a URL, if so if (s || s !== 0) { // They may not have given us a valid start string... if (bt == 'timer' || bt == 'quantity') { if (isNaN(parseInt(s))) { GWS.util.warning('Bonus with label "' + obj.label + '" is being created and an integer is expected for start time, but the start received was "' + s + '". Setting the start to 0, which means it will start immediately.'); s = 0; } } else if (bt == 'calendar') { if (!Date.parse(s)) { GWS.util.warning('Bonus with label "' + obj.label + '" is being created and a date is expected for start time, but the start received was "' + s + '". Setting the start to 0, which means it will start immediately.'); s = 0; } } else { // if it is not any of those bonus types then we are going to be making use of // the reference URL, but now we will try and implicitly determine the bonusType // from the start (or later) the end string (which will take precedence). bonusRef = bt; if (Date.parse(s)) { bt = 'calendar'; } else if (!isNaN(parseInt(s))) { bt = 'quantity'; } else { // if we don't get either of those then lets just use echo bt = 'echo'; } } } // if there was a start set if (e || e !== 0) { // They may not have given us a valid end string... if (bt == 'timer' || bt == 'quantity') { if (isNaN(parseInt(e))) { GWS.util.warning('Bonus with label "' + obj.label + '" is being created and an integer is expected for end time, but the end received was "' + e + '". Setting the end to nothing, which means it never end.'); e = ''; } } else if (bt == 'calendar') { if (!Date.parse(e)) { GWS.util.warning('Bonus with label "' + obj.label + '" is being created and a date is expected for end time, but the end received was "' + e + '". Setting the start to nothing, which means it will never end.'); e = ''; } } else { // if it is not any of those bonus types then we are going to be making use of // the reference URL, but now we will try and implicitly determine the bonusType // from the end string. bonusRef = bt; if (Date.parse(e)) { bt = 'calendar'; } else if (!isNaN(parseInt(e))) { bt = 'quantity'; } else { // if we don't get either of those then lets just use echo bt = 'echo'; } } } // if there was an end set obj.bonusType = bt; obj.reference = bonusReference; return obj; }; // }}} // {{{ createDisplay GWS.BonusCollection.prototype.createDisplay = function(label, reportType, element, data) { if (!this.bonuses[label]) { GWS.util.warning('An attempt has been made to add a bonusDisplay to a labeled bonus of "' + label + '" which does not exist.'); return false; } this.bonuses[label].createDisplay(reportType, element, data); }; // }}} // {{{ addElement GWS.BonusCollection.prototype.addElement = function(data) { var label = data[0]; var element = data[1]; if (!this.bonuses[label]) { GWS.util.warning('Attempted to add an element to a bonus named "' + label + '" that' + 'had not been created yet.'); return false; } return this.bonuses[label].addElement(element); }; // }}} // {{{ setConfig GWS.BonusCollection.prototype.setConfig = function(data) { var b = this.bonuses; for (var i in b) { b[i].setConfig(data); } this.configData = data; }; // }}} // {{{ refresh GWS.BonusCollection.prototype.refresh = function(reference, data) { var b = this.bonuses; for (var l in b) { b[l].refresh(reference, data); } }; // }}} // GWS.BonusCollection }}} // Bonuses }}} var bully_514825 = new GWS.SalesBullyUI('514825'); bully_514825.check = 'no'; bully_514825.official.sourceURL = '/salesbully/date.php'; bully_514825.official.type = 'header'; bully_514825.official.element = ''; bully_514825.official.pattern = ''; bully_514825.official.en = 0; bully_514825.stock.sourceURL = 'http://www.woprot.com/quantitynumber.php'; bully_514825.stock.startAmount = 1000; bully_514825.stock.element = ''; bully_514825.stock.en = 0; bully_514825.stock.timeoutSeconds = 10; bully_514825.identifier.requestType = 'post'; bully_514825.stock.destination = 'http://www.woprot.com/soldout.html'; bully_514825.key = 'bully_514825'; bully_514825.stockDisplay('remaining', 'stockRemaining_514825'); bully_514825.stockDisplay('total', 'stockTotal_514825'); bully_514825.stockDisplay('sold', 'stockSold_514825'); bully_514825.remaining = ['', 'days', 'hours', 'minutes', 'seconds', '', 'day', 'hour', 'minute', 'second']; bully_514825.and = 'and'; bully_514825.trueOTO = 'no'; bully_514825.fullDateFormat = 'MMM d, yyyy hh:mma'; bully_514825.schedule.start = 'no'; bully_514825.schedule.end = 'no'; bully_514825.schedule.offset = 0; bully_514825.timer.seconds = 0; bully_514825.timer.track = 'yes'; bully_514825.timer.override = 'yes'; bully_514825.timer.heartSource = '/salesbully/heartbeat.php'; bully_514825.startTime = 'Wed, 08 Sep 2010 21:39:14 -0500'; bully_514825.heartbeat = ''; bully_514825.heartbeatVariable = 'bully_hb'; bully_514825.projectVariable = 'bully_p';