(函数($){/***基本States命名空间。**拥有局部状态变量允许我们使用states名称空间*而不必总是声明“Drupal.states”。*/var状态=药物状态={//应该推迟的函数数组。延期:[]};/***附加状态。*/Drupal.behaviors.states={附加:函数(上下文,设置){var$context=$(context);for(settings.states中的var选择器){for(设置中的var状态。状态[选择器]){新的州。受抚养人({元素:$context.find(选择器),状态:状态。州。消毒(州),约束:settings.states[选择器][状态]});}}//立即执行所有延迟的函数。while(状态。延迟。长度){(states.delayed.shift())();}}};/***表示依赖于其他元素的元素的对象。**@param参数*对象具有以下键(所有键都是必需的):*-element:从属元素的jQuery对象*-state:描述依赖状态的state对象*-constraints:具有依赖性规范的对象。列出所有元素*此元素所依赖的。它可以嵌套,也可以包含任意*AND和OR子句。*/状态。从属=函数(args){扩展(this,{values:{},oldValue:null},args);this.dependees=this.getDependee();for(this.dependees中的var选择器){this.initializeDependee(选择器,this.dependees[选择器]);}};/***比较函数,用于将元素的值与*依赖项设置中的规范。如果对象类型不能为*在该列表中,默认使用===运算符。*/状态。Dependent.com比较={“RegExp”:函数(引用,值){返回参考测试(值);},“函数”:函数(引用,值){//“reference”变量是一个比较函数。返回参考(值);},“数字”:功能(参考,值){//如果“reference”是数字,“value”是字符串,则强制转换引用//作为字符串,然后在compare()中应用严格比较。否则//表单的states数组中的数字键无法匹配字符串值//从jQuery的val()返回。return(typeof value===“字符串”)?compare(reference.toString(),value):比较(reference,value);}};状态。Dependent.protype={/***初始化此依赖项所依赖的元素之一。**@param选择器*描述被依赖者的CSS选择器。*@param dependeeStates*必须监视以跟踪*受抚养人的合规状态。*/initializeDependee:函数(选择器,dependeeStates){var状态;//缓存此依赖项的状态。this.values[选择器]={};for(dependeeStates中的var i){if(dependeeStates.hasOwnProperty(i)){state=依赖状态[i];//确保我们没有两次初始化这个选择器/状态组合。if($.inArray(state,dependeeStates)===-1){继续;}state=状态。州。消毒(州);//初始化此状态的值。this.values[选择器][状态名称]=空;//监视此依赖项的指定状态的状态更改。$(选择器).bind('state:'+状态,$.proxy(函数(e)){this.update(选择器、状态、e.value);});//确保我们刚刚参与的活动确实已被解雇。新的州。触发器({选择器:选择器,状态:状态});}}},/***将值与引用值进行比较。**@param引用*用于引用的值。*@param选择器*描述受抚养人的CSS选择器。*@param状态*描述受依赖者更新状态的State对象。**@返回*正确或错误。*/比较:函数(引用、选择器、状态){var值=this.values[选择器][状态名称];if(状态中的reference.constructor.name。从属比较){//对某些引用值类型使用自定义比较函数。返回状态。依赖比较[reference.constructor.name](reference,value);}其他{//否则做一个简单的比较。返回比较(引用、值);}},/***更新受依赖者状态的值。**@param选择器*描述受抚养人的CSS选择器。*@param状态*描述受依赖者更新状态的State对象。*@param值*被依赖者更新状态的新值。*/更新:函数(选择器、状态、值){//只有当“new”值实际上是新值时才执行操作。if(value!==this.values[selector][state.name]){this.values[选择器][状态名称]=值;this.reevaluate();}},/***触发器在状态更改时更改事件。*/重新评估:函数(){//检查是否满足此依赖状态的任何约束。var值=this.verifyConstraints(this.constraints);//仅在值实际更改时调用状态更改事件。if(value!==this.oldValue){//存储新值,以便稍后比较该值//实际上发生了变化。this.oldValue=值;//规格化该值以匹配规格化的状态名称。value=反转(value,this.state.invert);//通过添加“触发器:true”,我们确保状态更改不会进入//无限循环。this.element.trigger({type:'state:'+this.state,value:value,trigger:true});}},/***计算子约束以确定是否满足约束。**@param约束*约束对象或约束数组。*@param选择器*这些约束的选择器。如果未定义,则还没有*这些约束应用于的选择器。在这种情况下*对象被解释为选择器(如果遇到)。**@返回*true或false,取决于是否满足这些约束。*/verifyConstraints:函数(约束,选择器){var结果;if($.isArray(约束)){//此约束是一个数组(OR或XOR)。var hasXor=$.inArray('xor',constraints)===-1;for(var i=0,len=constraints.length;i<len;i++){if(约束[i]!='xor'){var constraint=this.checkConstraints(约束[i],选择器,i);//如果这是OR并且我们有一个满意的约束,或者如果这是//是XOR,我们有第二个满足的约束。if(constraint&&(hasXor||result)){返回hasXor;}result=result||约束;}}}//确保我们不会尝试迭代对象以外的其他内容。这个//通常不应该发生,但如果条件定义是虚假的,//我们不想以无限循环结束。else if($.isPlainObject(约束)){//此约束是一个对象(AND)。for(约束中的var n){if(constraints.hasOwnProperty(n)){result=三元(result,this.checkConstraints(constraints[n],selector,n));//False,其他任何内容都将求值为False,因此如果有//发现错误条件。if(result===false){return false;}}}}返回结果;},/***检查该值是否符合此约束的要求。**@param值*状态值或约束数组/对象。*在后一种情况下,继续解决约束。*@param选择器*此约束的选择器。如果未定义,则还没有*此约束应用于的选择器。在这种情况下,状态键为*传播到选择器并继续解析。*@param状态*要检查此约束的状态。如果未定义,则解析*继续。*如果选择器和状态都不是未定义的有效非数字*字符串,该选择器状态的实际值的查找为*已执行。此参数不是State对象,而是原始状态*字符串。**@返回*true或false,取决于是否满足此约束。*/checkConstraints:函数(值、选择器、状态){//规范化最后一个参数。如果它是非数字的,我们将其视为//选择器(如果还没有)或作为触发器/状态。if(状态类型!=='string'|(/[0-9]/).test(状态[0])){状态=空;}else if(选择器类型===“未定义”){//当还没有选择器时,将状态传播到选择器。选择器=状态;状态=空;}if(state!==null){//约束是要检查的元素的实际约束。state=状态。州。消毒(州);返回invert(this.compare(value,selector,state),state.invert);}其他{//将此约束解析为AND/OR运算符。return this.verifyConstraints(value,selector);}},/***收集有关所有必需触发器的信息。*/getDependees:function(){var缓存={};//旋转查找功能,以便我们可以记录所有可用的选择器-//用于初始化的状态组合。var _compare=this.compare;this.copare=函数(引用、选择器、状态){(缓存[选择器]| |(缓存[选择符]=[]).push(状态名称);//不返回任何内容(===未定义),以便约束循环不//断裂。};//此调用实际上不验证任何内容,而是使用解析//遍历约束数组的机制,尝试查找每个约束//值。由于我们旋转了compare函数,此比较返回//未定义,查找一直持续到最后。而不是查找//值,我们记录选择器和状态的组合,以便//可以初始化所有触发器。this.verifyConstraints(this.constraints);//恢复原来的功能。this.compare=比较;返回缓存;}};状态。触发器=函数(args){$.extend(this,args);if(states.Trigger.states中的this.state){this.element=$(this.selector);//仅当触发器初始值设定项尚未附加到此//元素。否则,我们将以重复事件告终。if(!this.element.data('trigger:'+this.state)){this.initialize();}}};状态。触发器原型={初始化:函数(){var触发器=状态。触发器状态[this.state];if(触发器类型==“函数”){//我们有一个自定义触发器初始化函数。trigger.call(window,this.element);}其他{for(触发器中的var事件){if(trigger.hasOwnProperty(event)){this.defaultTrigger(事件,触发器[事件]);}}}//将此触发器标记为已为此元素初始化。this.element.data('trigger:'+this.state,true);},defaultTrigger:函数(事件,valueFn){var oldValue=valueFn.call(this.element);//附加事件回调。this.element.bind(事件,$.proxy(函数(e)){var值=valueFn.call(this.element,e);//仅当值实际更改时才触发事件。if(oldValue!==值){this.element.trigger({type:'state:'+this.state,value:value,oldValue:oldValue});oldValue=值;}});states.delayed.push($.proxy(function()){//触发事件一次以进行初始化。this.element.trigger({type:'state:'+this.state,value:oldValue,oldValue:null});});}};/***此状态列表包含用于监视状态的函数*元素的。每当一个元素依赖于另一个元素的状态时,*将这些触发函数之一添加到从属对象,以便*可以更新从属元素。*/状态。触发器状态={//“空”描述要监视的状态空:{//“keyup”是我们关注的(本机DOM)事件。“keyup”:函数(){//与该触发器关联的函数返回//状态。return this.val()==“”;}},已检查:{“change”:函数(){返回this.is(':checked');}},//对于单选按钮,仅在选中单选按钮时返回值。值:{“keyup”:函数(){//单选按钮具有相同的功能:输入[name=“key”]选择器。如果(this.length>1){//无线电的初始检查值未定义,因此我们返回false。return this.filter(':checked').val()|false;}返回this.val();},“change”:函数(){//单选按钮共享相同的功能:输入[name=“key”]选择器。如果(this.length>1){//无线电的初始检查值未定义,因此我们返回false。return this.filter(':checked').val()|false;}return this.val();}},塌陷:{“折叠”:函数(e){return(类型e!==“未定义”&e中的“值”)?e.value:this.is('.scolledge');}}};/***状态对象用于描述状态和执行别名。*/状态。状态=功能(状态){//稍后我们可能需要原始的未解析名称。this.pristine=this.name=状态;//规范化状态名称。while(真){//迭代删除感叹号并反转值。while(this.name.charAt(0)==“!”){this.name=this.name.substring(1);this.invert=!this.invert;}//将状态替换为其规范化名称。if(states.State.aliases中的this.name){this.name=状态。州别名[this.name];}其他{断裂;}}};/***通过清理传递的值创建新的State对象。*/状态。State.sanitize=函数(状态){if(state instanceof states.state){返回状态;}其他{返回新状态。州(州);}};/***此别名列表用于规范化状态并关联否定名称*具有各自的逆状态。*/状态。State.aliases=州别名{“已启用”:“!”!禁用',“不可见”:“!可见','无效':'!有效',“未受影响”:“!触摸’,'可选':'!必需','填充':'!空','未选中':'!选中',“无关”:“!相关',“展开”:“!”!坍塌','读写':'!只读'};状态。州原型={反转:假,/***确保仅使用状态对象返回名称。*/toString:function(){返回this.name;}};/***全局状态更改处理程序。这些都绑定到“文档”以涵盖所有*状态发生变化的元素。发送到页面内元素的事件*向这些处理程序冒泡。我们使用这个系统,以便主题和模块*可以为页面的特定部分重写这些状态更改处理程序。*/$(document).bind('状态:禁用',函数(e){//仅当此更改由依赖项而不是//元素监视本身。if(e.触发器){$(e.target).attr(“禁用”,e.value)最近的('.form-item,.form-submit,.form-wrapper').toggleClass('form-disabled',e.value).find('select,input,textarea').attr('disabled',e.value);//注意:WebKit晚间节目没有正确反映这一变化。//请参见https://bugs.webkit.org/show_bug.cgi?id=23789}});$(document).bind(“状态:必需”,函数(e){if(e.触发器){如果(e.value){var$label=$(e.target).fest('.form item,.form wrapper').find('label');//避免在初始化时重复所需的标记。if(!$label.find('.form-required').length){$label.append('*');}}其他{$(e.target).closest('.form-item,.form-wrapper').find('label.form-required').remove();}}});$(document).bind('状态:可见',函数(e){if(e.触发器){$(e.target).closest('.form-item,.form-submit,.form-wrapper').togle(e.value);}});$(document).bind('状态:已检查',函数(e){if(e.触发器){$(e.target).attr('checked',e.value);}});$(document).bind('状态:折叠',函数(e){if(e.触发器){if($(e.target).is('.scolledge')!==e.value){$('>图例a',e.target).click();}}});/***这些是实现加法“操作符”的助手函数*实现特定于状态的任何逻辑。*///与第三个未定义状态按位AND。三元函数(a,b){返回a===“未定义”的类型?b:(类型b===‘未定义’?a:a&&b);}//当invert为true时,反转a(如果未定义)。函数反转(a,反转){return(反转&&typeof a!==“未定义”)!a:a;}//比较两个值,同时忽略未定义的值。功能比较(a,b){返回(a===b)?(typeof a===‘undefined’?a:true):(typeof a===’undefined'||typeof b==='undefined\');}})(jQuery);