").attr("name",c.submitButton.name).val(a(c.submitButton).val()).appendTo(c.currentForm)),!c.settings.submitHandler||(e=c.settings.submitHandler.call(c,c.currentForm,b),d&&d.remove(),void 0!==e&&e)}return c.settings.debug&&b.preventDefault(),c.cancelSubmit?(c.cancelSubmit=!1,d()):c.form()?c.pendingRequest?(c.formSubmitted=!0,!1):d():(c.focusInvalid(),!1)})),c)},valid:function(){var b,c,d;return a(this[0]).is("form")?b=this.validate().form():(d=[],b=!0,c=a(this[0].form).validate(),this.each(function(){b=c.element(this)&&b,b||(d=d.concat(c.errorList))}),c.errorList=d),b},rules:function(b,c){var d,e,f,g,h,i,j=this[0];if(null!=j&&(!j.form&&j.hasAttribute("contenteditable")&&(j.form=this.closest("form")[0],j.name=this.attr("name")),null!=j.form)){if(b)switch(d=a.data(j.form,"validator").settings,e=d.rules,f=a.validator.staticRules(j),b){case"add":a.extend(f,a.validator.normalizeRule(c)),delete f.messages,e[j.name]=f,c.messages&&(d.messages[j.name]=a.extend(d.messages[j.name],c.messages));break;case"remove":return c?(i={},a.each(c.split(/\s/),function(a,b){i[b]=f[b],delete f[b]}),i):(delete e[j.name],f)}return g=a.validator.normalizeRules(a.extend({},a.validator.classRules(j),a.validator.attributeRules(j),a.validator.dataRules(j),a.validator.staticRules(j)),j),g.required&&(h=g.required,delete g.required,g=a.extend({required:h},g)),g.remote&&(h=g.remote,delete g.remote,g=a.extend(g,{remote:h})),g}}}),a.extend(a.expr.pseudos||a.expr[":"],{blank:function(b){return!a.trim(""+a(b).val())},filled:function(b){var c=a(b).val();return null!==c&&!!a.trim(""+c)},unchecked:function(b){return!a(b).prop("checked")}}),a.validator=function(b,c){this.settings=a.extend(!0,{},a.validator.defaults,b),this.currentForm=c,this.init()},a.validator.format=function(b,c){return 1===arguments.length?function(){var c=a.makeArray(arguments);return c.unshift(b),a.validator.format.apply(this,c)}:void 0===c?b:(arguments.length>2&&c.constructor!==Array&&(c=a.makeArray(arguments).slice(1)),c.constructor!==Array&&(c=[c]),a.each(c,function(a,c){b=b.replace(new RegExp("\\{"+a+"\\}","g"),function(){return c})}),b)},a.extend(a.validator,{defaults:{messages:{},groups:{},rules:{},errorClass:"error",pendingClass:"pending",validClass:"valid",errorElement:"label",focusCleanup:!1,focusInvalid:!0,errorContainer:a([]),errorLabelContainer:a([]),onsubmit:!0,ignore:":hidden",ignoreTitle:!1,onfocusin:function(a){this.lastActive=a,this.settings.focusCleanup&&(this.settings.unhighlight&&this.settings.unhighlight.call(this,a,this.settings.errorClass,this.settings.validClass),this.hideThese(this.errorsFor(a)))},onfocusout:function(a){this.checkable(a)||!(a.name in this.submitted)&&this.optional(a)||this.element(a)},onkeyup:function(b,c){var d=[16,17,18,20,35,36,37,38,39,40,45,144,225];9===c.which&&""===this.elementValue(b)||a.inArray(c.keyCode,d)!==-1||(b.name in this.submitted||b.name in this.invalid)&&this.element(b)},onclick:function(a){a.name in this.submitted?this.element(a):a.parentNode.name in this.submitted&&this.element(a.parentNode)},highlight:function(b,c,d){"radio"===b.type?this.findByName(b.name).addClass(c).removeClass(d):a(b).addClass(c).removeClass(d)},unhighlight:function(b,c,d){"radio"===b.type?this.findByName(b.name).removeClass(c).addClass(d):a(b).removeClass(c).addClass(d)}},setDefaults:function(b){a.extend(a.validator.defaults,b)},messages:{required:"This field is required.",remote:"Please fix this field.",email:"Please enter a valid email address.",url:"Please enter a valid URL.",date:"Please enter a valid date.",dateISO:"Please enter a valid date (ISO).",number:"Please enter a valid number.",digits:"Please enter only digits.",equalTo:"Please enter the same value again.",maxlength:a.validator.format("Please enter no more than {0} characters."),minlength:a.validator.format("Please enter at least {0} characters."),rangelength:a.validator.format("Please enter a value between {0} and {1} characters long."),range:a.validator.format("Please enter a value between {0} and {1}."),max:a.validator.format("Please enter a value less than or equal to {0}."),min:a.validator.format("Please enter a value greater than or equal to {0}."),step:a.validator.format("Please enter a multiple of {0}.")},autoCreateRanges:!1,prototype:{init:function(){function b(b){!this.form&&this.hasAttribute("contenteditable")&&(this.form=a(this).closest("form")[0],this.name=a(this).attr("name"));var c=a.data(this.form,"validator"),d="on"+b.type.replace(/^validate/,""),e=c.settings;e[d]&&!a(this).is(e.ignore)&&e[d].call(c,this,b)}this.labelContainer=a(this.settings.errorLabelContainer),this.errorContext=this.labelContainer.length&&this.labelContainer||a(this.currentForm),this.containers=a(this.settings.errorContainer).add(this.settings.errorLabelContainer),this.submitted={},this.valueCache={},this.pendingRequest=0,this.pending={},this.invalid={},this.reset();var c,d=this.groups={};a.each(this.settings.groups,function(b,c){"string"==typeof c&&(c=c.split(/\s/)),a.each(c,function(a,c){d[c]=b})}),c=this.settings.rules,a.each(c,function(b,d){c[b]=a.validator.normalizeRule(d)}),a(this.currentForm).on("focusin.validate focusout.validate keyup.validate",":text, [type='password'], [type='file'], select, textarea, [type='number'], [type='search'], [type='tel'], [type='url'], [type='email'], [type='datetime'], [type='date'], [type='month'], [type='week'], [type='time'], [type='datetime-local'], [type='range'], [type='color'], [type='radio'], [type='checkbox'], [contenteditable], [type='button']",b).on("click.validate","select, option, [type='radio'], [type='checkbox']",b),this.settings.invalidHandler&&a(this.currentForm).on("invalid-form.validate",this.settings.invalidHandler)},form:function(){return this.checkForm(),a.extend(this.submitted,this.errorMap),this.invalid=a.extend({},this.errorMap),this.valid()||a(this.currentForm).triggerHandler("invalid-form",[this]),this.showErrors(),this.valid()},checkForm:function(){this.prepareForm();for(var a=0,b=this.currentElements=this.elements();b[a];a++)this.check(b[a]);return this.valid()},element:function(b){var c,d,e=this.clean(b),f=this.validationTargetFor(e),g=this,h=!0;return void 0===f?delete this.invalid[e.name]:(this.prepareElement(f),this.currentElements=a(f),d=this.groups[f.name],d&&a.each(this.groups,function(a,b){b===d&&a!==f.name&&(e=g.validationTargetFor(g.clean(g.findByName(a))),e&&e.name in g.invalid&&(g.currentElements.push(e),h=g.check(e)&&h))}),c=this.check(f)!==!1,h=h&&c,c?this.invalid[f.name]=!1:this.invalid[f.name]=!0,this.numberOfInvalids()||(this.toHide=this.toHide.add(this.containers)),this.showErrors(),a(b).attr("aria-invalid",!c)),h},showErrors:function(b){if(b){var c=this;a.extend(this.errorMap,b),this.errorList=a.map(this.errorMap,function(a,b){return{message:a,element:c.findByName(b)[0]}}),this.successList=a.grep(this.successList,function(a){return!(a.name in b)})}this.settings.showErrors?this.settings.showErrors.call(this,this.errorMap,this.errorList):this.defaultShowErrors()},resetForm:function(){a.fn.resetForm&&a(this.currentForm).resetForm(),this.invalid={},this.submitted={},this.prepareForm(),this.hideErrors();var b=this.elements().removeData("previousValue").removeAttr("aria-invalid");this.resetElements(b)},resetElements:function(a){var b;if(this.settings.unhighlight)for(b=0;a[b];b++)this.settings.unhighlight.call(this,a[b],this.settings.errorClass,""),this.findByName(a[b].name).removeClass(this.settings.validClass);else a.removeClass(this.settings.errorClass).removeClass(this.settings.validClass)},numberOfInvalids:function(){return this.objectLength(this.invalid)},objectLength:function(a){var b,c=0;for(b in a)void 0!==a[b]&&null!==a[b]&&a[b]!==!1&&c++;return c},hideErrors:function(){this.hideThese(this.toHide)},hideThese:function(a){a.not(this.containers).text(""),this.addWrapper(a).hide()},valid:function(){return 0===this.size()},size:function(){return this.errorList.length},focusInvalid:function(){if(this.settings.focusInvalid)try{a(this.findLastActive()||this.errorList.length&&this.errorList[0].element||[]).filter(":visible").focus().trigger("focusin")}catch(b){}},findLastActive:function(){var b=this.lastActive;return b&&1===a.grep(this.errorList,function(a){return a.element.name===b.name}).length&&b},elements:function(){var b=this,c={};return a(this.currentForm).find("input, select, textarea, [contenteditable]").not(":submit, :reset, :image, :disabled").not(this.settings.ignore).filter(function(){var d=this.name||a(this).attr("name");return!d&&b.settings.debug&&window.console&&console.error("%o has no name assigned",this),this.hasAttribute("contenteditable")&&(this.form=a(this).closest("form")[0],this.name=d),!(d in c||!b.objectLength(a(this).rules()))&&(c[d]=!0,!0)})},clean:function(b){return a(b)[0]},errors:function(){var b=this.settings.errorClass.split(" ").join(".");return a(this.settings.errorElement+"."+b,this.errorContext)},resetInternals:function(){this.successList=[],this.errorList=[],this.errorMap={},this.toShow=a([]),this.toHide=a([])},reset:function(){this.resetInternals(),this.currentElements=a([])},prepareForm:function(){this.reset(),this.toHide=this.errors().add(this.containers)},prepareElement:function(a){this.reset(),this.toHide=this.errorsFor(a)},elementValue:function(b){var c,d,e=a(b),f=b.type;return"radio"===f||"checkbox"===f?this.findByName(b.name).filter(":checked").val():"number"===f&&"undefined"!=typeof b.validity?b.validity.badInput?"NaN":e.val():(c=b.hasAttribute("contenteditable")?e.text():e.val(),"file"===f?"C:\\fakepath\\"===c.substr(0,12)?c.substr(12):(d=c.lastIndexOf("/"),d>=0?c.substr(d+1):(d=c.lastIndexOf("\\"),d>=0?c.substr(d+1):c)):"string"==typeof c?c.replace(/\r/g,""):c)},check:function(b){b=this.validationTargetFor(this.clean(b));var c,d,e,f,g=a(b).rules(),h=a.map(g,function(a,b){return b}).length,i=!1,j=this.elementValue(b);if("function"==typeof g.normalizer?f=g.normalizer:"function"==typeof this.settings.normalizer&&(f=this.settings.normalizer),f){if(j=f.call(b,j),"string"!=typeof j)throw new TypeError("The normalizer should return a string value.");delete g.normalizer}for(d in g){e={method:d,parameters:g[d]};try{if(c=a.validator.methods[d].call(this,j,b,e.parameters),"dependency-mismatch"===c&&1===h){i=!0;continue}if(i=!1,"pending"===c)return void(this.toHide=this.toHide.not(this.errorsFor(b)));if(!c)return this.formatAndAdd(b,e),!1}catch(k){throw this.settings.debug&&window.console&&console.log("Exception occurred when checking element "+b.id+", check the '"+e.method+"' method.",k),k instanceof TypeError&&(k.message+=". Exception occurred when checking element "+b.id+", check the '"+e.method+"' method."),k}}if(!i)return this.objectLength(g)&&this.successList.push(b),!0},customDataMessage:function(b,c){return a(b).data("msg"+c.charAt(0).toUpperCase()+c.substring(1).toLowerCase())||a(b).data("msg")},customMessage:function(a,b){var c=this.settings.messages[a];return c&&(c.constructor===String?c:c[b])},findDefined:function(){for(var a=0;aWarning: No message defined for "+b.name+""),e=/\$?\{(\d+)\}/g;return"function"==typeof d?d=d.call(this,c.parameters,b):e.test(d)&&(d=a.validator.format(d.replace(e,"{$1}"),c.parameters)),d},formatAndAdd:function(a,b){var c=this.defaultMessage(a,b);this.errorList.push({message:c,element:a,method:b.method}),this.errorMap[a.name]=c,this.submitted[a.name]=c},addWrapper:function(a){return this.settings.wrapper&&(a=a.add(a.parent(this.settings.wrapper))),a},defaultShowErrors:function(){var a,b,c;for(a=0;this.errorList[a];a++)c=this.errorList[a],this.settings.highlight&&this.settings.highlight.call(this,c.element,this.settings.errorClass,this.settings.validClass),this.showLabel(c.element,c.message);if(this.errorList.length&&(this.toShow=this.toShow.add(this.containers)),this.settings.success)for(a=0;this.successList[a];a++)this.showLabel(this.successList[a]);if(this.settings.unhighlight)for(a=0,b=this.validElements();b[a];a++)this.settings.unhighlight.call(this,b[a],this.settings.errorClass,this.settings.validClass);this.toHide=this.toHide.not(this.toShow),this.hideErrors(),this.addWrapper(this.toShow).show()},validElements:function(){return this.currentElements.not(this.invalidElements())},invalidElements:function(){return a(this.errorList).map(function(){return this.element})},showLabel:function(b,c){var d,e,f,g,h=this.errorsFor(b),i=this.idOrName(b),j=a(b).attr("aria-describedby");h.length?(h.removeClass(this.settings.validClass).addClass(this.settings.errorClass),h.html(c)):(h=a("<"+this.settings.errorElement+">").attr("id",i+"-error").addClass(this.settings.errorClass).html(c||""),d=h,this.settings.wrapper&&(d=h.hide().show().wrap("<"+this.settings.wrapper+"/>").parent()),this.labelContainer.length?this.labelContainer.append(d):this.settings.errorPlacement?this.settings.errorPlacement.call(this,d,a(b)):d.insertAfter(b),h.is("label")?h.attr("for",i):0===h.parents("label[for='"+this.escapeCssMeta(i)+"']").length&&(f=h.attr("id"),j?j.match(new RegExp("\\b"+this.escapeCssMeta(f)+"\\b"))||(j+=" "+f):j=f,a(b).attr("aria-describedby",j),e=this.groups[b.name],e&&(g=this,a.each(g.groups,function(b,c){c===e&&a("[name='"+g.escapeCssMeta(b)+"']",g.currentForm).attr("aria-describedby",h.attr("id"))})))),!c&&this.settings.success&&(h.text(""),"string"==typeof this.settings.success?h.addClass(this.settings.success):this.settings.success(h,b)),this.toShow=this.toShow.add(h)},errorsFor:function(b){var c=this.escapeCssMeta(this.idOrName(b)),d=a(b).attr("aria-describedby"),e="label[for='"+c+"'], label[for='"+c+"'] *";return d&&(e=e+", #"+this.escapeCssMeta(d).replace(/\s+/g,", #")),this.errors().filter(e)},escapeCssMeta:function(a){return a.replace(/([\\!"#$%&'()*+,.\/:;<=>?@\[\]^`{|}~])/g,"\\$1")},idOrName:function(a){return this.groups[a.name]||(this.checkable(a)?a.name:a.id||a.name)},validationTargetFor:function(b){return this.checkable(b)&&(b=this.findByName(b.name)),a(b).not(this.settings.ignore)[0]},checkable:function(a){return/radio|checkbox/i.test(a.type)},findByName:function(b){return a(this.currentForm).find("[name='"+this.escapeCssMeta(b)+"']")},getLength:function(b,c){switch(c.nodeName.toLowerCase()){case"select":return a("option:selected",c).length;case"input":if(this.checkable(c))return this.findByName(c.name).filter(":checked").length}return b.length},depend:function(a,b){return!this.dependTypes[typeof a]||this.dependTypes[typeof a](a,b)},dependTypes:{"boolean":function(a){return a},string:function(b,c){return!!a(b,c.form).length},"function":function(a,b){return a(b)}},optional:function(b){var c=this.elementValue(b);return!a.validator.methods.required.call(this,c,b)&&"dependency-mismatch"},startRequest:function(b){this.pending[b.name]||(this.pendingRequest++,a(b).addClass(this.settings.pendingClass),this.pending[b.name]=!0)},stopRequest:function(b,c){this.pendingRequest--,this.pendingRequest<0&&(this.pendingRequest=0),delete this.pending[b.name],a(b).removeClass(this.settings.pendingClass),c&&0===this.pendingRequest&&this.formSubmitted&&this.form()?(a(this.currentForm).submit(),this.submitButton&&a("input:hidden[name='"+this.submitButton.name+"']",this.currentForm).remove(),this.formSubmitted=!1):!c&&0===this.pendingRequest&&this.formSubmitted&&(a(this.currentForm).triggerHandler("invalid-form",[this]),this.formSubmitted=!1)},previousValue:function(b,c){return c="string"==typeof c&&c||"remote",a.data(b,"previousValue")||a.data(b,"previousValue",{old:null,valid:!0,message:this.defaultMessage(b,{method:c})})},destroy:function(){this.resetForm(),a(this.currentForm).off(".validate").removeData("validator").find(".validate-equalTo-blur").off(".validate-equalTo").removeClass("validate-equalTo-blur")}},classRuleSettings:{required:{required:!0},email:{email:!0},url:{url:!0},date:{date:!0},dateISO:{dateISO:!0},number:{number:!0},digits:{digits:!0},creditcard:{creditcard:!0}},addClassRules:function(b,c){b.constructor===String?this.classRuleSettings[b]=c:a.extend(this.classRuleSettings,b)},classRules:function(b){var c={},d=a(b).attr("class");return d&&a.each(d.split(" "),function(){this in a.validator.classRuleSettings&&a.extend(c,a.validator.classRuleSettings[this])}),c},normalizeAttributeRule:function(a,b,c,d){/min|max|step/.test(c)&&(null===b||/number|range|text/.test(b))&&(d=Number(d),isNaN(d)&&(d=void 0)),d||0===d?a[c]=d:b===c&&"range"!==b&&(a[c]=!0)},attributeRules:function(b){var c,d,e={},f=a(b),g=b.getAttribute("type");for(c in a.validator.methods)"required"===c?(d=b.getAttribute(c),""===d&&(d=!0),d=!!d):d=f.attr(c),this.normalizeAttributeRule(e,g,c,d);return e.maxlength&&/-1|2147483647|524288/.test(e.maxlength)&&delete e.maxlength,e},dataRules:function(b){var c,d,e={},f=a(b),g=b.getAttribute("type");for(c in a.validator.methods)d=f.data("rule"+c.charAt(0).toUpperCase()+c.substring(1).toLowerCase()),this.normalizeAttributeRule(e,g,c,d);return e},staticRules:function(b){var c={},d=a.data(b.form,"validator");return d.settings.rules&&(c=a.validator.normalizeRule(d.settings.rules[b.name])||{}),c},normalizeRules:function(b,c){return a.each(b,function(d,e){if(e===!1)return void delete b[d];if(e.param||e.depends){var f=!0;switch(typeof e.depends){case"string":f=!!a(e.depends,c.form).length;break;case"function":f=e.depends.call(c,c)}f?b[d]=void 0===e.param||e.param:(a.data(c.form,"validator").resetElements(a(c)),delete b[d])}}),a.each(b,function(d,e){b[d]=a.isFunction(e)&&"normalizer"!==d?e(c):e}),a.each(["minlength","maxlength"],function(){b[this]&&(b[this]=Number(b[this]))}),a.each(["rangelength","range"],function(){var c;b[this]&&(a.isArray(b[this])?b[this]=[Number(b[this][0]),Number(b[this][1])]:"string"==typeof b[this]&&(c=b[this].replace(/[\[\]]/g,"").split(/[\s,]+/),b[this]=[Number(c[0]),Number(c[1])]))}),a.validator.autoCreateRanges&&(null!=b.min&&null!=b.max&&(b.range=[b.min,b.max],delete b.min,delete b.max),null!=b.minlength&&null!=b.maxlength&&(b.rangelength=[b.minlength,b.maxlength],delete b.minlength,delete b.maxlength)),b},normalizeRule:function(b){if("string"==typeof b){var c={};a.each(b.split(/\s/),function(){c[this]=!0}),b=c}return b},addMethod:function(b,c,d){a.validator.methods[b]=c,a.validator.messages[b]=void 0!==d?d:a.validator.messages[b],c.length<3&&a.validator.addClassRules(b,a.validator.normalizeRule(b))},methods:{required:function(b,c,d){if(!this.depend(d,c))return"dependency-mismatch";if("select"===c.nodeName.toLowerCase()){var e=a(c).val();return e&&e.length>0}return this.checkable(c)?this.getLength(b,c)>0:b.length>0},email:function(a,b){return this.optional(b)||/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test(a)},url:function(a,b){return this.optional(b)||/^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[\/?#]\S*)?$/i.test(a)},date:function(a,b){return this.optional(b)||!/Invalid|NaN/.test(new Date(a).toString())},dateISO:function(a,b){return this.optional(b)||/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test(a)},number:function(a,b){return this.optional(b)||/^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(a)},digits:function(a,b){return this.optional(b)||/^\d+$/.test(a)},minlength:function(b,c,d){var e=a.isArray(b)?b.length:this.getLength(b,c);return this.optional(c)||e>=d},maxlength:function(b,c,d){var e=a.isArray(b)?b.length:this.getLength(b,c);return this.optional(c)||e<=d},rangelength:function(b,c,d){var e=a.isArray(b)?b.length:this.getLength(b,c);return this.optional(c)||e>=d[0]&&e<=d[1]},min:function(a,b,c){return this.optional(b)||a>=c},max:function(a,b,c){return this.optional(b)||a<=c},range:function(a,b,c){return this.optional(b)||a>=c[0]&&a<=c[1]},step:function(b,c,d){var e,f=a(c).attr("type"),g="Step attribute on input type "+f+" is not supported.",h=["text","number","range"],i=new RegExp("\\b"+f+"\\b"),j=f&&!i.test(h.join()),k=function(a){var b=(""+a).match(/(?:\.(\d+))?$/);return b&&b[1]?b[1].length:0},l=function(a){return Math.round(a*Math.pow(10,e))},m=!0;if(j)throw new Error(g);return e=k(d),(k(b)>e||l(b)%l(d)!==0)&&(m=!1),this.optional(c)||m},equalTo:function(b,c,d){var e=a(d);return this.settings.onfocusout&&e.not(".validate-equalTo-blur").length&&e.addClass("validate-equalTo-blur").on("blur.validate-equalTo",function(){a(c).valid()}),b===e.val()},remote:function(b,c,d,e){if(this.optional(c))return"dependency-mismatch";e="string"==typeof e&&e||"remote";var f,g,h,i=this.previousValue(c,e);return this.settings.messages[c.name]||(this.settings.messages[c.name]={}),i.originalMessage=i.originalMessage||this.settings.messages[c.name][e],this.settings.messages[c.name][e]=i.message,d="string"==typeof d&&{url:d}||d,h=a.param(a.extend({data:b},d.data)),i.old===h?i.valid:(i.old=h,f=this,this.startRequest(c),g={},g[c.name]=b,a.ajax(a.extend(!0,{mode:"abort",port:"validate"+c.name,dataType:"json",data:g,context:f.currentForm,success:function(a){var d,g,h,j=a===!0||"true"===a;f.settings.messages[c.name][e]=i.originalMessage,j?(h=f.formSubmitted,f.resetInternals(),f.toHide=f.errorsFor(c),f.formSubmitted=h,f.successList.push(c),f.invalid[c.name]=!1,f.showErrors()):(d={},g=a||f.defaultMessage(c,{method:e,parameters:b}),d[c.name]=i.message=g,f.invalid[c.name]=!0,f.showErrors(d)),i.valid=j,f.stopRequest(c,j)}},d)),"pending")}}});var b,c={};return a.ajaxPrefilter?a.ajaxPrefilter(function(a,b,d){var e=a.port;"abort"===a.mode&&(c[e]&&c[e].abort(),c[e]=d)}):(b=a.ajax,a.ajax=function(d){var e=("mode"in d?d:a.ajaxSettings).mode,f=("port"in d?d:a.ajaxSettings).port;return"abort"===e?(c[f]&&c[f].abort(),c[f]=b.apply(this,arguments),c[f]):b.apply(this,arguments)}),a});!function(e){var n;if("function"==typeof define&&define.amd&&(define(e),n=!0),"object"==typeof exports&&(module.exports=e(),n=!0),!n){var t=window.Cookies,o=window.Cookies=e();o.noConflict=function(){return window.Cookies=t,o}}}(function(){function e(){for(var e=0,n={};e
Hướng dẫn xây dựng workflow tự động đăng bình luận Facebook với n8n
09/07/2025
Facebook chỉ hỗ trợ lên lịch đăng bài trên fanpage nhưng đăng bình luận thì không. Vì vậy, muốn comment vào những post đã lên lịch sẵn, chúng ta chỉ có cách canh giờ bài được đăng và vào bình luận thủ công. Làm thế nào tự động hóa toàn bộ? Trong bài viết này, TotHost sẽ hướng dẫn bạn cách xây dựng workflow trên n8n để có thể tự động comment cho các bài viết trên fanpage, kể các những bài mới chỉ có lịch đăng, chưa thực sự “lên sóng”.
Mục lục
Mục lục
1. Workflow tổng thể cho tác vụ đăng bình luận tự động trên Facebook:
Đầu tiên để thực hiện được workflow thì bạn cần có công cụ n8n đã được cài đặt thành công, nếu như bạn chưa biết cách cài đặt như thế nào thì có thể tham khảo bài viết:
● Get reels & Get post: Lấy danh sách bài đăng và reels trên kênh
● Merge: Gộp danh sách dó lại
● Filter: Lọc ra bài mới đăng
● Comment: Đăng comment vào bài đăng đó
● Result: Ghi lại kết quả vào Google Sheet
Bạn có thể hiểu đơn giản chuỗi workflow này sẽ hoạt động như sau: sau mỗi khoảng thời gian được cài đặt sẵn (ví dụ: 5 phút) thì workflow trong n8n sẽ thực hiện quét lấy danh sách bài viết trên kênh Facebook của bạn, sau đó tổng hợp lại và lọc ra bài viết mới nhất, rồi thực hiện bình luận vào bài viết đó và cuối cùng là ghi lại kết quả vào Google Sheet để bạn có thể tiện theo dõi. Như vậy, mỗi bài đăng mới của bạn đều được bình luận tự động từ n8n, bạn không cần mất công thực hiện tác vụ đó.
Workflow này được thiết kế hết sức đơn giản, ngay cả một người mới bắt đầu làm quen với n8n cũng có thể thực hiện được. Ngoài ra, sau này bạn cũng có thể dễ dàng tùy chỉnh cho phù hợp với mục đích sử dụng cá nhân.
2. Hướng dẫn chi tiết:
2.1. Schedule Trigger:
Cài đặt node Schedule Trigger
Đầu tiên là node Schedule Trigger, đây là node để bạn cài đặt lịch hoạt động của workflow này, bạn có thể tùy chỉnh thời gian tùy theo nhu cầu. Ở đây, mình sẽ cài đặt là trigger mỗi 5 phút, có nghĩa là mỗi 5 phút hệ thống sẽ tự động quét kênh của mình xem có bài viết nào mới không để tự động comment. Trong quá trình làm workflow thì bạn cũng có thể nhấn “execute workflow” hoặc “execute step” để cho workflow chạy thử luôn chứ không cần chờ theo thời gian cài đặt.
2.2. Get Post & Get Reels (Facebook Graph API):
Get reels & Get post
Đây là 2 node có vai trò thực hiện quét và lấy danh sách bài đăng trên kênh Facebook của bạn, do Facebook đã chia ra Reels là một loại nội dung riêng nên để lấy được đầy đủ danh sách bài đăng trên page bao gồm ảnh, video, reels, text,... thì bạn cần tạo 2 node này, đối với trang mạng xã hội khác thì có thể chỉ cần 1 node là đủ để lấy hết danh sách bài đăng rồi. Các bạn cần cài đặt cho bao nhiêu trang Facebook thì tạo bấy nhiêu cặp node này là được.
Để tạo node Get Reels các bạn nhấp vào dấu “+” sau node Schedule Trigger hoặc chuột phải vào màn hình -> add node -> Facebook Graph API
Trong Facebook Graph API các bạn cài đặt như sau:
Ở phần “Credential to connect with” các bạn lựa chọn trang Fanpage đã được kết nối thành công với n8n bằng access token, nếu các bạn chưa biết cách kết nối trang Facebook của mình với n8n thì có thể tham khảo bài viết sau:
Hướng dẫn kết nối API Facebook, Instagram, Twitter (X), Linkedin, Weibo, Wechat với n8n
Các cài đặt trong node Get Reels:
● Host URL: Default
● HTTP Request Method: GET
● Graph API Version: cứ bản mới nhất mà chọn thôi (ở đây mình để là v22.0)
● Node: me
● Edge: videos
● Add option -> Fields -> điền các trường id, created_time, permalink_url, description để lấy thông tin phục vụ cho workflow
Tương tự với node Get Post, bạn cũng thực hiện và cài đặt như sau:
● Host URL: Default
● HTTP Request Method: GET
● Graph API Version: v22.0
● Node: me
● Edge: published_posts
Cả 2 node khi bấm Execute step ra được thông tin bài đăng như thế này là đã thành công.
2.3. Merge (append):
node Merge
Sau khi đã có danh sách bài đăng từ 2 node Get Reels và Get Posts, ta tiến hành tạo 1 node Merge để tổng hợp 2 danh sách đó lại (nếu bạn chỉ có 1 node lấy bài đăng thì có thể bỏ qua node này) để bước tiếp theo có thể dễ dàng xử lý dữ liệu. Ở node này chúng ta chỉ cần cài đặt đơn giản như sau là đủ:
● Mode: Append
● Number of Inputs: 2 (tương ứng với số node ở bước trước bạn muốn tổng hợp)
Cài đặt xong bạn nhấn “Execute step” để chạy thử thấy kết quả đã có dữ liệu tổng hợp đầy đủ từ 2 node trước là đã thành công.
2.4. Filter (Code):
node Filter (Code)
Đây là node quan trọng dùng để lọc ra loại bài viết ta cần đăng bình luận. Đây thực chất là 1 node Code và có thể tùy biến được điều kiện lọc bằng ngôn ngữ JavaScript hoặc Python, với bạn nào biết code thì việc này quá dễ rồi, còn với bạn nào không biết thì có thể hỏi AI như ChatGPT để nó tạo cho dòng code lọc ra bài viết phù hợp với nhu cầu sử dụng của bản thân cũng được.
Ở đây, mình sử dụng JavaScript để lọc ra những bài đăng trong vòng 5 phút tính từ lúc workflow bắt đầu quét, tương ứng với tần suất quét bài 5 phút một lần mình đã cài ở nút Trigger ban đầu. Mình dùng câu lệnh như sau, các bạn có thể tham khảo:
const recentPosts = allPosts.filter(post => { const created = new Date(post.created_time); return created > fiveMinutesAgo; });
// Trả ra từng bài là 1 item riêng (tránh lồng vào 1 data[])
return recentPosts.map(post => ({ json: post }));
Khi Execute Step kiểm tra thấy kết quả trả về bài viết đã đáp ứng đúng điều kiện lọc là thành công.
2.5. Comment (Facebook Graph API):
node Comment
Tiếp theo ta đến với node Comment, để tạo node này thì ta tiếp tục tạo node Facebook Graph API và cài đặt như sau:
● Host URL: Default
● HTTP Request Method: POST
● Graph API Version: v22.0
● Node: chuyển sang tab expression rồi nhập {{ $json.id }} hoặc bạn có thể kéo thanh id ở mục Input bên tay trái rồi kéo thả vào cũng được
● Edge: comments
Ở mục Options các bạn lựa chọn Add option -> Query Parameters -> Add Parameter và điền như sau:
● Name: message
● Value: Nội dung bạn muốn bình luận
● Name: attachment_url
● Value: đường dẫn hình ảnh của bạn
* Lưu ý: để có đường dẫn hình ảnh có thể sử dụng được thì bạn cần phải thực hiện như sau:
Cách 1: upload ảnh đó lên website của bạn (nếu bạn có website), sau đó lấy đường dẫn ảnh từ web, ví dụ: https://tothost.vn/uploads/banner.png
Cách 2: sử dụng một số dịch vụ CDN hoặc lưu trữ công khai ví dụ như Imgur, Cloudinary để upload hình ảnh, sau đó lấy đường dẫn ảnh ví dụ như: https://i.imgur.com/abc123.jpg
Cách 3: sử dụng Google Drive để upload ảnh, nhớ để chế độ công khai sau đó lấy đường dẫn ảnh có dạng https://drive.google.com/uc?export=view&id=..., id ở đây là dãy ký tự nằm giữa /d/ và /view… trên link chia sẻ hình ảnh của bạn.
Ta có đường link hoàn chỉnh có thể sử dụng được có cấu trúc ví dụ như sau: https://drive.google.com/uc?export=view&id=1RX95eAuHl_54ZkJkXEMT8zATNy5kp7wO
Ở bước này,ấn Execute Step nếu thấy Output đã trả ra kết quả, đồng thời thấy bình luận đã được đăng tải trên Fanpage thì có nghĩa là đã thành công.
kiểm tra thấy bình luận đã được đăng là thành công
2.6. Result (Google Sheet):
node Google Sheet
Bước cuối cùng, tạo một node Google Sheet để hệ thống báo lại kết quả của chuỗi workflow xem công việc đã được hoàn thành hay chưa. Để có thể thực hiện bước này thì trước tiên bạn cần tạo một file Google Sheet để sử dụng. Cấu trúc file sheet thì sẽ tùy vào trường dữ liệu mà bạn muốn nhận kết quả về, như ở đây mình muốn nhận về những kết quả như URL, Status, Description nên tạo bảng gồm các cột như sau:
Sau khi đã tạo file Sheet thành công thì bạn có thể tạo node Google Sheet trong n8n như sau: add note -> Google Sheet -> Append row in sheet rồi tiến hành cài đặt như sau:
Ở mục Credential to connect with, bạn chọn tài khoản Google mà mình đã kết nối, nếu chưa có thì chọn vào Create new credential để tạo kết nối giữa Google Drive với n8n. Nếu bạn chưa biết cách kết nối API các app của Google với n8n thì có thể tham khảo bài viết sau:
Hướng dẫn kết nối API Google với n8n
● Resource: Sheet Within Document
● Operation: Append Row
● Document: From list -> chọn file sheet của bạn hoặc chọn By URL rồi dán đường dẫn đến file của bạn
● Sheet: From list -> chọn trang tính trong file sheet của bạn
Ở phần Mapping Column Mode: chọn Map Each Column Manually, tại đây ta đã thấy sẵn các cột URL, Status, Description hiện trong phần Value to Send, sau đó mình đặt các giá trị như sau:
● URL: nhập fb.com/ rồi kéo mục id từ node Code bên tay trái để thả vào
● Status: Đã đăng (khi đăng thành công hệ thống sẽ báo “Đã đăng” vào file Sheet)
● Description: kéo thanh description từ node Code bên tay trái để thả vào
Cuối cùng thì Execute Step để kiểm tra kết quả, nếu thấy Output trả ra kết quả và kiểm tra file Sheet thực tế thấy thông tin mình cần đã được ghi lại thì đã thành công.
Trên đây là hướng dẫn rất chi tiết về một workflow đơn giản bạn có thể sử dụng để tự động đăng bình luận cho bài viết lên lịch sẵn trên Facebook với n8n. Chúc bạn thành công!
Sử dụng VPS Forex để chạy nhiều tài khoản MT4/MT5 giao dịch hoặc copy trade là một lựa chọn thông minh và phổ biến. Vậy một VPS có thể chạy được bao nhiêu tài khoản cùng lúc? Cùng TotHost tìm câu trả lời trong bài viết này nhé.
Bạn có biết rằng một VPS được tối ưu đúng cách không chỉ giúp bạn khai thác tiền điện tử hiệu quả hơn mà còn tiết kiệm chi phí duy trì vận hành đáng kể? Trong bài viết này, chúng tôi sẽ hướng dẫn bạn những bí quyết thực tiễn nhất để khai thác tối đa hiệu suất của VPS – từ cách chọn cấu hình, nhà cung cấp cho đến triển khai công cụ phù hợp.
Tìm hiểu n8n - nền tảng workflow automation mã nguồn mở, linh hoạt, dễ dàng tùy biến, giúp tự động hóa quy trình marketing, CRM, IT chỉ trong vài bước đơn giản.
Khi lựa chọn dịch vụ VPS cho website, game server hay livestream, một trong những yếu tố quan trọng nhất cần quan tâm chính là băng thông mạng. Hiện nay, TotHost vừa ra mắt dòng VPS TOT GT (Global Traffic) với băng thông riêng lên đến 250Mbps, mang đến hiệu năng vượt trội so với các dòng VPS thông thường. Vậy băng thông riêng có thực sự quan trọng không? Và VPS TOT GT khác gì so với VPS thường? Hãy cùng tìm hiểu trong bài viết dưới đây.
Trong lĩnh vực dịch vụ internet, bandwidth (băng thông) là yếu tố chính ảnh hưởng trực tiếp tới tốc độ và chất lượng kết nối. Trong đó có hai thuật ngữ phổ biến liên quan là shared bandwidth và dedicated bandwidth, hãy cùng tìm hiểu về sự khác biệt giữa chúng qua bài viết của TotHost nhé!
Năm 2024 chứng kiến không ít sự cố của các ông lớn công nghệ khiến hàng trăm triệu người dùng bao gồm cả cá nhân và doanh nghiệp bị ảnh hưởng. Cùng TotHost điểm danh những sự cố nổi cộm ngành công nghệ 2024 trong bài viết sau đây nhé!