weaver 0.8.14 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/data/weaver/css/plugins/codemirror/codemirror.css +97 -96
  3. data/data/weaver/css/style-dark.css +8628 -0
  4. data/data/weaver/css/style.css +37 -0
  5. data/data/weaver/js/plugins/codemirror/codemirror.js +8817 -6763
  6. data/data/weaver/js/plugins/skeuocard/Gruntfile.coffee +74 -0
  7. data/data/weaver/js/plugins/skeuocard/LICENSE +21 -0
  8. data/data/weaver/js/plugins/skeuocard/README.md +393 -0
  9. data/data/weaver/js/plugins/skeuocard/bower.json +40 -0
  10. data/data/weaver/js/plugins/skeuocard/fonts/ocra-webfont.eot +0 -0
  11. data/data/weaver/js/plugins/skeuocard/fonts/ocra-webfont.svg +138 -0
  12. data/data/weaver/js/plugins/skeuocard/fonts/ocra-webfont.ttf +0 -0
  13. data/data/weaver/js/plugins/skeuocard/fonts/ocra-webfont.woff +0 -0
  14. data/data/weaver/js/plugins/skeuocard/images/card-flip-arrow.png +0 -0
  15. data/data/weaver/js/plugins/skeuocard/images/card-front-background.png +0 -0
  16. data/data/weaver/js/plugins/skeuocard/images/card-invalid-indicator.png +0 -0
  17. data/data/weaver/js/plugins/skeuocard/images/card-valid-anim.gif +0 -0
  18. data/data/weaver/js/plugins/skeuocard/images/card-valid-indicator.png +0 -0
  19. data/data/weaver/js/plugins/skeuocard/images/error-pointer.png +0 -0
  20. data/data/weaver/js/plugins/skeuocard/images/issuers/visa-chase-sapphire.png +0 -0
  21. data/data/weaver/js/plugins/skeuocard/images/issuers/visa-simple-front.png +0 -0
  22. data/data/weaver/js/plugins/skeuocard/images/products/amex-front.png +0 -0
  23. data/data/weaver/js/plugins/skeuocard/images/products/dinersclubintl-front.png +0 -0
  24. data/data/weaver/js/plugins/skeuocard/images/products/discover-front.png +0 -0
  25. data/data/weaver/js/plugins/skeuocard/images/products/generic-back.png +0 -0
  26. data/data/weaver/js/plugins/skeuocard/images/products/generic-front.png +0 -0
  27. data/data/weaver/js/plugins/skeuocard/images/products/jcb-front.png +0 -0
  28. data/data/weaver/js/plugins/skeuocard/images/products/maestro-front.png +0 -0
  29. data/data/weaver/js/plugins/skeuocard/images/products/mastercard-front.png +0 -0
  30. data/data/weaver/js/plugins/skeuocard/images/products/unionpay-front.png +0 -0
  31. data/data/weaver/js/plugins/skeuocard/images/products/visa-back.png +0 -0
  32. data/data/weaver/js/plugins/skeuocard/images/products/visa-front.png +0 -0
  33. data/data/weaver/js/plugins/skeuocard/images/src/card-front-background.fw.png +0 -0
  34. data/data/weaver/js/plugins/skeuocard/images/src/error-pointer.png +0 -0
  35. data/data/weaver/js/plugins/skeuocard/images/src/product-amex-front.fw.png +0 -0
  36. data/data/weaver/js/plugins/skeuocard/images/src/product-dinersclub-front.fw.png +0 -0
  37. data/data/weaver/js/plugins/skeuocard/images/src/product-discover-front.fw.png +0 -0
  38. data/data/weaver/js/plugins/skeuocard/images/src/product-generic-front.fw.png +0 -0
  39. data/data/weaver/js/plugins/skeuocard/images/src/product-jcb-front.fw.png +0 -0
  40. data/data/weaver/js/plugins/skeuocard/images/src/product-maestro-front.fw.png +0 -0
  41. data/data/weaver/js/plugins/skeuocard/images/src/product-mastercard-front.fw.png +0 -0
  42. data/data/weaver/js/plugins/skeuocard/images/src/product-unionpay-front.fw.png +0 -0
  43. data/data/weaver/js/plugins/skeuocard/images/src/product-visa-front.fw.png +0 -0
  44. data/data/weaver/js/plugins/skeuocard/index.html +124 -0
  45. data/data/weaver/js/plugins/skeuocard/javascripts/skeuocard.js +1748 -0
  46. data/data/weaver/js/plugins/skeuocard/javascripts/skeuocard.min.js +2 -0
  47. data/data/weaver/js/plugins/skeuocard/javascripts/src/CardProduct.coffee +284 -0
  48. data/data/weaver/js/plugins/skeuocard/javascripts/src/ExpirationInputView.coffee +206 -0
  49. data/data/weaver/js/plugins/skeuocard/javascripts/src/FlipTabView.coffee +67 -0
  50. data/data/weaver/js/plugins/skeuocard/javascripts/src/SegmentedCardNumberInputView.coffee +284 -0
  51. data/data/weaver/js/plugins/skeuocard/javascripts/src/Skeuocard.coffee +439 -0
  52. data/data/weaver/js/plugins/skeuocard/javascripts/src/TextInputView.coffee +42 -0
  53. data/data/weaver/js/plugins/skeuocard/javascripts/vendor/cssua.min.js +7 -0
  54. data/data/weaver/js/plugins/skeuocard/javascripts/vendor/demo.fix.js +17 -0
  55. data/data/weaver/js/plugins/skeuocard/javascripts/vendor/jquery-2.0.3.min.js +5 -0
  56. data/data/weaver/js/plugins/skeuocard/package-lock.json +760 -0
  57. data/data/weaver/js/plugins/skeuocard/package.json +19 -0
  58. data/data/weaver/js/plugins/skeuocard/screenshot.png +0 -0
  59. data/data/weaver/js/plugins/skeuocard/styles/demo.css +2 -0
  60. data/data/weaver/js/plugins/skeuocard/styles/skeuocard.css +2 -0
  61. data/data/weaver/js/plugins/skeuocard/styles/skeuocard.reset.css +2 -0
  62. data/data/weaver/js/plugins/skeuocard/styles/src/_browser_hacks.scss +52 -0
  63. data/data/weaver/js/plugins/skeuocard/styles/src/_cards.scss +516 -0
  64. data/data/weaver/js/plugins/skeuocard/styles/src/_util.scss +15 -0
  65. data/data/weaver/js/plugins/skeuocard/styles/src/demo.scss +265 -0
  66. data/data/weaver/js/plugins/skeuocard/styles/src/skeuocard.reset.scss +60 -0
  67. data/data/weaver/js/plugins/skeuocard/styles/src/skeuocard.scss +190 -0
  68. data/lib/weaver/page_types/page.rb +5 -0
  69. data/lib/weaver/page_types/structured_page.rb +1 -1
  70. data/lib/weaver/version.rb +1 -1
  71. metadata +69 -6
