Plugin Directory

Changeset 1707679


Ignore:
Timestamp:
08/03/2017 10:50:33 AM (9 years ago)
Author:
hidaka.bizplugin
Message:

1.5.0

Location:
shop-menu/trunk
Files:
1 added
4 edited

Legend:

Unmodified
Added
Removed
  • shop-menu/trunk/readme.txt

    r1118446 r1707679  
    33Donate link:
    44Tags: menu, shop, shortcode, ajax
    5 Requires at least: 3.5
    6 Tested up to: 4.1.1
    7 Stable tag: 1.4.0
     5Requires at least: 4.0
     6Tested up to: 4.8.1
     7Stable tag: 1.5.0
    88License: GPLv2 or later
    99License URI: http://www.gnu.org/licenses/gpl-2.0.html
     
    3434== Changelog ==
    3535
     36= 1.5.0 =
     37* ライブラリを更新しました
     38
    3639= 1.4.0 =
    3740* 商品一覧のデザインを変更しました
  • shop-menu/trunk/shop-menu.php

    r1118446 r1707679  
    44Plugin URI: http://residentbird.main.jp/bizplugin/
    55Description: 商品一覧、メニュー一覧を作成するプラグインです
    6 Version: 1.4.0
     6Version: 1.5.0
    77Author:Hideki Tanaka
    88Author URI: http://residentbird.main.jp/bizplugin/
     
    1717
    1818class SM{
    19     const VERSION = "1.4.0";
     19    const VERSION = "1.5.0";
    2020    const SHORTCODE = "showshopmenu";
    2121    const SHORTCODE_PRICE = "showprice";
  • shop-menu/trunk/wpalchemy/MediaAccess.php

    r725643 r1707679  
    66 * @license     http://en.wikipedia.org/wiki/MIT_License The MIT License
    77 * @package     WPAlchemy
    8  * @version     0.2.1
     8 * @version     0.2.2
    99 * @link        http://github.com/farinspace/wpalchemy/
    1010 * @link        http://farinspace.com/
     
    119119        $this->tab = $name;
    120120
    121         $this;
     121        return $this;
    122122    }
    123123
     
    152152    {
    153153        $groupname = isset($attr['groupname']) ? $attr['groupname'] : $this->groupname ;
    154        
     154
    155155        $attr_default = array
    156156        (
     
    201201
    202202        $tab = ! empty($tab) ? $tab : 'library' ;
    203        
     203
    204204        return 'media-upload.php?post_id=' . $post_ID . '&tab=' . $tab . '&TB_iframe=1';
    205205    }
     
    219219    {
    220220        $groupname = isset($groupname) ? $groupname : $this->groupname ;
    221        
     221
    222222        return $this->button_class_name . '-' . $groupname . ' thickbox';
    223223    }
     
    255255
    256256        $tab = isset($attr['tab']) ? $attr['tab'] : $this->tab ;
    257        
     257
    258258        $attr_default = array
    259259        (
     
    341341                                var url = src ? src : href ;
    342342
    343                                 wpalchemy_mediafield.val(url);
     343                                wpalchemy_mediafield.val(url).trigger('change');
    344344
    345345                                // reset insert button label
     
    366366                        }
    367367
    368                         $('[class*=<?php echo $this->button_class_name; ?>]').live('click', function()
     368                        $('body').on('click', '[class*=<?php echo $this->button_class_name; ?>]', function()
    369369                        {
    370370                            var name = $(this).attr('class').match(/<?php echo $this->button_class_name; ?>-([a-zA-Z0-9_-]*)/i);
     
    411411    }
    412412}
    413 
    414 /* End of file */
  • shop-menu/trunk/wpalchemy/MetaBox.php

    r725643 r1707679  
    22
    33/**
    4  * @author      Dimas Begunoff
    5  * @copyright   Copyright (c) 2009, Dimas Begunoff, http://farinspace.com
    6  * @license     http://en.wikipedia.org/wiki/MIT_License The MIT License
    7  * @package     WPAlchemy
    8  * @version     1.5.1
    9  * @link        http://github.com/farinspace/wpalchemy
    10  * @link        http://farinspace.com
     4 * @author      Dimas Begunoff
     5 * @copyright   Copyright (c) 2009, Dimas Begunoff, http://farinspace.com
     6 * @license     http://en.wikipedia.org/wiki/MIT_License The MIT License
     7 * @package     WPAlchemy
     8 * @version     1.6.1
     9 * @link        http://github.com/farinspace/wpalchemy
     10 * @link        http://farinspace.com
    1111 */
    12 
    13 // todo: perhaps move _global_head and _global_foot locally, when first run
    14 // define a constant to prevent other instances from running again ...
    15 
    16 add_action('admin_head', array('WPAlchemy_MetaBox', '_global_head'));
    17 
    18 add_action('admin_footer', array('WPAlchemy_MetaBox', '_global_foot'));
    1912
    2013define('WPALCHEMY_MODE_ARRAY', 'array');
     
    5548class WPAlchemy_MetaBox
    5649{
    57     /**
    58      * User defined identifier for the meta box, prefix with an underscore to
    59      * prevent option(s) form showing up in the custom fields meta box, this
    60      * option should be used when instantiating the class.
    61      *
    62      * @since   1.0
    63      * @access  public
    64      * @var     string required
    65      */
    66     var $id;
    67 
    68     /**
    69      * Used to set the title of the meta box, this option should be used when
    70      * instantiating the class.
    71      *
    72      * @since   1.0
    73      * @access  public
    74      * @var     string required
    75      * @see     $hide_title
    76      */
    77     var $title = 'Custom Meta';
    78 
    79     /**
    80      * Used to set the meta box content, the contents of your meta box should be
    81      * defined within this file, this option should be used when instantiating
    82      * the class.
    83      *
    84      * @since   1.0
    85      * @access  public
    86      * @var     string required
    87      */
    88     var $template;
    89 
    90     /**
    91      * Used to set the post types that the meta box can appear in, this option
    92      * should be used when instantiating the class.
    93      *
    94      * @since   1.0
    95      * @access  public
    96      * @var     array
    97      */
    98     var $types;
    99 
    100     /**
    101      * @since   1.0
    102      * @access  public
    103      * @var     bool
    104      */
    105     var $context = 'normal';
    106 
    107     /**
    108      * @since   1.0
    109      * @access  public
    110      * @var     bool
    111      */
    112     var $priority = 'high';
    113    
    114     /**
    115      * @since   1.0
    116      * @access  public
    117      * @var     bool
    118      */
    119     var $autosave = TRUE;
    120 
    121     /**
    122      * Used to set how the class does its data storage, data will be stored as
    123      * an associative array in a single meta entry in the wp_postmeta table or
    124      * data can be set and individual entries in the wp_postmeta table, the
    125      * following constants should be used when setting this option,
    126      * WPALCHEMY_MODE_ARRAY (default) and WPALCHEMY_MODE_EXTRACT, this option
    127      * should be used when instantiating the class.
    128      *
    129      * @since   1.2
    130      * @access  public
    131      * @var     string
    132      */
    133     var $mode = WPALCHEMY_MODE_ARRAY;
    134 
    135     /**
    136      * When the mode option is set to WPALCHEMY_MODE_EXTRACT, you have to take
    137      * care to avoid name collisions with other meta entries. Use this option to
    138      * automatically add a prefix to your variables, this option should be used
    139      * when instantiating the class.
    140      *
    141      * @since   1.2
    142      * @access  public
    143      * @var     array
    144      */
    145     var $prefix;
    146 
    147     /**
    148      * @since   1.0
    149      * @access  public
    150      * @var     bool
    151      */
    152     var $exclude_template;
    153 
    154     /**
    155      * @since   1.0
    156      * @access  public
    157      * @var     bool
    158      */
    159     var $exclude_category_id;
    160 
    161     /**
    162      * @since   1.0
    163      * @access  public
    164      * @var     bool
    165      */
    166     var $exclude_category;
    167 
    168     /**
    169      * @since   1.0
    170      * @access  public
    171      * @var     bool
    172      */
    173     var $exclude_tag_id;
    174 
    175     /**
    176      * @since   1.0
    177      * @access  public
    178      * @var     bool
    179      */
    180     var $exclude_tag;
    181 
    182     /**
    183      * @since   1.0
    184      * @access  public
    185      * @var     bool
    186      */
    187     var $exclude_post_id;
    188 
    189     /**
    190      * @since   1.0
    191      * @access  public
    192      * @var     bool
    193      */
    194     var $include_template;
    195 
    196     /**
    197      * @since   1.0
    198      * @access  public
    199      * @var     bool
    200      */
    201     var $include_category_id;
    202 
    203     /**
    204      * @since   1.0
    205      * @access  public
    206      * @var     bool
    207      */
    208     var $include_category;
    209 
    210     /**
    211      * @since   1.0
    212      * @access  public
    213      * @var     bool
    214      */
    215     var $include_tag_id;
    216 
    217     /**
    218      * @since   1.0
    219      * @access  public
    220      * @var     bool
    221      */
    222     var $include_tag;
    223 
    224     /**
    225      * @since   1.0
    226      * @access  public
    227      * @var     bool
    228      */
    229     var $include_post_id;
    230 
    231     /**
    232      * Callback used on the WordPress "admin_init" action, the main benefit is
    233      * that this callback is executed only when the meta box is present, this
    234      * option should be used when instantiating the class.
    235      *
    236      * @since   1.3.4
    237      * @access  public
    238      * @var     string|array optional
    239      */
    240     var $init_action;
    241 
    242     /**
    243      * Callback used to override when the meta box gets displayed, must return
    244      * true or false to determine if the meta box should or should not be
    245      * displayed, this option should be used when instantiating the class.
    246      *
    247      * @since   1.3
    248      * @access  public
    249      * @var     string|array optional
    250      * @param   array $post_id first variable passed to the callback function
    251      * @see     can_output()
    252      */
    253     var $output_filter;
    254 
    255     /**
    256      * Callback used to override or insert meta data before saving, you can halt
    257      * saving by passing back FALSE (return FALSE), this option should be used
    258      * when instantiating the class.
    259      *
    260      * @since   1.3
    261      * @access  public
    262      * @var     string|array optional
    263      * @param   array $meta meta box data, first variable passed to the callback function
    264      * @param   string $post_id second variable passed to the callback function
    265      * @see     $save_action, add_filter()
    266      */
    267     var $save_filter;
    268 
    269     /**
    270      * Callback used to execute custom code after saving, this option should be
    271      * used when instantiating the class.
    272      *
    273      * @since   1.3
    274      * @access  public
    275      * @var     string|array optional
    276      * @param   array $meta meta box data, first variable passed to the callback function
    277      * @param   string $post_id second variable passed to the callback function
    278      * @see     $save_filter, add_filter()
    279      */
    280     var $save_action;
    281 
    282     /**
    283      * Callback used to override or insert STYLE or SCRIPT tags into the head,
    284      * this option should be used when instantiating the class.
    285      *
    286      * @since   1.3
    287      * @access  public
    288      * @var     string|array optional
    289      * @param   array $content current head content, first variable passed to the callback function
    290      * @see     $head_action, add_filter()
    291      */
    292     var $head_filter;
    293 
    294     /**
    295      * Callback used to insert STYLE or SCRIPT tags into the head,
    296      * this option should be used when instantiating the class.
    297      *
    298      * @since   1.3
    299      * @access  public
    300      * @var     string|array optional
    301      * @see     $head_filter, add_action()
    302      */
    303     var $head_action;
    304 
    305     /**
    306      * Callback used to override or insert SCRIPT tags into the footer, this
    307      * option should be used when instantiating the class.
    308      *
    309      * @since   1.3
    310      * @access  public
    311      * @var     string|array optional
    312      * @param   array $content current foot content, first variable passed to the callback function
    313      * @see     $foot_action, add_filter()
    314      */
    315     var $foot_filter;
    316 
    317     /**
    318      * Callback used to insert SCRIPT tags into the footer, this option should
    319      * be used when instantiating the class.
    320      *
    321      * @since   1.3
    322      * @access  public
    323      * @var     string|array optional
    324      * @see     $foot_filter, add_action()
    325      */
    326     var $foot_action;
    327 
    328     /**
    329      * Used to hide the default content editor in a page or post, this option
    330      * should be used when instantiating the class.
    331      *
    332      * @since   1.3
    333      * @access  public
    334      * @var     bool optional
    335      */
    336     var $hide_editor = FALSE;
    337 
    338     /**
    339      * Used in conjunction with the "hide_editor" option, prevents the media
    340      * buttons from also being hidden.
    341      *
    342      * @since   1.5
    343      * @access  public
    344      * @var     bool optional
    345      */
    346     var $use_media_buttons = FALSE;
    347    
    348     /**
    349      * Used to hide the meta box title, this option should be used when
    350      * instantiating the class.
    351      *
    352      * @since   1.3
    353      * @access  public
    354      * @var     bool optional
    355      * @see     $title
    356      */
    357     var $hide_title = FALSE;
    358 
    359     /**
    360      * Used to lock a meta box in place, possible values are: top, bottom,
    361      * before_post_title, after_post_title, this option should be used when
    362      * instantiating the class.
    363      *
    364      * @since       1.3.3
    365      * @access      public
    366      * @var         string optional possible values are: top, bottom, before_post_title, after_post_title
    367      */
    368     var $lock;
    369 
    370     /**
    371      * Used to lock a meta box at top (below the default content editor), this
    372      * option should be used when instantiating the class.
    373      *
    374      * @deprecated  deprecated since version 1.3.3
    375      * @since       1.3
    376      * @access      public
    377      * @var         bool optional
    378      * @see         $lock
    379      */
    380     var $lock_on_top = FALSE;
    381 
    382     /**
    383      * Used to lock a meta box at bottom, this option should be used when
    384      * instantiating the class.
    385      *
    386      * @deprecated  deprecated since version 1.3.3
    387      * @since       1.3
    388      * @access      public
    389      * @var         bool optional
    390      * @see         $lock
    391      */
    392     var $lock_on_bottom = FALSE;
    393 
    394     /**
    395      * Used to set the initial view state of the meta box, possible values are:
    396      * opened, closed, always_opened, this option should be used when
    397      * instantiating the class.
    398      *
    399      * @since   1.3.3
    400      * @access  public
    401      * @var     string optional possible values are: opened, closed, always_opened
    402      */
    403     var $view;
    404 
    405     /**
    406      * Used to hide the show/hide checkbox option from the screen options area,
    407      * this option should be used when instantiating the class.
    408      *
    409      * @since       1.3.4
    410      * @access      public
    411      * @var         bool optional
    412      */
    413     var $hide_screen_option = FALSE;
    414 
    415     // private
    416 
    417     var $meta;
    418     var $name;
    419     var $subname;
    420 
    421     /**
    422      * Used to provide field type hinting
    423      *
    424      * @since   1.3
    425      * @access  private
    426      * @var     string
    427      * @see     the_field()
    428      */
    429     var $hint;
    430 
    431     var $length = 0;
    432     var $current = -1;
    433     var $in_loop = FALSE;
    434     var $in_template = FALSE;
    435     var $group_tag;
    436     var $current_post_id;
    437 
    438     /**
    439      * Used to store current loop details, cleared after loop ends
    440      *
    441      * @since   1.4
    442      * @access  private
    443      * @var     stdClass
    444      * @see     have_fields_and_multi(), have_fields()
    445      */
    446     var $_loop_data;
    447    
    448     function WPAlchemy_MetaBox($arr)
    449     {
    450         $this->_loop_data = new stdClass;
    451        
    452         $this->meta = array();
    453 
    454         $this->types = array('post', 'page');
    455 
    456         if (is_array($arr))
    457         {
    458             foreach ($arr as $n => $v)
    459             {
    460                 $this->$n = $v;
    461             }
    462 
    463             if (empty($this->id)) die('Meta box ID required');
    464 
    465             if (is_numeric($this->id)) die('Meta box ID must be a string');
    466 
    467             if (empty($this->template)) die('Meta box template file required');
    468 
    469             // check for nonarray values
    470            
    471             $exc_inc = array
    472             (
    473                 'exclude_template',
    474                 'exclude_category_id',
    475                 'exclude_category',
    476                 'exclude_tag_id',
    477                 'exclude_tag',
    478                 'exclude_post_id',
    479 
    480                 'include_template',
    481                 'include_category_id',
    482                 'include_category',
    483                 'include_tag_id',
    484                 'include_tag',
    485                 'include_post_id'
    486             );
    487 
    488             foreach ($exc_inc as $v)
    489             {
    490                 // ideally the exclude and include values should be in array form, convert to array otherwise
    491                 if (!empty($this->$v) AND !is_array($this->$v))
    492                 {
    493                     $this->$v = array_map('trim',explode(',',$this->$v));
    494                 }
    495             }
    496 
    497             // convert depreciated variables
    498             if ($this->lock_on_top) $this->lock = WPALCHEMY_LOCK_TOP;
    499             elseif ($this->lock_on_bottom) $this->lock = WPALCHEMY_LOCK_BOTTOM;
    500            
    501             add_action('admin_init', array($this,'_init'));
    502 
    503             // uses the default wordpress-importer plugin hook
    504             add_action('import_post_meta', array($this, '_import'), 10, 3);
    505         }
    506         else
    507         {
    508             die('Associative array parameters required');
    509         }
    510     }
    511 
    512     /**
    513      * Used to correct double serialized data during post/page export/import,
    514      * additionally will try to fix corrupted serialized data by recalculating
    515      * string length values
    516      *
    517      * @since   1.3.16
    518      * @access  private
    519      */
    520     function _import($post_id, $key, $value)
    521     {
    522         if (WPALCHEMY_MODE_ARRAY == $this->mode AND $key == $this->id)
    523         {
    524             // using $wp_import to get access to the raw postmeta data prior to it getting passed
    525             // through "maybe_unserialize()" in "plugins/wordpress-importer/wordpress-importer.php"
    526             // the "import_post_meta" action is called after "maybe_unserialize()"
    527            
    528             global $wp_import;
    529 
    530             foreach ( $wp_import->posts as $post )
    531             {
    532                 if ( $post_id == $post['post_id'] )
    533                 {
    534                     foreach( $post['postmeta'] as $meta )
    535                     {
    536                         if ( $key == $meta['key'] )
    537                         {
    538                             // try to fix corrupted serialized data, specifically "\r\n" being converted to "\n" during wordpress XML export (WXR)
    539                             // "maybe_unserialize()" fixes a wordpress bug which double serializes already serialized data during export/import
    540                             $value = maybe_unserialize( preg_replace( '!s:(\d+):"(.*?)";!es', "'s:'.strlen('$2').':\"$2\";'", stripslashes( $meta['value'] ) ) );
    541                            
    542                             update_post_meta( $post_id, $key,  $value );
    543                         }
    544                     }
    545                 }
    546             }
    547         }
    548     }
    549 
    550     /**
    551      * Used to initialize the meta box, runs on WordPress admin_init action,
    552      * properly calls internal WordPress methods
    553      *
    554      * @since   1.0
    555      * @access  private
    556      */
    557     function _init()
    558     {
    559         // must be creating or editing a post or page
    560         if ( ! WPAlchemy_MetaBox::_is_post() AND ! WPAlchemy_MetaBox::_is_page()) return;
    561        
    562         if ( ! empty($this->output_filter))
    563         {
    564             $this->add_filter('output', $this->output_filter);
    565         }
    566 
    567         if ($this->can_output())
    568         {
    569             foreach ($this->types as $type)
    570             {
    571                 add_meta_box($this->id . '_metabox', $this->title, array($this, '_setup'), $type, $this->context, $this->priority);
    572             }
    573 
    574             add_action('save_post', array($this,'_save'));
    575 
    576             $filters = array('save', 'head', 'foot');
    577 
    578             foreach ($filters as $filter)
    579             {
    580                 $var = $filter . '_filter';
    581 
    582                 if (!empty($this->$var))
    583                 {
    584                     if ('save' == $filter)
    585                     {
    586                         $this->add_filter($filter, $this->$var, 10, 2);
    587                     }
    588                     else
    589                     {
    590                         $this->add_filter($filter, $this->$var);
    591                     }
    592                 }
    593             }
    594 
    595             $actions = array('save', 'head', 'foot', 'init');
    596 
    597             foreach ($actions as $action)
    598             {
    599                 $var = $action . '_action';
    600 
    601                 if (!empty($this->$var))
    602                 {
    603                     if ('save' == $action)
    604                     {
    605                         $this->add_action($action, $this->$var, 10, 2);
    606                     }
    607                     else
    608                     {
    609                         $this->add_action($action, $this->$var);
    610                     }
    611                 }
    612             }
    613 
    614             add_action('admin_head', array($this,'_head'), 11);
    615 
    616             add_action('admin_footer', array($this,'_foot'), 11);
    617 
    618             // action: init
    619             if ($this->has_action('init'))
    620             {
    621                 $this->do_action('init');
    622             }
    623         }
    624     }
    625 
    626     /**
    627      * Used to insert STYLE or SCRIPT tags into the head, called on WordPress
    628      * admin_head action.
    629      *
    630      * @since   1.3
    631      * @access  private
    632      * @see     _foot()
    633      */
    634     function _head()
    635     {
    636         $content = NULL;
    637 
    638         ob_start();
    639 
    640         ?>
    641         <style type="text/css">
    642             <?php if ($this->hide_editor) { ?> #wp-content-editor-container, #post-status-info, <?php if ($this->use_media_buttons) { ?> #content-html, #content-tmce<?php } else { ?> #wp-content-wrap<?php } ?> { display:none; } <?php } ?>
    643         </style>
    644         <?php
    645 
    646         $content = ob_get_contents();
    647 
    648         ob_end_clean();
    649 
    650         // filter: head
    651         if ($this->has_filter('head'))
    652         {
    653             $content = $this->apply_filters('head', $content);
    654         }
    655 
    656         echo $content;
    657 
    658         // action: head
    659         if ($this->has_action('head'))
    660         {
    661             $this->do_action('head');
    662         }
    663     }
    664 
    665     /**
    666      * Used to insert SCRIPT tags into the footer, called on WordPress
    667      * admin_footer action.
    668      *
    669      * @since   1.3
    670      * @access  private
    671      * @see     _head()
    672      */
    673     function _foot()
    674     {
    675         $content = NULL;
    676 
    677         if
    678         (
    679             $this->lock OR
    680             $this->hide_title OR
    681             $this->view OR
    682             $this->hide_screen_option
    683         )
    684         {
    685             ob_start();
    686 
    687             ?>
    688             <script type="text/javascript">
    689             /* <![CDATA[ */
    690             (function($){ /* not using jQuery ondomready, code runs right away in footer */
    691 
    692                 var mb_id = '<?php echo $this->id; ?>';
    693                 var mb = $('#' + mb_id + '_metabox');
    694 
    695                 <?php if (WPALCHEMY_LOCK_TOP == $this->lock): ?>
    696                 <?php if ('side' == $this->context): ?>
    697                 var id = 'wpalchemy-side-top';
    698                 if ( ! $('#'+id).length)
    699                 {
    700                     $('<div></div>').attr('id',id).prependTo('#side-info-column');
    701                 }
    702                 <?php else: ?>
    703                 var id = 'wpalchemy-content-top';
    704                 if ( ! $('#'+id).length)
    705                 {
    706                     $('<div></div>').attr('id',id).insertAfter('#postdiv, #postdivrich');
    707                 }
    708                 <?php endif; ?>
    709                 $('#'+id).append(mb);
    710                 <?php elseif (WPALCHEMY_LOCK_BOTTOM == $this->lock): ?>
    711                 <?php if ('side' == $this->context): ?>
    712                 var id = 'wpalchemy-side-bottom';
    713                 if ( ! $('#'+id).length)
    714                 {
    715                     $('<div></div>').attr('id',id).appendTo('#side-info-column');
    716                 }
    717                 <?php else: ?>
    718                 if ( ! $('#advanced-sortables').children().length)
    719                 {
    720                     $('#advanced-sortables').css('display','none');
    721                 }
    722 
    723                 var id = 'wpalchemy-content-bottom';
    724                 if ( ! $('#'+id).length)
    725                 {
    726                     $('<div></div>').attr('id',id).insertAfter('#advanced-sortables');
    727                 }
    728                 <?php endif; ?>
    729                 $('#'+id).append(mb);
    730                 <?php elseif (WPALCHEMY_LOCK_BEFORE_POST_TITLE == $this->lock): ?>
    731                 <?php if ('side' != $this->context): ?>
    732                 var id = 'wpalchemy-content-bpt';
    733                 if ( ! $('#'+id).length)
    734                 {
    735                     $('<div></div>').attr('id',id).prependTo('#post-body-content');
    736                 }
    737                 $('#'+id).append(mb);
    738                 <?php endif; ?>
    739                 <?php elseif (WPALCHEMY_LOCK_AFTER_POST_TITLE == $this->lock): ?>
    740                 <?php if ('side' != $this->context): ?>
    741                 var id = 'wpalchemy-content-apt';
    742                 if ( ! $('#'+id).length)
    743                 {
    744                     $('<div></div>').attr('id',id).insertAfter('#titlediv');
    745                 }
    746                 $('#'+id).append(mb);
    747                 <?php endif; ?>
    748                 <?php endif; ?>
    749 
    750                 <?php if ( ! empty($this->lock)): ?>
    751                 $('.hndle', mb).css('cursor','pointer');
    752                 $('.handlediv', mb).remove();
    753                 <?php endif; ?>
    754 
    755                 <?php if ($this->hide_title): ?>
    756                 $('.hndle', mb).remove();
    757                 $('.handlediv', mb).remove();
    758                 mb.removeClass('closed'); /* start opened */
    759                 <?php endif; ?>
    760 
    761                 <?php if (WPALCHEMY_VIEW_START_OPENED == $this->view): ?>
    762                 mb.removeClass('closed');
    763                 <?php elseif (WPALCHEMY_VIEW_START_CLOSED == $this->view): ?>
    764                 mb.addClass('closed');
    765                 <?php elseif (WPALCHEMY_VIEW_ALWAYS_OPENED == $this->view): ?>
    766                 /* todo: need to find a way to add this script block below, load-scripts.php?... */
    767                 var h3 = mb.children('h3');
    768                 setTimeout(function(){ h3.unbind('click'); }, 1000);
    769                 $('.handlediv', mb).remove();
    770                 mb.removeClass('closed'); /* start opened */
    771                 $('.hndle', mb).css('cursor','auto');
    772                 <?php endif; ?>
    773 
    774                 <?php if ($this->hide_screen_option): ?>
    775                     $('.metabox-prefs label[for='+ mb_id +'_metabox-hide]').remove();
    776                 <?php endif; ?>
    777 
    778                 mb = null;
    779 
    780             })(jQuery);
    781             /* ]]> */
    782             </script>
    783             <?php
    784 
    785             $content = ob_get_contents();
    786 
    787             ob_end_clean();
    788         }
    789        
    790         // filter: foot
    791         if ($this->has_filter('foot'))
    792         {
    793             $content = $this->apply_filters('foot', $content);
    794         }
    795 
    796         echo $content;
    797 
    798         // action: foot
    799         if ($this->has_action('foot'))
    800         {
    801             $this->do_action('foot');
    802         }
    803     }
    804 
    805     /**
    806      * Used to setup the meta box content template
    807      *
    808      * @since   1.0
    809      * @access  private
    810      * @see     _init()
    811      */
    812     function _setup()
    813     {
    814         $this->in_template = TRUE;
    815        
    816         // also make current post data available
    817         global $post;
    818 
    819         // shortcuts
    820         $mb =& $this;
    821         $metabox =& $this;
    822         $id = $this->id;
    823         $meta = $this->_meta(NULL, TRUE);
    824 
    825         // use include because users may want to use one templete for multiple meta boxes
    826         include $this->template;
    827      
    828         // create a nonce for verification
    829         echo '<input type="hidden" name="'. $this->id .'_nonce" value="' . wp_create_nonce($this->id) . '" />';
    830 
    831         $this->in_template = FALSE;
    832     }
    833 
    834     /**
    835      * Used to properly prefix the filter tag, the tag is unique to the meta
    836      * box instance
    837      *
    838      * @since   1.3
    839      * @access  private
    840      * @param   string $tag name of the filter
    841      * @return  string uniquely prefixed tag name
    842      */
    843     function _get_filter_tag($tag)
    844     {
    845         $prefix = 'wpalchemy_filter_' . $this->id . '_';
    846         $prefix = preg_replace('/_+/', '_', $prefix);
    847 
    848         $tag = preg_replace('/^'. $prefix .'/i', '', $tag);
    849         return $prefix . $tag;
    850     }
    851 
    852     /**
    853      * Uses WordPress add_filter() function, see WordPress add_filter()
    854      *
    855      * @since   1.3
    856      * @access  public
    857      * @link    http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L65
    858      */
    859     function add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1)
    860     {
    861         $tag = $this->_get_filter_tag($tag);;
    862         add_filter($tag, $function_to_add, $priority, $accepted_args);
    863     }
    864 
    865     /**
    866      * Uses WordPress has_filter() function, see WordPress has_filter()
    867      *
    868      * @since   1.3
    869      * @access  public
    870      * @link    http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L86
    871      */
    872     function has_filter($tag, $function_to_check = FALSE)
    873     {
    874         $tag = $this->_get_filter_tag($tag);
    875         return has_filter($tag, $function_to_check);
    876     }
    877 
    878     /**
    879      * Uses WordPress apply_filters() function, see WordPress apply_filters()
    880      *
    881      * @since   1.3
    882      * @access  public
    883      * @link    http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L134
    884      */
    885     function apply_filters($tag, $value)
    886     {
    887         $args = func_get_args();
    888         $args[0] = $this->_get_filter_tag($tag);
    889         return call_user_func_array('apply_filters', $args);
    890     }
    891 
    892     /**
    893      * Uses WordPress remove_filter() function, see WordPress remove_filter()
    894      *
    895      * @since   1.3
    896      * @access  public
    897      * @link    http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L250
    898      */
    899     function remove_filter($tag, $function_to_remove, $priority = 10, $accepted_args = 1)
    900     {
    901         $tag = $this->_get_filter_tag($tag);
    902         return remove_filter($tag, $function_to_remove, $priority, $accepted_args);
    903     }
    904 
    905     /**
    906      * Used to properly prefix the action tag, the tag is unique to the meta
    907      * box instance
    908      *
    909      * @since   1.3
    910      * @access  private
    911      * @param   string $tag name of the action
    912      * @return  string uniquely prefixed tag name
    913      */
    914     function _get_action_tag($tag)
    915     {
    916         $prefix = 'wpalchemy_action_' . $this->id . '_';
    917         $prefix = preg_replace('/_+/', '_', $prefix);
    918 
    919         $tag = preg_replace('/^'. $prefix .'/i', '', $tag);
    920         return $prefix . $tag;
    921     }
    922 
    923     /**
    924      * Uses WordPress add_action() function, see WordPress add_action()
    925      *
    926      * @since   1.3
    927      * @access  public
    928      * @link    http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L324
    929      */
    930     function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1)
    931     {
    932         $tag = $this->_get_action_tag($tag);
    933         add_action($tag, $function_to_add, $priority, $accepted_args);
    934     }
    935 
    936     /**
    937      * Uses WordPress has_action() function, see WordPress has_action()
    938      *
    939      * @since   1.3
    940      * @access  public
    941      * @link    http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L492
    942      */
    943     function has_action($tag, $function_to_check = FALSE)
    944     {
    945         $tag = $this->_get_action_tag($tag);
    946         return has_action($tag, $function_to_check);
    947     }
    948 
    949     /**
    950      * Uses WordPress remove_action() function, see WordPress remove_action()
    951      *
    952      * @since   1.3
    953      * @access  public
    954      * @link    http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L513
    955      */
    956     function remove_action($tag, $function_to_remove, $priority = 10, $accepted_args = 1)
    957     {
    958         $tag = $this->_get_action_tag($tag);
    959         return remove_action($tag, $function_to_remove, $priority, $accepted_args);
    960     }
    961 
    962     /**
    963      * Uses WordPress do_action() function, see WordPress do_action()
    964      * @since   1.3
    965      * @access  public
    966      * @link    http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L352
    967      */
    968     function do_action($tag, $arg = '')
    969     {
    970         $args = func_get_args();
    971         $args[0] = $this->_get_action_tag($tag);
    972         return call_user_func_array('do_action', $args);
    973     }
    974 
    975     /**
    976      * Used to check if creating a new post or editing one
    977      *
    978      * @static
    979      * @since   1.3.7
    980      * @access  private
    981      * @return  bool
    982      * @see     _is_page()
    983      */
    984     function _is_post()
    985     {
    986         if ('post' == WPAlchemy_MetaBox::_is_post_or_page())
    987         {
    988             return TRUE;
    989         }
    990 
    991         return FALSE;
    992     }
    993 
    994     /**
    995      * Used to check if creating a new page or editing one
    996      *
    997      * @static
    998      * @since   1.3.7
    999      * @access  private
    1000      * @return  bool
    1001      * @see     _is_post()
    1002      */
    1003     function _is_page()
    1004     {
    1005         if ('page' == WPAlchemy_MetaBox::_is_post_or_page())
    1006         {
    1007             return TRUE;
    1008         }
    1009 
    1010         return FALSE;
    1011     }
    1012 
    1013     /**
    1014      * Used to check if creating or editing a post or page
    1015      *
    1016      * @static
    1017      * @since   1.3.8
    1018      * @access  private
    1019      * @return  string "post" or "page"
    1020      * @see     _is_post(), _is_page()
    1021      */
    1022     function _is_post_or_page()
    1023     {
    1024         $post_type = WPAlchemy_MetaBox::_get_current_post_type();
    1025 
    1026         if (isset($post_type))
    1027         {
    1028             if ('page' == $post_type)
    1029             {
    1030                 return 'page';
    1031             }
    1032             else
    1033             {
    1034                 return 'post';
    1035             }
    1036         }
    1037 
    1038         return NULL;
    1039     }
    1040 
    1041     /**
    1042      * Used to check for the current post type, works when creating or editing a
    1043      * new post, page or custom post type.
    1044      *
    1045      * @static
    1046      * @since   1.4.6
    1047      * @return  string [custom_post_type], page or post
    1048      */
    1049     function _get_current_post_type()
    1050     {
    1051         $uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : NULL ;
    1052 
    1053         if ( isset( $uri ) )
    1054         {
    1055             $uri_parts = parse_url($uri);
    1056 
    1057             $file = basename($uri_parts['path']);
    1058 
    1059             if ($uri AND in_array($file, array('post.php', 'post-new.php')))
    1060             {
    1061                 $post_id = WPAlchemy_MetaBox::_get_post_id();
    1062 
    1063                 $post_type = isset($_GET['post_type']) ? $_GET['post_type'] : NULL ;
    1064 
    1065                 $post_type = $post_id ? get_post_type($post_id) : $post_type ;
    1066 
    1067                 if (isset($post_type))
    1068                 {
    1069                     return $post_type;
    1070                 }
    1071                 else
    1072                 {
    1073                     // because of the 'post.php' and 'post-new.php' checks above, we can default to 'post'
    1074                     return 'post';
    1075                 }
    1076             }
    1077         }
    1078 
    1079         return NULL;
    1080     }
    1081 
    1082     /**
    1083      * Used to get the current post id.
    1084      *
    1085      * @static
    1086      * @since   1.4.8
    1087      * @return  int post ID
    1088      */
    1089     function _get_post_id()
    1090     {
    1091         global $post;
    1092 
    1093         $p_post_id = isset($_POST['post_ID']) ? $_POST['post_ID'] : null ;
    1094 
    1095         $g_post_id = isset($_GET['post']) ? $_GET['post'] : null ;
    1096 
    1097         $post_id = $g_post_id ? $g_post_id : $p_post_id ;
    1098 
    1099         $post_id = isset($post->ID) ? $post->ID : $post_id ;
    1100 
    1101         if (isset($post_id))
    1102         {
    1103             return (integer) $post_id;
    1104         }
    1105        
    1106         return null;
    1107     }
    1108 
    1109     /**
    1110      * @since   1.0
    1111      */
    1112     function can_output()
    1113     {
    1114         $post_id = WPAlchemy_MetaBox::_get_post_id();
    1115 
    1116         if (!empty($this->exclude_template) OR !empty($this->include_template))
    1117         {
    1118             $template_file = get_post_meta($post_id,'_wp_page_template',TRUE);
    1119         }
    1120 
    1121         if
    1122         (
    1123             !empty($this->exclude_category) OR
    1124             !empty($this->exclude_category_id) OR
    1125             !empty($this->include_category) OR
    1126             !empty($this->include_category_id)
    1127         )
    1128         {
    1129             $categories = wp_get_post_categories($post_id,'fields=all');
    1130         }
    1131 
    1132         if
    1133         (
    1134             !empty($this->exclude_tag) OR
    1135             !empty($this->exclude_tag_id) OR
    1136             !empty($this->include_tag) OR
    1137             !empty($this->include_tag_id)
    1138         )
    1139         {
    1140             $tags = wp_get_post_tags($post_id);
    1141         }
    1142 
    1143         // processing order: "exclude" then "include"
    1144         // processing order: "template" then "category" then "post"
    1145 
    1146         $can_output = TRUE; // include all
    1147 
    1148         if
    1149         (
    1150             !empty($this->exclude_template) OR
    1151             !empty($this->exclude_category_id) OR
    1152             !empty($this->exclude_category) OR
    1153             !empty($this->exclude_tag_id) OR
    1154             !empty($this->exclude_tag) OR
    1155             !empty($this->exclude_post_id) OR
    1156             !empty($this->include_template) OR
    1157             !empty($this->include_category_id) OR
    1158             !empty($this->include_category) OR
    1159             !empty($this->include_tag_id) OR
    1160             !empty($this->include_tag) OR
    1161             !empty($this->include_post_id)
    1162         )
    1163         {
    1164             if (!empty($this->exclude_template))
    1165             {
    1166                 if (in_array($template_file,$this->exclude_template))
    1167                 {
    1168                     $can_output = FALSE;
    1169                 }
    1170             }
    1171 
    1172             if (!empty($this->exclude_category_id))
    1173             {
    1174                 foreach ($categories as $cat)
    1175                 {
    1176                     if (in_array($cat->term_id,$this->exclude_category_id))
    1177                     {
    1178                         $can_output = FALSE;
    1179                         break;
    1180                     }
    1181                 }
    1182             }
    1183 
    1184             if (!empty($this->exclude_category))
    1185             {
    1186                 foreach ($categories as $cat)
    1187                 {
    1188                     if
    1189                     (
    1190                         in_array($cat->slug,$this->exclude_category) OR
    1191                         in_array($cat->name,$this->exclude_category)
    1192                     )
    1193                     {
    1194                         $can_output = FALSE;
    1195                         break;
    1196                     }
    1197                 }
    1198             }
    1199 
    1200             if (!empty($this->exclude_tag_id))
    1201             {
    1202                 foreach ($tags as $tag)
    1203                 {
    1204                     if (in_array($tag->term_id,$this->exclude_tag_id))
    1205                     {
    1206                         $can_output = FALSE;
    1207                         break;
    1208                     }
    1209                 }
    1210             }
    1211 
    1212             if (!empty($this->exclude_tag))
    1213             {
    1214                 foreach ($tags as $tag)
    1215                 {
    1216                     if
    1217                     (
    1218                         in_array($tag->slug,$this->exclude_tag) OR
    1219                         in_array($tag->name,$this->exclude_tag)
    1220                     )
    1221                     {
    1222                         $can_output = FALSE;
    1223                         break;
    1224                     }
    1225                 }
    1226             }
    1227 
    1228             if (!empty($this->exclude_post_id))
    1229             {
    1230                 if (in_array($post_id,$this->exclude_post_id))
    1231                 {
    1232                     $can_output = FALSE;
    1233                 }
    1234             }
    1235 
    1236             // excludes are not set use "include only" mode
    1237 
    1238             if
    1239             (
    1240                 empty($this->exclude_template) AND
    1241                 empty($this->exclude_category_id) AND
    1242                 empty($this->exclude_category) AND
    1243                 empty($this->exclude_tag_id) AND
    1244                 empty($this->exclude_tag) AND
    1245                 empty($this->exclude_post_id)
    1246             )
    1247             {
    1248                 $can_output = FALSE;
    1249             }
    1250 
    1251             if (!empty($this->include_template))
    1252             {
    1253                 if (in_array($template_file,$this->include_template))
    1254                 {
    1255                     $can_output = TRUE;
    1256                 }
    1257             }
    1258 
    1259             if (!empty($this->include_category_id))
    1260             {
    1261                 foreach ($categories as $cat)
    1262                 {
    1263                     if (in_array($cat->term_id,$this->include_category_id))
    1264                     {
    1265                         $can_output = TRUE;
    1266                         break;
    1267                     }
    1268                 }
    1269             }
    1270 
    1271             if (!empty($this->include_category))
    1272             {
    1273                 foreach ($categories as $cat)
    1274                 {
    1275                     if
    1276                     (
    1277                         in_array($cat->slug,$this->include_category) OR
    1278                         in_array($cat->name,$this->include_category)
    1279                     )
    1280                     {
    1281                         $can_output = TRUE;
    1282                         break;
    1283                     }
    1284                 }
    1285             }
    1286 
    1287             if (!empty($this->include_tag_id))
    1288             {
    1289                 foreach ($tags as $tag)
    1290                 {
    1291                     if (in_array($tag->term_id,$this->include_tag_id))
    1292                     {
    1293                         $can_output = TRUE;
    1294                         break;
    1295                     }
    1296                 }
    1297             }
    1298 
    1299             if (!empty($this->include_tag))
    1300             {
    1301                 foreach ($tags as $tag)
    1302                 {
    1303                     if
    1304                     (
    1305                         in_array($tag->slug,$this->include_tag) OR
    1306                         in_array($tag->name,$this->include_tag)
    1307                     )
    1308                     {
    1309                         $can_output = TRUE;
    1310                         break;
    1311                     }
    1312                 }
    1313             }
    1314 
    1315             if (!empty($this->include_post_id))
    1316             {
    1317                 if (in_array($post_id,$this->include_post_id))
    1318                 {
    1319                     $can_output = TRUE;
    1320                 }
    1321             }
    1322         }
    1323 
    1324         $post_type = WPAlchemy_MetaBox::_get_current_post_type();
    1325 
    1326         if (isset($post_type) AND ! in_array($post_type, $this->types))
    1327         {
    1328             $can_output = FALSE;
    1329         }
    1330 
    1331         // filter: output (can_output)
    1332         if ($this->has_filter('output'))
    1333         {
    1334             $can_output = $this->apply_filters('output', $post_id);
    1335         }
    1336 
    1337         return $can_output;
    1338     }
    1339 
    1340     /**
    1341      * Used to insert global STYLE or SCRIPT tags into the head, called on
    1342      * WordPress admin_footer action.
    1343      *
    1344      * @static
    1345      * @since   1.3
    1346      * @access  private
    1347      * @see     _global_foot()
    1348      */
    1349     function _global_head()
    1350     {
    1351         // must be creating or editing a post or page
    1352         if ( ! WPAlchemy_MetaBox::_is_post() AND ! WPAlchemy_MetaBox::_is_page()) return;
    1353 
    1354         // todo: you're assuming people will want to use this exact functionality
    1355         // consider giving a developer access to change this via hooks/callbacks
    1356 
    1357         // include javascript for special functionality
    1358         ?><style type="text/css"> .wpa_group.tocopy { display:none; } </style>
    1359         <script type="text/javascript">
    1360         /* <![CDATA[ */
    1361         jQuery(function($)
    1362         {
    1363             $(document).click(function(e)
    1364             {       
    1365                 var elem = $(e.target);
    1366 
    1367                 if (elem.attr('class') && elem.filter('[class*=dodelete]').length)
    1368                 {
    1369                     e.preventDefault();
    1370 
    1371                     var p = elem.parents('.postbox'); /*wp*/
    1372 
    1373                     var the_name = elem.attr('class').match(/dodelete-([a-zA-Z0-9_-]*)/i);
    1374 
    1375                     the_name = (the_name && the_name[1]) ? the_name[1] : null ;
    1376 
    1377                     /* todo: expose and allow editing of this message */
    1378                     if (confirm('This action can not be undone, are you sure?'))
    1379                     {
    1380                         if (the_name)
    1381                         {
    1382                             $('.wpa_group-'+ the_name, p).not('.tocopy').remove();
    1383                         }
    1384                         else
    1385                         {
    1386                             elem.parents('.wpa_group').remove();
    1387                         }
    1388 
    1389                         the_name = elem.parents('.wpa_group').attr('class').match(/wpa_group-([a-zA-Z0-9_-]*)/i)[1];
    1390 
    1391                         checkLoopLimit(the_name);
    1392 
    1393                         $.wpalchemy.trigger('wpa_delete');
    1394                     }
    1395                 }
    1396             });
    1397 
    1398             $('[class*=docopy-]').click(function(e)
    1399             {
    1400                 e.preventDefault();
    1401 
    1402                 var p = $(this).parents('.postbox'); /*wp*/
    1403 
    1404                 var the_name = $(this).attr('class').match(/docopy-([a-zA-Z0-9_-]*)/i)[1];
    1405 
    1406                 var the_group = $('.wpa_group-'+ the_name +':first.tocopy', p);
    1407                
    1408                 var the_clone = the_group.clone().removeClass('tocopy last');
    1409 
    1410                 var the_props = ['name', 'id', 'for', 'class'];
    1411 
    1412                 the_group.find('*').each(function(i, elem)
    1413                 {
    1414                     for (var j = 0; j < the_props.length; j++)
    1415                     {
    1416                         var the_prop = $(elem).attr(the_props[j]);
    1417 
    1418                         if (the_prop)
    1419                         {
    1420                             var the_match = the_prop.match(/\[(\d+)\]/i);
    1421 
    1422                             if (the_match)
    1423                             {
    1424                                 the_prop = the_prop.replace(the_match[0],'['+ (+the_match[1]+1) +']');
    1425 
    1426                                 $(elem).attr(the_props[j], the_prop);
    1427                             }
    1428 
    1429                             the_match = null;
    1430 
    1431                             // todo: this may prove to be too broad of a search
    1432                             the_match = the_prop.match(/n(\d+)/i);
    1433 
    1434                             if (the_match)
    1435                             {
    1436                                 the_prop = the_prop.replace(the_match[0], 'n' + (+the_match[1]+1));
    1437 
    1438                                 $(elem).attr(the_props[j], the_prop);
    1439                             }
    1440                         }
    1441                     }
    1442                 });
    1443 
    1444                 if ($(this).hasClass('ontop'))
    1445                 {
    1446                     $('.wpa_group-'+ the_name +':first', p).before(the_clone);
    1447                 }
    1448                 else
    1449                 {
    1450                     the_group.before(the_clone);
    1451                 }
    1452 
    1453                 checkLoopLimit(the_name);
    1454 
    1455                 $.wpalchemy.trigger('wpa_copy', [the_clone]);
    1456             });
    1457 
    1458             function checkLoopLimit(name)
    1459             {
    1460                 var elem = $('.docopy-' + name);
    1461 
    1462                 var the_class = $('.wpa_loop-' + name).attr('class');
    1463 
    1464                 if (the_class)
    1465                 {
    1466                     var the_match = the_class.match(/wpa_loop_limit-([0-9]*)/i);
    1467 
    1468                     if (the_match)
    1469                     {
    1470                         var the_limit = the_match[1];
    1471 
    1472                         if ($('.wpa_group-' + name).not('.wpa_group.tocopy').length >= the_limit)
    1473                         {
    1474                             elem.hide();
    1475                         }
    1476                         else
    1477                         {
    1478                             elem.show();
    1479                         }
    1480                     }
    1481                 }
    1482             }
    1483            
    1484             /* do an initial limit check, show or hide buttons */
    1485             $('[class*=docopy-]').each(function()
    1486             {
    1487                 var the_name = $(this).attr('class').match(/docopy-([a-zA-Z0-9_-]*)/i)[1];
    1488 
    1489                 checkLoopLimit(the_name);
    1490             });
    1491         });
    1492         /* ]]> */
    1493         </script>
    1494         <?php
    1495     }
    1496 
    1497     /**
    1498      * Used to insert global SCRIPT tags into the footer, called on WordPress
    1499      * admin_footer action.
    1500      *
    1501      * @static
    1502      * @since   1.3
    1503      * @access  private
    1504      * @see     _global_head()
    1505      */
    1506     function _global_foot()
    1507     {
    1508         // must be creating or editing a post or page
    1509         if ( ! WPAlchemy_MetaBox::_is_post() AND ! WPAlchemy_MetaBox::_is_page()) return;
    1510 
    1511         ?>
    1512         <script type="text/javascript">
    1513         /* <![CDATA[ */
    1514         (function($){ /* not using jQuery ondomready, code runs right away in footer */
    1515 
    1516             /* use a global dom element to attach events to */
    1517             $.wpalchemy = $('<div></div>').attr('id','wpalchemy').appendTo('body');
    1518 
    1519         })(jQuery);
    1520         /* ]]> */
    1521         </script>
    1522         <?php
    1523     }
    1524 
    1525     /**
    1526      * Gets the meta data for a meta box
    1527      *
    1528      * @since   1.0
    1529      * @access  public
    1530      * @param   int $post_id optional post ID for which to retrieve the meta data
    1531      * @return  array
    1532      * @see     _meta
    1533      */
    1534     function the_meta($post_id = NULL)
    1535     {
    1536         return $this->_meta($post_id);
    1537     }
    1538 
    1539     /**
    1540      * Gets the meta data for a meta box
    1541      *
    1542      * Internal method calls will typically bypass the data retrieval and will
    1543      * immediately return the current meta data
    1544      *
    1545      * @since   1.3
    1546      * @access  private
    1547      * @param   int $post_id optional post ID for which to retrieve the meta data
    1548      * @param   bool $internal optional boolean if internally calling
    1549      * @return  array
    1550      * @see     the_meta()
    1551      */
    1552     function _meta($post_id = NULL, $internal = FALSE)
    1553     {
    1554         if ( ! is_numeric($post_id))
    1555         {
    1556             if ($internal AND $this->current_post_id)
    1557             {
    1558                 $post_id = $this->current_post_id;
    1559             }
    1560             else
    1561             {
    1562                 global $post;
    1563 
    1564                 $post_id = $post->ID;
    1565             }
    1566         }
    1567 
    1568         // this allows multiple internal calls to _meta() without having to fetch data everytime
    1569         if ($internal AND !empty($this->meta) AND $this->current_post_id == $post_id) return $this->meta;
    1570 
    1571         $this->current_post_id = $post_id;
    1572 
    1573         // WPALCHEMY_MODE_ARRAY
    1574 
    1575         $meta = get_post_meta($post_id, $this->id, TRUE);
    1576 
    1577         // WPALCHEMY_MODE_EXTRACT
    1578 
    1579         $fields = get_post_meta($post_id, $this->id . '_fields', TRUE);
    1580 
    1581         if ( ! empty($fields) AND is_array($fields))
    1582         {
    1583             $meta = array();
    1584            
    1585             foreach ($fields as $field)
    1586             {
    1587                 $field_noprefix = preg_replace('/^' . $this->prefix . '/i', '', $field);
    1588                 $meta[$field_noprefix] = get_post_meta($post_id, $field, TRUE);
    1589             }
    1590         }
    1591 
    1592         $this->meta = $meta;
    1593 
    1594         return $this->meta;
    1595     }
    1596 
    1597     // user can also use the_ID(), php functions are case-insensitive
    1598     /**
    1599      * @since   1.0
    1600      * @access  public
    1601      */
    1602     function the_id()
    1603     {
    1604         echo $this->get_the_id();
    1605     }
    1606 
    1607     /**
    1608      * @since   1.0
    1609      * @access  public
    1610      */
    1611     function get_the_id()
    1612     {
    1613         return $this->id;
    1614     }
    1615 
    1616     /**
    1617      * @since   1.0
    1618      * @access  public
    1619      */
    1620     function the_field($n, $hint = NULL)
    1621     {
    1622         if ($this->in_loop) $this->subname = $n;
    1623         else $this->name = $n;
    1624 
    1625         $this->hint = $hint;
    1626     }
    1627 
    1628     /**
    1629      * @since   1.0
    1630      * @access  public
    1631      */
    1632     function have_value($n = NULL)
    1633     {
    1634         if ($this->get_the_value($n)) return TRUE;
    1635        
    1636         return FALSE;
    1637     }
    1638 
    1639     /**
    1640      * @since   1.0
    1641      * @access  public
    1642      */
    1643     function the_value($n = NULL)
    1644     {
    1645         echo $this->get_the_value($n);
    1646     }
    1647 
    1648     /**
    1649      * @since   1.0
    1650      * @access  public
    1651      */
    1652     function get_the_value($n = NULL, $collection = FALSE)
    1653     {
    1654         $this->_meta(NULL, TRUE);
    1655 
    1656         $value = null;
    1657 
    1658         if ($this->in_loop)
    1659         {
    1660             if(isset($this->meta[$this->name]))
    1661             {
    1662                 $n = is_null($n) ? $this->subname : $n ;
    1663 
    1664                 if(!is_null($n))
    1665                 {
    1666                     if ($collection)
    1667                     {
    1668                         if(isset($this->meta[$this->name][$this->current]))
    1669                         {
    1670                             $value = $this->meta[$this->name][$this->current];
    1671                         }
    1672                     }
    1673                     else
    1674                     {
    1675                         if(isset($this->meta[$this->name][$this->current][$n]))
    1676                         {
    1677                             $value = $this->meta[$this->name][$this->current][$n];
    1678                         }
    1679                     }
    1680                 }
    1681                 else
    1682                 {
    1683                     if ($collection)
    1684                     {
    1685                         if(isset($this->meta[$this->name]))
    1686                         {
    1687                             $value = $this->meta[$this->name];
    1688                         }
    1689                     }
    1690                     else
    1691                     {
    1692                         if(isset($this->meta[$this->name][$this->current]))
    1693                         {
    1694                             $value = $this->meta[$this->name][$this->current];
    1695                         }
    1696                     }
    1697                 }
    1698             }
    1699         }
    1700         else
    1701         {
    1702             $n = is_null($n) ? $this->name : $n ;
    1703 
    1704             if(isset($this->meta[$n]))
    1705             {
    1706                 $value = $this->meta[$n];
    1707             }
    1708         }
    1709 
    1710         if (is_string($value) || is_numeric($value))
    1711         {
    1712             if ($this->in_template)
    1713             {
    1714                 return htmlentities($value, ENT_QUOTES, 'UTF-8');
    1715             }
    1716             else
    1717             {
    1718                 // http://wordpress.org/support/topic/call-function-called-by-embed-shortcode-direct
    1719                 // http://phpdoc.wordpress.org/trunk/WordPress/Embed/WP_Embed.html#run_shortcode
    1720 
    1721                 global $wp_embed;
    1722 
    1723                 return do_shortcode($wp_embed->run_shortcode($value));
    1724             }
    1725         }
    1726         else
    1727         {
    1728             // value can sometimes be an array
    1729             return $value;
    1730         }
    1731     }
    1732 
    1733     /**
    1734      * @since   1.0
    1735      * @access  public
    1736      */
    1737     function the_name($n = NULL)
    1738     {
    1739         echo $this->get_the_name($n);
    1740     }
    1741 
    1742     /**
    1743      * @since   1.0
    1744      * @access  public
    1745      */
    1746     function get_the_name($n = NULL)
    1747     {
    1748         if (!$this->in_template AND $this->mode == WPALCHEMY_MODE_EXTRACT)
    1749         {
    1750             return $this->prefix . str_replace($this->prefix, '', is_null($n) ? $this->name : $n);
    1751         }
    1752 
    1753         if ($this->in_loop)
    1754         {
    1755             $n = is_null($n) ? $this->subname : $n ;
    1756 
    1757             if (!is_null($n)) $the_field = $this->id . '[' . $this->name . '][' . $this->current . '][' . $n . ']' ;
    1758 
    1759             else $the_field = $this->id . '[' . $this->name . '][' . $this->current . ']' ;
    1760         }
    1761         else
    1762         {
    1763             $n = is_null($n) ? $this->name : $n ;
    1764 
    1765             $the_field = $this->id . '[' . $n . ']';
    1766         }
    1767 
    1768         $hints = array
    1769         (
    1770             WPALCHEMY_FIELD_HINT_CHECKBOX_MULTI,
    1771             WPALCHEMY_FIELD_HINT_SELECT_MULTI,
    1772             WPALCHEMY_FIELD_HINT_SELECT_MULTIPLE,
    1773         );
    1774 
    1775         if (in_array($this->hint, $hints))
    1776         {
    1777             $the_field .= '[]';
    1778         }
    1779 
    1780         return $the_field;
    1781     }
    1782 
    1783     /**
    1784      * @since   1.1
    1785      * @access  public
    1786      */
    1787     function the_index()
    1788     {
    1789         echo $this->get_the_index();
    1790     }
    1791 
    1792     /**
    1793      * @since   1.1
    1794      * @access  public
    1795      */
    1796     function get_the_index()
    1797     {
    1798         return $this->in_loop ? $this->current : 0 ;
    1799     }
    1800 
    1801     /**
    1802      * @since   1.0
    1803      * @access  public
    1804      */
    1805     function is_first()
    1806     {
    1807         if ($this->in_loop AND $this->current == 0) return TRUE;
    1808 
    1809         return FALSE;
    1810     }
    1811 
    1812     /**
    1813      * @since   1.0
    1814      * @access  public
    1815      */
    1816     function is_last()
    1817     {
    1818         if ($this->in_loop AND ($this->current+1) == $this->length) return TRUE;
    1819 
    1820         return FALSE;
    1821     }
    1822 
    1823     /**
    1824      * Used to check if a value is a match
    1825      *
    1826      * @since   1.1
    1827      * @access  public
    1828      * @param   string $n the field name to check or the value to check for (if the_field() is used prior)
    1829      * @param   string $v optional the value to check for
    1830      * @return  bool
    1831      * @see     is_value()
    1832      */
    1833     function is_value($n, $v = NULL)
    1834     {
    1835         if (is_null($v))
    1836         {
    1837             $the_value = $this->get_the_value();
    1838 
    1839             $v = $n;
    1840         }
    1841         else
    1842         {
    1843             $the_value = $this->get_the_value($n);
    1844         }
    1845 
    1846         if($v == $the_value) return TRUE;
    1847 
    1848         return FALSE;
    1849     }
    1850 
    1851     /**
    1852      * Used to check if a value is selected, useful when working with checkbox,
    1853      * radio and select values.
    1854      *
    1855      * @since   1.3
    1856      * @access  public
    1857      * @param   string $n the field name to check or the value to check for (if the_field() is used prior)
    1858      * @param   string $v optional the value to check for
    1859      * @return  bool
    1860      * @see     is_value()
    1861      */
    1862     function is_selected($n, $v = NULL)
    1863     {
    1864         if (is_null($v))
    1865         {
    1866             $the_value = $this->get_the_value(NULL);
    1867 
    1868             $v = $n;
    1869         }
    1870         else
    1871         {
    1872             $the_value = $this->get_the_value($n);
    1873         }
    1874 
    1875         if (is_array($the_value))
    1876         {
    1877             if (in_array($v, $the_value)) return TRUE;
    1878         }
    1879         elseif($v == $the_value)
    1880         {
    1881             return TRUE;
    1882         }
    1883 
    1884         return FALSE;
    1885     }
    1886 
    1887     /**
    1888      * Prints the current state of a checkbox field and should be used inline
    1889      * within the INPUT tag.
    1890      *
    1891      * @since   1.3
    1892      * @access  public
    1893      * @param   string $n the field name to check or the value to check for (if the_field() is used prior)
    1894      * @param   string $v optional the value to check for
    1895      * @see     get_the_checkbox_state()
    1896      */
    1897     function the_checkbox_state($n, $v = NULL)
    1898     {
    1899         echo $this->get_the_checkbox_state($n, $v);
    1900     }
    1901 
    1902     /**
    1903      * Returns the current state of a checkbox field, the returned string is
    1904      * suitable to be used inline within the INPUT tag.
    1905      *
    1906      * @since   1.3
    1907      * @access  public
    1908      * @param   string $n the field name to check or the value to check for (if the_field() is used prior)
    1909      * @param   string $v optional the value to check for
    1910      * @return  string suitable to be used inline within the INPUT tag
    1911      * @see     the_checkbox_state()
    1912      */
    1913     function get_the_checkbox_state($n, $v = NULL)
    1914     {
    1915         if ($this->is_selected($n, $v)) return ' checked="checked"';
    1916     }
    1917 
    1918     /**
    1919      * Prints the current state of a radio field and should be used inline
    1920      * within the INPUT tag.
    1921      *
    1922      * @since   1.3
    1923      * @access  public
    1924      * @param   string $n the field name to check or the value to check for (if the_field() is used prior)
    1925      * @param   string $v optional the value to check for
    1926      * @see     get_the_radio_state()
    1927      */
    1928     function the_radio_state($n, $v = NULL)
    1929     {
    1930         echo $this->get_the_checkbox_state($n, $v);
    1931     }
    1932 
    1933     /**
    1934      * Returns the current state of a radio field, the returned string is
    1935      * suitable to be used inline within the INPUT tag.
    1936      *
    1937      * @since   1.3
    1938      * @access  public
    1939      * @param   string $n the field name to check or the value to check for (if the_field() is used prior)
    1940      * @param   string $v optional the value to check for
    1941      * @return  string suitable to be used inline within the INPUT tag
    1942      * @see     the_radio_state()
    1943      */
    1944     function get_the_radio_state($n, $v = NULL)
    1945     {
    1946         return $this->get_the_checkbox_state($n, $v);
    1947     }
    1948 
    1949     /**
    1950      * Prints the current state of a select field and should be used inline
    1951      * within the SELECT tag.
    1952      *
    1953      * @since   1.3
    1954      * @access  public
    1955      * @param   string $n the field name to check or the value to check for (if the_field() is used prior)
    1956      * @param   string $v optional the value to check for
    1957      * @see     get_the_select_state()
    1958      */
    1959     function the_select_state($n, $v = NULL)
    1960     {
    1961         echo $this->get_the_select_state($n, $v);
    1962     }
    1963 
    1964     /**
    1965      * Returns the current state of a select field, the returned string is
    1966      * suitable to be used inline within the SELECT tag.
    1967      *
    1968      * @since   1.3
    1969      * @access  public
    1970      * @param   string $n the field name to check or the value to check for (if the_field() is used prior)
    1971      * @param   string $v optional the value to check for
    1972      * @return  string suitable to be used inline within the SELECT tag
    1973      * @see     the_select_state()
    1974      */
    1975     function get_the_select_state($n, $v = NULL)
    1976     {
    1977         if ($this->is_selected($n, $v)) return ' selected="selected"';
    1978     }
    1979 
    1980     /**
    1981      * @since   1.1
    1982      * @access  public
    1983      */
    1984     function the_group_open($t = 'div')
    1985     {
    1986         echo $this->get_the_group_open($t);
    1987     }
    1988 
    1989     /**
    1990      * @since   1.1
    1991      * @access  public
    1992      */
    1993     function get_the_group_open($t = 'div')
    1994     {
    1995         $this->group_tag = $t;
    1996 
    1997         $loop_open = NULL;
    1998 
    1999         $loop_open_classes = array('wpa_loop', 'wpa_loop-' . $this->name);
    2000        
    2001         $css_class = array('wpa_group', 'wpa_group-'. $this->name);
    2002 
    2003         if ($this->is_first())
    2004         {
    2005             array_push($css_class, 'first');
    2006 
    2007             $loop_open = '<div class="wpa_loop">';
    2008 
    2009             if (isset($this->_loop_data->limit))
    2010             {
    2011                 array_push($loop_open_classes, 'wpa_loop_limit-' . $this->_loop_data->limit);
    2012             }
    2013 
    2014             $loop_open = '<div id="wpa_loop-'. $this->name .'" class="' . implode(' ', $loop_open_classes) . '">';
    2015         }
    2016 
    2017         if ($this->is_last())
    2018         {
    2019             array_push($css_class, 'last');
    2020 
    2021             if ($this->in_loop == 'multi')
    2022             {
    2023                 array_push($css_class, 'tocopy');
    2024             }
    2025         }
    2026 
    2027         return $loop_open . '<' . $t . ' class="'. implode(' ', $css_class) . '">';
    2028     }
    2029 
    2030     /**
    2031      * @since   1.1
    2032      * @access  public
    2033      */
    2034     function the_group_close()
    2035     {
    2036         echo $this->get_the_group_close();
    2037     }
    2038 
    2039     /**
    2040      * @since   1.1
    2041      * @access  public
    2042      */
    2043     function get_the_group_close()
    2044     {
    2045         $loop_close = NULL;
    2046        
    2047         if ($this->is_last())
    2048         {
    2049             $loop_close = '</div>';
    2050         }
    2051        
    2052         return '</' . $this->group_tag . '>' . $loop_close;
    2053     }
    2054 
    2055     /**
    2056      * @since   1.1
    2057      * @access  public
    2058      */
    2059     function have_fields_and_multi($n, $options = NULL)
    2060     {
    2061         if (is_array($options))
    2062         {
    2063             // use as stdClass object
    2064             $options = (object)$options;
    2065            
    2066             $length = @$options->length;
    2067 
    2068             $this->_loop_data->limit = @$options->limit;
    2069         }
    2070         else
    2071         {
    2072             // backward compatibility (bc)
    2073             $length = $options;
    2074         }
    2075 
    2076         $this->_meta(NULL, TRUE);
    2077 
    2078         $this->in_loop = 'multi';
    2079 
    2080         return $this->_loop($n, $length, 2);
    2081     }
    2082 
    2083     /**
    2084      * @deprecated
    2085      * @since   1.0
    2086      * @access  public
    2087      */
    2088     function have_fields_and_one($n)
    2089     {
    2090         $this->_meta(NULL, TRUE);
    2091         $this->in_loop = 'single';
    2092         return $this->_loop($n,NULL,1);
    2093     }
    2094 
    2095     /**
    2096      * @since   1.0
    2097      * @access  public
    2098      */
    2099     function have_fields($n,$length=NULL)
    2100     {
    2101         $this->_meta(NULL, TRUE);
    2102         $this->in_loop = 'normal';
    2103         return $this->_loop($n,$length);
    2104     }
    2105 
    2106     /**
    2107      * @since   1.0
    2108      * @access  private
    2109      */
    2110     function _loop($n,$length=NULL,$and_one=0)
    2111     {
    2112         if (!$this->in_loop)
    2113         {
    2114             $this->in_loop = TRUE;
    2115         }
    2116        
    2117         $this->name = $n;
    2118 
    2119         $cnt = count(!empty($this->meta[$n])?$this->meta[$n]:NULL);
    2120 
    2121         $length = is_null($length) ? $cnt : $length ;
    2122        
    2123         if ($this->in_loop == 'multi' AND $cnt > $length) $length = $cnt;
    2124 
    2125         $this->length = $length;
    2126 
    2127         if ($this->in_template AND $and_one)
    2128         {
    2129             if ($length == 0)
    2130             {
    2131                 $this->length = $and_one;
    2132             }
    2133             else
    2134             {
    2135                 $this->length = $length+1;
    2136             }
    2137         }
    2138 
    2139         $this->current++;
    2140 
    2141         if ($this->current < $this->length)
    2142         {
    2143             $this->subname = NULL;
    2144 
    2145             $this->fieldtype = NULL;
    2146 
    2147             return TRUE;
    2148         }
    2149         else if ($this->current == $this->length)
    2150         {
    2151             $this->name = NULL;
    2152 
    2153             $this->current = -1;
    2154         }
    2155 
    2156         $this->in_loop = FALSE;
    2157 
    2158         $this->_loop_data = new stdClass;
    2159 
    2160         return FALSE;
    2161     }
    2162 
    2163     /**
    2164      * @since   1.0
    2165      * @access  private
    2166      */
    2167     function _save($post_id)
    2168     {
    2169         /**
    2170          * note: the "save_post" action fires for saving revisions and post/pages,
    2171          * when saving a post this function fires twice, once for a revision save,
    2172          * and again for the post/page save ... the $post_id is different for the
    2173          * revision save, this means that "get_post_meta()" will not work if trying
    2174          * to get values for a revision (as it has no post meta data)
    2175          * see http://alexking.org/blog/2008/09/06/wordpress-26x-duplicate-custom-field-issue
    2176          *
    2177          * why let the code run twice? wordpress does not currently save post meta
    2178          * data per revisions (I think it should, so users can do a complete revert),
    2179          * so in the case that this functionality changes, let it run twice
    2180          */
    2181 
    2182         $real_post_id = isset($_POST['post_ID']) ? $_POST['post_ID'] : NULL ;
    2183        
    2184         // check autosave
    2185         if (defined('DOING_AUTOSAVE') AND DOING_AUTOSAVE AND !$this->autosave) return $post_id;
    2186      
    2187         // make sure data came from our meta box, verify nonce
    2188         $nonce = isset($_POST[$this->id.'_nonce']) ? $_POST[$this->id.'_nonce'] : NULL ;
    2189         if (!wp_verify_nonce($nonce, $this->id)) return $post_id;
    2190      
    2191         // check user permissions
    2192         if ($_POST['post_type'] == 'page')
    2193         {
    2194             if (!current_user_can('edit_page', $post_id)) return $post_id;
    2195         }
    2196         else
    2197         {
    2198             if (!current_user_can('edit_post', $post_id)) return $post_id;
    2199         }
    2200      
    2201         // authentication passed, save data
    2202      
    2203         $new_data = isset( $_POST[$this->id] ) ? $_POST[$this->id] : NULL ;
    2204      
    2205         WPAlchemy_MetaBox::clean($new_data);
    2206 
    2207         if (empty($new_data))
    2208         {
    2209             $new_data = NULL;
    2210         }
    2211 
    2212         // filter: save
    2213         if ($this->has_filter('save'))
    2214         {
    2215             $new_data = $this->apply_filters('save', $new_data, $real_post_id);
    2216 
    2217             /**
    2218              * halt saving
    2219              * @since 1.3.4
    2220              */
    2221             if (FALSE === $new_data) return $post_id;
    2222 
    2223             WPAlchemy_MetaBox::clean($new_data);
    2224         }
    2225 
    2226         // get current fields, use $real_post_id (checked for in both modes)
    2227         $current_fields = get_post_meta($real_post_id, $this->id . '_fields', TRUE);
    2228 
    2229         if ($this->mode == WPALCHEMY_MODE_EXTRACT)
    2230         {
    2231             $new_fields = array();
    2232 
    2233             if (is_array($new_data))
    2234             {
    2235                 foreach ($new_data as $k => $v)
    2236                 {
    2237                     $field = $this->prefix . $k;
    2238                    
    2239                     array_push($new_fields,$field);
    2240 
    2241                     $new_value = $new_data[$k];
    2242 
    2243                     if (is_null($new_value))
    2244                     {
    2245                         delete_post_meta($post_id, $field);
    2246                     }
    2247                     else
    2248                     {
    2249                         update_post_meta($post_id, $field, $new_value);
    2250                     }
    2251                 }
    2252             }
    2253 
    2254             $diff_fields = array_diff((array)$current_fields,$new_fields);
    2255 
    2256             if (is_array($diff_fields))
    2257             {
    2258                 foreach ($diff_fields as $field)
    2259                 {
    2260                     delete_post_meta($post_id,$field);
    2261                 }
    2262             }
    2263 
    2264             delete_post_meta($post_id, $this->id . '_fields');
    2265 
    2266             if ( ! empty($new_fields))
    2267             {
    2268                 add_post_meta($post_id,$this->id . '_fields', $new_fields, TRUE);
    2269             }
    2270 
    2271             // keep data tidy, delete values if previously using WPALCHEMY_MODE_ARRAY
    2272             delete_post_meta($post_id, $this->id);
    2273         }
    2274         else
    2275         {
    2276             if (is_null($new_data))
    2277             {
    2278                 delete_post_meta($post_id, $this->id);
    2279             }
    2280             else
    2281             {
    2282                 update_post_meta($post_id, $this->id, $new_data);
    2283             }
    2284 
    2285             // keep data tidy, delete values if previously using WPALCHEMY_MODE_EXTRACT
    2286             if (is_array($current_fields))
    2287             {
    2288                 foreach ($current_fields as $field)
    2289                 {
    2290                     delete_post_meta($post_id, $field);
    2291                 }
    2292 
    2293                 delete_post_meta($post_id, $this->id . '_fields');
    2294             }
    2295         }
    2296 
    2297         // action: save
    2298         if ($this->has_action('save'))
    2299         {
    2300             $this->do_action('save', $new_data, $real_post_id);
    2301         }
    2302 
    2303         return $post_id;
    2304     }
    2305 
    2306     /**
    2307      * Cleans an array, removing blank ('') values
    2308      *
    2309      * @static
    2310      * @since   1.0
    2311      * @access  public
    2312      * @param   array the array to clean (passed by reference)
    2313      */
    2314     function clean(&$arr)
    2315     {
    2316         if (is_array($arr))
    2317         {
    2318             foreach ($arr as $i => $v)
    2319             {
    2320                 if (is_array($arr[$i]))
    2321                 {
    2322                     WPAlchemy_MetaBox::clean($arr[$i]);
    2323      
    2324                     if (!count($arr[$i]))
    2325                     {
    2326                         unset($arr[$i]);
    2327                     }
    2328                 }
    2329                 else
    2330                 {
    2331                     if ('' == trim($arr[$i]) OR is_null($arr[$i]))
    2332                     {
    2333                         unset($arr[$i]);
    2334                     }
    2335                 }
    2336             }
    2337 
    2338             if (!count($arr))
    2339             {
    2340                 $arr = array();
    2341             }
    2342             else
    2343             {
    2344                 $keys = array_keys($arr);
    2345 
    2346                 $is_numeric = TRUE;
    2347 
    2348                 foreach ($keys as $key)
    2349                 {
    2350                     if (!is_numeric($key))
    2351                     {
    2352                         $is_numeric = FALSE;
    2353                         break;
    2354                     }
    2355                 }
    2356 
    2357                 if ($is_numeric)
    2358                 {
    2359                     $arr = array_values($arr);
    2360                 }
    2361             }
    2362         }
    2363     }
     50    /**
     51     * User defined identifier for the meta box, prefix with an underscore to
     52     * prevent option(s) form showing up in the custom fields meta box, this
     53     * option should be used when instantiating the class.
     54     *
     55     * @since   1.0
     56     * @var     string required
     57     */
     58    public $id;
     59
     60    /**
     61     * Used to set the title of the meta box, this option should be used when
     62     * instantiating the class.
     63     *
     64     * @since   1.0
     65     * @var     string required
     66     * @see     $hide_title
     67     */
     68    public $title = 'Custom Meta';
     69
     70    /**
     71     * Used to set the meta box content, the contents of your meta box should be
     72     * defined within this file, this option should be used when instantiating
     73     * the class.
     74     *
     75     * @since   1.0
     76     * @var     string required
     77     */
     78    public $template;
     79
     80    /**
     81     * Used to set the post types that the meta box can appear in, this option
     82     * should be used when instantiating the class.
     83     *
     84     * @since   1.0
     85     * @var     array
     86     */
     87    public $types;
     88
     89    /**
     90     * @since   1.0
     91     * @var     bool
     92     */
     93    public $context = 'normal';
     94
     95    /**
     96     * @since   1.0
     97     * @var     bool
     98     */
     99    public $priority = 'high';
     100
     101    /**
     102     * @since   1.0
     103     * @var     bool
     104     */
     105    public $autosave = TRUE;
     106
     107    /**
     108     * Used to set how the class does its data storage, data will be stored as
     109     * an associative array in a single meta entry in the wp_postmeta table or
     110     * data can be set and individual entries in the wp_postmeta table, the
     111     * following constants should be used when setting this option,
     112     * WPALCHEMY_MODE_ARRAY (default) and WPALCHEMY_MODE_EXTRACT, this option
     113     * should be used when instantiating the class.
     114     *
     115     * @since   1.2
     116     * @var     string
     117     */
     118    public $mode = WPALCHEMY_MODE_ARRAY;
     119
     120    /**
     121     * When the mode option is set to WPALCHEMY_MODE_EXTRACT, you have to take
     122     * care to avoid name collisions with other meta entries. Use this option to
     123     * automatically add a prefix to your variables, this option should be used
     124     * when instantiating the class.
     125     *
     126     * @since   1.2
     127     * @var     array
     128     */
     129    public $prefix;
     130
     131    /**
     132     * @since   1.0
     133     * @var     bool
     134     */
     135    public $exclude_template;
     136
     137    /**
     138     * @since   1.0
     139     * @var     bool
     140     */
     141    public $exclude_category_id;
     142
     143    /**
     144     * @since   1.0
     145     * @var     bool
     146     */
     147    public $exclude_category;
     148
     149    /**
     150     * @since   1.0
     151     * @var     bool
     152     */
     153    public $exclude_tag_id;
     154
     155    /**
     156     * @since   1.0
     157     * @var     bool
     158     */
     159    public $exclude_tag;
     160
     161    /**
     162     * @since   1.0
     163     * @var     bool
     164     */
     165    public $exclude_post_id;
     166
     167    /**
     168     * @since   1.0
     169     * @var     bool
     170     */
     171    public $include_template;
     172
     173    /**
     174     * @since   1.0
     175     * @var     bool
     176     */
     177    public $include_category_id;
     178
     179    /**
     180     * @since   1.0
     181     * @var     bool
     182     */
     183    public $include_category;
     184
     185    /**
     186     * @since   1.0
     187     * @var     bool
     188     */
     189    public $include_tag_id;
     190
     191    /**
     192     * @since   1.0
     193     * @var     bool
     194     */
     195    public $include_tag;
     196
     197    /**
     198     * @since   1.0
     199     * @var     bool
     200     */
     201    public $include_post_id;
     202
     203    /**
     204     * Callback used on the WordPress "admin_init" action, the main benefit is
     205     * that this callback is executed only when the meta box is present, this
     206     * option should be used when instantiating the class.
     207     *
     208     * @since   1.3.4
     209     * @var     string|array optional
     210     */
     211    public $init_action;
     212
     213    /**
     214     * Callback used to override when the meta box gets displayed, must return
     215     * true or false to determine if the meta box should or should not be
     216     * displayed, this option should be used when instantiating the class.
     217     *
     218     * @since   1.3
     219     * @var     string|array optional
     220     * @param   array $post_id first variable passed to the callback function
     221     * @see     can_output()
     222     */
     223    public $output_filter;
     224
     225    /**
     226     * Callback used to override or insert meta data before saving, you can halt
     227     * saving by passing back FALSE (return FALSE), this option should be used
     228     * when instantiating the class.
     229     *
     230     * @since   1.3
     231     * @var     string|array optional
     232     * @param   array $meta meta box data, first variable passed to the callback function
     233     * @param   string $post_id second variable passed to the callback function
     234     * @see     $save_action, add_filter()
     235     */
     236    public $save_filter;
     237
     238    /**
     239     * Callback used to execute custom code after saving, this option should be
     240     * used when instantiating the class.
     241     *
     242     * @since   1.3
     243     * @var     string|array optional
     244     * @param   array $meta meta box data, first variable passed to the callback function
     245     * @param   string $post_id second variable passed to the callback function
     246     * @see     $save_filter, add_filter()
     247     */
     248    public $save_action;
     249
     250    /**
     251     * Callback used to override or insert STYLE or SCRIPT tags into the head,
     252     * this option should be used when instantiating the class.
     253     *
     254     * @since   1.3
     255     * @var     string|array optional
     256     * @param   array $content current head content, first variable passed to the callback function
     257     * @see     $head_action, add_filter()
     258     */
     259    public $head_filter;
     260
     261    /**
     262     * Callback used to insert STYLE or SCRIPT tags into the head,
     263     * this option should be used when instantiating the class.
     264     *
     265     * @since   1.3
     266     * @var     string|array optional
     267     * @see     $head_filter, add_action()
     268     */
     269    public $head_action;
     270
     271    /**
     272     * Callback used to override or insert SCRIPT tags into the footer, this
     273     * option should be used when instantiating the class.
     274     *
     275     * @since   1.3
     276     * @var     string|array optional
     277     * @param   array $content current foot content, first variable passed to the callback function
     278     * @see     $foot_action, add_filter()
     279     */
     280    public $foot_filter;
     281
     282    /**
     283     * Callback used to insert SCRIPT tags into the footer, this option should
     284     * be used when instantiating the class.
     285     *
     286     * @since   1.3
     287     * @var     string|array optional
     288     * @see     $foot_filter, add_action()
     289     */
     290    public $foot_action;
     291
     292    /**
     293     * Used to hide the default content editor in a page or post, this option
     294     * should be used when instantiating the class.
     295     *
     296     * @since   1.3
     297     * @var     bool optional
     298     */
     299    public $hide_editor = FALSE;
     300
     301    /**
     302     * Used in conjunction with the "hide_editor" option, prevents the media
     303     * buttons from also being hidden.
     304     *
     305     * @since   1.5
     306     * @var     bool optional
     307     */
     308    public $use_media_buttons = FALSE;
     309
     310    /**
     311     * Used to hide the meta box title, this option should be used when
     312     * instantiating the class.
     313     *
     314     * @since   1.3
     315     * @var     bool optional
     316     * @see     $title
     317     */
     318    public $hide_title = FALSE;
     319
     320    /**
     321     * Used to lock a meta box in place, possible values are: top, bottom,
     322     * before_post_title, after_post_title, this option should be used when
     323     * instantiating the class.
     324     *
     325     * @since   1.3.3
     326     * @var     string optional possible values are: top, bottom, before_post_title, after_post_title
     327     */
     328    public $lock;
     329
     330    /**
     331     * Used to lock a meta box at top (below the default content editor), this
     332     * option should be used when instantiating the class.
     333     *
     334     * @deprecated  deprecated since version 1.3.3
     335     * @since       1.3
     336     * @var         bool optional
     337     * @see         $lock
     338     */
     339    public $lock_on_top = FALSE;
     340
     341    /**
     342     * Used to lock a meta box at bottom, this option should be used when
     343     * instantiating the class.
     344     *
     345     * @deprecated  deprecated since version 1.3.3
     346     * @since       1.3
     347     * @var         bool optional
     348     * @see         $lock
     349     */
     350    public $lock_on_bottom = FALSE;
     351
     352    /**
     353     * Used to set the initial view state of the meta box, possible values are:
     354     * opened, closed, always_opened, this option should be used when
     355     * instantiating the class.
     356     *
     357     * @since   1.3.3
     358     * @var     string optional possible values are: opened, closed, always_opened
     359     */
     360    public $view;
     361
     362    /**
     363     * Used to hide the show/hide checkbox option from the screen options area,
     364     * this option should be used when instantiating the class.
     365     *
     366     * @since   1.3.4
     367     * @var     bool optional
     368     */
     369    public $hide_screen_option = FALSE;
     370
     371    // private
     372
     373    var $meta;
     374    var $name;
     375    var $subname;
     376
     377    /**
     378     * Used to provide field type hinting
     379     *
     380     * @since   1.3
     381     * @var     string
     382     * @see     the_field()
     383     */
     384    protected $hint;
     385
     386    var $length = 0;
     387    var $current = -1;
     388    var $in_loop = FALSE;
     389    var $in_template = FALSE;
     390    var $group_tag;
     391    var $current_post_id;
     392
     393    /**
     394     * Used to store current loop details, cleared after loop ends
     395     *
     396     * @since   1.4
     397     * @var     stdClass
     398     * @see     have_fields_and_multi(), have_fields()
     399     */
     400    protected $_loop_data;
     401
     402    public function __construct($arr)
     403    {
     404        $this->_loop_data = new \stdClass;
     405
     406        $this->meta = array();
     407
     408        $this->types = array('post', 'page');
     409
     410        if (is_array($arr))
     411        {
     412            foreach ($arr as $n => $v)
     413            {
     414                $this->$n = $v;
     415            }
     416
     417            if (empty($this->id)) die('Meta box ID required');
     418
     419            if (is_numeric($this->id)) die('Meta box ID must be a string');
     420
     421            if (empty($this->template)) die('Meta box template file required');
     422
     423            // check for nonarray values
     424
     425            $exc_inc = array
     426            (
     427                'exclude_template',
     428                'exclude_category_id',
     429                'exclude_category',
     430                'exclude_tag_id',
     431                'exclude_tag',
     432                'exclude_post_id',
     433
     434                'include_template',
     435                'include_category_id',
     436                'include_category',
     437                'include_tag_id',
     438                'include_tag',
     439                'include_post_id'
     440            );
     441
     442            foreach ($exc_inc as $v)
     443            {
     444                // ideally the exclude and include values should be in array form, convert to array otherwise
     445                if (!empty($this->$v) AND !is_array($this->$v))
     446                {
     447                    $this->$v = array_map('trim',explode(',',$this->$v));
     448                }
     449            }
     450
     451            // convert depreciated variables
     452            if ($this->lock_on_top) $this->lock = WPALCHEMY_LOCK_TOP;
     453            elseif ($this->lock_on_bottom) $this->lock = WPALCHEMY_LOCK_BOTTOM;
     454
     455            add_action('admin_init', array($this,'_init'));
     456
     457            // uses the default wordpress-importer plugin hook
     458            add_action('import_post_meta', array($this, '_import'), 10, 3);
     459
     460            // todo: when first run define a constant to prevent other instances from running again ...
     461            add_action( 'admin_head', array( $this, '_global_head' ) );
     462
     463            add_action( 'admin_footer', array( $this, '_global_foot' ) );
     464        }
     465        else
     466        {
     467            die('Associative array parameters required');
     468        }
     469    }
     470
     471    /**
     472     * Used to correct double serialized data during post/page export/import,
     473     * additionally will try to fix corrupted serialized data by recalculating
     474     * string length values
     475     *
     476     * @since   1.3.16
     477     */
     478    public function _import($post_id, $key, $value)
     479    {
     480        if (WPALCHEMY_MODE_ARRAY == $this->mode AND $key == $this->id)
     481        {
     482            // using $wp_import to get access to the raw postmeta data prior to it getting passed
     483            // through "maybe_unserialize()" in "plugins/wordpress-importer/wordpress-importer.php"
     484            // the "import_post_meta" action is called after "maybe_unserialize()"
     485
     486            if ( isset( $wp_import->posts ) && ( is_array( $wp_import->posts ) || is_object( $wp_import->posts ) ) )
     487            {
     488                foreach ( $wp_import->posts as $post )
     489                {
     490                    if ( $post_id == $post['post_id'] )
     491                    {
     492                        foreach( $post['postmeta'] as $meta )
     493                        {
     494                            if ( $key == $meta['key'] )
     495                            {
     496                                // try to fix corrupted serialized data, specifically "\r\n" being converted to "\n" during wordpress XML export (WXR)
     497                                // "maybe_unserialize()" fixes a wordpress bug which double serializes already serialized data during export/import
     498                                $value = maybe_unserialize( $this->fix_serialized_string_type( $meta['value'] ) );
     499
     500                                update_post_meta( $post_id, $key,  $value );
     501                            }
     502                        }
     503                    }
     504                }
     505            }
     506        }
     507    }
     508
     509    /**
     510     * @since   1.6
     511     */
     512    protected function fix_serialized_string_type( $serialized_data ) {
     513        return preg_replace_callback( '!s:(\d+):"(.*?)";!s', array( $this, 'fix_serialized_string_type_callback' ), stripslashes( $serialized_data ) );
     514    }
     515
     516    /**
     517     * @since   1.6
     518     */
     519    protected function fix_serialized_string_type_callback( $matches ) {
     520        return sprintf( 's:%s:"%s";', strlen( $matches[2] ), $matches[2] );
     521    }
     522
     523    /**
     524     * Used to initialize the meta box, runs on WordPress admin_init action,
     525     * properly calls internal WordPress methods
     526     *
     527     * @since   1.0
     528     */
     529    public function _init()
     530    {
     531        // must be creating or editing a post or page
     532        if ( ! self::_is_post() AND ! self::_is_page()) return;
     533
     534        if ( ! empty($this->output_filter))
     535        {
     536            $this->add_filter('output', $this->output_filter);
     537        }
     538
     539        if ($this->can_output())
     540        {
     541            foreach ($this->types as $type)
     542            {
     543                add_meta_box($this->id . '_metabox', $this->title, array($this, '_setup'), $type, $this->context, $this->priority);
     544            }
     545
     546            add_action('save_post', array($this,'_save'));
     547
     548            $filters = array('save', 'head', 'foot');
     549
     550            foreach ($filters as $filter)
     551            {
     552                $var = $filter . '_filter';
     553
     554                if (!empty($this->$var))
     555                {
     556                    if ('save' == $filter)
     557                    {
     558                        $this->add_filter($filter, $this->$var, 10, 2);
     559                    }
     560                    else
     561                    {
     562                        $this->add_filter($filter, $this->$var);
     563                    }
     564                }
     565            }
     566
     567            $actions = array('save', 'head', 'foot', 'init');
     568
     569            foreach ($actions as $action)
     570            {
     571                $var = $action . '_action';
     572
     573                if (!empty($this->$var))
     574                {
     575                    if ('save' == $action)
     576                    {
     577                        $this->add_action($action, $this->$var, 10, 2);
     578                    }
     579                    else
     580                    {
     581                        $this->add_action($action, $this->$var);
     582                    }
     583                }
     584            }
     585
     586            add_action('admin_head', array($this,'_head'), 11);
     587
     588            add_action('admin_footer', array($this,'_foot'), 11);
     589
     590            // action: init
     591            if ($this->has_action('init'))
     592            {
     593                $this->do_action('init');
     594            }
     595        }
     596    }
     597
     598    /**
     599     * Used to insert STYLE or SCRIPT tags into the head, called on WordPress
     600     * admin_head action.
     601     *
     602     * @since   1.3
     603     * @see     _foot()
     604     */
     605    public function _head()
     606    {
     607        $content = NULL;
     608
     609        ob_start();
     610
     611        ?>
     612        <style type="text/css">
     613            <?php if ($this->hide_editor) { ?> #wp-content-editor-container, #post-status-info, <?php if ($this->use_media_buttons) { ?> #content-html, #content-tmce<?php } else { ?> #wp-content-wrap<?php } ?> { display:none; } <?php } ?>
     614        </style>
     615        <?php
     616
     617        $content = ob_get_contents();
     618
     619        ob_end_clean();
     620
     621        // filter: head
     622        if ($this->has_filter('head'))
     623        {
     624            $content = $this->apply_filters('head', $content);
     625        }
     626
     627        echo $content;
     628
     629        // action: head
     630        if ($this->has_action('head'))
     631        {
     632            $this->do_action('head');
     633        }
     634    }
     635
     636    /**
     637     * Used to insert SCRIPT tags into the footer, called on WordPress
     638     * admin_footer action.
     639     *
     640     * @since   1.3
     641     * @see     _head()
     642     */
     643    public function _foot()
     644    {
     645        $content = NULL;
     646
     647        if
     648        (
     649            $this->lock OR
     650            $this->hide_title OR
     651            $this->view OR
     652            $this->hide_screen_option
     653        )
     654        {
     655            ob_start();
     656
     657            ?>
     658            <script type="text/javascript">
     659            /* <![CDATA[ */
     660            (function($){ /* not using jQuery ondomready, code runs right away in footer */
     661
     662                var mb_id = '<?php echo $this->id; ?>';
     663                var mb = $('#' + mb_id + '_metabox');
     664
     665                <?php if (WPALCHEMY_LOCK_TOP == $this->lock): ?>
     666                <?php if ('side' == $this->context): ?>
     667                var id = 'wpalchemy-side-top';
     668                if ( ! $('#'+id).length)
     669                {
     670                    $('<div></div>').attr('id',id).prependTo('#side-info-column');
     671                }
     672                <?php else: ?>
     673                var id = 'wpalchemy-content-top';
     674                if ( ! $('#'+id).length)
     675                {
     676                    $('<div></div>').attr('id',id).insertAfter('#postdiv, #postdivrich');
     677                }
     678                <?php endif; ?>
     679                $('#'+id).append(mb);
     680                <?php elseif (WPALCHEMY_LOCK_BOTTOM == $this->lock): ?>
     681                <?php if ('side' == $this->context): ?>
     682                var id = 'wpalchemy-side-bottom';
     683                if ( ! $('#'+id).length)
     684                {
     685                    $('<div></div>').attr('id',id).appendTo('#side-info-column');
     686                }
     687                <?php else: ?>
     688                if ( ! $('#advanced-sortables').children().length)
     689                {
     690                    $('#advanced-sortables').css('display','none');
     691                }
     692
     693                var id = 'wpalchemy-content-bottom';
     694                if ( ! $('#'+id).length)
     695                {
     696                    $('<div></div>').attr('id',id).insertAfter('#advanced-sortables');
     697                }
     698                <?php endif; ?>
     699                $('#'+id).append(mb);
     700                <?php elseif (WPALCHEMY_LOCK_BEFORE_POST_TITLE == $this->lock): ?>
     701                <?php if ('side' != $this->context): ?>
     702                var id = 'wpalchemy-content-bpt';
     703                if ( ! $('#'+id).length)
     704                {
     705                    $('<div></div>').attr('id',id).prependTo('#post-body-content');
     706                }
     707                $('#'+id).append(mb);
     708                <?php endif; ?>
     709                <?php elseif (WPALCHEMY_LOCK_AFTER_POST_TITLE == $this->lock): ?>
     710                <?php if ('side' != $this->context): ?>
     711                var id = 'wpalchemy-content-apt';
     712                if ( ! $('#'+id).length)
     713                {
     714                    $('<div></div>').attr('id',id).insertAfter('#titlediv');
     715                }
     716                $('#'+id).append(mb);
     717                <?php endif; ?>
     718                <?php endif; ?>
     719
     720                <?php if ( ! empty($this->lock)): ?>
     721                $('.hndle', mb).css('cursor','pointer');
     722                $('.handlediv', mb).remove();
     723                <?php endif; ?>
     724
     725                <?php if ($this->hide_title): ?>
     726                $('.hndle', mb).remove();
     727                $('.handlediv', mb).remove();
     728                mb.removeClass('closed'); /* start opened */
     729                <?php endif; ?>
     730
     731                <?php if (WPALCHEMY_VIEW_START_OPENED == $this->view): ?>
     732                mb.removeClass('closed');
     733                <?php elseif (WPALCHEMY_VIEW_START_CLOSED == $this->view): ?>
     734                mb.addClass('closed');
     735                <?php elseif (WPALCHEMY_VIEW_ALWAYS_OPENED == $this->view): ?>
     736                /* todo: need to find a way to add this script block below, load-scripts.php?... */
     737                var h3 = mb.children('h3');
     738                setTimeout(function(){ h3.unbind('click'); }, 1000);
     739                $('.handlediv', mb).remove();
     740                mb.removeClass('closed'); /* start opened */
     741                $('.hndle', mb).css('cursor','auto');
     742                <?php endif; ?>
     743
     744                <?php if ($this->hide_screen_option): ?>
     745                    $('.metabox-prefs label[for='+ mb_id +'_metabox-hide]').remove();
     746                <?php endif; ?>
     747
     748                mb = null;
     749
     750            })(jQuery);
     751            /* ]]> */
     752            </script>
     753            <?php
     754
     755            $content = ob_get_contents();
     756
     757            ob_end_clean();
     758        }
     759
     760        // filter: foot
     761        if ($this->has_filter('foot'))
     762        {
     763            $content = $this->apply_filters('foot', $content);
     764        }
     765
     766        echo $content;
     767
     768        // action: foot
     769        if ($this->has_action('foot'))
     770        {
     771            $this->do_action('foot');
     772        }
     773    }
     774
     775    /**
     776     * Used to setup the meta box content template
     777     *
     778     * @since   1.0
     779     * @see     _init()
     780     */
     781    public function _setup()
     782    {
     783        $this->in_template = TRUE;
     784
     785        // also make current post data available
     786        global $post;
     787
     788        // shortcuts
     789        $mb =& $this;
     790        $metabox =& $this;
     791        $id = $this->id;
     792        $meta = $this->_meta(NULL, TRUE);
     793
     794        // use include because users may want to use one templete for multiple meta boxes
     795        include $this->template;
     796
     797        // create a nonce for verification
     798        echo '<input type="hidden" name="'. $this->id .'_nonce" value="' . wp_create_nonce($this->id) . '" />';
     799
     800        $this->in_template = FALSE;
     801    }
     802
     803    /**
     804     * Used to properly prefix the filter tag, the tag is unique to the meta
     805     * box instance
     806     *
     807     * @since   1.3
     808     * @param   string $tag name of the filter
     809     * @return  string uniquely prefixed tag name
     810     */
     811    protected function _get_filter_tag($tag)
     812    {
     813        $prefix = 'wpalchemy_filter_' . $this->id . '_';
     814        $prefix = preg_replace('/_+/', '_', $prefix);
     815
     816        $tag = preg_replace('/^'. $prefix .'/i', '', $tag);
     817        return $prefix . $tag;
     818    }
     819
     820    /**
     821     * Uses WordPress add_filter() function, see WordPress add_filter()
     822     *
     823     * @since   1.3
     824     * @link    http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L65
     825     */
     826    public function add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1)
     827    {
     828        $tag = $this->_get_filter_tag($tag);;
     829        add_filter($tag, $function_to_add, $priority, $accepted_args);
     830    }
     831
     832    /**
     833     * Uses WordPress has_filter() function, see WordPress has_filter()
     834     *
     835     * @since   1.3
     836     * @link    http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L86
     837     */
     838    public function has_filter($tag, $function_to_check = FALSE)
     839    {
     840        $tag = $this->_get_filter_tag($tag);
     841        return has_filter($tag, $function_to_check);
     842    }
     843
     844    /**
     845     * Uses WordPress apply_filters() function, see WordPress apply_filters()
     846     *
     847     * @since   1.3
     848     * @link    http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L134
     849     */
     850    public function apply_filters($tag, $value)
     851    {
     852        $args = func_get_args();
     853        $args[0] = $this->_get_filter_tag($tag);
     854        return call_user_func_array('apply_filters', $args);
     855    }
     856
     857    /**
     858     * Uses WordPress remove_filter() function, see WordPress remove_filter()
     859     *
     860     * @since   1.3
     861     * @link    http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L250
     862     */
     863    public function remove_filter($tag, $function_to_remove, $priority = 10, $accepted_args = 1)
     864    {
     865        $tag = $this->_get_filter_tag($tag);
     866        return remove_filter($tag, $function_to_remove, $priority, $accepted_args);
     867    }
     868
     869    /**
     870     * Used to properly prefix the action tag, the tag is unique to the meta
     871     * box instance
     872     *
     873     * @since   1.3
     874     * @param   string $tag name of the action
     875     * @return  string uniquely prefixed tag name
     876     */
     877    protected function _get_action_tag($tag)
     878    {
     879        $prefix = 'wpalchemy_action_' . $this->id . '_';
     880        $prefix = preg_replace('/_+/', '_', $prefix);
     881
     882        $tag = preg_replace('/^'. $prefix .'/i', '', $tag);
     883        return $prefix . $tag;
     884    }
     885
     886    /**
     887     * Uses WordPress add_action() function, see WordPress add_action()
     888     *
     889     * @since   1.3
     890     * @link    http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L324
     891     */
     892    public function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1)
     893    {
     894        $tag = $this->_get_action_tag($tag);
     895        add_action($tag, $function_to_add, $priority, $accepted_args);
     896    }
     897
     898    /**
     899     * Uses WordPress has_action() function, see WordPress has_action()
     900     *
     901     * @since   1.3
     902     * @link    http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L492
     903     */
     904    public function has_action($tag, $function_to_check = FALSE)
     905    {
     906        $tag = $this->_get_action_tag($tag);
     907        return has_action($tag, $function_to_check);
     908    }
     909
     910    /**
     911     * Uses WordPress remove_action() function, see WordPress remove_action()
     912     *
     913     * @since   1.3
     914     * @link    http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L513
     915     */
     916    public function remove_action($tag, $function_to_remove, $priority = 10, $accepted_args = 1)
     917    {
     918        $tag = $this->_get_action_tag($tag);
     919        return remove_action($tag, $function_to_remove, $priority, $accepted_args);
     920    }
     921
     922    /**
     923     * Uses WordPress do_action() function, see WordPress do_action()
     924     * @since   1.3
     925     * @link    http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L352
     926     */
     927    public function do_action($tag, $arg = '')
     928    {
     929        $args = func_get_args();
     930        $args[0] = $this->_get_action_tag($tag);
     931        return call_user_func_array('do_action', $args);
     932    }
     933
     934    /**
     935     * Used to check if creating a new post or editing one
     936     *
     937     * @since   1.3.7
     938     * @return  bool
     939     * @see     _is_page()
     940     */
     941    static public function _is_post()
     942    {
     943        if ('post' == self::_is_post_or_page())
     944        {
     945            return TRUE;
     946        }
     947
     948        return FALSE;
     949    }
     950
     951    /**
     952     * Used to check if creating a new page or editing one
     953     *
     954     * @since   1.3.7
     955     * @return  bool
     956     * @see     _is_post()
     957     */
     958    static public function _is_page()
     959    {
     960        if ('page' == self::_is_post_or_page())
     961        {
     962            return TRUE;
     963        }
     964
     965        return FALSE;
     966    }
     967
     968    /**
     969     * Used to check if creating or editing a post or page
     970     *
     971     * @since   1.3.8
     972     * @return  string "post" or "page"
     973     * @see     _is_post(), _is_page()
     974     */
     975    static public function _is_post_or_page()
     976    {
     977        $post_type = self::_get_current_post_type();
     978
     979        if (isset($post_type))
     980        {
     981            if ('page' == $post_type)
     982            {
     983                return 'page';
     984            }
     985            else
     986            {
     987                return 'post';
     988            }
     989        }
     990
     991        return NULL;
     992    }
     993
     994    /**
     995     * Used to check for the current post type, works when creating or editing a
     996     * new post, page or custom post type.
     997     *
     998     * @since   1.4.6
     999     * @return  string [custom_post_type], page or post
     1000     */
     1001    static public function _get_current_post_type()
     1002    {
     1003        $uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : NULL ;
     1004
     1005        if ( isset( $uri ) )
     1006        {
     1007            $uri_parts = parse_url($uri);
     1008
     1009            $file = basename($uri_parts['path']);
     1010
     1011            if ($uri AND in_array($file, array('post.php', 'post-new.php')))
     1012            {
     1013                $post_id = self::_get_post_id();
     1014
     1015                $post_type = isset($_GET['post_type']) ? $_GET['post_type'] : NULL ;
     1016
     1017                $post_type = $post_id ? get_post_type($post_id) : $post_type ;
     1018
     1019                if (isset($post_type))
     1020                {
     1021                    return $post_type;
     1022                }
     1023                else
     1024                {
     1025                    // because of the 'post.php' and 'post-new.php' checks above, we can default to 'post'
     1026                    return 'post';
     1027                }
     1028            }
     1029        }
     1030
     1031        return NULL;
     1032    }
     1033
     1034    /**
     1035     * Used to get the current post id.
     1036     *
     1037     * @since   1.4.8
     1038     * @return  int post ID
     1039     */
     1040    static public function _get_post_id()
     1041    {
     1042        global $post;
     1043
     1044        $p_post_id = isset($_POST['post_ID']) ? $_POST['post_ID'] : null ;
     1045
     1046        $g_post_id = isset($_GET['post']) ? $_GET['post'] : null ;
     1047
     1048        $post_id = $g_post_id ? $g_post_id : $p_post_id ;
     1049
     1050        $post_id = isset($post->ID) ? $post->ID : $post_id ;
     1051
     1052        if (isset($post_id))
     1053        {
     1054            return (integer) $post_id;
     1055        }
     1056
     1057        return null;
     1058    }
     1059
     1060    /**
     1061     * @since   1.0
     1062     */
     1063    public function can_output()
     1064    {
     1065        $post_id = self::_get_post_id();
     1066
     1067        if (!empty($this->exclude_template) OR !empty($this->include_template))
     1068        {
     1069            $template_file = get_post_meta($post_id,'_wp_page_template',TRUE);
     1070        }
     1071
     1072        if
     1073        (
     1074            !empty($this->exclude_category) OR
     1075            !empty($this->exclude_category_id) OR
     1076            !empty($this->include_category) OR
     1077            !empty($this->include_category_id)
     1078        )
     1079        {
     1080            $categories = wp_get_post_categories($post_id,'fields=all');
     1081        }
     1082
     1083        if
     1084        (
     1085            !empty($this->exclude_tag) OR
     1086            !empty($this->exclude_tag_id) OR
     1087            !empty($this->include_tag) OR
     1088            !empty($this->include_tag_id)
     1089        )
     1090        {
     1091            $tags = wp_get_post_tags($post_id);
     1092        }
     1093
     1094        // processing order: "exclude" then "include"
     1095        // processing order: "template" then "category" then "post"
     1096
     1097        $can_output = TRUE; // include all
     1098
     1099        if
     1100        (
     1101            !empty($this->exclude_template) OR
     1102            !empty($this->exclude_category_id) OR
     1103            !empty($this->exclude_category) OR
     1104            !empty($this->exclude_tag_id) OR
     1105            !empty($this->exclude_tag) OR
     1106            !empty($this->exclude_post_id) OR
     1107            !empty($this->include_template) OR
     1108            !empty($this->include_category_id) OR
     1109            !empty($this->include_category) OR
     1110            !empty($this->include_tag_id) OR
     1111            !empty($this->include_tag) OR
     1112            !empty($this->include_post_id)
     1113        )
     1114        {
     1115            if (!empty($this->exclude_template))
     1116            {
     1117                if (in_array($template_file,$this->exclude_template))
     1118                {
     1119                    $can_output = FALSE;
     1120                }
     1121            }
     1122
     1123            if (!empty($this->exclude_category_id))
     1124            {
     1125                foreach ($categories as $cat)
     1126                {
     1127                    if (in_array($cat->term_id,$this->exclude_category_id))
     1128                    {
     1129                        $can_output = FALSE;
     1130                        break;
     1131                    }
     1132                }
     1133            }
     1134
     1135            if (!empty($this->exclude_category))
     1136            {
     1137                foreach ($categories as $cat)
     1138                {
     1139                    if
     1140                    (
     1141                        in_array($cat->slug,$this->exclude_category) OR
     1142                        in_array($cat->name,$this->exclude_category)
     1143                    )
     1144                    {
     1145                        $can_output = FALSE;
     1146                        break;
     1147                    }
     1148                }
     1149            }
     1150
     1151            if (!empty($this->exclude_tag_id))
     1152            {
     1153                foreach ($tags as $tag)
     1154                {
     1155                    if (in_array($tag->term_id,$this->exclude_tag_id))
     1156                    {
     1157                        $can_output = FALSE;
     1158                        break;
     1159                    }
     1160                }
     1161            }
     1162
     1163            if (!empty($this->exclude_tag))
     1164            {
     1165                foreach ($tags as $tag)
     1166                {
     1167                    if
     1168                    (
     1169                        in_array($tag->slug,$this->exclude_tag) OR
     1170                        in_array($tag->name,$this->exclude_tag)
     1171                    )
     1172                    {
     1173                        $can_output = FALSE;
     1174                        break;
     1175                    }
     1176                }
     1177            }
     1178
     1179            if (!empty($this->exclude_post_id))
     1180            {
     1181                if (in_array($post_id,$this->exclude_post_id))
     1182                {
     1183                    $can_output = FALSE;
     1184                }
     1185            }
     1186
     1187            // excludes are not set use "include only" mode
     1188
     1189            if
     1190            (
     1191                empty($this->exclude_template) AND
     1192                empty($this->exclude_category_id) AND
     1193                empty($this->exclude_category) AND
     1194                empty($this->exclude_tag_id) AND
     1195                empty($this->exclude_tag) AND
     1196                empty($this->exclude_post_id)
     1197            )
     1198            {
     1199                $can_output = FALSE;
     1200            }
     1201
     1202            if (!empty($this->include_template))
     1203            {
     1204                if (in_array($template_file,$this->include_template))
     1205                {
     1206                    $can_output = TRUE;
     1207                }
     1208            }
     1209
     1210            if (!empty($this->include_category_id))
     1211            {
     1212                foreach ($categories as $cat)
     1213                {
     1214                    if (in_array($cat->term_id,$this->include_category_id))
     1215                    {
     1216                        $can_output = TRUE;
     1217                        break;
     1218                    }
     1219                }
     1220            }
     1221
     1222            if (!empty($this->include_category))
     1223            {
     1224                foreach ($categories as $cat)
     1225                {
     1226                    if
     1227                    (
     1228                        in_array($cat->slug,$this->include_category) OR
     1229                        in_array($cat->name,$this->include_category)
     1230                    )
     1231                    {
     1232                        $can_output = TRUE;
     1233                        break;
     1234                    }
     1235                }
     1236            }
     1237
     1238            if (!empty($this->include_tag_id))
     1239            {
     1240                foreach ($tags as $tag)
     1241                {
     1242                    if (in_array($tag->term_id,$this->include_tag_id))
     1243                    {
     1244                        $can_output = TRUE;
     1245                        break;
     1246                    }
     1247                }
     1248            }
     1249
     1250            if (!empty($this->include_tag))
     1251            {
     1252                foreach ($tags as $tag)
     1253                {
     1254                    if
     1255                    (
     1256                        in_array($tag->slug,$this->include_tag) OR
     1257                        in_array($tag->name,$this->include_tag)
     1258                    )
     1259                    {
     1260                        $can_output = TRUE;
     1261                        break;
     1262                    }
     1263                }
     1264            }
     1265
     1266            if (!empty($this->include_post_id))
     1267            {
     1268                if (in_array($post_id,$this->include_post_id))
     1269                {
     1270                    $can_output = TRUE;
     1271                }
     1272            }
     1273        }
     1274
     1275        $post_type = self::_get_current_post_type();
     1276
     1277        if (isset($post_type) AND ! in_array($post_type, $this->types))
     1278        {
     1279            $can_output = FALSE;
     1280        }
     1281
     1282        // filter: output (can_output)
     1283        if ($this->has_filter('output'))
     1284        {
     1285            $can_output = $this->apply_filters('output', $post_id);
     1286        }
     1287
     1288        return $can_output;
     1289    }
     1290
     1291    /**
     1292     * Used to insert global STYLE or SCRIPT tags into the head, called on
     1293     * WordPress admin_footer action.
     1294     *
     1295     * @since   1.3
     1296     * @see     _global_foot()
     1297     */
     1298    static public function _global_head()
     1299    {
     1300        // must be creating or editing a post or page
     1301        if ( ! self::_is_post() AND ! self::_is_page()) return;
     1302
     1303        // todo: you're assuming people will want to use this exact functionality
     1304        // consider giving a developer access to change this via hooks/callbacks
     1305
     1306        // include javascript for special functionality
     1307        ?><style type="text/css"> .wpa_group.tocopy { display:none; } </style>
     1308        <script type="text/javascript">
     1309        /* <![CDATA[ */
     1310        jQuery(function($)
     1311        {
     1312            $(document).click(function(e)
     1313            {
     1314                var elem = $(e.target);
     1315
     1316                if (elem.attr('class') && elem.filter('[class*=dodelete]').length)
     1317                {
     1318                    e.preventDefault();
     1319
     1320                    var p = elem.parents('.postbox'); /*wp*/
     1321
     1322                    var the_name = elem.attr('class').match(/dodelete-([a-zA-Z0-9_-]*)/i);
     1323
     1324                    the_name = (the_name && the_name[1]) ? the_name[1] : null ;
     1325
     1326                    /* todo: expose and allow editing of this message */
     1327                    if (confirm('This action can not be undone, are you sure?'))
     1328                    {
     1329                        if (the_name)
     1330                        {
     1331                            $('.wpa_group-'+ the_name, p).not('.tocopy').remove();
     1332                        }
     1333                        else
     1334                        {
     1335                            elem.parents('.wpa_group').remove();
     1336                        }
     1337
     1338                        var the_group = elem.parents('.wpa_group');
     1339
     1340                        if(the_group && the_group.attr('class'))
     1341                        {
     1342                            the_name = the_group.attr('class').match(/wpa_group-([a-zA-Z0-9_-]*)/i);
     1343
     1344                            the_name = (the_name && the_name[1]) ? the_name[1] : null ;
     1345
     1346                            checkLoopLimit(the_name);
     1347                        }
     1348
     1349                        $.wpalchemy.trigger('wpa_delete');
     1350                    }
     1351                }
     1352            });
     1353
     1354            $('[class*=docopy-]').click(function(e)
     1355            {
     1356                e.preventDefault();
     1357
     1358                var p = $(this).parents('.postbox'); /*wp*/
     1359
     1360                var the_name = $(this).attr('class').match(/docopy-([a-zA-Z0-9_-]*)/i)[1];
     1361
     1362                var the_group = $('.wpa_group-'+ the_name +'.tocopy', p).first();
     1363
     1364                var the_clone = the_group.clone().removeClass('tocopy last');
     1365
     1366                var the_props = ['name', 'id', 'for', 'class'];
     1367
     1368                the_group.find('*').each(function(i, elem)
     1369                {
     1370                    for (var j = 0; j < the_props.length; j++)
     1371                    {
     1372                        var the_prop = $(elem).attr(the_props[j]);
     1373
     1374                        if (the_prop)
     1375                        {
     1376                            var the_match = the_prop.match(/\[(\d+)\]/i);
     1377
     1378                            if (the_match)
     1379                            {
     1380                                the_prop = the_prop.replace(the_match[0],'['+ (+the_match[1]+1) +']');
     1381
     1382                                $(elem).attr(the_props[j], the_prop);
     1383                            }
     1384
     1385                            the_match = null;
     1386
     1387                            // todo: this may prove to be too broad of a search
     1388                            the_match = the_prop.match(/n(\d+)/i);
     1389
     1390                            if (the_match)
     1391                            {
     1392                                the_prop = the_prop.replace(the_match[0], 'n' + (+the_match[1]+1));
     1393
     1394                                $(elem).attr(the_props[j], the_prop);
     1395                            }
     1396                        }
     1397                    }
     1398                });
     1399
     1400                if ($(this).hasClass('ontop'))
     1401                {
     1402                    $('.wpa_group-'+ the_name, p).first().before(the_clone);
     1403                }
     1404                else
     1405                {
     1406                    the_group.before(the_clone);
     1407                }
     1408
     1409                checkLoopLimit(the_name);
     1410
     1411                $.wpalchemy.trigger('wpa_copy', [the_clone]);
     1412            });
     1413
     1414            function checkLoopLimit(name)
     1415            {
     1416                var elem = $('.docopy-' + name);
     1417
     1418                var the_class = $('.wpa_loop-' + name).attr('class');
     1419
     1420                if (the_class)
     1421                {
     1422                    var the_match = the_class.match(/wpa_loop_limit-([0-9]*)/i);
     1423
     1424                    if (the_match)
     1425                    {
     1426                        var the_limit = the_match[1];
     1427
     1428                        if ($('.wpa_group-' + name).not('.wpa_group.tocopy').length >= the_limit)
     1429                        {
     1430                            elem.hide();
     1431                        }
     1432                        else
     1433                        {
     1434                            elem.show();
     1435                        }
     1436                    }
     1437                }
     1438            }
     1439
     1440            /* do an initial limit check, show or hide buttons */
     1441            $('[class*=docopy-]').each(function()
     1442            {
     1443                var the_name = $(this).attr('class').match(/docopy-([a-zA-Z0-9_-]*)/i)[1];
     1444
     1445                checkLoopLimit(the_name);
     1446            });
     1447        });
     1448        /* ]]> */
     1449        </script>
     1450        <?php
     1451    }
     1452
     1453    /**
     1454     * Used to insert global SCRIPT tags into the footer, called on WordPress
     1455     * admin_footer action.
     1456     *
     1457     * @since   1.3
     1458     * @see     _global_head()
     1459     */
     1460    static public function _global_foot()
     1461    {
     1462        // must be creating or editing a post or page
     1463        if ( ! self::_is_post() AND ! self::_is_page()) return;
     1464
     1465        ?>
     1466        <script type="text/javascript">
     1467        /* <![CDATA[ */
     1468        (function($){ /* not using jQuery ondomready, code runs right away in footer */
     1469
     1470            /* use a global dom element to attach events to */
     1471            $.wpalchemy = $('<div></div>').attr('id','wpalchemy').appendTo('body');
     1472
     1473        })(jQuery);
     1474        /* ]]> */
     1475        </script>
     1476        <?php
     1477    }
     1478
     1479    /**
     1480     * Gets the meta data for a meta box
     1481     *
     1482     * @since   1.0
     1483     * @param   int $post_id optional post ID for which to retrieve the meta data
     1484     * @return  array
     1485     * @see     _meta
     1486     */
     1487    public function the_meta($post_id = NULL)
     1488    {
     1489        return $this->_meta($post_id);
     1490    }
     1491
     1492    /**
     1493     * Gets the meta data for a meta box
     1494     *
     1495     * Internal method calls will typically bypass the data retrieval and will
     1496     * immediately return the current meta data
     1497     *
     1498     * @since   1.3
     1499     * @param   int $post_id optional post ID for which to retrieve the meta data
     1500     * @param   bool $internal optional boolean if internally calling
     1501     * @return  array
     1502     * @see     the_meta()
     1503     */
     1504    protected function _meta($post_id = NULL, $internal = FALSE)
     1505    {
     1506        if ( ! is_numeric($post_id))
     1507        {
     1508            if ($internal AND $this->current_post_id)
     1509            {
     1510                $post_id = $this->current_post_id;
     1511            }
     1512            else
     1513            {
     1514                global $post;
     1515
     1516                $post_id = $post->ID;
     1517            }
     1518        }
     1519
     1520        // this allows multiple internal calls to _meta() without having to fetch data everytime
     1521        if ($internal AND !empty($this->meta) AND $this->current_post_id == $post_id) return $this->meta;
     1522
     1523        $this->current_post_id = $post_id;
     1524
     1525        // WPALCHEMY_MODE_ARRAY
     1526
     1527        $meta = get_post_meta($post_id, $this->id, TRUE);
     1528
     1529        // WPALCHEMY_MODE_EXTRACT
     1530
     1531        $fields = get_post_meta($post_id, $this->id . '_fields', TRUE);
     1532
     1533        if ( ! empty($fields) AND is_array($fields))
     1534        {
     1535            $meta = array();
     1536
     1537            foreach ($fields as $field)
     1538            {
     1539                $field_noprefix = preg_replace('/^' . $this->prefix . '/i', '', $field);
     1540                $meta[$field_noprefix] = get_post_meta($post_id, $field, TRUE);
     1541            }
     1542        }
     1543
     1544        $this->meta = $meta;
     1545
     1546        return $this->meta;
     1547    }
     1548
     1549    // user can also use the_ID(), php functions are case-insensitive
     1550    /**
     1551     * @since   1.0
     1552     */
     1553    public function the_id()
     1554    {
     1555        echo $this->get_the_id();
     1556    }
     1557
     1558    /**
     1559     * @since   1.0
     1560     */
     1561    public function get_the_id()
     1562    {
     1563        return $this->id;
     1564    }
     1565
     1566    /**
     1567     * @since   1.0
     1568     */
     1569    public function the_field($n, $hint = NULL)
     1570    {
     1571        if ($this->in_loop) $this->subname = $n;
     1572        else $this->name = $n;
     1573
     1574        $this->hint = $hint;
     1575    }
     1576
     1577    /**
     1578     * @since   1.0
     1579     */
     1580    public function have_value($n = NULL)
     1581    {
     1582        if ($this->get_the_value($n)) return TRUE;
     1583
     1584        return FALSE;
     1585    }
     1586
     1587    /**
     1588     * @since   1.0
     1589     */
     1590    public function the_value($n = NULL)
     1591    {
     1592        echo $this->get_the_value($n);
     1593    }
     1594
     1595    /**
     1596     * @since   1.0
     1597     */
     1598    public function get_the_value($n = NULL, $collection = FALSE)
     1599    {
     1600        $this->_meta(NULL, TRUE);
     1601
     1602        $value = null;
     1603
     1604        if ($this->in_loop)
     1605        {
     1606            if(isset($this->meta[$this->name]))
     1607            {
     1608                $n = is_null($n) ? $this->subname : $n ;
     1609
     1610                if(!is_null($n))
     1611                {
     1612                    if ($collection)
     1613                    {
     1614                        if(isset($this->meta[$this->name][$this->current]))
     1615                        {
     1616                            $value = $this->meta[$this->name][$this->current];
     1617                        }
     1618                    }
     1619                    else
     1620                    {
     1621                        if(isset($this->meta[$this->name][$this->current][$n]))
     1622                        {
     1623                            $value = $this->meta[$this->name][$this->current][$n];
     1624                        }
     1625                    }
     1626                }
     1627                else
     1628                {
     1629                    if ($collection)
     1630                    {
     1631                        if(isset($this->meta[$this->name]))
     1632                        {
     1633                            $value = $this->meta[$this->name];
     1634                        }
     1635                    }
     1636                    else
     1637                    {
     1638                        if(isset($this->meta[$this->name][$this->current]))
     1639                        {
     1640                            $value = $this->meta[$this->name][$this->current];
     1641                        }
     1642                    }
     1643                }
     1644            }
     1645        }
     1646        else
     1647        {
     1648            $n = is_null($n) ? $this->name : $n ;
     1649
     1650            if(isset($this->meta[$n]))
     1651            {
     1652                $value = $this->meta[$n];
     1653            }
     1654        }
     1655
     1656        if (is_string($value) || is_numeric($value))
     1657        {
     1658            if ($this->in_template)
     1659            {
     1660                return htmlentities($value, ENT_QUOTES, 'UTF-8');
     1661            }
     1662            else
     1663            {
     1664                // http://wordpress.org/support/topic/call-function-called-by-embed-shortcode-direct
     1665                // http://phpdoc.wordpress.org/trunk/WordPress/Embed/WP_Embed.html#run_shortcode
     1666
     1667                global $wp_embed;
     1668
     1669                return do_shortcode($wp_embed->run_shortcode($value));
     1670            }
     1671        }
     1672        else
     1673        {
     1674            // value can sometimes be an array
     1675            return $value;
     1676        }
     1677    }
     1678
     1679    /**
     1680     * @since   1.0
     1681     */
     1682    public function the_name($n = NULL)
     1683    {
     1684        echo $this->get_the_name($n);
     1685    }
     1686
     1687    /**
     1688     * @since   1.0
     1689     */
     1690    public function get_the_name($n = NULL)
     1691    {
     1692        if (!$this->in_template AND $this->mode == WPALCHEMY_MODE_EXTRACT)
     1693        {
     1694            return $this->prefix . str_replace($this->prefix, '', is_null($n) ? $this->name : $n);
     1695        }
     1696
     1697        if ($this->in_loop)
     1698        {
     1699            $n = is_null($n) ? $this->subname : $n ;
     1700
     1701            if (!is_null($n)) $the_field = $this->id . '[' . $this->name . '][' . $this->current . '][' . $n . ']' ;
     1702
     1703            else $the_field = $this->id . '[' . $this->name . '][' . $this->current . ']' ;
     1704        }
     1705        else
     1706        {
     1707            $n = is_null($n) ? $this->name : $n ;
     1708
     1709            $the_field = $this->id . '[' . $n . ']';
     1710        }
     1711
     1712        $hints = array
     1713        (
     1714            WPALCHEMY_FIELD_HINT_CHECKBOX_MULTI,
     1715            WPALCHEMY_FIELD_HINT_SELECT_MULTI,
     1716            WPALCHEMY_FIELD_HINT_SELECT_MULTIPLE,
     1717        );
     1718
     1719        if (in_array($this->hint, $hints))
     1720        {
     1721            $the_field .= '[]';
     1722        }
     1723
     1724        return $the_field;
     1725    }
     1726
     1727    /**
     1728     * @since   1.1
     1729     */
     1730    public function the_index()
     1731    {
     1732        echo $this->get_the_index();
     1733    }
     1734
     1735    /**
     1736     * @since   1.1
     1737     */
     1738    public function get_the_index()
     1739    {
     1740        return $this->in_loop ? $this->current : 0 ;
     1741    }
     1742
     1743    /**
     1744     * @since   1.0
     1745     */
     1746    public function is_first()
     1747    {
     1748        if ($this->in_loop AND $this->current == 0) return TRUE;
     1749
     1750        return FALSE;
     1751    }
     1752
     1753    /**
     1754     * @since   1.0
     1755     */
     1756    public function is_last()
     1757    {
     1758        if ($this->in_loop AND ($this->current+1) == $this->length) return TRUE;
     1759
     1760        return FALSE;
     1761    }
     1762
     1763    /**
     1764     * Used to check if a value is a match
     1765     *
     1766     * @since   1.1
     1767     * @param   string $n the field name to check or the value to check for (if the_field() is used prior)
     1768     * @param   string $v optional the value to check for
     1769     * @return  bool
     1770     * @see     is_value()
     1771     */
     1772    public function is_value($n, $v = NULL)
     1773    {
     1774        if (is_null($v))
     1775        {
     1776            $the_value = $this->get_the_value();
     1777
     1778            $v = $n;
     1779        }
     1780        else
     1781        {
     1782            $the_value = $this->get_the_value($n);
     1783        }
     1784
     1785        if($v == $the_value) return TRUE;
     1786
     1787        return FALSE;
     1788    }
     1789
     1790    /**
     1791     * Used to check if a value is selected, useful when working with checkbox,
     1792     * radio and select values.
     1793     *
     1794     * @since   1.3
     1795     * @param   string $n the field name to check or the value to check for (if the_field() is used prior)
     1796     * @param   string $v optional the value to check for
     1797     * @return  bool
     1798     * @see     is_value()
     1799     */
     1800    public function is_selected($n, $v = NULL, $is_default = FALSE)
     1801    {
     1802        if (is_null($v))
     1803        {
     1804            $the_value = $this->get_the_value(NULL);
     1805
     1806            $v = $n;
     1807        }
     1808        else
     1809        {
     1810            $the_value = $this->get_the_value($n);
     1811        }
     1812
     1813        if (is_array($the_value))
     1814        {
     1815            if (in_array($v, $the_value)) return TRUE;
     1816        }
     1817        elseif($v == $the_value)
     1818        {
     1819            return TRUE;
     1820        }
     1821
     1822        if( empty( $the_value ) && $is_default )
     1823        {
     1824            return TRUE;
     1825        }
     1826
     1827        return FALSE;
     1828    }
     1829
     1830    /**
     1831     * Prints the current state of a checkbox field and should be used inline
     1832     * within the INPUT tag.
     1833     *
     1834     * @since   1.3
     1835     * @param   string $n the field name to check or the value to check for (if the_field() is used prior)
     1836     * @param   string $v optional the value to check for
     1837     * @see     get_the_checkbox_state()
     1838     */
     1839    public function the_checkbox_state($n, $v = NULL, $is_default = FALSE)
     1840    {
     1841        echo $this->get_the_checkbox_state($n, $v, $is_default);
     1842    }
     1843
     1844    /**
     1845     * Returns the current state of a checkbox field, the returned string is
     1846     * suitable to be used inline within the INPUT tag.
     1847     *
     1848     * @since   1.3
     1849     * @param   string $n the field name to check or the value to check for (if the_field() is used prior)
     1850     * @param   string $v optional the value to check for
     1851     * @return  string suitable to be used inline within the INPUT tag
     1852     * @see     the_checkbox_state()
     1853     */
     1854    public function get_the_checkbox_state($n, $v = NULL, $is_default = FALSE)
     1855    {
     1856        if ($this->is_selected($n, $v, $is_default)) return ' checked="checked"';
     1857    }
     1858
     1859    /**
     1860     * Prints the current state of a radio field and should be used inline
     1861     * within the INPUT tag.
     1862     *
     1863     * @since   1.3
     1864     * @param   string $n the field name to check or the value to check for (if the_field() is used prior)
     1865     * @param   string $v optional the value to check for
     1866     * @see     get_the_radio_state()
     1867     */
     1868    public function the_radio_state($n, $v = NULL, $is_default = FALSE)
     1869    {
     1870        echo $this->get_the_checkbox_state($n, $v, $is_default);
     1871    }
     1872
     1873    /**
     1874     * Returns the current state of a radio field, the returned string is
     1875     * suitable to be used inline within the INPUT tag.
     1876     *
     1877     * @since   1.3
     1878     * @param   string $n the field name to check or the value to check for (if the_field() is used prior)
     1879     * @param   string $v optional the value to check for
     1880     * @return  string suitable to be used inline within the INPUT tag
     1881     * @see     the_radio_state()
     1882     */
     1883    public function get_the_radio_state($n, $v = NULL, $is_default = FALSE)
     1884    {
     1885        return $this->get_the_checkbox_state($n, $v, $is_default);
     1886    }
     1887
     1888    /**
     1889     * Prints the current state of a select field and should be used inline
     1890     * within the SELECT tag.
     1891     *
     1892     * @since   1.3
     1893     * @param   string $n the field name to check or the value to check for (if the_field() is used prior)
     1894     * @param   string $v optional the value to check for
     1895     * @see     get_the_select_state()
     1896     */
     1897    public function the_select_state($n, $v = NULL, $is_default = FALSE)
     1898    {
     1899        echo $this->get_the_select_state($n, $v, $is_default);
     1900    }
     1901
     1902    /**
     1903     * Returns the current state of a select field, the returned string is
     1904     * suitable to be used inline within the SELECT tag.
     1905     *
     1906     * @since   1.3
     1907     * @param   string $n the field name to check or the value to check for (if the_field() is used prior)
     1908     * @param   string $v optional the value to check for
     1909     * @return  string suitable to be used inline within the SELECT tag
     1910     * @see     the_select_state()
     1911     */
     1912    public function get_the_select_state($n, $v = NULL, $is_default = FALSE)
     1913    {
     1914        if ($this->is_selected($n, $v, $is_default)) return ' selected="selected"';
     1915    }
     1916
     1917    /**
     1918     * @since   1.1
     1919     */
     1920    public function the_group_open($t = 'div')
     1921    {
     1922        echo $this->get_the_group_open($t);
     1923    }
     1924
     1925    /**
     1926     * @since   1.1
     1927     */
     1928    public function get_the_group_open($t = 'div')
     1929    {
     1930        $this->group_tag = $t;
     1931
     1932        $loop_open = NULL;
     1933
     1934        $loop_open_classes = array('wpa_loop', 'wpa_loop-' . $this->name);
     1935
     1936        $css_class = array('wpa_group', 'wpa_group-'. $this->name);
     1937
     1938        if ($this->is_first())
     1939        {
     1940            array_push($css_class, 'first');
     1941
     1942            $loop_open = '<div class="wpa_loop">';
     1943
     1944            if (isset($this->_loop_data->limit))
     1945            {
     1946                array_push($loop_open_classes, 'wpa_loop_limit-' . $this->_loop_data->limit);
     1947            }
     1948
     1949            $loop_open = '<div id="wpa_loop-'. $this->name .'" class="' . implode(' ', $loop_open_classes) . '">';
     1950        }
     1951
     1952        if ($this->is_last())
     1953        {
     1954            array_push($css_class, 'last');
     1955
     1956            if ($this->in_loop == 'multi')
     1957            {
     1958                array_push($css_class, 'tocopy');
     1959            }
     1960        }
     1961
     1962        return $loop_open . '<' . $t . ' class="'. implode(' ', $css_class) . '">';
     1963    }
     1964
     1965    /**
     1966     * @since   1.1
     1967     */
     1968    public function the_group_close()
     1969    {
     1970        echo $this->get_the_group_close();
     1971    }
     1972
     1973    /**
     1974     * @since   1.1
     1975     */
     1976    public function get_the_group_close()
     1977    {
     1978        $loop_close = NULL;
     1979
     1980        if ($this->is_last())
     1981        {
     1982            $loop_close = '</div>';
     1983        }
     1984
     1985        return '</' . $this->group_tag . '>' . $loop_close;
     1986    }
     1987
     1988    /**
     1989     * @since   1.1
     1990     */
     1991    public function have_fields_and_multi($n, $options = NULL)
     1992    {
     1993        if (is_array($options))
     1994        {
     1995            // use as stdClass object
     1996            $options = (object)$options;
     1997
     1998            $length = @$options->length;
     1999
     2000            $this->_loop_data->limit = @$options->limit;
     2001        }
     2002        else
     2003        {
     2004            // backward compatibility (bc)
     2005            $length = $options;
     2006        }
     2007
     2008        $this->_meta(NULL, TRUE);
     2009
     2010        $this->in_loop = 'multi';
     2011
     2012        return $this->_loop($n, $length, 2);
     2013    }
     2014
     2015    /**
     2016     * @deprecated
     2017     * @since   1.0
     2018     */
     2019    public function have_fields_and_one($n)
     2020    {
     2021        $this->_meta(NULL, TRUE);
     2022        $this->in_loop = 'single';
     2023        return $this->_loop($n,NULL,1);
     2024    }
     2025
     2026    /**
     2027     * @since   1.0
     2028     */
     2029    public function have_fields($n,$length=NULL)
     2030    {
     2031        $this->_meta(NULL, TRUE);
     2032        $this->in_loop = 'normal';
     2033        return $this->_loop($n,$length);
     2034    }
     2035
     2036    /**
     2037     * @since   1.0
     2038     */
     2039    protected function _loop($n,$length=NULL,$and_one=0)
     2040    {
     2041        if (!$this->in_loop)
     2042        {
     2043            $this->in_loop = TRUE;
     2044        }
     2045
     2046        $this->name = $n;
     2047
     2048        $cnt = count(!empty($this->meta[$n])?$this->meta[$n]:NULL);
     2049
     2050        $length = is_null($length) ? $cnt : $length ;
     2051
     2052        if ($this->in_loop == 'multi' AND $cnt > $length) $length = $cnt;
     2053
     2054        $this->length = $length;
     2055
     2056        if ($this->in_template AND $and_one)
     2057        {
     2058            if ($length == 0)
     2059            {
     2060                $this->length = $and_one;
     2061            }
     2062            else
     2063            {
     2064                $this->length = $length+1;
     2065            }
     2066        }
     2067
     2068        $this->current++;
     2069
     2070        if ($this->current < $this->length)
     2071        {
     2072            $this->subname = NULL;
     2073
     2074            $this->fieldtype = NULL;
     2075
     2076            return TRUE;
     2077        }
     2078        else if ($this->current == $this->length)
     2079        {
     2080            $this->name = NULL;
     2081
     2082            $this->current = -1;
     2083        }
     2084
     2085        $this->in_loop = FALSE;
     2086
     2087        $this->_loop_data = new \stdClass;
     2088
     2089        return FALSE;
     2090    }
     2091
     2092    /**
     2093     * @since   1.0
     2094     */
     2095    public function _save($post_id)
     2096    {
     2097        /**
     2098         * note: the "save_post" action fires for saving revisions and post/pages,
     2099         * when saving a post this function fires twice, once for a revision save,
     2100         * and again for the post/page save ... the $post_id is different for the
     2101         * revision save, this means that "get_post_meta()" will not work if trying
     2102         * to get values for a revision (as it has no post meta data)
     2103         * see http://alexking.org/blog/2008/09/06/wordpress-26x-duplicate-custom-field-issue
     2104         *
     2105         * why let the code run twice? wordpress does not currently save post meta
     2106         * data per revisions (I think it should, so users can do a complete revert),
     2107         * so in the case that this functionality changes, let it run twice
     2108         */
     2109
     2110        $real_post_id = isset($_POST['post_ID']) ? $_POST['post_ID'] : NULL ;
     2111
     2112        // check autosave
     2113        if (defined('DOING_AUTOSAVE') AND DOING_AUTOSAVE AND !$this->autosave) return $post_id;
     2114
     2115        // make sure data came from our meta box, verify nonce
     2116        $nonce = isset($_POST[$this->id.'_nonce']) ? $_POST[$this->id.'_nonce'] : NULL ;
     2117        if (!wp_verify_nonce($nonce, $this->id)) return $post_id;
     2118
     2119        // check user permissions
     2120        if ($_POST['post_type'] == 'page')
     2121        {
     2122            if (!current_user_can('edit_page', $post_id)) return $post_id;
     2123        }
     2124        else
     2125        {
     2126            if (!current_user_can('edit_post', $post_id)) return $post_id;
     2127        }
     2128
     2129        // authentication passed, save data
     2130
     2131        $new_data = isset( $_POST[$this->id] ) ? $_POST[$this->id] : NULL ;
     2132
     2133        self::clean($new_data);
     2134
     2135        if (empty($new_data))
     2136        {
     2137            $new_data = NULL;
     2138        }
     2139
     2140        // filter: save
     2141        if ($this->has_filter('save'))
     2142        {
     2143            $new_data = $this->apply_filters('save', $new_data, $real_post_id);
     2144
     2145            /**
     2146             * halt saving
     2147             * @since 1.3.4
     2148             */
     2149            if (FALSE === $new_data) return $post_id;
     2150
     2151            self::clean($new_data);
     2152        }
     2153
     2154        // get current fields, use $real_post_id (checked for in both modes)
     2155        $current_fields = get_post_meta($real_post_id, $this->id . '_fields', TRUE);
     2156
     2157        if ($this->mode == WPALCHEMY_MODE_EXTRACT)
     2158        {
     2159            $new_fields = array();
     2160
     2161            if (is_array($new_data))
     2162            {
     2163                foreach ($new_data as $k => $v)
     2164                {
     2165                    $field = $this->prefix . $k;
     2166
     2167                    array_push($new_fields,$field);
     2168
     2169                    $new_value = $new_data[$k];
     2170
     2171                    if (is_null($new_value))
     2172                    {
     2173                        delete_post_meta($post_id, $field);
     2174                    }
     2175                    else
     2176                    {
     2177                        update_post_meta($post_id, $field, $new_value);
     2178                    }
     2179                }
     2180            }
     2181
     2182            $diff_fields = array_diff((array)$current_fields,$new_fields);
     2183
     2184            if (is_array($diff_fields))
     2185            {
     2186                foreach ($diff_fields as $field)
     2187                {
     2188                    delete_post_meta($post_id,$field);
     2189                }
     2190            }
     2191
     2192            delete_post_meta($post_id, $this->id . '_fields');
     2193
     2194            if ( ! empty($new_fields))
     2195            {
     2196                add_post_meta($post_id,$this->id . '_fields', $new_fields, TRUE);
     2197            }
     2198
     2199            // keep data tidy, delete values if previously using WPALCHEMY_MODE_ARRAY
     2200            delete_post_meta($post_id, $this->id);
     2201        }
     2202        else
     2203        {
     2204            if (is_null($new_data))
     2205            {
     2206                delete_post_meta($post_id, $this->id);
     2207            }
     2208            else
     2209            {
     2210                update_post_meta($post_id, $this->id, $new_data);
     2211            }
     2212
     2213            // keep data tidy, delete values if previously using WPALCHEMY_MODE_EXTRACT
     2214            if (is_array($current_fields))
     2215            {
     2216                foreach ($current_fields as $field)
     2217                {
     2218                    delete_post_meta($post_id, $field);
     2219                }
     2220
     2221                delete_post_meta($post_id, $this->id . '_fields');
     2222            }
     2223        }
     2224
     2225        // action: save
     2226        if ($this->has_action('save'))
     2227        {
     2228            $this->do_action('save', $new_data, $real_post_id);
     2229        }
     2230
     2231        return $post_id;
     2232    }
     2233
     2234    /**
     2235     * Cleans an array, removing blank ('') values
     2236     *
     2237     * @since   1.0
     2238     * @param   array the array to clean (passed by reference)
     2239     */
     2240    static public function clean(&$arr)
     2241    {
     2242        if (is_array($arr))
     2243        {
     2244            foreach ($arr as $i => $v)
     2245            {
     2246                if (is_array($arr[$i]))
     2247                {
     2248                    self::clean($arr[$i]);
     2249
     2250                    if (!count($arr[$i]))
     2251                    {
     2252                        unset($arr[$i]);
     2253                    }
     2254                }
     2255                else
     2256                {
     2257                    if ('' == trim($arr[$i]) OR is_null($arr[$i]))
     2258                    {
     2259                        unset($arr[$i]);
     2260                    }
     2261                }
     2262            }
     2263
     2264            if (!count($arr))
     2265            {
     2266                $arr = array();
     2267            }
     2268            else
     2269            {
     2270                $keys = array_keys($arr);
     2271
     2272                $is_numeric = TRUE;
     2273
     2274                foreach ($keys as $key)
     2275                {
     2276                    if (!is_numeric($key))
     2277                    {
     2278                        $is_numeric = FALSE;
     2279                        break;
     2280                    }
     2281                }
     2282
     2283                if ($is_numeric)
     2284                {
     2285                    $arr = array_values($arr);
     2286                }
     2287            }
     2288        }
     2289    }
    23642290}
    2365 
    2366 /* eof */
Note: See TracChangeset for help on using the changeset viewer.