<?php
/*
Plugin Name: New Tag Section
Plugin URI: 
Description: Modified tag entry section for new admin interface
Version: 1.0
Author: Jennifer Hodgdon, Poplar ProductivityWare
Author URI: http://poplarware.com

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.
*/

// ---  Filters and actions

add_action('admin_print_scripts', 'new_tag_js_header');
add_action('dbx_post_advanced', 'new_tag_edit_form' );
$new_tag_initial_txt = __( "Add new tag" );

// ---  Functions

// JavaScript for editing screen
function new_tag_js_header() {

  global $editing, $post, $post_ID;
  global $new_tag_initial_txt;

  if( !$editing ) {
    // not on a post/page edit screen
    return;
  }

  if( $post && $post->post_type == 'page' ) {
    // it's a page, not a post -- note that $post is NOT set for a new
    // page/post, see below
    return;
  }

  // check the URL to see if it's a new page screen
  $req_url = $_SERVER['REQUEST_URI'];
  if( preg_match( '/page-new.php$/', $req_url )) {
    // it's the "new page" editing screen, not new post
    return;
  }

  // if we get here, we are on a post editing page, so we need the 
  // JavaScript for this page

  // use JQuery library
  wp_print_scripts( array( 'jquery' ));

  // set up button functions and tag arrays

?>
<style type="text/css">
#new_tag_popup ul { list-style: none; color: black; background-color: #eeeeee; border: 1px solid black; margin: 0; padding: 0; width: 150px; }
#new_tag_popup ul li { margin-left: 0; padding: 2px; text-indent: 0; background: inherit;}
.ntselected { background: white; font-style: bold;}
.ntdelbutton { border: 1px solid black; padding: 0;}
</style>
<script type="text/javascript">
  //<![CDATA[
    
    // put tags from DB into an array
    var new_tag_all_tags = new Array();
    var new_tag_new_tags = new Array();
    var new_tag_initial_txt = "<?php echo $new_tag_initial_txt; ?>";

<?php

    $all_tags = get_terms( 'post_tag', 'hide_empty=0' );
    $curr_tags = wp_get_object_terms( $post_ID, 'post_tag' );

    echo _new_tag_terms_to_js( $all_tags, $curr_tags, 'new_tag_all_tags' );
?>

  var new_tag_curr_choices;

  // Pop-up list of tags for type-ahead list
  function new_tag_list_choices() {
    new_tag_hide_choices();

    // find what the user is typing
    var txt = jQuery( '#new_tag_add_tag' ).val();

    // go back to previous comma
    var start = txt.lastIndexOf(',');
    if( start ) {
      txt = txt.substr( start + 1 );
    }
    while( txt.charAt(0) == ' ' && txt.length ) {
      txt = txt.substr( 1 );
    }

    if( !txt.length ) {
      // nothing to suggest
      return;
    }

    // see which tags match this, and add them to pop-up

    jQuery( '#new_tag_popup' ).html( '<ul></ul>' );

    new_tag_curr_choices = new Array();
    var count = 0;
    var i;
    for( i = 0; i < new_tag_all_tags.length; i++ ) {
      if( new_tag_all_tags[i][1].indexOf( txt ) == 0 ) {
        new_tag_curr_choices[ count ] = new_tag_all_tags[i][1];
        count++;
        jQuery( '#new_tag_popup ul' ).append( '<li>' + new_tag_all_tags[i][1] + '</li>' );
      }
    }

    if( !count ) {
      return;
    }

    // Hover action: change class
    jQuery( '#new_tag_popup ul li' ).hover( 
      function( ) {
        jQuery( this ).addClass( "ntselected" );
      },
      function( ) {
        jQuery( this ).removeClass( "ntselected" );
      });

    // Click action: replace text after previous comma with item clicked
    // and hide pop-up
    jQuery( '#new_tag_popup ul li' ).click( function() {
        var prev = jQuery( '#new_tag_add_tag' ).val();
        if( prev.lastIndexOf( ',' ) > 1 ) {
          prev = prev.substring( 0, prev.lastIndexOf( ',' ) + 1) + ' ';
        } else {
          prev = '';
        }
        prev = prev + jQuery( this ).text() + ", ";

        jQuery( '#new_tag_add_tag' ).val( prev );
        jQuery( '#new_tag_add_tag' ).focus();
        new_tag_hide_choices();
      });

    // OK, it's built: display pop-up
    jQuery( '#new_tag_popup' ).show();

  }

  // Hide list of type-ahead choices
  function new_tag_hide_choices() {
    new_tag_curr_choices = new Array();
    jQuery( '#new_tag_popup' ).hide();
  }

  // Add some new tags to the current tags list
  function new_tag_add_curr_tags( newtags ) {

    var i;
    var j;

    for( i = 0; i < newtags.length; i++ ) {
      // trim whitespace at both ends
      var tag = newtags[i].replace(/^\s+/g, '' ).replace( /\s+$/g, '' );
      if( tag.length ) {
        var found = 0;
        for( j = 0; j < new_tag_all_tags.length; j++ ) {
          if( new_tag_all_tags[j][0] == tag ||
              new_tag_all_tags[j][1] == tag ) {
            found = 1;
            new_tag_all_tags[j][2] = 1;
            break;
          }
        }
        if( !found ) {
          new_tag_new_tags[new_tag_new_tags.length] = tag;
        }
      }
    }
  }

  // Mark a tag as not current when a remove button is clicked
  function new_tag_remove_tag() {
    // which one was clicked?
    var id = jQuery( this ).attr( 'id' );
    var num = id.substr( 18 );

    // mark as not current and update
    new_tag_all_tags[num][2] = 0;
    new_tag_update_curr_tags();
  }

  // Remove a new tag from array when a remove button is clicked
  function new_tag_remove_new_tag() {
    // which one was clicked?
    var id = jQuery( this ).attr( 'id' );
    var num = id.substr( 18 );

    // remove from array and update
    new_tag_new_tags.splice( num, 1 );
    new_tag_update_curr_tags();
  }

  // Update main tag entry field and Current Tags section from tags array
  function new_tag_update_curr_tags() {

    // Update main tags input section with tag slugs 
    // (invisible, but this is what gets saved with post)
    // Update Current Tags section with delete  buttons on each tag

    jQuery( '#new_tag_curr_tags_span' ).html( '' );

    var i;
    var tagstr = '';
    var comma = '';
    for( i = 0; i < new_tag_all_tags.length; i++ ) {
      // see if this tag is being used...
      if( new_tag_all_tags[i][2] ) {
        tagstr += comma + new_tag_all_tags[i][0];
        comma = ',';

        var id = 'new_tag_del_button' + i;
        var txt = '<button id="' + id + '" class="ntdelbutton">X</button>&nbsp;' + new_tag_all_tags[i][1] + " ";

        jQuery( '#new_tag_curr_tags_span' ).append( txt );

        jQuery( '#' + id ).click( new_tag_remove_tag );
      }
    }

    for( i = 0; i < new_tag_new_tags.length; i++ ) {
        tagstr += "," + new_tag_new_tags[i];

        var id = 'new_tag_del_button' + i;
        var txt = '<button id="' + id + '" class="ntdelbutton">X</button>&nbsp;' + new_tag_new_tags[i] + " ";

        jQuery( '#new_tag_curr_tags_span' ).append( txt );

        jQuery( '#' + id ).click( new_tag_remove_new_tag );
    }

    jQuery( '#tags-input' ).val( tagstr );

  }


addLoadEvent( function() {
    // Hide the regular tag entry field
    jQuery( '#tagdiv' ).hide();

    // Update curr tags section
    new_tag_update_curr_tags();

    // When user clicks Add Tag field, clear text if it still says'
    // "Add New Tag"

    jQuery( '#new_tag_add_tag' ).click( function() {
        if( jQuery( '#new_tag_add_tag' ).val() ==  new_tag_initial_txt ) {
          jQuery( '#new_tag_add_tag' ).val('');
        }
    });
    
    // When user types in Add tag field, manage type-ahead
    jQuery( '#new_tag_add_tag' ).keyup( function( e ) {

        // which key was clicked? 2 methods to find, to cover browsers...
        var kc = e.which;
        if( !e.which ) {
          kc = window.keyCode;
        }

        // What to do:
        //   escape/comma -- hide choices
        //   alpha character, or backspace: put up choice list

        if( kc == 27  || kc == 44 || kc == 188 ) { 
          new_tag_hide_choices();
        } else if(( kc >= 65 && 
                    kc <= 90 ) ||
                  ( kc >= 97 && 
                    kc <= 122 ) ||
                  kc == 8 ) { 
          // note: timeout is to wait for keystroke to be in text field
          setTimeout( new_tag_list_choices, 100 );
        }
          
    }); 


    // When user clicks on Add button, add tags they typed to curr
    // tags array and update
    jQuery('#new_tag_add_button' ).click( function() {

       // see what was entered
       var txt = jQuery( '#new_tag_add_tag' ).val();
       if( !txt.length ) {
         alert( "<?php _e( 'No tags entered' ); ?>" );
         return;
       }

       // add tags to list and update

       var taglist = txt.split(',');
       new_tag_add_curr_tags( taglist );

       new_tag_update_curr_tags();

       // clear field
       jQuery( '#new_tag_add_tag' ).val( '' );

    });

  });

//]]>
</script>
<?php
}

