ject.freeze(function(){})}catch(k){Object.freeze=function(t){return function(n){return typeof n=="function"?n:t(n)}}(Object.freeze)}Object.preventExtensions||(Object.preventExtensions=function(t){return t}),Object.isSealed||(Object.isSealed=function(t){return!1}),Object.isFrozen||(Object.isFrozen=function(t){return!1}),Object.isExtensible||(Object.isExtensible=function(t){if(Object(t)===t)throw new TypeError;var n="";while(f(t,n))n+="?";t[n]=!0;var r=f(t,n);return delete t[n],r});if(!Object.keys){var L=!0,A=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],O=A.length;for(var M in{toString:null})L=!1;Object.keys=function I(e){if(typeof e!="object"&&typeof e!="function"||e===null)throw new TypeError("Object.keys called on a non-object");var I=[];for(var t in e)f(e,t)&&I.push(t);if(L)for(var n=0,r=O;n",n.end.row=e.getCurrentTokenRow(),n.end.column=e.getCurrentTokenColumn()+t.value.length)}while(t=e.stepBackward());return null},this._pop=function(e,t){while(e.length){var n=e[e.length-1];if(!t||n.tagName==t.tagName)return e.pop();if(this.optionalEndTags.hasOwnProperty(n.tagName)){e.pop();continue}return null}},this.getFoldWidgetRange=function(e,t,n){var r=this._getFirstTagInLine(e,n);if(!r)return null;var i=r.closing||r.selfClosing,o=[],a;if(!i){var f=new u(e,n,r.start.column),l={row:n,column:r.start.column+r.tagName.length+2};r.start.row==r.end.row&&(l.column=r.end.column);while(a=this._readTagForward(f)){if(a.selfClosing){if(!o.length)return a.start.column+=a.tagName.length+2,a.end.column-=2,s.fromPoints(a.start,a.end);continue}if(a.closing){this._pop(o,a);if(o.length==0)return s.fromPoints(l,a.start)}else o.push(a)}}else{var f=new u(e,n,r.end.column),c={row:n,column:r.start.column};while(a=this._readTagBackward(f)){if(a.selfClosing){if(!o.length)return a.start.column+=a.tagName.length+2,a.end.column-=2,s.fromPoints(a.start,a.end);continue}if(!a.closing){this._pop(o,a);if(o.length==0)return a.start.column+=a.tagName.length+2,a.start.row==a.end.row&&a.start.column-1}function l(e,t){var n=new r(e,t.row,t.column),i=n.getCurrentToken();while(i&&!f(i,"tag-name"))i=n.stepBackward();if(i)return i.value}function c(e,t){var n=new r(e,t.row,t.column),i=n.getCurrentToken();while(i&&!f(i,"attribute-name"))i=n.stepBackward();if(i)return i.value}var r=e("../token_iterator").TokenIterator,i=["accesskey","class","contenteditable","contextmenu","dir","draggable","dropzone","hidden","id","inert","itemid","itemprop","itemref","itemscope","itemtype","lang","spellcheck","style","tabindex","title","translate"],s=["onabort","onblur","oncancel","oncanplay","oncanplaythrough","onchange","onclick","onclose","oncontextmenu","oncuechange","ondblclick","ondrag","ondragend","ondragenter","ondragleave","ondragover","ondragstart","ondrop","ondurationchange","onemptied","onended","onerror","onfocus","oninput","oninvalid","onkeydown","onkeypress","onkeyup","onload","onloadeddata","onloadedmetadata","onloadstart","onmousedown","onmousemove","onmouseout","onmouseover","onmouseup","onmousewheel","onpause","onplay","onplaying","onprogress","onratechange","onreset","onscroll","onseeked","onseeking","onselect","onshow","onstalled","onsubmit","onsuspend","ontimeupdate","onvolumechange","onwaiting"],o=i.concat(s),u={html:{manifest:1},head:{},title:{},base:{href:1,target:1},link:{href:1,hreflang:1,rel:{stylesheet:1,icon:1},media:{all:1,screen:1,print:1},type:{"text/css":1,"image/png":1,"image/jpeg":1,"image/gif":1},sizes:1},meta:{"http-equiv":{"content-type":1},name:{description:1,keywords:1},content:{"text/html; charset=UTF-8":1},charset:1},style:{type:1,media:{all:1,screen:1,print:1},scoped:1},script:{charset:1,type:{"text/javascript":1},src:1,defer:1,async:1},noscript:{href:1},body:{onafterprint:1,onbeforeprint:1,onbeforeunload:1,onhashchange:1,onmessage:1,onoffline:1,onpopstate:1,onredo:1,onresize:1,onstorage:1,onundo:1,onunload:1},section:{},nav:{},article:{pubdate:1},aside:{},h1:{},h2:{},h3:{},h4:{},h5:{},h6:{},header:{},footer:{},address:{},main:{},p:{},hr:{},pre:{},blockquote:{cite:1},ol:{start:1,reversed:1},ul:{},li:{value:1},dl:{},dt:{},dd:{},figure:{},figcaption:{},div:{},a:{href:1,target:{_blank:1,top:1},ping:1,rel:{nofollow:1,alternate:1,author:1,bookmark:1,help:1,license:1,next:1,noreferrer:1,prefetch:1,prev:1,search:1,tag:1},media:1,hreflang:1,type:1},em:{},strong:{},small:{},s:{},cite:{},q:{cite:1},dfn:{},abbr:{},data:{},time:{datetime:1},code:{},"var":{},samp:{},kbd:{},sub:{},sup:{},i:{},b:{},u:{},mark:{},ruby:{},rt:{},rp:{},bdi:{},bdo:{},span:{},br:{},wbr:{},ins:{cite:1,datetime:1},del:{cite:1,datetime:1},img:{alt:1,src:1,height:1,width:1,usemap:1,ismap:1},iframe:{name:1,src:1,height:1,width:1,sandbox:{"allow-same-origin":1,"allow-top-navigation":1,"allow-forms":1,"allow-scripts":1},seamless:{seamless:1}},embed:{src:1,height:1,width:1,type:1},object:{param:1,data:1,type:1,height:1,width:1,usemap:1,name:1,form:1,classid:1},param:{name:1,value:1},video:{src:1,autobuffer:1,autoplay:{autoplay:1},loop:{loop:1},controls:{controls:1},width:1,height:1,poster:1,muted:{muted:1},preload:{auto:1,metadata:1,none:1}},audio:{src:1,autobuffer:1,autoplay:{autoplay:1},loop:{loop:1},controls:{controls:1},muted:{muted:1},preload:{auto:1,metadata:1,none:1}},source:{src:1,type:1,media:1},track:{kind:1,src:1,srclang:1,label:1,"default":1},canvas:{width:1,height:1},map:{name:1},area:{shape:1,coords:1,href:1,hreflang:1,alt:1,target:1,media:1,rel:1,ping:1,type:1},svg:{},math:{},table:{summary:1},caption:{},colgroup:{span:1},col:{span:1},tbody:{},thead:{},tfoot:{},tr:{},td:{headers:1,rowspan:1,colspan:1},th:{headers:1,rowspan:1,colspan:1,scope:1},form:{"accept-charset":1,action:1,autocomplete:1,enctype:{"multipart/form-data":1,"application/x-www-form-urlencoded":1},method:{get:1,post:1},name:1,novalidate:1,target:{_blank:1,top:1}},fieldset:{disabled:1,form:1,name:1},legend:{},label:{form:1,"for":1},input:{type:{text:1,password:1,hidden:1,checkbox:1,submit:1,radio:1,file:1,button:1,reset:1,image:31,color:1,date:1,datetime:1,"datetime-local":1,email:1,month:1,number:1,range:1,search:1,tel:1,time:1,url:1,week:1},accept:1,alt:1,autocomplete:{on:1,off:1},autofocus:{autofocus:1},checked:{checked:1},disabled:{disabled:1},form:1,formaction:1,formenctype:{"application/x-www-form-urlencoded":1,"multipart/form-data":1,"text/plain":1},formmethod:{get:1,post:1},formnovalidate:{formnovalidate:1},formtarget:{_blank:1,_self:1,_parent:1,_top:1},height:1,list:1,max:1,maxlength:1,min:1,multiple:{multiple:1},pattern:1,placeholder:1,readonly:{readonly:1},required:{required:1},size:1,src:1,step:1,width:1,files:1,value:1},button:{autofocus:1,disabled:{disabled:1},form:1,formaction:1,formenctype:1,formmethod:1,formnovalidate:1,formtarget:1,name:1,value:1,type:{button:1,submit:1}},select:{autofocus:1,disabled:1,form:1,multiple:{multiple:1},name:1,size:1,readonly:{readonly:1}},datalist:{},optgroup:{disabled:1,label:1},option:{disabled:1,selected:1,label:1,value:1},textarea:{autofocus:{autofocus:1},disabled:{disabled:1},form:1,maxlength:1,name:1,placeholder:1,readonly:{readonly:1},required:{required:1},rows:1,cols:1,wrap:{on:1,off:1,hard:1,soft:1}},keygen:{autofocus:1,challenge:{challenge:1},disabled:{disabled:1},form:1,keytype:{rsa:1,dsa:1,ec:1},name:1},output:{"for":1,form:1,name:1},progress:{value:1,max:1},meter:{value:1,min:1,max:1,low:1,high:1,optimum:1},details:{open:1},summary:{},command:{type:1,label:1,icon:1,disabled:1,checked:1,radiogroup:1,command:1},menu:{type:1,label:1},dialog:{open:1}},a=Object.keys(u),h=function(){};(function(){this.getCompletions=function(e,t,n,r){var i=t.getTokenAt(n.row,n.column);if(!i)return[];if(f(i,"tag-name")||f(i,"tag-open")||f(i,"end-tag-open"))return this.getTagCompletions(e,t,n,r);if(f(i,"tag-whitespace")||f(i,"attribute-name"))return this.getAttributeCompletions(e,t,n,r);if(f(i,"attribute-value"))return this.getAttributeValueCompletions(e,t,n,r);var s=t.getLine(n.row).substr(0,n.column);return/&[A-z]*$/i.test(s)?this.getHTMLEntityCompletions(e,t,n,r):[]},this.getTagCompletions=function(e,t,n,r){return a.map(function(e){return{value:e,meta:"tag",score:Number.MAX_VALUE}})},this.getAttributeCompletions=function(e,t,n,r){var i=l(t,n);if(!i)return[];var s=o;return i in u&&(s=s.concat(Object.keys(u[i]))),s.map(function(e){return{caption:e,snippet:e+'="$0"',meta:"attribute",score:Number.MAX_VALUE}})},this.getAttributeValueCompletions=function(e,t,n,r){var i=l(t,n),s=c(t,n);if(!i)return[];var o=[];return i in u&&s in u[i]&&typeof u[i][s]=="object"&&(o=Object.keys(u[i][s])),o.map(function(e){return{caption:e,snippet:e,meta:"attribute value",score:Number.MAX_VALUE}})},this.getHTMLEntityCompletions=function(e,t,n,r){var i=["Á","á","Â","â","´","Æ","æ","À","à","ℵ","Α","α","&","∧","∠","Å","å","≈","Ã","ã","Ä","ä","„","Β","β","¦","•","∩","Ç","ç","¸","¢","Χ","χ","ˆ","♣","≅","©","↵","∪","¤","‡","†","⇓","↓","°","Δ","δ","♦","÷","É","é","Ê","ê","È","è","∅"," "," ","Ε","ε","≡","Η","η","Ð","ð","Ë","ë","€","∃","ƒ","∀","½","¼","¾","⁄","Γ","γ","≥",">","⇔","↔","♥","…","Í","í","Î","î","¡","Ì","ì","ℑ","∞","∫","Ι","ι","¿","∈","Ï","ï","Κ","κ","Λ","λ","⟨","«","⇐","←","⌈","“","≤","⌊","∗","◊","‎","‹","‘","<","¯","—","µ","·","−","Μ","μ","∇"," ","–","≠","∋","¬","∉","⊄","Ñ","ñ","Ν","ν","Ó","ó","Ô","ô","Œ","œ","Ò","ò","‾","Ω","ω","Ο","ο","⊕","∨","ª","º","Ø","ø","Õ","õ","⊗","Ö","ö","¶","∂","‰","⊥","Φ","φ","Π","π","ϖ","±","£","″","′","∏","∝","Ψ","ψ",""","√","⟩","»","⇒","→","⌉","”","ℜ","®","⌋","Ρ","ρ","‏","›","’","‚","Š","š","⋅","§","­","Σ","σ","ς","∼","♠","⊂","⊆","∑","⊃","¹","²","³","⊇","ß","Τ","τ","∴","Θ","θ","ϑ"," ","Þ","þ","˜","×","™","Ú","ú","⇑","↑","Û","û","Ù","ù","¨","ϒ","Υ","υ","Ü","ü","℘","Ξ","ξ","Ý","ý","¥","Ÿ","ÿ","Ζ","ζ","‍","‌"];return i.map(function(e){return{caption:e,snippet:e.substr(1),meta:"html entity",score:Number.MAX_VALUE}})}}).call(h.prototype),t.HtmlCompletions=h}),define("ace/mode/html",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text","ace/mode/javascript","ace/mode/css","ace/mode/html_highlight_rules","ace/mode/behaviour/xml","ace/mode/folding/html","ace/mode/html_completions","ace/worker/worker_client"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text").Mode,o=e("./javascript").Mode,u=e("./css").Mode,a=e("./html_highlight_rules").HtmlHighlightRules,f=e("./behaviour/xml").XmlBehaviour,l=e("./folding/html").FoldMode,c=e("./html_completions").HtmlCompletions,h=e("../worker/worker_client").WorkerClient,p=["area","base","br","col","embed","hr","img","input","keygen","link","meta","menuitem","param","source","track","wbr"],d=["li","dt","dd","p","rt","rp","optgroup","option","colgroup","td","th"],v=function(e){this.fragmentContext=e&&e.fragmentContext,this.HighlightRules=a,this.$behaviour=new f,this.$completer=new c,this.createModeDelegates({"js-":o,"css-":u}),this.foldingRules=new l(this.voidElements,i.arrayToMap(d))};r.inherits(v,s),function(){this.blockComment={start:""},this.voidElements=i.arrayToMap(p),this.getNextLineIndent=function(e,t,n){return this.$getIndent(t)},this.checkOutdent=function(e,t,n){return!1},this.getCompletions=function(e,t,n,r){return this.$completer.getCompletions(e,t,n,r)},this.createWorker=function(e){if(this.constructor!=v)return;var t=new h(["ace"],"ace/mode/html_worker","Worker");return t.attachToDocument(e.getDocument()),this.fragmentContext&&t.call("setOptions",[{context:this.fragmentContext}]),t.on("error",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/html"}.call(v.prototype),t.Mode=v})f we have them. if ( $widgets ) { $categories[$widgets_key] = $widgets; } // Sort the modules. foreach($categories as $title => $modules) { if(count($categories[$title]) == 0) { unset($categories[$title]); } else { ksort($categories[$title]); } } // Return sorted categories. return $categories; } /** * Returns the slug for a module category. * * @since 1.0 * @param string $name The category name. * @return string */ static public function get_module_category_slug( $name ) { // Get the core category keys. $basic_key = __('基础元素', 'fl-builder'); $advanced_key = __('高级元素', 'fl-builder'); $other_key = __('其他元素', 'fl-builder'); $widgets_key = __('插件', 'fl-builder'); if ( $name == $basic_key ) { return 'basic'; } if ( $name == $advanced_key ) { return 'advanced'; } if ( $name == $other_key ) { return 'other'; } if ( $name == $widgets_key ) { return 'widgets'; } return sanitize_html_class( $name ); } /** * Returns an instance of a module. * * @since 1.0 * @param string|object $node_id A module node ID or object. * @return object|bool The module or false if it doesn't exist. */ static public function get_module( $node_id ) { $module = is_object( $node_id ) ? $node_id : self::get_node( $node_id ); if( self::is_module_registered( $module->settings->type ) ) { $class = get_class(self::$modules[$module->settings->type]); $instance = new $class(); $instance->node = $module->node; $instance->parent = $module->parent; $instance->position = $module->position; $instance->settings = $module->settings; $instance->type = 'module'; $instance->form = self::$modules[$module->settings->type]->form; if ( isset( $module->template_id ) ) { $instance->template_id = $module->template_id; $instance->template_node_id = $module->template_node_id; } if ( isset( $module->template_root_node ) ) { $instance->template_root_node = true; } return $instance; } return false; } /** * Returns an array of all modules in the current layout * or in a column if a column id or object is supplied. * * @since 1.0 * @param string|object $col_id A column ID or object. * @return array */ static public function get_modules($col_id = null) { $col = is_object( $col_id ) ? $col_id : self::get_node( $col_id ); $modules = self::get_nodes('module', $col); $instances = array(); $i = 0; foreach($modules as $module) { if ( self::is_module_registered( $module->settings->type ) ) { $class = get_class(self::$modules[$module->settings->type]); $instances[$i] = new $class(); $instances[$i]->node = $module->node; $instances[$i]->parent = $module->parent; $instances[$i]->position = $module->position; $instances[$i]->settings = $module->settings; $instances[$i]->type = 'module'; $instances[$i]->form = self::$modules[$module->settings->type]->form; if ( isset( $module->template_id ) ) { $instances[$i]->template_id = $module->template_id; $instances[$i]->template_node_id = $module->template_node_id; } if ( isset( $module->template_root_node ) ) { $instances[$i]->template_root_node = true; } $i++; } } return $instances; } /** * Returns an array of all modules in the current layout. * * @since 1.0 * @return array */ static public function get_all_modules() { return self::get_modules(); } /** * Add a new module to a column in the current layout. * * @since 1.0 * @param string $type The type of module to add. * @param array $settings The new module's settings. * @param string $parent_id The new module's parent node ID. * @param int $position The new module's position. * @return object The new module object. */ static public function add_module($type = null, $settings = array(), $parent_id = null, $position = false ) { $data = self::get_layout_data(); $parent = self::get_node( $parent_id ); $module_node_id = self::generate_node_id(); $settings->type = $type; // Run module update method. $class = get_class(self::$modules[$type]); $instance = new $class(); $instance->node = $module_node_id; $instance->settings = $settings; $settings = $instance->update($settings); // Save the module. $data[$module_node_id] = new StdClass(); $data[$module_node_id]->node = $module_node_id; $data[$module_node_id]->type = 'module'; $data[$module_node_id]->parent = $parent_id; $data[$module_node_id]->position = self::next_node_position('module', $parent_id); $data[$module_node_id]->settings = $settings; // Add node template data. if ( self::is_node_global( $parent ) ) { $data[$module_node_id]->template_id = $parent->template_id; $data[$module_node_id]->template_node_id = $module_node_id; } // Update the layout data. self::update_layout_data($data); // Position the module. if($position !== false) { self::reorder_node($module_node_id, $position); } // Send back the inserted module. return self::get_module($module_node_id); } /** * Adds a parent node for a module if a parent with the supplied * parent ID doesn't exist. * * @since 1.6.3 * @param string $parent_id The node ID of the parent to look for. * @param int $position The position of the parent. * @return string|null The new parent ID or null if none exists. */ static public function add_module_parent( $parent_id = null, $position = null ) { $parent = ! $parent_id ? null : self::get_node( $parent_id ); // Add a new row if we don't have a parent. if ( ! $parent ) { $row = self::add_row( '1-col', $position ); $col_groups = self::get_nodes( 'column-group', $row->node ); $col_group = array_shift( $col_groups ); $cols = self::get_nodes( 'column', $col_group->node ); $parent = array_shift( $cols ); $parent_id = $parent->node; } // Add a new column group if the parent is a row. else if ( $parent->type == 'row' ) { $col_group = self::add_col_group( $parent->node, '1-col', $position ); $cols = self::get_nodes( 'column', $col_group->node ); $parent = array_shift( $cols ); $parent_id = $parent->node; } // Add a new column if the parent is a column group. else if ( $parent->type == 'column-group' ) { $parent = self::add_col( $parent->node, $position ); $parent_id = $parent->node; } return $parent_id; } /** * Returns a module's parent node of the specified type. * * @since 1.7 * @param string $type The type of parent to return. * @param string|object $module_id The module's node ID. Can also be a module object. * @return object The parent node. */ static public function get_module_parent( $type, $module_id ) { $module = is_object( $module_id ) ? $module_id : self::get_module( $module_id ); $nodes = self::get_categorized_nodes(); foreach ( $nodes['columns'] as $column ) { if ( $column->node == $module->parent ) { if ( 'column' == $type ) { return $column; } foreach ( $nodes['groups'] as $group ) { if ( $group->node == $column->parent ) { if ( 'column-group' == $type ) { return $group; } foreach ( $nodes['rows'] as $row ) { if ( $row->node == $group->parent ) { return $row; } } } } } } return null; } /** * Add a new module with default settings to a column * in the current layout. * * @since 1.0 * @param string $parent_id The new module's parent node ID. * @param string $type The type of module to add. * @param int $position The new module's position. * @return object The new module object. */ static public function add_default_module($parent_id = null, $type = null, $position = null) { $parent = $parent_id == 0 ? null : self::get_node($parent_id); $settings = self::get_module_defaults($type); $module_node_id = self::generate_node_id(); // Add a new parent if one is needed. if ( ! $parent || 'row' == $parent->type || 'column-group' == $parent->type ) { $parent_id = self::add_module_parent( $parent_id, $position ); $parent = self::get_node( $parent_id ); $position = null; } // Run module update method. $class = get_class(self::$modules[$type]); $instance = new $class(); $instance->node = $module_node_id; $instance->settings = $settings; $settings = $instance->update($settings); // Save the module. $data = self::get_layout_data(); $data[$module_node_id] = new StdClass(); $data[$module_node_id]->node = $module_node_id; $data[$module_node_id]->type = 'module'; $data[$module_node_id]->parent = $parent_id; $data[$module_node_id]->position = self::next_node_position('module', $parent_id); $data[$module_node_id]->settings = $settings; // Add node template data. if ( self::is_node_global( $parent ) ) { $data[$module_node_id]->template_id = $parent->template_id; $data[$module_node_id]->template_node_id = $module_node_id; } // Update the layout data. self::update_layout_data($data); // Position the module. if(null !== $position) { self::reorder_node($module_node_id, $position); } // Send back the inserted module. return self::get_module($module_node_id); } /** * Make a copy of a module. * * @since 1.0 * @param string $node_id Node ID of the module to copy. * @return object The new module object. */ static public function copy_module( $node_id = null ) { $module = self::get_module( $node_id ); return self::add_module( $module->settings->type, $module->settings, $module->parent, $module->position + 1 ); } /** * Run module specific logic on new node settings. * * @since 1.0 * @param object $module A module node object. * @param object $new_settings The new settings. * @return object */ static public function process_module_settings($module, $new_settings) { // Get a new node instance to work with. $class = get_class(self::$modules[$module->settings->type]); $instance = new $class(); $instance->node = $module->node; // Run node delete to clear any cache. $instance->settings = $module->settings; $instance->delete(); // Run node update. $instance->settings = $new_settings; $new_settings = $instance->update($new_settings); return $new_settings; } /** * Returns a cloned settings object for a module. * * @since 1.9 * @param object $settings * @return object */ static public function clone_module_settings( $settings ) { $new_settings = new stdClass; foreach ( $settings as $key => $val ) { $new_settings->$key = $val; } return $new_settings; } /** * Returns the default settings for a module. * * @since 1.0 * @param string $type The type of module. * @return object */ static public function get_module_defaults($type) { $defaults = new StdClass(); if(isset(self::$modules[$type]->form)) { $defaults = self::get_settings_form_defaults( $type ); $defaults->type = $type; } return $defaults; } /** * Merges the default settings for nested forms in a module. * * @since 1.7 * @param string $type The type of module. * @param object $settings The module settings object. * @return object */ static public function merge_nested_module_defaults( $type, $settings ) { // Make sure the module form exists. if ( isset( self::$modules[ $type ] ) ) { // Get the fields. $fields = self::get_settings_form_fields( self::$modules[ $type ]->form ); // Loop through the settings. foreach ( $settings as $key => $val ) { // Make sure this field is a nested form. if ( ! isset( $fields[ $key ]['form'] ) ) { continue; } // Get the nested form defaults. $nested_defaults = self::get_settings_form_defaults( $fields[ $key ]['form'] ); // Merge the defaults. if ( is_array( $val ) ) { foreach ( $val as $nested_key => $nested_val ) { $settings->{ $key }[ $nested_key ] = ( object )array_merge( ( array )$nested_defaults, ( array )$nested_val ); } } else { $settings->{ $key } = ( object )array_merge( ( array )$nested_defaults, ( array )$settings->{ $key } ); } } } return $settings; } /** * Returns an array of data for each core WordPress widget. * * @since 1.0 * @return array */ static public function get_wp_widgets() { global $wp_widget_factory; $widgets = array(); foreach($wp_widget_factory->widgets as $class => $widget) { $widget->class = $class; $widgets[$widget->name] = $widget; } ksort($widgets); return $widgets; } /** * Returns an array of data for all registered sidebars. * * @since 1.0 * @return array */ static public function get_wp_sidebars() { global $wp_registered_sidebars; $sidebars = array(); foreach($wp_registered_sidebars as $sidebar) { $sidebars[$sidebar['name']] = $sidebar; } ksort($sidebars); return $sidebars; } /** * Loads the files for all core builder settings. * * @since 1.0 * @return void */ static public function load_settings() { require_once FL_BUILDER_DIR . 'includes/global-settings.php'; require_once FL_BUILDER_DIR . 'includes/layout-settings.php'; require_once FL_BUILDER_DIR . 'includes/row-settings.php'; require_once FL_BUILDER_DIR . 'includes/column-settings.php'; require_once FL_BUILDER_DIR . 'includes/module-settings.php'; } /** * Register a settings form with the builder. * * @since 1.0 * @param string $id The form id. * @param array $form The form data. * @return void */ static public function register_settings_form($id, $form) { self::$settings_forms[$id] = apply_filters( 'fl_builder_register_settings_form', $form, $id ); } /** * Returns the data for a settings form. * * @since 1.0 * @param string $id The form id. * @return array */ static public function get_settings_form( $id ) { return self::$settings_forms[ $id ]; } /** * Returns an array of fields in a settings form. * * @since 1.0 * @param array $form The form data array. * @return array */ static public function get_settings_form_fields($form) { $fields = array(); foreach ( $form as $tab ) { if ( isset( $tab['sections'] ) ) { foreach ( $tab['sections'] as $section ) { if ( isset( $section['fields'] ) ) { foreach ( $section['fields'] as $name => $field ) { $fields[ $name ] = $field; } } } } } return $fields; } /** * Returns a settings object with the defaults for a form. * * @since 1.0 * @param string $type The type of form. * @return object */ static public function get_settings_form_defaults( $type ) { // Check to see if the defaults are cached first. if ( isset( self::$settings_form_defaults[ $type ] ) ) { return self::$settings_form_defaults[ $type ]; } // They aren't cached, let's get them. $defaults = new StdClass(); // Check the registered forms first. if ( isset( self::$settings_forms[ $type ] ) ) { $form_type = $type; $tabs = self::$settings_forms[ $type ]['tabs']; } // If it's not a registered form, it must be a module form. else if ( isset( self::$modules[ $type ] ) ) { $form_type = $type . '-module'; $tabs = self::$modules[ $type ]->form; } // The form can't be found. else { return $defaults; } // Get the fields. $fields = self::get_settings_form_fields( $tabs ); // Loop through the fields and get the defaults. foreach($fields as $name => $field) { $default = isset($field['default']) ? $field['default'] : ''; $is_multiple = isset($field['multiple']) && $field['multiple'] === true; $supports_multiple = $field['type'] != 'editor' && $field['type'] != 'photo'; $responsive = isset($field['responsive']) && $field['responsive'] ? $field['responsive'] : false; $responsive_name = ''; if($is_multiple && $supports_multiple) { $defaults->$name = array($default); } else if ( $responsive ) { foreach ( array( 'default', 'medium', 'responsive' ) as $device ) { $responsive_name = $name . ( 'default' == $device ? '' : '_' . $device ); if ( is_array( $responsive ) && isset( $responsive['default'] ) && isset( $responsive['default'][ $device ] ) ) { $defaults->{ $responsive_name } = $responsive['default'][ $device ]; } else if( 'default' == $device ) { $defaults->$name = $default; } else { $defaults->{ $responsive_name } = ''; } } } else { $defaults->$name = $default; } } // Cache the defaults. self::$settings_form_defaults[ $type ] = apply_filters( 'fl_builder_settings_form_defaults', $defaults, $form_type ); return self::$settings_form_defaults[ $type ]; } /** * Save the settings for a node. * * @since 1.0 * @param string $node_id The node ID. * @param object $settings The settings to save. * @return void */ static public function save_settings($node_id = null, $settings = null) { $node = self::get_node($node_id); $new_settings = (object)array_merge((array)$node->settings, (array)$settings); $template_post_id = self::is_node_global( $node ); // Process the settings. $new_settings = self::process_node_settings($node, $new_settings); // Save the settings to the node. $data = self::get_layout_data(); $data[$node_id]->settings = $new_settings; // Update the layout data. self::update_layout_data($data); // Save settings for a global node template? if ( $template_post_id && ! self::is_post_node_template() ) { // Get the template data. $template_data = self::get_layout_data( 'published', $template_post_id ); // Update the template node settings. $template_data[ $node->template_node_id ]->settings = $new_settings; // Save the template data. self::update_layout_data( $template_data, 'published', $template_post_id ); self::update_layout_data( $template_data, 'draft', $template_post_id ); // Delete the template asset cache. self::delete_all_asset_cache( $template_post_id ); self::delete_node_template_asset_cache( $template_post_id ); } // Return the new layout. return FLBuilderAJAXLayout::render(); } /** * Adds slashes to settings going into the database as WordPress * removes them when we save using update_metadata. This is done * to ensure slashes in user input aren't removed. * * @since 1.5.6 * @param mixed $data The data to slash. * @return mixed The slashed data. */ static public function slash_settings( $data ) { if ( is_array( $data ) ) { foreach ( $data as $key => $val ) { $data[ $key ] = self::slash_settings( $val ); } } else if ( is_object( $data ) ) { foreach ( $data as $key => $val ) { $data->$key = self::slash_settings( $val ); } } else if ( is_string( $data ) ) { $data = wp_slash( $data ); } return $data; } /** * Merge defaults into a settings object. * * @since 1.0 * @param object $settings Reference to a settings object. * @param array $defaults The defaults to merge in. * @return void */ static public function default_settings(&$settings, $defaults) { foreach($defaults as $name => $value) { if(!isset($settings->$name)) { $settings->$name = $value; } } } /** * Get the global builder settings. * * @since 1.0 * @return object */ static public function get_global_settings() { if ( null === self::$global_settings ) { $settings = get_option('_fl_builder_settings'); $defaults = self::get_settings_form_defaults( 'global' ); if ( !$settings ) { $settings = new StdClass(); } // Merge in defaults and cache settings self::$global_settings = (object) array_merge((array) $defaults, (array) $settings); } return self::$global_settings; } /** * Save the global builder settings. * * @since 1.0 * @param array $settings The new global settings. * @return object */ static public function save_global_settings($settings = array()) { $old_settings = self::get_global_settings(); $new_settings = (object)array_merge((array)$old_settings, (array)$settings); self::delete_asset_cache_for_all_posts(); self::$global_settings = null; update_option('_fl_builder_settings', $settings); return self::get_global_settings(); } /** * Duplicate the current post. * * @since 1.0 * @return int The new post ID. */ static public function duplicate_post() { global $wpdb; $post_id = self::get_post_id(); $post = get_post($post_id); $current_user = wp_get_current_user(); // Duplicate the post. $data = array( 'comment_status' => $post->comment_status, 'ping_status' => $post->ping_status, 'post_author' => $current_user->ID, 'post_content' => $post->post_content, 'post_excerpt' => $post->post_excerpt, 'post_name' => $post->post_name, 'post_parent' => $post->post_parent, 'post_password' => $post->post_password, 'post_status' => 'draft', 'post_title' => sprintf( _x( 'Copy of %s', '%s stands for post/page title.', 'fl-builder' ), $post->post_title ), 'post_type' => $post->post_type, 'to_ping' => $post->to_ping, 'menu_order' => $post->menu_order ); // Get the new post id. $new_post_id = wp_insert_post($data); // Duplicate post meta. $post_meta = $wpdb->get_results("SELECT meta_key, meta_value FROM {$wpdb->postmeta} WHERE post_id= {$post_id}"); if(count($post_meta) !== 0) { $sql = "INSERT INTO {$wpdb->postmeta} (post_id, meta_key, meta_value) "; foreach($post_meta as $meta_info) { $meta_key = $meta_info->meta_key; $meta_value = addslashes($meta_info->meta_value); $sql_select[] = "SELECT {$new_post_id}, '{$meta_key}', '{$meta_value}'"; } $sql .= implode(" UNION ALL ", $sql_select); $wpdb->query($sql); } // Duplicate post terms. $taxonomies = get_object_taxonomies($post->post_type); foreach($taxonomies as $taxonomy) { $post_terms = wp_get_object_terms($post_id, $taxonomy); for($i = 0; $i < count($post_terms); $i++) { wp_set_object_terms($new_post_id, $post_terms[$i]->slug, $taxonomy, true); } } // Get the duplicated layout data. $data = self::get_layout_data('published', $new_post_id); // Generate new node ids. $data = self::generate_new_node_ids($data); // Save the duplicated layout data. self::update_layout_data($data, 'published', $new_post_id); // Return the new post id. return $new_post_id; } /** * Deletes all layout data and asset cache for a post. * * @since 1.0 * @param int $post_id The post ID to delete data and cache for. * @return void */ static public function delete_post( $post_id ) { // If this is a global template, unlink it from other posts. self::unlink_global_node_template_from_all_posts( $post_id ); // Delete all published and draft data. self::delete_layout_data( 'published', $post_id ); self::delete_layout_data( 'draft', $post_id ); // Delete all css and js. self::delete_all_asset_cache( $post_id ); } /** * Save a revision of a builder layout. * * @since 1.0 * @param int $post_id * @return void */ static public function save_revision($post_id) { $parent_id = wp_is_post_revision($post_id); if($parent_id) { $parent = get_post($parent_id); $data = self::get_layout_data('published', $parent->ID); $settings = self::get_layout_settings('published', $parent->ID); if(!empty($data)) { self::update_layout_data($data, 'published', $post_id); self::update_layout_settings($settings, 'published', $post_id); } } } /** * Restore a revision of a builder layout. * * @since 1.0 * @param int $post_id * @param int $revision_id * @return void */ static public function restore_revision($post_id, $revision_id) { $post = get_post($post_id); $revision = get_post($revision_id); if($revision) { $data = self::get_layout_data('published', $revision->ID); $settings = self::get_layout_settings('published', $revision->ID); if(!empty($data)) { self::update_layout_data($data, 'published', $post_id); self::update_layout_data($data, 'draft', $post_id); self::update_layout_settings($settings, 'published', $post_id); self::update_layout_settings($settings, 'draft', $post_id); } else { self::delete_layout_data('published', $post_id); self::delete_layout_data('draft', $post_id); self::delete_layout_settings('published', $post_id); self::delete_layout_settings('draft', $post_id); } self::delete_all_asset_cache( $post_id ); } } /** * Get all of the layout data for a post. We use get_metadata * here instead of get_post_meta to ensure revisions are queried accordingly. * * @since 1.0 * @param string $status Either published or draft. * @param int $post_id The ID of the post to get data for. * @return array */ static public function get_layout_data($status = null, $post_id = null) { $post_id = !$post_id ? self::get_post_id() : $post_id; $status = !$status ? self::get_node_status() : $status; // Get published data? if($status == 'published') { if(isset(self::$published_layout_data[$post_id])) { $data = self::$published_layout_data[$post_id]; } else { $data = get_metadata('post', $post_id, '_fl_builder_data', true); self::$published_layout_data[$post_id] = self::clean_layout_data( $data ); } } // Get draft data? else if($status == 'draft') { if(isset(self::$draft_layout_data[$post_id])) { $data = self::$draft_layout_data[$post_id]; } else { $data = get_metadata('post', $post_id, '_fl_builder_draft', true); self::$draft_layout_data[$post_id] = self::clean_layout_data( $data ); } } // Make sure we have an array. if(empty($data)) { $data = array(); } // Clone the layout data to ensure the cache remains intact. foreach($data as $node_id => $node) { $data[$node_id] = clone $node; } // Return the data. return apply_filters( 'fl_builder_layout_data', $data, $status, $post_id ); } /** * Update the layout data for a post. We use update_metadata * here instead of update_post_meta to ensure revisions are updated accordingly. * * @since 1.0 * @param array $data The layout data to update. * @param string $status Either published or draft. * @param int $post_id The ID of the post to update. * @return void */ static public function update_layout_data($data, $status = null, $post_id = null) { $post_id = !$post_id ? self::get_post_id() : $post_id; $status = !$status ? self::get_node_status() : $status; $key = 'published' == $status ? '_fl_builder_data' : '_fl_builder_draft'; $raw_data = get_metadata( 'post', $post_id, $key ); $data = self::slash_settings( self::clean_layout_data( $data ) ); // Update the data. if ( 0 === count( $raw_data ) ) { add_metadata( 'post', $post_id, $key, $data ); } else { update_metadata( 'post', $post_id, $key, $data ); } // Cache the data. if($status == 'published') { self::$published_layout_data[$post_id] = $data; } else if($status == 'draft') { self::$draft_layout_data[$post_id] = $data; } } /** * Delete the layout data for a post. * * @since 1.0 * @param string $status Either published or draft. * @param int $post_id The ID of the post to delete data. * @return void */ static public function delete_layout_data($status = null, $post_id = null) { // Make sure we have a status to delete. if(!$status) { return; } // Get the post id. $post_id = !$post_id ? self::get_post_id() : $post_id; // Get the data to delete. $data = self::get_layout_data($status, $post_id); // Delete the nodes. foreach($data as $node) { self::call_module_delete($node); } // Update the layout data. self::update_layout_data(array(), $status, $post_id); } /** * Ensures the integrity of layout data key/value pairs. * * Also makes sure we're not serializing any FLBuilderModule * instances because those are too big and bloat the data array. * * @since 1.0 * @param array $data An array of layout data. * @return array */ static public function clean_layout_data( $data = array() ) { $cleaned = array(); if ( is_array( $data ) ) { foreach ( $data as $node ) { if ( is_object( $node ) && isset( $node->node ) ) { if ( is_a( $node, 'FLBuilderModule' ) ) { $cleaned[ $node->node ] = new StdClass(); $cleaned[ $node->node ]->node = $node->node; $cleaned[ $node->node ]->type = $node->type; $cleaned[ $node->node ]->parent = $node->parent; $cleaned[ $node->node ]->position = $node->position; $cleaned[ $node->node ]->settings = $node->settings; } else { $cleaned[ $node->node ] = $node; } } } } return $cleaned; } /** * Get the builder settings for a layout. * * @since 1.7 * @param string $status Either published or draft. * @param int $post_id The ID of the post to get settings for. * @return object */ static public function get_layout_settings( $status = null, $post_id = null ) { $status = ! $status ? self::get_node_status() : $status; $post_id = ! $post_id ? self::get_post_id() : $post_id; $key = 'published' == $status ? '_fl_builder_data_settings' : '_fl_builder_draft_settings'; $settings = get_metadata( 'post', $post_id, $key, true ); $defaults = self::get_settings_form_defaults( 'layout' ); if ( ! $settings ) { $settings = new StdClass(); } $settings = (object)array_merge( (array)$defaults, (array)$settings ); return apply_filters( 'fl_builder_layout_settings', $settings, $status, $post_id ); } /** * Updates the layout settings for a post. * * @since 1.7 * @param array $settings The new layout settings. * @param string $status Either published or draft. * @param int $post_id The ID of the post to update. * @return object */ static public function update_layout_settings( $settings = array(), $status = null, $post_id = null ) { $status = ! $status ? self::get_node_status() : $status; $post_id = ! $post_id ? self::get_post_id() : $post_id; $key = 'published' == $status ? '_fl_builder_data_settings' : '_fl_builder_draft_settings'; $raw_settings = get_metadata( 'post', $post_id, $key ); $old_settings = self::get_layout_settings( $status, $post_id ); $new_settings = (object)array_merge( (array)$old_settings, (array)$settings ); if ( 0 === count( $raw_settings ) ) { add_metadata( 'post', $post_id, $key, self::slash_settings( $new_settings ) ); } else { update_metadata( 'post', $post_id, $key, self::slash_settings( $new_settings ) ); } return $new_settings; } /** * Called via AJAX to save the layout settings. * * @since 1.7 * @param array $settings The new layout settings. * @param string $status Either published or draft. * @param int $post_id The ID of the post to update. * @return object */ static public function save_layout_settings( $settings = array(), $status = null, $post_id = null ) { return self::update_layout_settings( $settings, $status, $post_id ); } /** * Delete the layout settings for a post. * * @since 1.7 * @param string $status Either published or draft. * @param int $post_id The ID of a post whose settings to delete. * @return void */ static public function delete_layout_settings( $status = null, $post_id = null ) { $status = ! $status ? self::get_node_status() : $status; $post_id = ! $post_id ? self::get_post_id() : $post_id; $key = 'published' == $status ? '_fl_builder_data_settings' : '_fl_builder_draft_settings'; update_metadata( 'post', $post_id, $key, array() ); } /** * Merge two sets of layout settings together. * * @since 1.7 * @param object $settings The layout settings to merge into. * @param object $merge_settings The layout settings to merge. * @return object */ static public function merge_layout_settings( $settings, $merge_settings ) { $keys = array( 'css', 'js' ); foreach ( $keys as $key ) { if ( empty( $merge_settings->{$key} ) ) { continue; } else if ( strstr( $settings->{$key}, $merge_settings->{$key} ) ) { continue; } else { if ( ! empty( $settings->{$key} ) ) { $settings->{$key} .= "\n"; } $settings->{$key} .= $merge_settings->{$key}; } } return $settings; } /** * Clears a draft layout and saves a new draft using * the currently published layout data. * * @since 1.0 * @return void */ static public function clear_draft_layout() { $post_id = self::get_post_id(); $data = self::get_layout_data('published', $post_id); $settings = self::get_layout_settings('published', $post_id); // Delete the old draft layout. self::delete_layout_data('draft'); // Save the new draft layout. self::update_layout_data($data, 'draft', $post_id); // Save the new draft layout settings. self::update_layout_settings($settings, 'draft', $post_id); // Clear the asset cache. self::delete_all_asset_cache($post_id); } /** * Saves layout data when a user chooses to publish. * * @since 1.0 * @param bool $publish Whether to publish the parent post or not. * @return void */ static public function save_layout( $publish = true ) { $editor_content = FLBuilder::render_editor_content(); $post_id = self::get_post_id(); $data = self::get_layout_data('draft', $post_id); $settings = self::get_layout_settings('draft', $post_id); // Fire the before action. do_action( 'fl_builder_before_save_layout', $post_id, $publish, $data, $settings ); // Delete the old published layout. self::delete_layout_data('published', $post_id); self::delete_layout_settings('published', $post_id); // Save the new published layout. self::update_layout_data($data, 'published', $post_id); self::update_layout_settings($settings, 'published', $post_id); // Clear the asset cache. self::delete_all_asset_cache($post_id); self::delete_node_template_asset_cache($post_id); // Enable the builder to take over the post content. self::enable(); // Get the post status. $post_status = get_post_status($post_id); // Publish the post? if ( $publish ) { $is_draft = strstr($post_status, 'draft'); $is_pending = strstr($post_status, 'pending'); if ( current_user_can( 'publish_posts' ) ) { $post_status = $is_draft || $is_pending ? 'publish' : $post_status; } else if( $is_draft ) { $post_status = 'pending'; } } // Update the post with stripped down content. wp_update_post(array( 'ID' => self::get_post_id(), 'post_status' => $post_status, 'post_content' => $editor_content )); // Fire the after action. do_action( 'fl_builder_after_save_layout', $post_id, $publish, $data, $settings ); } /** * Publishes the current builder layout only if the parent post * is still a draft. The layout will be published but the parent * post will remain a draft so the post can be scheduled and the * layout can be viewed while the builder is not active. If the * parent post is already published, nothing happens. * * @since 1.6.1 * @return void */ static public function save_draft() { $post_id = self::get_post_id(); $post_status = get_post_status( $post_id ); if ( strstr( $post_status, 'draft' ) ) { self::save_layout( false ); } } /** * Duplicates a layout for WPML when the copy from original * button has been clicked. * * @since 1.1.7 * @param int $original_post_id * @param int $new_post_id * @return array */ static public function duplicate_wpml_layout($original_post_id = null, $new_post_id = null) { $post_data = self::get_post_data(); $original_post_id = isset($post_data['original_post_id']) ? $post_data['original_post_id'] : $original_post_id; $new_post_id = isset($post_data['post_id']) ? $post_data['post_id'] : $new_post_id; $enabled = get_post_meta($original_post_id, '_fl_builder_enabled', true); $published = self::get_layout_data('published', $original_post_id); $draft = self::get_layout_data('draft', $original_post_id); $response = array( 'enabled' => false, 'has_layout' => false ); if(!empty($enabled)) { update_post_meta($new_post_id, '_fl_builder_enabled', true); $response['enabled'] = true; } if(!empty($published)) { self::update_layout_data($published, 'published', $new_post_id); $response['has_layout'] = true; } if(!empty($draft)) { self::update_layout_data($draft, 'draft', $new_post_id); $response['has_layout'] = true; } return $response; } /** * Returns the type of templates that are enabled. * * @since 1.1.3 * @return string */ static public function get_enabled_templates() { $value = self::get_admin_settings_option( '_fl_builder_enabled_templates', true ); return ! $value ? 'enabled' : $value; } /** * Returns whether the user templates admin UI is enabled. * * @since 1.5.7 * @return string */ static public function user_templates_admin_enabled() { $value = self::get_admin_settings_option( '_fl_builder_user_templates_admin', true ); return ! $value ? 0 : $value; } /** * Checks to see if the current post is a user template. * * @since 1.6.3 * @param string $type The type of user template to check for. * @return bool */ static public function is_post_user_template( $type = null ) { $post = FLBuilderModel::get_post(); if ( ! $post ) { return false; } else if ( 'fl-builder-template' == $post->post_type ) { if ( null === $type ) { return true; } else { $saved_type = self::get_user_template_type( $post->ID ); if ( $saved_type == $type ) { return true; } } } return false; } /** * Saves a user defined template via AJAX. * * @since 1.1.3 * @return void */ static public function save_user_template( $settings = array() ) { // Save the user template post. $post_id = wp_insert_post(array( 'post_title' => $settings['name'], 'post_type' => 'fl-builder-template', 'post_status' => 'publish', 'ping_status' => 'closed', 'comment_status' => 'closed' )); // Set the template type. wp_set_post_terms( $post_id, 'layout', 'fl-builder-template-type' ); // Get the layout data and settings to copy. $data = self::get_layout_data(); $settings = self::get_layout_settings(); // Generate new node ids. $data = self::generate_new_node_ids($data); // Save the template layout data and settings. self::update_layout_data($data, 'published', $post_id); self::update_layout_settings($settings, 'published', $post_id); // Enable the builder for this template. update_post_meta($post_id, '_fl_builder_enabled', true); } /** * Returns data for all user defined templates. * * @since 1.1.3 * @since 1.5.7 Added support for template categories. * @param string $type The type of user template to return. * @return array */ static public function get_user_templates( $type = 'layout' ) { $categorized = array( 'uncategorized' => array( 'name' => _x( 'Uncategorized', 'Default user template category.', 'fl-builder' ), 'templates' => array() ) ); $posts = get_posts( array( 'post_type' => 'fl-builder-template', 'orderby' => 'menu_order title', 'order' => 'ASC', 'posts_per_page' => '-1', 'tax_query' => array( array( 'taxonomy' => 'fl-builder-template-type', 'field' => 'slug', 'terms' => $type ) ) ) ); $templates = array(); // Loop through templates posts and build the templates array. foreach( $posts as $post ) { if ( has_post_thumbnail( $post->ID ) ) { $image_data = wp_get_attachment_image_src( get_post_thumbnail_id( $post->ID ), 'medium' ); $image = $image_data[0]; } else { $image = FL_BUILDER_URL . 'img/templates/blank.jpg'; } $templates[] = array( 'id' => $post->ID, 'name' => $post->post_title, 'image' => $image, 'type' => 'user' ); } // Loop through templates and build the categorized array. foreach ( $templates as $template ) { $cats = wp_get_post_terms( $template['id'], 'fl-builder-template-category' ); if ( 0 === count( $cats ) || is_wp_error( $cats ) ) { $categorized['uncategorized']['templates'][] = $template; } else { Croatian
  • Dutch
  • Estonian
  • Filipino
  • Finnish
  • Frisian
  • Galician
  • Georgian
  • Gujarati
  • Haitian
  • Hausa
  • Hawaiian
  • Hebrew
  • Hmong
  • Hungarian
  • Icelandic
  • Igbo
  • Javanese