@@ -0,0 +1,2 @@
1
+ (function(){var $,Skeuocard,visaProduct,__slice=[].slice,__hasProp={}.hasOwnProperty,__indexOf=[].indexOf||function(item){for(var i=0,l=this.length;l>i;i++)if(i in this&&this[i]===item)return i;return-1};$=jQuery,Skeuocard=function(){function Skeuocard(el,opts){var optDefaults;null==opts&&(opts={}),this.el={container:$(el),underlyingFields:{}},this._inputViews={},this._inputViewsByFace={front:[],back:[]},this._tabViews={},this._state={},this.product=null,this.visibleFace="front",optDefaults={debug:!1,dontFocus:!1,acceptedCardProducts:null,cardNumberPlaceholderChar:"X",genericPlaceholder:"XXXX XXXX XXXX XXXX",typeInputSelector:'[name="cc_type"]',numberInputSelector:'[name="cc_number"]',expMonthInputSelector:'[name="cc_exp_month"]',expYearInputSelector:'[name="cc_exp_year"]',nameInputSelector:'[name="cc_name"]',cvcInputSelector:'[name="cc_cvc"]',initialValues:{},validationState:{},strings:{hiddenFaceFillPrompt:"<strong>Click here</strong> to <br>fill in the other side.",hiddenFaceErrorWarning:"There's a problem on the other side.",hiddenFaceSwitchPrompt:"Forget something?<br> Flip the card over."}},this.options=$.extend(optDefaults,opts),this._conformDOM(),this._bindInputEvents(),this._importImplicitOptions(),this.render()}return Skeuocard.currentDate=new Date,Skeuocard.prototype._log=function(){var msg;return msg=1<=arguments.length?__slice.call(arguments,0):[],("undefined"!=typeof console&&null!==console?console.log:void 0)&&this.options.debug&&null!=this.options.debug?console.log.apply(console,["[skeuocard]"].concat(__slice.call(msg))):void 0},Skeuocard.prototype.trigger=function(){var args,_ref;return args=1<=arguments.length?__slice.call(arguments,0):[],(_ref=this.el.container).trigger.apply(_ref,args)},Skeuocard.prototype.bind=function(){var args,_ref;return args=1<=arguments.length?__slice.call(arguments,0):[],(_ref=this.el.container).bind.apply(_ref,args)},Skeuocard.prototype._conformDOM=function(){var elem,name,_ref,_ref1;this.el.container.removeClass("no-js"),this.el.container.addClass("skeuocard js"),this.el.underlyingFields={type:this.el.container.find(this.options.typeInputSelector),number:this.el.container.find(this.options.numberInputSelector),expMonth:this.el.container.find(this.options.expMonthInputSelector),expYear:this.el.container.find(this.options.expYearInputSelector),name:this.el.container.find(this.options.nameInputSelector),cvc:this.el.container.find(this.options.cvcInputSelector)},_ref=this.el.underlyingFields;for(name in _ref)__hasProp.call(_ref,name)&&(elem=_ref[name],$(elem).detach());this.el.container.find("> :not(input,select,textarea)").remove(),_ref1=this.el.underlyingFields;for(name in _ref1)__hasProp.call(_ref1,name)&&(elem=_ref1[name],$(elem).appendTo(this.el.container));return this.el.container.find("> input,select,textarea").hide(),this.el.front=$("<div>").attr({"class":"face front"}),this.el.back=$("<div>").attr({"class":"face back"}),this.el.cardBody=$("<div>").attr({"class":"card-body"}),this.el.front.appendTo(this.el.cardBody),this.el.back.appendTo(this.el.cardBody),this.el.cardBody.appendTo(this.el.container),this._tabViews.front=new Skeuocard.prototype.FlipTabView(this,"front",{strings:this.options.strings}),this._tabViews.back=new Skeuocard.prototype.FlipTabView(this,"back",{strings:this.options.strings}),this.el.front.prepend(this._tabViews.front.el),this.el.back.prepend(this._tabViews.back.el),this._tabViews.front.hide(),this._tabViews.back.hide(),this._inputViews={number:new this.SegmentedCardNumberInputView,exp:new this.ExpirationInputView({currentDate:this.options.currentDate}),name:new this.TextInputView({"class":"cc-name",placeholder:"YOUR NAME"}),cvc:new this.TextInputView({"class":"cc-cvc",placeholder:"XXX",requireMaxLength:!0})},this._inputViews.number.el.addClass("cc-number"),this._inputViews.number.el.appendTo(this.el.front),this._inputViews.name.el.appendTo(this.el.front),this._inputViews.exp.el.addClass("cc-exp"),this._inputViews.exp.el.appendTo(this.el.front),this._inputViews.cvc.el.appendTo(this.el.back),this.el.container},Skeuocard.prototype._importImplicitOptions=function(){var fieldEl,fieldName,_initialExp,_ref,_ref1,_ref2,_ref3,_ref4,_ref5,_ref6,_this=this;_ref=this.el.underlyingFields;for(fieldName in _ref)fieldEl=_ref[fieldName],null==this.options.initialValues[fieldName]?this.options.initialValues[fieldName]=fieldEl.val():(this.options.initialValues[fieldName]=this.options.initialValues[fieldName].toString(),this._setUnderlyingValue(fieldName,this.options.initialValues[fieldName])),(null!=(_ref1=this.options.initialValues[fieldName])?_ref1.length:void 0)>0&&(this._state.initiallyFilled=!0),null==this.options.validationState[fieldName]&&(this.options.validationState[fieldName]=!fieldEl.hasClass("invalid"));return null==this.options.acceptedCardProducts&&(this.options.acceptedCardProducts=[],this.el.underlyingFields.type.find("option").each(function(i,_el){var el,shortname;return el=$(_el),shortname=el.attr("data-sc-type")||el.attr("value"),_this.options.acceptedCardProducts.push(shortname)})),(null!=(_ref2=this.options.initialValues.number)?_ref2.length:void 0)>0&&this.set("number",this.options.initialValues.number),(null!=(_ref3=this.options.initialValues.name)?_ref3.length:void 0)>0&&this.set("name",this.options.initialValues.name),(null!=(_ref4=this.options.initialValues.cvc)?_ref4.length:void 0)>0&&this.set("cvc",this.options.initialValues.cvc),(null!=(_ref5=this.options.initialValues.expYear)?_ref5.length:void 0)>0&&(null!=(_ref6=this.options.initialValues.expMonth)?_ref6.length:void 0)>0&&(_initialExp=new Date(parseInt(this.options.initialValues.expYear),parseInt(this.options.initialValues.expMonth)-1,1),this.set("exp",_initialExp)),this._updateValidationForFace("front"),this._updateValidationForFace("back")},Skeuocard.prototype.set=function(field,newValue){return this._inputViews[field].setValue(newValue),this._inputViews[field].trigger("valueChanged",this._inputViews[field])},Skeuocard.prototype._bindInputEvents=function(){var _expirationChange,_this=this;return this.el.underlyingFields.number.bind("change",function(e){return _this._inputViews.number.setValue(_this._getUnderlyingValue("number")),_this.render()}),_expirationChange=function(e){var month,year;return month=parseInt(_this._getUnderlyingValue("expMonth")),year=parseInt(_this._getUnderlyingValue("expYear")),_this._inputViews.exp.setValue(new Date(year,month-1)),_this.render()},this.el.underlyingFields.expMonth.bind("change",_expirationChange),this.el.underlyingFields.expYear.bind("change",_expirationChange),this.el.underlyingFields.name.bind("change",function(e){return _this._inputViews.exp.setValue(_this._getUnderlyingValue("name")),_this.render()}),this.el.underlyingFields.cvc.bind("change",function(e){return _this._inputViews.exp.setValue(_this._getUnderlyingValue("cvc")),_this.render()}),this._inputViews.number.bind("change valueChanged",function(e,input){var cardNumber,matchedProduct,number,previousProduct,_ref,_ref1;return cardNumber=input.getValue(),_this._setUnderlyingValue("number",cardNumber),_this._updateValidation("number",cardNumber),number=_this._getUnderlyingValue("number"),matchedProduct=Skeuocard.prototype.CardProduct.firstMatchingNumber(number),(null!=(_ref=_this.product)?_ref.eql(matchedProduct):void 0)?void 0:(_this._log("Product will change:",_this.product,"=>",matchedProduct),_ref1=null!=matchedProduct?matchedProduct.attrs.companyShortname:void 0,__indexOf.call(_this.options.acceptedCardProducts,_ref1)>=0?(_this.trigger("productWillChange.skeuocard",[_this,_this.product,matchedProduct]),previousProduct=_this.product,_this.el.container.removeClass("unaccepted"),_this._renderProduct(matchedProduct),_this.product=matchedProduct):null!=matchedProduct?(_this.trigger("productWillChange.skeuocard",[_this,_this.product,null]),_this.el.container.addClass("unaccepted"),_this._renderProduct(null),_this.product=null):(_this.trigger("productWillChange.skeuocard",[_this,_this.product,null]),_this.el.container.removeClass("unaccepted"),_this._renderProduct(null),_this.product=null),_this.trigger("productDidChange.skeuocard",[_this,previousProduct,_this.product]))}),this._inputViews.exp.bind("keyup valueChanged",function(e,input){var newDate;return newDate=input.getValue(),_this._updateValidation("exp",newDate),null!=newDate?(_this._setUnderlyingValue("expMonth",newDate.getMonth()+1),_this._setUnderlyingValue("expYear",newDate.getFullYear())):void 0}),this._inputViews.name.bind("keyup valueChanged",function(e,input){var value;return value=input.getValue(),_this._setUnderlyingValue("name",value),_this._updateValidation("name",value)}),this._inputViews.cvc.bind("keyup valueChanged",function(e,input){var value;return value=input.getValue(),_this._setUnderlyingValue("cvc",value),_this._updateValidation("cvc",value)}),this.el.container.delegate("input","keyup keydown",this._handleFieldTab.bind(this)),this._tabViews.front.el.click(function(){return _this.flip()}),this._tabViews.back.el.click(function(){return _this.flip()})},Skeuocard.prototype._handleFieldTab=function(e){var backFieldEls,currentFieldEl,frontFieldEls,_currentFace,_oppositeFace;return 9===e.which&&(currentFieldEl=$(e.currentTarget),_oppositeFace="front"===this.visibleFace?"back":"front",_currentFace="front"===this.visibleFace?"front":"back",backFieldEls=this.el[_oppositeFace].find("input"),frontFieldEls=this.el[_currentFace].find("input"),"front"===this.visibleFace&&this.el.front.hasClass("filled")&&backFieldEls.length>0&&frontFieldEls.index(currentFieldEl)===frontFieldEls.length-1&&!e.shiftKey&&(this.flip(),backFieldEls.first().focus(),e.preventDefault()),"back"===this.visibleFace&&e.shiftKey&&(this.flip(),backFieldEls.last().focus(),e.preventDefault())),!0},Skeuocard.prototype._updateValidation=function(fieldName,newValue){var fillStateChanged,isFilled,isFixed,isValid,needsFix,validationStateChanged;return null==this.product?!1:(isFilled=this.product[fieldName].isFilled(newValue),needsFix=null!=this.options.validationState[fieldName]==!1,isFixed=null!=this.options.initialValues[fieldName]&&newValue!==this.options.initialValues[fieldName],isValid=this.product[fieldName].isValid(newValue)&&(needsFix&&isFixed||!0),fillStateChanged=this._state[""+fieldName+"Filled"]!==isFilled,validationStateChanged=this._state[""+fieldName+"Valid"]!==isValid,fillStateChanged&&(this.trigger("fieldFillStateWillChange.skeuocard",[this,fieldName,isFilled]),this._inputViews[fieldName].el.toggleClass("filled",isFilled),this._state[""+fieldName+"Filled"]=isFilled,this.trigger("fieldFillStateDidChange.skeuocard",[this,fieldName,isFilled])),validationStateChanged&&(this.trigger("fieldValidationStateWillChange.skeuocard",[this,fieldName,isValid]),this._inputViews[fieldName].el.toggleClass("valid",isValid),this._inputViews[fieldName].el.toggleClass("invalid",!isValid),this._state[""+fieldName+"Valid"]=isValid,this.trigger("fieldValidationStateDidChange.skeuocard",[this,fieldName,isValid])),this._updateValidationForFace("front"),this._updateValidationForFace("back"))},Skeuocard.prototype._updateValidationForFace=function(face){var fieldsFilled,fieldsValid,fillStateChanged,isFilled,isValid,iv,validationStateChanged;return fieldsFilled=function(){var _i,_len,_ref,_results;for(_ref=this._inputViewsByFace[face],_results=[],_i=0,_len=_ref.length;_len>_i;_i++)iv=_ref[_i],_results.push(iv.el.hasClass("filled"));return _results}.call(this).every(Boolean),fieldsValid=function(){var _i,_len,_ref,_results;for(_ref=this._inputViewsByFace[face],_results=[],_i=0,_len=_ref.length;_len>_i;_i++)iv=_ref[_i],_results.push(iv.el.hasClass("valid"));return _results}.call(this).every(Boolean),isFilled=fieldsFilled&&null!=this.product||this._state.initiallyFilled||!1,isValid=fieldsValid&&null!=this.product,fillStateChanged=this._state[""+face+"Filled"]!==isFilled,validationStateChanged=this._state[""+face+"Valid"]!==isValid,fillStateChanged&&(this.trigger("faceFillStateWillChange.skeuocard",[this,face,isFilled]),this.el[face].toggleClass("filled",isFilled),this._state[""+face+"Filled"]=isFilled,this.trigger("faceFillStateDidChange.skeuocard",[this,face,isFilled])),validationStateChanged?(this.trigger("faceValidationStateWillChange.skeuocard",[this,face,isValid]),this.el[face].toggleClass("valid",isValid),this.el[face].toggleClass("invalid",!isValid),this._state[""+face+"Valid"]=isValid,this.trigger("faceValidationStateDidChange.skeuocard",[this,face,isValid])):void 0},Skeuocard.prototype._renderProduct=function(product){var destFace,fieldName,focused,view,viewEl,_ref,_ref1;if(this._log("[_renderProduct]","Rendering product:",product),this.el.container.removeClass(function(index,css){return(css.match(/\b(product|issuer)-\S+/g)||[]).join(" ")}),null!=(null!=product?product.attrs.companyShortname:void 0)&&this.el.container.addClass("product-"+product.attrs.companyShortname),null!=(null!=product?product.attrs.issuerShortname:void 0)&&this.el.container.addClass("issuer-"+product.attrs.issuerShortname),this._setUnderlyingValue("type",(null!=product?product.attrs.companyShortname:void 0)||null),this._inputViews.number.setGroupings((null!=product?product.attrs.cardNumberGrouping:void 0)||[this.options.genericPlaceholder.length],this.options.dontFocus),delete this.options.dontFocus,null!=product){this._inputViews.exp.reconfigure({pattern:(null!=product?product.attrs.expirationFormat:void 0)||"MM/YY"}),this._inputViews.cvc.attr({maxlength:product.attrs.cvcLength,placeholder:new Array(product.attrs.cvcLength+1).join(this.options.cardNumberPlaceholderChar)}),this._inputViewsByFace={front:[],back:[]},focused=$("input:focus"),_ref=product.attrs.layout;for(fieldName in _ref)destFace=_ref[fieldName],this._log("Moving",fieldName,"to",destFace),viewEl=this._inputViews[fieldName].el.detach(),viewEl.appendTo(this.el[destFace]),this._inputViewsByFace[destFace].push(this._inputViews[fieldName]),this._inputViews[fieldName].show();setTimeout(function(){var fieldEl,fieldLength;return fieldEl=focused.first(),fieldEl.length?(fieldLength=fieldEl[0].maxLength,fieldEl.focus(),fieldEl[0].setSelectionRange(fieldLength,fieldLength)):void 0},10)}else{_ref1=this._inputViews;for(fieldName in _ref1)view=_ref1[fieldName],"number"!==fieldName&&view.hide()}return product},Skeuocard.prototype._renderValidation=function(){var fieldName,fieldView,_ref,_results;_ref=this._inputViews,_results=[];for(fieldName in _ref)fieldView=_ref[fieldName],_results.push(this._updateValidation(fieldName,fieldView.getValue()));return _results},Skeuocard.prototype.render=function(){return this._renderProduct(this.product),this._renderValidation()},Skeuocard.prototype.flip=function(){var surfaceName,targetFace;return targetFace="front"===this.visibleFace?"back":"front",this.trigger("faceWillBecomeVisible.skeuocard",[this,targetFace]),this.visibleFace=targetFace,this.el.cardBody.toggleClass("flip"),surfaceName="front"===this.visibleFace?"front":"back",this.el[surfaceName].find(".cc-field").not(".filled").find("input").first().focus(),this.trigger("faceDidBecomeVisible.skeuocard",[this,targetFace])},Skeuocard.prototype._setUnderlyingValue=function(field,newValue){var fieldEl,remapAttrKey,_newValue,_this=this;if(fieldEl=this.el.underlyingFields[field],_newValue=(newValue||"").toString(),null==fieldEl)throw"Set underlying value of unknown field: "+field+".";return this.trigger("change.skeuocard",[this]),fieldEl.is("select")?(remapAttrKey="data-sc-"+field.toLowerCase(),fieldEl.find("option").each(function(i,_el){var optionEl;return optionEl=$(_el),_newValue===(optionEl.attr(remapAttrKey)||optionEl.attr("value"))?_this.el.underlyingFields[field].val(optionEl.attr("value")):void 0})):this.el.underlyingFields[field].val(_newValue)},Skeuocard.prototype._getUnderlyingValue=function(field){var _ref;return null!=(_ref=this.el.underlyingFields[field])?_ref.val():void 0},Skeuocard.prototype.isValid=function(){return this.product?"both"===this.product.faces?!this.el.front.hasClass("invalid")&&!this.el.back.hasClass("invalid"):"front"===this.product.faces?!this.el.front.hasClass("invalid"):!this.el.back.hasClass("invalid"):!1},Skeuocard}(),window.Skeuocard=Skeuocard,Skeuocard.prototype.FlipTabView=function(){function FlipTabView(sc,face,opts){var _this=this;null==opts&&(opts={}),this.card=sc,this.face=face,this.el=$('<div class="flip-tab '+face+'"><p></p></div>'),this.options=opts,this._state={},this.card.bind("faceFillStateWillChange.skeuocard",this._faceStateChanged.bind(this)),this.card.bind("faceValidationStateWillChange.skeuocard",this._faceValidationChanged.bind(this)),this.card.bind("productWillChange.skeuocard",function(e,card,prevProduct,newProduct){return null!=newProduct?newProduct.faces===_this.face?_this.hide():_this.show():_this.hide()})}return FlipTabView.prototype._faceStateChanged=function(e,card,face,isFilled){var oppositeFace;return oppositeFace="front"===face?"back":"front",isFilled===!0&&this.card._inputViewsByFace[oppositeFace].length>0&&this.show(),face!==this.face&&(this._state.opposingFaceFilled=isFilled),this._state.opposingFaceFilled!==!0?this.warn(this.options.strings.hiddenFaceFillPrompt,!0):void 0},FlipTabView.prototype._faceValidationChanged=function(e,card,face,isValid){return face!==this.face&&(this._state.opposingFaceValid=isValid),this._state.opposingFaceValid?this.prompt(this.options.strings.hiddenFaceSwitchPrompt):this._state.opposingFaceFilled?this.warn(this.options.strings.hiddenFaceErrorWarning):this.warn(this.options.strings.hiddenFaceFillPrompt)},FlipTabView.prototype._setText=function(text){return this.el.find("p").first().html(text)},FlipTabView.prototype.warn=function(message){return this._resetClasses(),this._setText(message),this.el.addClass("warn")},FlipTabView.prototype.prompt=function(message){return this._resetClasses(),this._setText(message),this.el.addClass("prompt")},FlipTabView.prototype._resetClasses=function(){return this.el.removeClass("warn"),this.el.removeClass("prompt")},FlipTabView.prototype.show=function(){return this.el.show()},FlipTabView.prototype.hide=function(){return this.el.hide()},FlipTabView}(),Skeuocard.prototype.SegmentedCardNumberInputView=function(){function SegmentedCardNumberInputView(opts){null==opts&&(opts={}),this.optDefaults={value:"",groupings:[19],placeholderChar:"X"},this.options=$.extend({},this.optDefaults,opts),this._state={selectingAll:!1},this._buildDOM(),this.setGroupings(this.options.groupings)}return SegmentedCardNumberInputView.prototype._digits=["0","1","2","3","4","5","6","7","8","9"],SegmentedCardNumberInputView.prototype._keys={backspace:8,tab:9,enter:13,del:46,arrowLeft:37,arrowUp:38,arrowRight:39,arrowDown:40,arrows:[37,38,39,40],command:16,alt:17},SegmentedCardNumberInputView.prototype._specialKeys=[8,9,13,46,37,38,39,40,16,17],SegmentedCardNumberInputView.prototype._buildDOM=function(){var _this=this;return this.el=$("<fieldset>"),this.el.addClass("cc-field"),this.el.delegate("input","keypress",this._handleGroupKeyPress.bind(this)),this.el.delegate("input","keydown",this._handleGroupKeyDown.bind(this)),this.el.delegate("input","keyup",this._handleGroupKeyUp.bind(this)),this.el.delegate("input","paste",this._handleGroupPaste.bind(this)),this.el.delegate("input","change",this._handleGroupChange.bind(this)),this.el.delegate("input","focus",function(e){return _this.el.addClass("focus")}),this.el.delegate("input","blur",function(e){return _this.el.removeClass("focus")})},SegmentedCardNumberInputView.prototype._handleGroupKeyDown=function(e){var currentTarget,cursorEnd,cursorStart,inputGroupEl,inputMaxLength,nextInputEl,prevInputEl,_ref;if(e.ctrlKey||e.metaKey)return this._handleModifiedKeyDown(e);switch(inputGroupEl=$(e.currentTarget),currentTarget=e.currentTarget,cursorStart=currentTarget.selectionStart,cursorEnd=currentTarget.selectionEnd,inputMaxLength=currentTarget.maxLength,prevInputEl=inputGroupEl.prevAll("input"),nextInputEl=inputGroupEl.nextAll("input"),e.which){case this._keys.backspace:prevInputEl.length>0&&0===cursorEnd&&this._focusField(prevInputEl.first(),"end");break;case this._keys.arrowUp:cursorEnd===inputMaxLength?this._focusField(inputGroupEl,"start"):this._focusField(inputGroupEl.prev(),"end"),e.preventDefault();break;case this._keys.arrowDown:cursorEnd===inputMaxLength?this._focusField(inputGroupEl.next(),"start"):this._focusField(inputGroupEl,"end"),e.preventDefault();break;case this._keys.arrowLeft:0===cursorEnd&&(this._focusField(inputGroupEl.prev(),"end"),e.preventDefault());break;case this._keys.arrowRight:cursorEnd===inputMaxLength&&(this._focusField(inputGroupEl.next(),"start"),e.preventDefault());break;default:_ref=e.which,__indexOf.call(this._specialKeys,_ref)>=0||cursorStart!==inputMaxLength||cursorEnd!==inputMaxLength||0===nextInputEl.length||this._focusField(nextInputEl.first(),"start")}return!0},SegmentedCardNumberInputView.prototype._handleGroupKeyPress=function(e){var inputGroupEl,isDigit,_ref,_ref1;return inputGroupEl=$(e.currentTarget),_ref=String.fromCharCode(e.which),isDigit=__indexOf.call(this._digits,_ref)>=0,e.ctrlKey||e.metaKey?!0:0===e.which?!0:!e.shiftKey&&(_ref1=e.which,__indexOf.call(this._specialKeys,_ref1)>=0)||isDigit?!0:(e.preventDefault(),!1)},SegmentedCardNumberInputView.prototype._handleGroupKeyUp=function(e){var currentTarget,cursorEnd,cursorStart,inputGroupEl,inputMaxLength,nextInputEl,_ref,_ref1,_ref2;return inputGroupEl=$(e.currentTarget),currentTarget=e.currentTarget,inputMaxLength=currentTarget.maxLength,cursorStart=currentTarget.selectionStart,cursorEnd=currentTarget.selectionEnd,nextInputEl=inputGroupEl.nextAll("input"),e.ctrlKey||e.metaKey?!0:(this._state.selectingAll&&(_ref=e.which,__indexOf.call(this._specialKeys,_ref)>=0)&&e.which!==this._keys.command&&e.which!==this._keys.alt&&this._endSelectAll(),_ref1=e.which,__indexOf.call(this._specialKeys,_ref1)>=0||e.shiftKey&&e.which===this._keys.tab||cursorStart!==inputMaxLength||cursorEnd!==inputMaxLength||0===nextInputEl.length||this._focusField(nextInputEl.first(),"start"),e.shiftKey&&(_ref2=e.which,__indexOf.call(this._specialKeys,_ref2)>=0)||this.trigger("change",[this]),!0)},SegmentedCardNumberInputView.prototype._handleModifiedKeyDown=function(e){var char;switch(char=String.fromCharCode(e.which)){case"a":case"A":return this._beginSelectAll(),e.preventDefault()}},SegmentedCardNumberInputView.prototype._handleGroupPaste=function(e){var _this=this;return setTimeout(function(){var newValue;return newValue=_this.getValue().replace(/[^0-9]+/g,""),_this._state.selectingAll&&_this._endSelectAll(),_this.setValue(newValue),_this.trigger("change",[_this])},50)},SegmentedCardNumberInputView.prototype._handleGroupChange=function(e){return e.stopPropagation()},SegmentedCardNumberInputView.prototype._getFocusedField=function(){return this.el.find("input:focus")},SegmentedCardNumberInputView.prototype._beginSelectAll=function(){var fieldEl;return this.el.hasClass("selecting-all")?(fieldEl=this.el.find("input"),fieldEl[0].setSelectionRange(0,fieldEl.val().length)):(this._state.lastGrouping=this.options.groupings,this._state.lastLength=this.getValue().length,this.setGroupings(this.optDefaults.groupings),this.el.addClass("selecting-all"),fieldEl=this.el.find("input"),fieldEl[0].setSelectionRange(0,fieldEl.val().length),this._state.selectingAll=!0)},SegmentedCardNumberInputView.prototype._endSelectAll=function(){return this.el.hasClass("selecting-all")?(this._state.selectingAll=!1,this._state.lastLength===this.getValue().length&&this.setGroupings(this._state.lastGrouping),this.el.removeClass("selecting-all")):void 0},SegmentedCardNumberInputView.prototype._indexInValueAtFieldSelection=function(field){var groupingIndex,i,len,offset,_i,_len,_ref;for(groupingIndex=this.el.find("input").index(field),offset=0,_ref=this.options.groupings,i=_i=0,_len=_ref.length;_len>_i;i=++_i)len=_ref[i],groupingIndex>i&&(offset+=len);return offset+field[0].selectionEnd},SegmentedCardNumberInputView.prototype.setGroupings=function(groupings,dontFocus){var groupEl,groupLength,_caretPosition,_currentField,_i,_len,_value;for(_currentField=this._getFocusedField(),_value=this.getValue(),_caretPosition=0,_currentField.length>0&&(_caretPosition=this._indexInValueAtFieldSelection(_currentField)),this.el.empty(),_i=0,_len=groupings.length;_len>_i;_i++)groupLength=groupings[_i],groupEl=$("<input>").attr({type:"text",pattern:"[0-9]*",size:groupLength,maxlength:groupLength,"class":"group"+groupLength,placeholder:new Array(groupLength+1).join(this.options.placeholderChar)}),this.el.append(groupEl);return this.options.groupings=groupings,this.setValue(_value),_currentField=this._focusFieldForValue([_caretPosition,_caretPosition],dontFocus),null!=_currentField&&_currentField[0].selectionEnd===_currentField[0].maxLength?this._focusField(_currentField.next(),"start"):void 0},SegmentedCardNumberInputView.prototype._focusFieldForValue=function(place,dontFocus){var field,fieldOffset,fieldPosition,groupIndex,groupLength,value,_i,_lastStartPos,_len,_ref;if(value=this.getValue(),"start"===place)field=this.el.find("input").first(),dontFocus||this._focusField(field,place);else if("end"===place)field=this.el.find("input").last(),dontFocus||this._focusField(field,place);else{for(field=null,fieldOffset=null,_lastStartPos=0,_ref=this.options.groupings,groupIndex=_i=0,_len=_ref.length;_len>_i;groupIndex=++_i)groupLength=_ref[groupIndex],place[1]>_lastStartPos&&place[1]<=_lastStartPos+groupLength&&(field=$(this.el.find("input")[groupIndex]),fieldPosition=place[1]-_lastStartPos),_lastStartPos+=groupLength;null!=field&&null!=fieldPosition?dontFocus||this._focusField(field,[fieldPosition,fieldPosition]):dontFocus||this._focusField(this.el.find("input"),"end")}return field},SegmentedCardNumberInputView.prototype._focusField=function(field,place){var fieldLen;return 0!==field.length&&(field[0].focus(),$(field[0]).is(":visible")&&field[0]===document.activeElement)?"start"===place?field[0].setSelectionRange(0,0):"end"===place?(fieldLen=field[0].maxLength,field[0].setSelectionRange(fieldLen,fieldLen)):field[0].setSelectionRange(place[0],place[1]):void 0},SegmentedCardNumberInputView.prototype.setValue=function(newValue){var el,groupIndex,groupLength,groupVal,_i,_lastStartPos,_len,_ref,_results;for(_lastStartPos=0,_ref=this.options.groupings,_results=[],groupIndex=_i=0,_len=_ref.length;_len>_i;groupIndex=++_i)groupLength=_ref[groupIndex],el=$(this.el.find("input").get(groupIndex)),groupVal=newValue.substr(_lastStartPos,groupLength),el.val(groupVal),_results.push(_lastStartPos+=groupLength);return _results},SegmentedCardNumberInputView.prototype.getValue=function(){var buffer,el,_i,_len,_ref;for(buffer="",_ref=this.el.find("input"),_i=0,_len=_ref.length;_len>_i;_i++)el=_ref[_i],buffer+=$(el).val();return buffer},SegmentedCardNumberInputView.prototype.maxLength=function(){return this.options.groupings.reduce(function(a,b){return a+b})},SegmentedCardNumberInputView.prototype.bind=function(){var args,_ref;return args=1<=arguments.length?__slice.call(arguments,0):[],(_ref=this.el).bind.apply(_ref,args)},SegmentedCardNumberInputView.prototype.trigger=function(){var args,_ref;return args=1<=arguments.length?__slice.call(arguments,0):[],(_ref=this.el).trigger.apply(_ref,args)},SegmentedCardNumberInputView.prototype.show=function(){return this.el.show()},SegmentedCardNumberInputView.prototype.hide=function(){return this.el.hide()},SegmentedCardNumberInputView.prototype.addClass=function(){var args,_ref;return args=1<=arguments.length?__slice.call(arguments,0):[],(_ref=this.el).addClass.apply(_ref,args)},SegmentedCardNumberInputView.prototype.removeClass=function(){var args,_ref;return args=1<=arguments.length?__slice.call(arguments,0):[],(_ref=this.el).removeClass.apply(_ref,args)},SegmentedCardNumberInputView}(),Skeuocard.prototype.ExpirationInputView=function(){function ExpirationInputView(opts){var _this=this;null==opts&&(opts={}),opts.pattern||(opts.pattern="MM/YY"),this.options=opts,this.date=null,this.el=$("<fieldset>"),this.el.addClass("cc-field"),this.el.delegate("input","keydown",function(e){return _this._onKeyDown(e)}),this.el.delegate("input","keyup",function(e){return _this._onKeyUp(e)}),this.el.delegate("input","focus",function(e){return _this.el.addClass("focus")}),this.el.delegate("input","blur",function(e){return _this.el.removeClass("focus")})}return ExpirationInputView.prototype.bind=function(){var args,_ref;return args=1<=arguments.length?__slice.call(arguments,0):[],(_ref=this.el).bind.apply(_ref,args)},ExpirationInputView.prototype.trigger=function(){var args,_ref;return args=1<=arguments.length?__slice.call(arguments,0):[],(_ref=this.el).trigger.apply(_ref,args)},ExpirationInputView.prototype._getFieldCaretPosition=function(el){var input,sel,selLength;return input=el.get(0),null!=input.selectionEnd?input.selectionEnd:document.selection?(input.focus(),sel=document.selection.createRange(),selLength=document.selection.createRange().text.length,sel.moveStart("character",-input.value.length),selLength):void 0},ExpirationInputView.prototype._setFieldCaretPosition=function(el,pos){var input,range;return input=el.get(0),null!=input.createTextRange?(range=input.createTextRange(),range.move("character",pos),range.select()):null!=input.selectionStart?(input.focus(),input.setSelectionRange(pos,pos)):void 0},ExpirationInputView.prototype.setPattern=function(pattern){var char,groupings,i,patternParts,_currentLength,_i,_len;for(groupings=[],patternParts=pattern.split(""),_currentLength=0,i=_i=0,_len=patternParts.length;_len>_i;i=++_i)char=patternParts[i],_currentLength++,patternParts[i+1]!==char&&(groupings.push([_currentLength,char]),_currentLength=0);return this.options.groupings=groupings,this._setGroupings(this.options.groupings)},ExpirationInputView.prototype._setGroupings=function(groupings){var fieldChars,group,groupChar,groupLength,input,sep,_i,_len,_startLength;for(fieldChars=["D","M","Y"],this.el.empty(),_startLength=0,_i=0,_len=groupings.length;_len>_i;_i++)group=groupings[_i],groupLength=group[0],groupChar=group[1],__indexOf.call(fieldChars,groupChar)>=0?(input=$("<input>").attr({type:"text",pattern:"[0-9]*",placeholder:new Array(groupLength+1).join(groupChar),maxlength:groupLength,"class":"cc-exp-field-"+groupChar.toLowerCase()+" group"+groupLength}),input.data("fieldtype",groupChar),this.el.append(input)):(sep=$("<span>").attr({"class":"separator"}),sep.html(new Array(groupLength+1).join(groupChar)),this.el.append(sep));return this.groupEls=this.el.find("input"),null!=this.date?this._updateFieldValues():void 0},ExpirationInputView.prototype._zeroPadNumber=function(num,places){var zero;return zero=places-num.toString().length+1,Array(zero).join("0")+num},ExpirationInputView.prototype._updateFieldValues=function(){var currentDate,_this=this;return currentDate=this.date,this.groupEls?this.groupEls.each(function(i,_el){var el,groupLength,year;switch(el=$(_el),groupLength=parseInt(el.attr("maxlength")),el.data("fieldtype")){case"M":return el.val(_this._zeroPadNumber(currentDate.getMonth()+1,groupLength));case"D":return el.val(_this._zeroPadNumber(currentDate.getDate(),groupLength));case"Y":return year=groupLength>=4?currentDate.getFullYear():currentDate.getFullYear().toString().substr(2,4),el.val(year)}}):this.setPattern(this.options.pattern)},ExpirationInputView.prototype.clear=function(){return this.value="",this.date=null,this.groupEls.each(function(){return $(this).val("")})},ExpirationInputView.prototype.setValue=function(newDate){return this.date=newDate,this._updateFieldValues()},ExpirationInputView.prototype.getValue=function(){return this.date},ExpirationInputView.prototype.reconfigure=function(opts){return null!=opts.pattern&&this.setPattern(opts.pattern),null!=opts.value?this.setValue(opts.value):void 0},ExpirationInputView.prototype._onKeyDown=function(e){var groupCaretPos,groupEl,groupMaxLength,nextInputEl,prevInputEl,_ref;if(e.stopPropagation(),groupEl=$(e.currentTarget),groupEl=$(e.currentTarget),groupMaxLength=parseInt(groupEl.attr("maxlength")),groupCaretPos=this._getFieldCaretPosition(groupEl),
2
+ prevInputEl=groupEl.prevAll("input").first(),nextInputEl=groupEl.nextAll("input").first(),8!==e.which||0!==groupCaretPos||$.isEmptyObject(prevInputEl)||prevInputEl.focus(),37===(_ref=e.which)||38===_ref||39===_ref||40===_ref)switch(e.which){case 37:if(0===groupCaretPos&&!$.isEmptyObject(prevInputEl))return prevInputEl.focus();break;case 39:if(groupCaretPos===groupMaxLength&&!$.isEmptyObject(nextInputEl))return nextInputEl.focus();break;case 38:if(!$.isEmptyObject(groupEl.prev("input")))return prevInputEl.focus();break;case 40:if(!$.isEmptyObject(groupEl.next("input")))return nextInputEl.focus()}},ExpirationInputView.prototype.getRawValue=function(fieldType){return parseInt(this.el.find(".cc-exp-field-"+fieldType).val())},ExpirationInputView.prototype._onKeyUp=function(e){var arrowKeys,dateObj,day,groupCaretPos,groupEl,groupMaxLength,groupValLength,month,nextInputEl,pattern,specialKeys,year,_ref,_ref1;return e.stopPropagation(),specialKeys=[8,9,16,17,18,19,20,27,33,34,35,36,37,38,39,40,45,46,91,93,144,145,224],arrowKeys=[37,38,39,40],groupEl=$(e.currentTarget),groupMaxLength=parseInt(groupEl.attr("maxlength")),groupCaretPos=this._getFieldCaretPosition(groupEl),_ref=e.which,__indexOf.call(specialKeys,_ref)<0&&(groupValLength=groupEl.val().length,pattern=new RegExp("[^0-9]+","g"),groupEl.val(groupEl.val().replace(pattern,"")),groupEl.val().length<groupValLength?this._setFieldCaretPosition(groupEl,groupCaretPos-1):this._setFieldCaretPosition(groupEl,groupCaretPos)),nextInputEl=groupEl.nextAll("input").first(),_ref1=e.which,__indexOf.call(specialKeys,_ref1)<0&&groupEl.val().length===groupMaxLength&&!$.isEmptyObject(nextInputEl)&&this._getFieldCaretPosition(groupEl)===groupMaxLength&&nextInputEl.focus(),day=this.getRawValue("d")||1,month=this.getRawValue("m"),year=this.getRawValue("y"),0===month||month>12||0===year?this.date=new Date(1900,0,1):(2e3>year&&(year+=2e3),dateObj=new Date(year,month-1,day),this.date=dateObj),this.trigger("keyup",[this]),!1},ExpirationInputView.prototype._inputGroupEls=function(){return this.el.find("input")},ExpirationInputView.prototype.show=function(){return this.el.show()},ExpirationInputView.prototype.hide=function(){return this.el.hide()},ExpirationInputView}(),Skeuocard.prototype.TextInputView=function(){function TextInputView(opts){var _this=this;this.el=$("<div>"),this.inputEl=$("<input>").attr({type:"text",placeholder:opts.placeholder,"class":opts["class"]}),this.el.append(this.inputEl),this.el.addClass("cc-field"),this.options=opts,this.el.delegate("input","focus",function(e){return _this.el.addClass("focus")}),this.el.delegate("input","blur",function(e){return _this.el.removeClass("focus")}),this.el.delegate("input","keyup",function(e){return e.stopPropagation(),_this.trigger("keyup",[_this])})}return TextInputView.prototype.clear=function(){return this.inputEl.val("")},TextInputView.prototype.attr=function(){var args,_ref;return args=1<=arguments.length?__slice.call(arguments,0):[],(_ref=this.inputEl).attr.apply(_ref,args)},TextInputView.prototype.setValue=function(newValue){return this.inputEl.val(newValue)},TextInputView.prototype.getValue=function(){return this.inputEl.val()},TextInputView.prototype.bind=function(){var args,_ref;return args=1<=arguments.length?__slice.call(arguments,0):[],(_ref=this.el).bind.apply(_ref,args)},TextInputView.prototype.trigger=function(){var args,_ref;return args=1<=arguments.length?__slice.call(arguments,0):[],(_ref=this.el).trigger.apply(_ref,args)},TextInputView.prototype.show=function(){return this.el.show()},TextInputView.prototype.hide=function(){return this.el.hide()},TextInputView}(),Skeuocard.prototype.CardProduct=function(){function CardProduct(attrs){var faces,k,v,_ref;this.attrs=$.extend({},attrs),this.pattern=this.attrs.pattern,this._variances=[],this.name={isFilled:this._isCardNameFilled.bind(this),isValid:this._isCardNameValid.bind(this)},this.number={isFilled:this._isCardNumberFilled.bind(this),isValid:this._isCardNumberValid.bind(this)},this.exp={isFilled:this._isCardExpirationFilled.bind(this),isValid:this._isCardExpirationValid.bind(this)},this.cvc={isFilled:this._isCardCVCFilled.bind(this),isValid:this._isCardCVCValid.bind(this)},faces={front:0,back:0},_ref=attrs.layout;for(k in _ref)v=_ref[k],faces[attrs.layout[k]]+=1;faces.front>0&&faces.back>0?this.faces="both":faces.front>0?this.faces="front":this.faces="back"}return CardProduct._registry=[],CardProduct.create=function(opts){return this._registry.push(new Skeuocard.prototype.CardProduct(opts))},CardProduct.firstMatchingShortname=function(shortname){var card,_i,_len,_ref;for(_ref=this._registry,_i=0,_len=_ref.length;_len>_i;_i++)if(card=_ref[_i],card.attrs.companyShortname===shortname)return card;return null},CardProduct.firstMatchingNumber=function(number){var card,combinedOptions,variation,_i,_len,_ref;for(_ref=this._registry,_i=0,_len=_ref.length;_len>_i;_i++)if(card=_ref[_i],card.pattern.test(number))return(variation=card.firstVariationMatchingNumber(number))?(combinedOptions=$.extend({},card.attrs,variation),new Skeuocard.prototype.CardProduct(combinedOptions)):new Skeuocard.prototype.CardProduct(card.attrs);return null},CardProduct.prototype.createVariation=function(attrs){return this._variances.push(attrs)},CardProduct.prototype.firstVariationMatchingNumber=function(number){var variance,_i,_len,_ref;for(_ref=this._variances,_i=0,_len=_ref.length;_len>_i;_i++)if(variance=_ref[_i],variance.pattern.test(number))return variance;return null},CardProduct.prototype.fieldsForLayoutFace=function(faceName){var face,fieldName,_ref,_results;_ref=this.attrs.layout,_results=[];for(fieldName in _ref)face=_ref[fieldName],face===faceName&&_results.push(fieldName);return _results},CardProduct.prototype._id=function(){var ident;return ident=this.attrs.companyShortname,null!=this.attrs.issuerShortname&&(ident+=this.attrs.issuerShortname),ident},CardProduct.prototype.eql=function(otherCardProduct){return(null!=otherCardProduct?otherCardProduct._id():void 0)===this._id()},CardProduct.prototype._daysInMonth=function(m,y){switch(m){case 1:return y%4===0&&y%100||y%400===0?29:28;case 3:case 5:case 8:case 10:return 30;default:return 31}},CardProduct.prototype._isCardNumberFilled=function(number){var _ref;return null!=this.attrs.cardNumberLength?(_ref=number.length,__indexOf.call(this.attrs.cardNumberLength,_ref)>=0):void 0},CardProduct.prototype._isCardExpirationFilled=function(exp){var currentDate,day,month,year;return currentDate=Skeuocard.currentDate,null==exp||null==exp.getMonth||null==exp.getFullYear?!1:(day=exp.getDate(),month=exp.getMonth(),year=exp.getFullYear(),day>0&&day<=this._daysInMonth(month,year)&&month>=0&&11>=month&&year>=1900&&year<=currentDate.getFullYear()+10)},CardProduct.prototype._isCardCVCFilled=function(cvc){return cvc.length===this.attrs.cvcLength},CardProduct.prototype._isCardNameFilled=function(name){return name.length>0},CardProduct.prototype._isCardNumberValid=function(number){return/^\d+$/.test(number)&&(this.attrs.validateLuhn===!1||this._isValidLuhn(number))&&this._isCardNumberFilled(number)},CardProduct.prototype._isCardExpirationValid=function(exp){var currentDate,day,isDateInFuture,month,year;return null==exp||null==exp.getMonth||null==exp.getFullYear?!1:(currentDate=Skeuocard.currentDate,day=exp.getDate(),month=exp.getMonth(),year=exp.getFullYear(),isDateInFuture=year===currentDate.getFullYear()&&month>=currentDate.getMonth()||year>currentDate.getFullYear(),isDateInFuture&&this._isCardExpirationFilled(exp))},CardProduct.prototype._isCardCVCValid=function(cvc){return this._isCardCVCFilled(cvc)},CardProduct.prototype._isCardNameValid=function(name){return this._isCardNameFilled(name)},CardProduct.prototype._isValidLuhn=function(number){var alt,i,num,sum,_i,_ref;for(sum=0,alt=!1,i=_i=_ref=number.length-1;_i>=0;i=_i+=-1){if(num=parseInt(number.charAt(i),10),isNaN(num))return!1;alt&&(num*=2,num>9&&(num=num%10+1)),alt=!alt,sum+=num}return sum%10===0},CardProduct}(),Skeuocard.prototype.CardProduct.create({pattern:/^(36|38|30[0-5])/,companyName:"Diners Club",companyShortname:"dinersclubintl",cardNumberGrouping:[4,6,4],cardNumberLength:[14],expirationFormat:"MM/YY",cvcLength:3,validateLuhn:!0,layout:{number:"front",exp:"front",name:"front",cvc:"back"}}),Skeuocard.prototype.CardProduct.create({pattern:/^35/,companyName:"JCB",companyShortname:"jcb",cardNumberGrouping:[4,4,4,4],cardNumberLength:[16],expirationFormat:"MM/'YY",cvcLength:3,validateLuhn:!0,layout:{number:"front",exp:"front",name:"front",cvc:"back"}}),Skeuocard.prototype.CardProduct.create({pattern:/^3[47]/,companyName:"American Express",companyShortname:"amex",cardNumberGrouping:[4,6,5],cardNumberLength:[15],expirationFormat:"MM/YY",cvcLength:4,validateLuhn:!0,layout:{number:"front",exp:"front",name:"front",cvc:"front"}}),Skeuocard.prototype.CardProduct.create({pattern:/^(6706|6771|6709)/,companyName:"Laser Card Services Ltd.",companyShortname:"laser",cardNumberGrouping:[4,4,4,4],cardNumberLength:[16,17,18,19],expirationFormat:"MM/YY",validateLuhn:!0,cvcLength:3,layout:{number:"front",exp:"front",name:"front",cvc:"back"}}),Skeuocard.prototype.CardProduct.create({pattern:/^4/,companyName:"Visa",companyShortname:"visa",cardNumberGrouping:[4,4,4,4],cardNumberLength:[13,14,15,16],expirationFormat:"MM/YY",validateLuhn:!0,cvcLength:3,layout:{number:"front",exp:"front",name:"front",cvc:"back"}}),Skeuocard.prototype.CardProduct.create({pattern:/^(62|88)/,companyName:"China UnionPay",companyShortname:"unionpay",cardNumberGrouping:[19],cardNumberLength:[16,17,18,19],expirationFormat:"MM/YY",validateLuhn:!1,cvcLength:3,layout:{number:"front",exp:"front",name:"front",cvc:"back"}}),Skeuocard.prototype.CardProduct.create({pattern:/^(5[1-5]|(222[1-9])|(22[3-9][0-9])|(2[3-6][0-9]{2})|(27[01][0-9])|2720)/,companyName:"Mastercard",companyShortname:"mastercard",cardNumberGrouping:[4,4,4,4],cardNumberLength:[16],expirationFormat:"MM/YY",validateLuhn:!0,cvcLength:3,layout:{number:"front",exp:"front",name:"front",cvc:"back"}}),Skeuocard.prototype.CardProduct.create({pattern:/^(5018|5020|5038|6304|6759|676[1-3])/,companyName:"Maestro (MasterCard)",companyShortname:"maestro",cardNumberGrouping:[4,4,4,4],cardNumberLength:[12,13,14,15,16,17,18,19],expirationFormat:"MM/YY",validateLuhn:!0,cvcLength:3,layout:{number:"front",exp:"front",name:"front",cvc:"back"}}),Skeuocard.prototype.CardProduct.create({pattern:/^(6011|65|64[4-9]|622)/,companyName:"Discover",companyShortname:"discover",cardNumberGrouping:[4,4,4,4],cardNumberLength:[16],expirationFormat:"MM/YY",validateLuhn:!0,cvcLength:3,layout:{number:"front",exp:"front",name:"front",cvc:"back"}}),visaProduct=Skeuocard.prototype.CardProduct.firstMatchingShortname("visa"),visaProduct.createVariation({pattern:/^414720/,issuingAuthority:"Chase",issuerName:"Chase Sapphire Card",issuerShortname:"chase-sapphire",layout:{name:"front",number:"front",exp:"front",cvc:"front"}})}).call(this);
@@ -0,0 +1,284 @@
1
+ ###
2
+ Skeuocard::CardProduct
3
+ ###
4
+
5
+ class Skeuocard::CardProduct
6
+ @_registry: [] # registry of stored CardProduct instances
7
+
8
+ # Create and register a new CardProduct instance.
9
+ @create: (opts)->
10
+ @_registry.push new Skeuocard::CardProduct(opts)
11
+
12
+ @firstMatchingShortname: (shortname)->
13
+ for card in @_registry
14
+ return card if card.attrs.companyShortname is shortname
15
+ return null
16
+
17
+ @firstMatchingNumber: (number)->
18
+ for card in @_registry
19
+ if card.pattern.test(number)
20
+ if (variation = card.firstVariationMatchingNumber(number))
21
+ combinedOptions = $.extend({}, card.attrs, variation)
22
+ return new Skeuocard::CardProduct(combinedOptions)
23
+ return new Skeuocard::CardProduct(card.attrs)
24
+ return null
25
+
26
+ constructor: (attrs)->
27
+ @attrs = $.extend({}, attrs)
28
+ @pattern = @attrs.pattern
29
+ @_variances = []
30
+ # syntactic sugar ;)
31
+ @name =
32
+ isFilled: @_isCardNameFilled.bind(@)
33
+ isValid: @_isCardNameValid.bind(@)
34
+ @number =
35
+ isFilled: @_isCardNumberFilled.bind(@)
36
+ isValid: @_isCardNumberValid.bind(@)
37
+ @exp =
38
+ isFilled: @_isCardExpirationFilled.bind(@)
39
+ isValid: @_isCardExpirationValid.bind(@)
40
+ @cvc =
41
+ isFilled: @_isCardCVCFilled.bind(@)
42
+ isValid: @_isCardCVCValid.bind(@)
43
+ faces = { front: 0, back: 0 }
44
+ for k,v of attrs.layout
45
+ faces[attrs.layout[k]] += 1
46
+ if faces.front > 0 && faces.back > 0
47
+ @faces = 'both'
48
+ else
49
+ if faces.front > 0
50
+ @faces = 'front'
51
+ else
52
+ @faces = 'back'
53
+
54
+ createVariation: (attrs)->
55
+ @_variances.push attrs
56
+
57
+ firstVariationMatchingNumber: (number)->
58
+ for variance in @_variances
59
+ return variance if variance.pattern.test(number)
60
+ return null
61
+
62
+ fieldsForLayoutFace: (faceName)->
63
+ (fieldName for fieldName, face of @attrs.layout when face is faceName)
64
+
65
+ _id: ->
66
+ ident = @attrs.companyShortname
67
+ if @attrs.issuerShortname?
68
+ ident += @attrs.issuerShortname
69
+ return ident
70
+
71
+ eql: (otherCardProduct)->
72
+ otherCardProduct?._id() is @_id()
73
+
74
+ _daysInMonth: (m, y)->
75
+ return switch m
76
+ when 1 then (if (y % 4 is 0 and y % 100) or y % 400 is 0 then 29 else 28)
77
+ when 3, 5, 8, 10 then 30
78
+ else 31
79
+
80
+ _isCardNumberFilled: (number)->
81
+ return (number.length in @attrs.cardNumberLength) if @attrs.cardNumberLength?
82
+
83
+ _isCardExpirationFilled: (exp)->
84
+ currentDate = Skeuocard.currentDate
85
+ return false unless exp? and exp.getMonth? and exp.getFullYear?
86
+ day = exp.getDate()
87
+ month = exp.getMonth()
88
+ year = exp.getFullYear()
89
+ return (day > 0 and day <= @_daysInMonth(month, year)) and
90
+ (month >= 0 and month <= 11) and
91
+ (year >= 1900 and year <= currentDate.getFullYear() + 10)
92
+
93
+ _isCardCVCFilled: (cvc)->
94
+ cvc.length is @attrs.cvcLength
95
+
96
+ _isCardNameFilled: (name)->
97
+ name.length > 0
98
+
99
+ _isCardNumberValid: (number)->
100
+ /^\d+$/.test(number) and
101
+ (@attrs.validateLuhn is false or @_isValidLuhn(number)) and
102
+ @_isCardNumberFilled(number)
103
+
104
+ _isCardExpirationValid: (exp)->
105
+ return false unless exp? and exp.getMonth? and exp.getFullYear?
106
+ currentDate = Skeuocard.currentDate
107
+ day = exp.getDate()
108
+ month = exp.getMonth()
109
+ year = exp.getFullYear()
110
+ isDateInFuture = (year == currentDate.getFullYear() and
111
+ month >= currentDate.getMonth()) or
112
+ year > currentDate.getFullYear()
113
+ return isDateInFuture and @_isCardExpirationFilled(exp)
114
+
115
+ _isCardCVCValid: (cvc)->
116
+ @_isCardCVCFilled(cvc)
117
+
118
+ _isCardNameValid: (name)->
119
+ @_isCardNameFilled(name)
120
+
121
+ _isValidLuhn: (number)->
122
+ sum = 0
123
+ alt = false
124
+ for i in [number.length - 1..0] by -1
125
+ num = parseInt number.charAt(i), 10
126
+ return false if isNaN(num)
127
+ if alt
128
+ num *= 2
129
+ num = (num % 10) + 1 if num > 9
130
+ alt = !alt
131
+ sum += num
132
+ sum % 10 is 0
133
+
134
+
135
+ ###
136
+ # Seed CardProducts.
137
+ ###
138
+ Skeuocard::CardProduct.create
139
+ pattern: /^(36|38|30[0-5])/
140
+ companyName: "Diners Club"
141
+ companyShortname: "dinersclubintl"
142
+ cardNumberGrouping: [4,6,4]
143
+ cardNumberLength: [14]
144
+ expirationFormat: "MM/YY"
145
+ cvcLength: 3
146
+ validateLuhn: true
147
+ layout:
148
+ number: 'front'
149
+ exp: 'front'
150
+ name: 'front'
151
+ cvc: 'back'
152
+
153
+ Skeuocard::CardProduct.create
154
+ pattern: /^35/
155
+ companyName: "JCB"
156
+ companyShortname: "jcb"
157
+ cardNumberGrouping: [4,4,4,4]
158
+ cardNumberLength: [16]
159
+ expirationFormat: "MM/'YY"
160
+ cvcLength: 3
161
+ validateLuhn: true
162
+ layout:
163
+ number: 'front'
164
+ exp: 'front'
165
+ name: 'front'
166
+ cvc: 'back'
167
+
168
+ Skeuocard::CardProduct.create
169
+ pattern: /^3[47]/
170
+ companyName: "American Express"
171
+ companyShortname: "amex"
172
+ cardNumberGrouping: [4,6,5]
173
+ cardNumberLength: [15]
174
+ expirationFormat: "MM/YY"
175
+ cvcLength: 4
176
+ validateLuhn: true
177
+ layout:
178
+ number: 'front'
179
+ exp: 'front'
180
+ name: 'front'
181
+ cvc: 'front'
182
+
183
+ Skeuocard::CardProduct.create
184
+ pattern: /^(6706|6771|6709)/
185
+ companyName: "Laser Card Services Ltd."
186
+ companyShortname: "laser"
187
+ cardNumberGrouping: [4,4,4,4]
188
+ cardNumberLength: [16..19]
189
+ expirationFormat: "MM/YY"
190
+ validateLuhn: true
191
+ cvcLength: 3
192
+ layout:
193
+ number: 'front'
194
+ exp: 'front'
195
+ name: 'front'
196
+ cvc: 'back'
197
+
198
+ Skeuocard::CardProduct.create
199
+ pattern: /^4/
200
+ companyName: "Visa"
201
+ companyShortname: "visa"
202
+ cardNumberGrouping: [4,4,4,4]
203
+ cardNumberLength: [13..16]
204
+ expirationFormat: "MM/YY"
205
+ validateLuhn: true
206
+ cvcLength: 3
207
+ layout:
208
+ number: 'front'
209
+ exp: 'front'
210
+ name: 'front'
211
+ cvc: 'back'
212
+
213
+ Skeuocard::CardProduct.create
214
+ pattern: /^(62|88)/
215
+ companyName: "China UnionPay"
216
+ companyShortname: "unionpay"
217
+ cardNumberGrouping: [19]
218
+ cardNumberLength: [16..19]
219
+ expirationFormat: "MM/YY"
220
+ validateLuhn: false
221
+ cvcLength: 3
222
+ layout:
223
+ number: 'front'
224
+ exp: 'front'
225
+ name: 'front'
226
+ cvc: 'back'
227
+
228
+ Skeuocard::CardProduct.create
229
+ pattern: /^(5[1-5]|(222[1-9])|(22[3-9][0-9])|(2[3-6][0-9]{2})|(27[01][0-9])|2720)/
230
+ companyName: "Mastercard"
231
+ companyShortname: "mastercard"
232
+ cardNumberGrouping: [4,4,4,4]
233
+ cardNumberLength: [16]
234
+ expirationFormat: "MM/YY"
235
+ validateLuhn: true
236
+ cvcLength: 3
237
+ layout:
238
+ number: 'front'
239
+ exp: 'front'
240
+ name: 'front'
241
+ cvc: 'back'
242
+
243
+ Skeuocard::CardProduct.create
244
+ pattern: /^(5018|5020|5038|6304|6759|676[1-3])/
245
+ companyName: "Maestro (MasterCard)"
246
+ companyShortname: "maestro"
247
+ cardNumberGrouping: [4,4,4,4]
248
+ cardNumberLength: [12..19]
249
+ expirationFormat: "MM/YY"
250
+ validateLuhn: true
251
+ cvcLength: 3
252
+ layout:
253
+ number: 'front'
254
+ exp: 'front'
255
+ name: 'front'
256
+ cvc: 'back'
257
+
258
+ Skeuocard::CardProduct.create
259
+ pattern: /^(6011|65|64[4-9]|622)/
260
+ companyName: "Discover"
261
+ companyShortname: "discover"
262
+ cardNumberGrouping: [4,4,4,4]
263
+ cardNumberLength: [16]
264
+ expirationFormat: "MM/YY"
265
+ validateLuhn: true
266
+ cvcLength: 3
267
+ layout:
268
+ number: 'front'
269
+ exp: 'front'
270
+ name: 'front'
271
+ cvc: 'back'
272
+
273
+ # Variation of Visa layout specific to Chase Sapphire Card.
274
+ visaProduct = Skeuocard::CardProduct.firstMatchingShortname 'visa'
275
+ visaProduct.createVariation
276
+ pattern: /^414720/
277
+ issuingAuthority: "Chase"
278
+ issuerName: "Chase Sapphire Card"
279
+ issuerShortname: "chase-sapphire"
280
+ layout:
281
+ name: 'front'
282
+ number: 'front'
283
+ exp: 'front'
284
+ cvc: 'front'
@@ -0,0 +1,206 @@
1
+ ###
2
+ Skeuocard::ExpirationInputView
3
+ ###
4
+ class Skeuocard::ExpirationInputView
5
+ constructor: (opts = {})->
6
+ # setup option defaults
7
+ opts.pattern ||= "MM/YY"
8
+
9
+ @options = opts
10
+ # setup default values
11
+ @date = null
12
+ # create dom container
13
+ @el = $("<fieldset>")
14
+ @el.addClass('cc-field')
15
+ @el.delegate "input", "keydown", (e)=> @_onKeyDown(e)
16
+ @el.delegate "input", "keyup", (e)=> @_onKeyUp(e)
17
+ @el.delegate "input", "focus", (e)=> @el.addClass('focus')
18
+ @el.delegate "input", "blur", (e)=> @el.removeClass('focus')
19
+
20
+ bind: (args...)->
21
+ @el.bind(args...)
22
+
23
+ trigger: (args...)->
24
+ @el.trigger(args...)
25
+
26
+ _getFieldCaretPosition: (el)->
27
+ input = el.get(0)
28
+ if input.selectionEnd?
29
+ return input.selectionEnd
30
+ else if document.selection
31
+ input.focus()
32
+ sel = document.selection.createRange()
33
+ selLength = document.selection.createRange().text.length
34
+ sel.moveStart('character', -input.value.length)
35
+ return selLength
36
+
37
+ _setFieldCaretPosition: (el, pos)->
38
+ input = el.get(0)
39
+ if input.createTextRange?
40
+ range = input.createTextRange()
41
+ range.move "character", pos
42
+ range.select()
43
+ else if input.selectionStart?
44
+ input.focus()
45
+ input.setSelectionRange(pos, pos)
46
+
47
+ setPattern: (pattern)->
48
+ groupings = []
49
+ patternParts = pattern.split('')
50
+ _currentLength = 0
51
+ for char, i in patternParts
52
+ _currentLength++
53
+ if patternParts[i+1] != char
54
+ groupings.push([_currentLength, char])
55
+ _currentLength = 0
56
+ @options.groupings = groupings
57
+ @_setGroupings(@options.groupings)
58
+
59
+ _setGroupings: (groupings)->
60
+ fieldChars = ['D', 'M', 'Y']
61
+ @el.empty()
62
+ _startLength = 0
63
+ for group in groupings
64
+ groupLength = group[0]
65
+ groupChar = group[1]
66
+ if groupChar in fieldChars # this group is a field
67
+ input = $('<input>').attr
68
+ type: 'text'
69
+ pattern: '[0-9]*'
70
+ placeholder: new Array(groupLength+1).join(groupChar)
71
+ maxlength: groupLength
72
+ class: 'cc-exp-field-' + groupChar.toLowerCase() +
73
+ ' group' + groupLength
74
+ input.data('fieldtype', groupChar)
75
+ @el.append(input)
76
+ else # this group is a separator
77
+ sep = $('<span>').attr
78
+ class: 'separator'
79
+ sep.html(new Array(groupLength + 1).join(groupChar))
80
+ @el.append(sep)
81
+ @groupEls = @el.find('input')
82
+ @_updateFieldValues() if @date?
83
+
84
+ _zeroPadNumber: (num, places)->
85
+ zero = places - num.toString().length + 1
86
+ return Array(zero).join("0") + num
87
+
88
+ _updateFieldValues: ->
89
+ currentDate = @date
90
+ unless @groupEls # they need to be created
91
+ return @setPattern(@options.pattern)
92
+ @groupEls.each (i,_el)=>
93
+ el = $(_el)
94
+ groupLength = parseInt(el.attr('maxlength'))
95
+ switch el.data('fieldtype')
96
+ when 'M'
97
+ el.val @_zeroPadNumber(currentDate.getMonth() + 1, groupLength)
98
+ when 'D'
99
+ el.val @_zeroPadNumber(currentDate.getDate(), groupLength)
100
+ when 'Y'
101
+ year = if groupLength >= 4 then currentDate.getFullYear() else
102
+ currentDate.getFullYear().toString().substr(2,4)
103
+ el.val(year)
104
+
105
+ clear: ->
106
+ @value = ""
107
+ @date = null
108
+ @groupEls.each ->
109
+ $(@).val('')
110
+
111
+ setValue: (newDate)->
112
+ @date = newDate
113
+ @_updateFieldValues()
114
+
115
+ getValue: ->
116
+ @date
117
+
118
+ reconfigure: (opts)->
119
+ if opts.pattern?
120
+ @setPattern(opts.pattern)
121
+ if opts.value?
122
+ @setValue(opts.value)
123
+
124
+ _onKeyDown: (e)->
125
+ e.stopPropagation()
126
+ groupEl = $(e.currentTarget)
127
+
128
+ groupEl = $(e.currentTarget)
129
+ groupMaxLength = parseInt(groupEl.attr('maxlength'))
130
+ groupCaretPos = @_getFieldCaretPosition(groupEl)
131
+
132
+ prevInputEl = groupEl.prevAll('input').first()
133
+ nextInputEl = groupEl.nextAll('input').first()
134
+
135
+ # Handle delete key
136
+ if e.which is 8 and groupCaretPos is 0 and
137
+ not $.isEmptyObject(prevInputEl)
138
+ prevInputEl.focus()
139
+
140
+ if e.which in [37, 38, 39, 40] # arrow keys
141
+ switch e.which
142
+ when 37 # left
143
+ if groupCaretPos is 0 and not $.isEmptyObject(prevInputEl)
144
+ prevInputEl.focus()
145
+ when 39 # right
146
+ if groupCaretPos is groupMaxLength and not $.isEmptyObject(nextInputEl)
147
+ nextInputEl.focus()
148
+ when 38 # up
149
+ if not $.isEmptyObject(groupEl.prev('input'))
150
+ prevInputEl.focus()
151
+ when 40 # down
152
+ if not $.isEmptyObject(groupEl.next('input'))
153
+ nextInputEl.focus()
154
+
155
+ getRawValue: (fieldType)->
156
+ parseInt(@el.find(".cc-exp-field-" + fieldType).val())
157
+
158
+ _onKeyUp: (e)->
159
+ e.stopPropagation()
160
+
161
+ specialKeys = [8, 9, 16, 17, 18, 19, 20, 27, 33, 34, 35, 36,
162
+ 37, 38, 39, 40, 45, 46, 91, 93, 144, 145, 224]
163
+ arrowKeys = [37, 38, 39, 40]
164
+ groupEl = $(e.currentTarget)
165
+ groupMaxLength = parseInt(groupEl.attr('maxlength'))
166
+ groupCaretPos = @_getFieldCaretPosition(groupEl)
167
+
168
+ if e.which not in specialKeys
169
+ # intercept bad chars, returning user to the right char pos if need be
170
+ groupValLength = groupEl.val().length
171
+ pattern = new RegExp('[^0-9]+', 'g')
172
+ groupEl.val(groupEl.val().replace(pattern, ''))
173
+ if groupEl.val().length < groupValLength # we caught bad char
174
+ @_setFieldCaretPosition(groupEl, groupCaretPos - 1)
175
+ else
176
+ @_setFieldCaretPosition(groupEl, groupCaretPos)
177
+
178
+ nextInputEl = groupEl.nextAll('input').first()
179
+
180
+ if e.which not in specialKeys and
181
+ groupEl.val().length is groupMaxLength and
182
+ not $.isEmptyObject(nextInputEl) and
183
+ @_getFieldCaretPosition(groupEl) is groupMaxLength
184
+ nextInputEl.focus()
185
+
186
+ # get a date object representing what's been entered
187
+ day = @getRawValue('d') || 1
188
+ month = @getRawValue('m')
189
+ year = @getRawValue('y')
190
+ if month is 0 or month > 12 or year is 0
191
+ @date = new Date(1900, 0, 1)
192
+ else
193
+ year += 2000 if year < 2000
194
+ dateObj = new Date(year, month-1, day)
195
+ @date = dateObj
196
+ @trigger("keyup", [@])
197
+ return false
198
+
199
+ _inputGroupEls: ->
200
+ @el.find("input")
201
+
202
+ show: ->
203
+ @el.show()
204
+
205
+ hide: ->
206
+ @el.hide()
@@ -0,0 +1,67 @@
1
+ ###
2
+ Skeuocard::FlipTabView
3
+ Handles rendering of the "flip button" control and its various warning and
4
+ prompt states.
5
+ ###
6
+
7
+ class Skeuocard::FlipTabView
8
+ constructor: (sc, face, opts = {})->
9
+ @card = sc
10
+ @face = face
11
+ @el = $("<div class=\"flip-tab #{face}\"><p></p></div>")
12
+ @options = opts
13
+ @_state = {}
14
+ @card.bind 'faceFillStateWillChange.skeuocard',
15
+ @_faceStateChanged.bind(@)
16
+ @card.bind 'faceValidationStateWillChange.skeuocard',
17
+ @_faceValidationChanged.bind(@)
18
+ @card.bind 'productWillChange.skeuocard', (e, card, prevProduct, newProduct)=>
19
+ if newProduct?
20
+ if newProduct.faces is @face
21
+ @hide()
22
+ else
23
+ @show()
24
+ else
25
+ @hide()
26
+
27
+ _faceStateChanged: (e, card, face, isFilled)->
28
+ oppositeFace = if face is 'front' then 'back' else 'front'
29
+ @show() if isFilled is true and @card._inputViewsByFace[oppositeFace].length > 0
30
+ @_state.opposingFaceFilled = isFilled if face isnt @face
31
+
32
+ unless @_state.opposingFaceFilled is true
33
+ @warn @options.strings.hiddenFaceFillPrompt, true
34
+
35
+ _faceValidationChanged: (e, card, face, isValid)->
36
+ @_state.opposingFaceValid = isValid if face isnt @face
37
+
38
+ if @_state.opposingFaceValid
39
+ @prompt @options.strings.hiddenFaceSwitchPrompt
40
+ else
41
+ if @_state.opposingFaceFilled
42
+ @warn @options.strings.hiddenFaceErrorWarning
43
+ else
44
+ @warn @options.strings.hiddenFaceFillPrompt
45
+
46
+ _setText: (text)->
47
+ @el.find('p').first().html(text)
48
+
49
+ warn: (message)->
50
+ @_resetClasses()
51
+ @_setText(message)
52
+ @el.addClass('warn')
53
+
54
+ prompt: (message)->
55
+ @_resetClasses()
56
+ @_setText(message)
57
+ @el.addClass('prompt')
58
+
59
+ _resetClasses: ->
60
+ @el.removeClass('warn')
61
+ @el.removeClass('prompt')
62
+
63
+ show: ->
64
+ @el.show()
65
+
66
+ hide: ->
67
+ @el.hide()