// Post edit screen section for advanced tag entry
function new_tag_edit_form() {
  global $post_ID;
  global $new_tag_initial_txt;
  
  // this is needed for IE6, which doesn't seem to dynamically
  // size select lists when elements are added later by jQuery
  $sel_width = "2.5in";

  // header info

  echo '<div class="dbx-b-ox-wrapper">' . "\n";
  echo '<fieldset id="new_tag_entry" class="dbx-box">' . "\n";
  echo '<div class="dbx-h-andle-wrapper"><h3 class="dbx-handle">' . 
        __( "New Tag Entry Section" ) . "</h3></div>";   
   
  echo '<div class="dbx-c-ontent-wrapper"><div class="dbx-content" ' .
    ' id="new_tag_entry_section">';

  // add tag section

  echo '<input type="text" id="new_tag_add_tag" name="new_tag_add_tag" value="' .
    $new_tag_initial_txt . '" style="width:90%">';

  echo '<input type="button" name="new_tag_add_button"' .
    'id="new_tag_add_button" value="' .
    __( 'Add' ) . 
    '" />' . "\n";

  echo '<br /><div id="new_tag_popup"></div>';

  echo "<br /><small>" . __("Separate tags with commas") . "</small>";
  echo "</p>\n" ;

  // Current tags on post section

  echo '<p><strong>' ;
  _e( 'Tags used on this post:' );
  echo '</strong></p> <span id="new_tag_curr_tags_span"></span>';

  echo "</div></fieldset></div>\n";
}

// Returns JavaScript for defining an array of tags
// Each entry is array( slug, display-filtered name, 
// true/false for used/not currently )
function _new_tag_terms_to_js( $terms, $curr, $jsvar ) {

  $i = 0;       
  $ret = $jsvar . " = new Array();\n";

  // make array of true/false for if term is currently used
  $arr = array();
  foreach( $terms as $term ) {
    $arr[ $term->term_id ] = 0;
  }
  foreach( $curr as $term ) {
    $arr[ $term->term_id ] = 1;
  }

  // build JS string to define array
  foreach( $terms as $item ) {
    $item = sanitize_term( $item, 'post_tag', 'display' );

    $ret .= $jsvar . "[" . $i . "] = new Array( ";
    $ret .= "'" . _new_tag_js_escape( $item->slug ) . "', " ;
    $ret .= "'" . _new_tag_js_escape( $item->name ) . "', ";
    $ret .= $arr[ $item->term_id ];
    $ret .= " );\n";

    $i++;
  }

  return $ret;
}

// Convert quotes for JS usage
function _new_tag_js_escape( $text ) {
  $text = str_replace("'", "\\'", $text);
  return $text;
}

// Internal function: returns the string with double quotes
// escaped for HTML use
function _new_tag_escape_dq( $str ) {
  return preg_replace( '/"/', '&quot;', $str );
}


?>
