# DynamicPreview - (c) 2014-2017 Juxtaposition. All Rights Reserved.
# This code cannot be redistributed without permission from juxtaposition.jp
# For more information, consult your DynamicPreview license.
#
package DynamicPreview::Callbacks;

use strict;
use warnings;
use utf8;
use DynamicPreview;
use MT::PublishOption;
use Data::Dumper;

sub init_app {
    my ($cb, $app) = @_;

    my $plugin = $app->component('DynamicPreview')
        or die "Plugin not found.";

    # for entry/page
    require MT::CMS::Entry;
    no strict 'refs';
    no warnings 'redefine';
    my $saved_preview = \&MT::CMS::Entry::preview;
    my $_preview = sub {
        my $app = shift;
        my $blog_id = $app->param('blog_id') || 0;
        my $enabled = $plugin->dynamic_preview_enabled($blog_id) || 0;
        unless($blog_id && $enabled) {
            return $saved_preview->($app, @_);
        }

        $app->validate_magic or return;
        # my $entry = MT::CMS::Entry::_create_temp_entry($app)

        require DynamicPreview::CMS;
        my $entry = DynamicPreview::CMS::_create_temp_entry($app)
            or return;
      
        if($app->param('__mode') eq 'dynamic_preview_entry') {
            my $blog = $app->blog;
            require MT::Util;
            if(!$app->param('authored_on_date')) {
                my $authored_on_date = MT::Util::format_ts( "%Y-%m-%d", $entry->authored_on, $blog,
                    $app->user ? $app->user->preferred_language : undef );
                $app->param('authored_on_date', $authored_on_date);
            }
            if(!$app->param('authored_on_time')) {
                my $authored_on_time = MT::Util::format_ts( "%H:%M:%S", $entry->authored_on, $blog,
                    $app->user ? $app->user->preferred_language : undef );
                $app->param('authored_on_time', $authored_on_time);
            }
        }

        # HACK: intercept MT::TemplateMap::load
        my $tmpl_id = $app->param('tmpl_id') || 0;
        my $tmpl_map;
        require MT::TemplateMap;
        my $has_load = defined &MT::TemplateMap::load ? 1 : 0;
        # my $saved_load = \&MT::TemplateMap::load;
        local *MT::TemplateMap::load = sub {
            my $self = shift;
            my $terms = shift;
            my $args = shift;
            my $do_cache = 0;
            if((caller(1))[0] eq __PACKAGE__) {
                if($tmpl_id) {
                    delete $terms->{is_preferred};
                    $terms->{template_id} = $tmpl_id;
                    $do_cache = 1;
                }
            }
            {
                package MT::TemplateMap;
                my $map = $self->SUPER::load($terms, $args, @_);
                if ($do_cache) {
                    $tmpl_map = $map;
                }
                return $map;
            }
        };

        my $html = '';
        my $doc_file = '';
        # HACK: intercept output data
        require MT::FileMgr::Local;
        my $saved_put_data = \&MT::FileMgr::Local::put_data;
        local *MT::FileMgr::Local::put_data = sub {
            my ($fmgr, $from, $to, $type) = @_;
            my @caller = caller(1);
            if(defined $caller[0] && $caller[0] eq __PACKAGE__) {
                $html = $from;
                $doc_file = $to;
                die [ '__DYNAMIC_PREVIEW_INTERCEPT__', $from, $to ];
            }
            return $saved_put_data->(@_);
        };
        my $out = eval {
            MT::CMS::Entry::_build_entry_preview( $app, $entry );
        };
        if(my $e = $@) {
            if(ref $e eq 'ARRAY' && $e->[0] =~ m!^__DYNAMIC_PREVIEW_INTERCEPT__!) {
                $html = $e->[1];
                $doc_file = $e->[2];
            }
            else {
                $plugin->doLog($e);
                die $@;
            };
        }
        else {
            return $out;
        }

        # simulate SSI include
        $html = $plugin->include_virtual($blog_id, $html, $doc_file);
        # insert base tag for relative url
        if ($tmpl_map) {
            # my ( $entry, $blog, $at, $cat, $map, $timestamp, $author ) = @_;
            my $blog = $entry->blog();
            my $at = $entry->is_entry ? 'Individual' : 'Page';
            require MT::Util;
            my $archive_file = MT::Util::archive_file_for($entry, $blog, $at, undef, $tmpl_map);
            if ($archive_file) {
                my $blog_path = $entry->is_entry ? ($blog->archive_path || $blog->site_path) : $blog->site_path;
                require File::Spec;
                $archive_file = File::Spec->catfile($blog_path, $archive_file);
                $doc_file = $archive_file;
            }
        }
        $html = $plugin->insert_base_tag($blog_id, $html, $doc_file, $entry);
        # replace url
        $html = $plugin->replace_url($blog_id, $html, $doc_file);
        # replace string
        $html = $plugin->replace_string($blog_id, $html, $doc_file);

        # # set X-XSS-Protection to 0
        # $app->set_header('X-XSS-Protection' => 0);

        return $html;
    };
    *MT::CMS::Entry::preview = $_preview; 

    # for template 
    require MT::CMS::Template;
    my $saved_template_preview = \&MT::CMS::Template::preview;
    my $_template_preview = sub {
        my $app = shift;
        my $blog_id = $app->param('blog_id') || 0;
        my $enabled = $plugin->dynamic_preview_enabled($blog_id) || 0;
        unless($blog_id && $enabled) {
            return $saved_template_preview->($app, @_);
        }
            
        my $html = '';
        my $doc_file = '';
        # HACK: intercept output data
        require MT::FileMgr::Local;
        my $saved_put_data = \&MT::FileMgr::Local::put_data;
        local *MT::FileMgr::Local::put_data = sub {
            my ($fmgr, $from, $to, $type) = @_;
            my @caller = caller(1);
            if(defined $caller[0] && $caller[0] eq __PACKAGE__) {
                $html = $from;
                $doc_file = $to;
                die [ '__DYNAMIC_PREVIEW_INTERCEPT__', $from, $to ];
            }
            return $saved_put_data->(@_);
        };

        MT->request('dynamic_preview_include_hold_entry',
            $plugin->dynamic_preview_include_hold_entry($blog_id));
        MT->request('dynamic_preview_include_future_entry',
            $plugin->dynamic_preview_include_future_entry($blog_id));

        my $out = eval {
            $saved_template_preview->($app, @_);
        };

        MT->request('dynamic_preview_include_hold_entry', undef);
        MT->request('dynamic_preview_include_future_entry', undef);

        if(my $e = $@) {
            if(ref $e eq 'ARRAY' && $e->[0] =~ m!^__DYNAMIC_PREVIEW_INTERCEPT__!) {
                $html = $e->[1];
                $doc_file = $e->[2];
            }
            else {
                $plugin->doLog($e);
                die;
            };
        }
        else {
            return $out;
        }

        # simulate SSI include
        $html = $plugin->include_virtual($blog_id, $html, $doc_file);
        # insert base tag for relative url
        my $id = $app->param('id');
        my $tmpl;
        if ($id) {
            $tmpl = MT::Template->load({ id => $id, blog_id => $blog_id });
        } else {
            $tmpl = MT::Template->new;
            $tmpl->id(-1);
            $tmpl->blog_id($blog_id);
        }
        if ($tmpl) {
            $html = $plugin->insert_base_tag($blog_id, $html, $doc_file, $tmpl);
        }
        # replace url
        $html = $plugin->replace_url($blog_id, $html, $doc_file);
        # replace string
        $html = $plugin->replace_string($blog_id, $html, $doc_file);

        # # set X-XSS-Protection to 0
        # $app->set_header('X-XSS-Protection' => 0);

        return $html;
    };
    *MT::CMS::Template::preview = $_template_preview;

    # for content_data
    my $saved_content_data_preview = \&MT::CMS::ContentData::preview;
    my $_content_data_preview = sub {
        my $app = shift;
        my $blog_id = $app->param('blog_id') || 0;
        my $enabled = $plugin->dynamic_preview_enabled($blog_id) || 0;
        unless($blog_id && $enabled) {
            return $saved_content_data_preview->($app, @_);
        }

        $app->validate_magic or return;

        require DynamicPreview::CMS;
        my $content_data = DynamicPreview::CMS::_create_temp_content_data($app)
           or return;
      
        if($app->param('__mode') eq 'dynamic_preview_content_data') {
            my $blog = $app->blog;
            require MT::Util;
            if(!$app->param('authored_on_date')) {
                my $authored_on_date = MT::Util::format_ts( "%Y-%m-%d", $content_data->authored_on, $blog,
                    $app->user ? $app->user->preferred_language : undef );
                $app->param('authored_on_date', $authored_on_date);
            }
            if(!$app->param('authored_on_time')) {
                my $authored_on_time = MT::Util::format_ts( "%H:%M:%S", $content_data->authored_on, $blog,
                    $app->user ? $app->user->preferred_language : undef );
                $app->param('authored_on_time', $authored_on_time);
            }
        }

        # HACK: intercept MT::TemplateMap::load
        my $tmpl_id = $app->param('tmpl_id') || 0;
        if ($tmpl_id) {
            require MT::Template;
            my $_tmpl = MT::Template->load($tmpl_id);
            unless($_tmpl) {
                $app->error($app->translate('The template [_1] is not found.', $tmpl_id));
                return;
            }
            if ($content_data->content_type_id != $_tmpl->content_type_id) {
                $app->error(
                    $app->translate('The template [_1] is not associated with the content type [_2].', $_tmpl->name, $content_data->content_type->name)
                );
                return;
            }
        }

        my $tmpl_map;
        require MT::TemplateMap;
        my $has_load = defined &MT::TemplateMap::load ? 1 : 0;
        # my $saved_load = \&MT::TemplateMap::load;
        local *MT::TemplateMap::load = sub {
            my $self = shift;
            my $terms = shift;
            my $args = shift;
            my $do_cache = 0;
            if((caller(1))[0] eq __PACKAGE__) {
                if($tmpl_id) {
                    delete $terms->{is_preferred};
                    $terms->{template_id} = $tmpl_id;
                    $do_cache = 1;
                }
            }
            {
                package MT::TemplateMap;
                my $map = $self->SUPER::load($terms, $args, @_);
                if ($do_cache) {
                    $tmpl_map = $map;
                }
                return $map;
            }
        };

        my $html = '';
        my $doc_file = '';
        # HACK: intercept output data
        require MT::FileMgr::Local;
        my $saved_put_data = \&MT::FileMgr::Local::put_data;
        local *MT::FileMgr::Local::put_data = sub {
            my ($fmgr, $from, $to, $type) = @_;
            my @caller = caller(1);
            if(defined $caller[0] && $caller[0] eq __PACKAGE__) {
                $html = $from;
                $doc_file = $to;
                die [ '__DYNAMIC_PREVIEW_INTERCEPT__', $from, $to ];
            }
            return $saved_put_data->(@_);
        };
        my $out = eval {
            require MT::CMS::ContentData;
            MT::CMS::ContentData::_build_content_data_preview( $app, $content_data );
        };
        if(my $e = $@) {
            if(ref $e eq 'ARRAY' && $e->[0] =~ m!^__DYNAMIC_PREVIEW_INTERCEPT__!) {
                $html = $e->[1];
                $doc_file = $e->[2];
            }
            else {
                $plugin->doLog($e);
                die $@;
            };
        }
        else {
            return $out;
        }

        # simulate SSI include
        $html = $plugin->include_virtual($blog_id, $html, $doc_file);
        # insert base tag for relative url
        if ($tmpl_map) {
            # my ( $entry, $blog, $at, $cat, $map, $timestamp, $author ) = @_;
            my $blog = $content_data->blog();
            # my $at = $entry->is_entry ? 'Individual' : 'Page';
            my $at = 'ContentType';
            require MT::Util;
            my $archive_file = MT::Util::archive_file_for($content_data, $blog, $at, undef, $tmpl_map);
            if ($archive_file) {
                #my $blog_path = $entry->is_entry ? ($blog->archive_path || $blog->site_path) : $blog->site_path;
                my $blog_path = $blog->site_path || '';
                require File::Spec;
                $archive_file = File::Spec->catfile($blog_path, $archive_file);
                $doc_file = $archive_file;
            }
        }
        $html = $plugin->insert_base_tag($blog_id, $html, $doc_file, $content_data);
        # replace url
        $html = $plugin->replace_url($blog_id, $html, $doc_file);
        # replace string
        $html = $plugin->replace_string($blog_id, $html, $doc_file);

        # # set X-XSS-Protection to 0
        # $app->set_header('X-XSS-Protection' => 0);

        return $html;
    };
    *MT::CMS::ContentData::preview = $_content_data_preview; 


    return 1;
}

sub edit_entry_source {
    my ($cb, $app, $tmpl_ref) = @_;

    my $blog_id = $app->param('blog_id')
        or return 1;
    my $plugin = $app->component('DynamicPreview')
        or die 'Plugin not found';
    my $enabled = $plugin->dynamic_preview_enabled($blog_id)
        or return 1;

    #    my $pattern = quotemeta(<<'TMPL');
    #<mt:include name="include/footer.tmpl" id="footer_include">
    #TMPL
    my $pattern = <<TMPL;
<mt:include name="layout/default.tmpl"(?:[^>]*)?>
TMPL
    my $replacement = <<'TMPL';
<mt:setvarblock name="jq_js_include" append="1">
    var docCookies = {
      getItem: function (sKey) {
        if (!sKey || !this.hasItem(sKey)) { return null; }
        return unescape(document.cookie.replace(new RegExp("(?:^|.*;\\s*)" + escape(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*((?:[^;](?!;))*[^;]?).*"), "$1"));
      },
      setItem: function (sKey, sValue, vEnd, sPath, sDomain, bSecure) {
        if (!sKey || /^(?:expires|max\-age|path|domain|secure)$/i.test(sKey)) { return; }
        var sExpires = "";
        if (vEnd) {
          switch (vEnd.constructor) {
            case Number:
              sExpires = vEnd === Infinity ? "; expires=Tue, 19 Jan 2038 03:14:07 GMT" : "; max-age=" + vEnd;
              break;
            case String:
              sExpires = "; expires=" + vEnd;
              break;
            case Date:
              sExpires = "; expires=" + vEnd.toGMTString();
              break;
          }
        }
        document.cookie = escape(sKey) + "=" + escape(sValue) + sExpires + (sDomain ? "; domain=" + sDomain : "") + (sPath ? "; path=" + sPath : "") + (bSecure ? "; secure" : "");
      },
      removeItem: function (sKey, sPath) {
        if (!sKey || !this.hasItem(sKey)) { return; }
        document.cookie = escape(sKey) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT" + (sPath ? "; path=" + sPath : "");
      },
      hasItem: function (sKey) {
        return (new RegExp("(?:^|;\\s*)" + escape(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=")).test(document.cookie);
      },
      keys: /* optional method: you can safely remove it! */ function () {
        var aKeys = document.cookie.replace(/((?:^|\s*;)[^\=]+)(?=;|$)|^\s*|\s*(?:\=[^;]*)?(?:\1|$)/g, "").split(/\s*(?:\=[^;]*)?;\s*/);
        for (var nIdx = 0; nIdx < aKeys.length; nIdx++) { aKeys[nIdx] = unescape(aKeys[nIdx]); }
        return aKeys;
      }
    };
    window.dynamicPreview = {
        queue: [],
        sortOrder: {},
        previewWindow: null
    };
    jQuery('button.preview').click(function(event) {
        event.preventDefault();
        var form = jQuery('#entry_form');
        if(form.length == 0) {
            return false;
        }

        dynamicPreview.queue = [];
        form.find('input[name="__mode"]').val('dynamic_preview_entry');
        var select = form.find('#tmpl_id');
        
        if (App && App.singleton && typeof App.singleton.saveHTML == 'function') {
            App.singleton.saveHTML();
        }

        var makeAndAddQueueItem = function(form, id, selected_option) {
            var queueItem = {
                'id': id,
                'name': selected_option.text(),
                'blog_id': selected_option.data('blog_id'),
                'type': selected_option.data('type'),
                'query': null
            };
            if(queueItem['type'] == 'individual' || queueItem['type'] == 'page') {
                queueItem['query'] = form.serialize();
            }
            else {
                queueItem['magic_token'] = form.find('[name="magic_token"]').val();
            }

            var at = selected_option.data('archive_type') || '';
            if(at == 'category') {
                var cat_ids = jQuery('input[name="category_ids"]').val().split(',');
                if(typeof cat_ids == "object" && cat_ids.length > 0) {
                    for(var i=0; i<cat_ids.length; i++) {
                        var cats = jQuery.grep(MT.App.categoryList, function(e) {
                            return e.id == cat_ids[i];
                        });
                        var label = '';
                        if(cats && cats.length > 0) {
                            label = cats[0].label;
                        }
                        var newQueueItem = jQuery.extend({ cat_id: cat_ids[i], cat_label: label }, queueItem);
                        dynamicPreview.queue.push(newQueueItem);
                    }
                }
                else {
                    dynamicPreview.queue.push(queueItem);
                }
            }
            else {
                dynamicPreview.queue.push(queueItem);
            }

            var length = dynamicPreview.queue.length;
            if(length > 0) {
                var idx = 0;
                for(var i=0; i<length; i++) {
                    var queueItem = dynamicPreview.queue[i];
                    dynamicPreview.sortOrder[queueItem['id']] = idx++;
                }
            }
        };

        if(form.find('#tmpl_id').val() == 0) {
            form.find('#tmpl_id option').each(function() {
                var id = jQuery(this).val();
                if(id != 0) {
                    select.val(id);
                    var option = select.children(':selected');
                    makeAndAddQueueItem(form, id, option);
                }
            });
            select.val(0);
        }
        else {
            var id = form.find('#tmpl_id').val();
            var option = select.children(':selected');
            makeAndAddQueueItem(form, id, option);
        }

        form.removeAttr('target');
        form.find('input[name="__mode"]').val('save_entry');
       
        var query = '?__mode=dynamic_preview&blog_id=<mt:var name="blog_id" escape="js" />&id=<mt:var name="id" escape="js" />&_type=<mt:var name="object_type" escape="js" /><mt:if name="rev_number">&r=<mt:var name="rev_number" escape="js" /></mt:if>'; 
        if(jQuery('input[name="dirty"]').val()) {
            query += '&dirty=1'
        }
        var w = window.open(form.attr('action') + query, 'dynamic_preview');
        if(w) {
            dynamicPreview.previewWindow = w;
        }
        return false;
    });
    jQuery('button.publish').click(function(event) {
        if(dynamicPreview.previewWindow != null && !dynamicPreview.previewWindow.closed) {
            docCookies.setItem('reloadPreview', true);
        }
        else {
            docCookies.setItem('reloadPreview', false);
        }
    });
    jQuery(function() {
        var reloadPreview = docCookies.getItem('reloadPreview');
        if(reloadPreview === 'true') {
            jQuery('button.preview').click();
            docCookies.setItem('reloadPreview', false);
        }
    });
</mt:setvarblock>
TMPL
    $$tmpl_ref =~ s!($pattern)!$replacement$1!;

    # hide preview and add out preview button
    if(DynamicPreview::REPLACE_PREVIEW_BUTTON()) {
        my $class_pattern = 'class="preview action button"';
        my $new_class_replacement = 'class="action button hidden" disabled="disabled"';
        $$tmpl_ref =~ s!$class_pattern!$new_class_replacement!;

        my $preview_action_pattern = '<div class="preview-action">';
        my $preview_button_replacement = <<TMPL;
    <__trans_section component="DynamicPreview">
      <button
         name="dynamic_preview"
         type="submit"
         title="<__trans phrase="PC Preview" />"
         class="preview action button">
        <__trans phrase="PC Preview" />
      </button>
    </__trans_section>
TMPL
        $$tmpl_ref =~ s!($preview_action_pattern)!$1$preview_button_replacement!;
    }

=pod
    if(DynamicPreview::MULTI_PREVIEW()) {
        my $actions_bar_pattern = '<div class="actions-bar">';
        my $template_selector_replacement = <<TMPL;
    <mt:if name="template_loop">
    <__trans_section component="DynamicPreview">
    <mtapp:setting
        id="template-selector"
        label="<__trans phrase="Preview Template">"
        label_class="top-label">
        <select name="tmpl_id" id="tmpl_id" style="width:100%;"><mt:loop name="template_loop">
            <option value="<mt:var name="id" escape="html">" data-blog_id="<mt:var name="blog_id" escape="html">" data-type="<mt:var name="type" escape="html">"<mt:if name="archive_type"> data-archive_type="<mt:var name="archive_type" escape="html">"</mt:if>><mt:var name="name" escape="html"></option></mt:loop>
        </select>
    </mtapp:setting>
    </__trans_section>
    </mt:if>
TMPL
        $$tmpl_ref =~ s!($actions_bar_pattern)!$template_selector_replacement$1!;
    }
=cut

    return 1;
}

sub _str_to_ids {
    my ($str) = @_;
    my @ids;
    return @ids unless $str;
    @ids = map { s!(\d+)!$1!; $_ } grep { m!^\s*\d+\s*$! } split(/[\r\n]/, $str);
    return @ids;
}

sub edit_entry_param {
    my ($cb, $app, $param, $tmpl) = @_;

    my $blog_id = $app->param('blog_id')
        or return 1;
    my $plugin = $app->component('DynamicPreview')
        or die 'Plugin not found';
    my $enabled = $plugin->dynamic_preview_enabled($blog_id)
        or return 1;

    require MT::TemplateMap;
    require MT::Template;

    if(DynamicPreview::MULTI_PREVIEW()) {
        my $node = $tmpl->createElement('app:setting', {
            id => 'template-selector',
            label => $plugin->translate('Preview Template'),
            label_class => 'top-label',
        });
        $node->innerHTML(q{
        <select name="tmpl_id" id="tmpl_id" class="custom-select form-control" style="width:100%;"><mt:loop name="template_loop">
            <option value="<mt:var name="id" escape="html">" data-blog_id="<mt:var name="blog_id" escape="html">" data-type="<mt:var name="type" escape="html">"<mt:if name="archive_type"> data-archive_type="<mt:var name="archive_type" escape="html">"</mt:if>><mt:var name="name" escape="html"></option></mt:loop>
        </select>
        });
        my $rev_note_node = $tmpl->getElementById('revision-note');
        if($rev_note_node) {
            my $if_use_revision_node = $rev_note_node->parentNode;
            if($if_use_revision_node) {
                if(!$tmpl->insertAfter($node, $if_use_revision_node)) {
                    warn "failed to insert template selector.";
                }
            }
        }
    }

    my @tmpl_params = ();
    my $type = $app->param('_type') || 'entry';
    my $at = $type eq 'page' ? 'Page' : 'Individual';
    my @tmpl = MT::Template->load(
        undef,
        {
            'join' => MT::TemplateMap->join_on('template_id',
                {
                    archive_type => $at,
                    blog_id      => $blog_id,
                    build_type   => { not => MT::PublishOption::DISABLED() },
                },
            ),
            sort => 'name',
            direction => 'ascend',
        },
    );

    # Exclude specified templates
    my @exclude_ids = _str_to_ids($plugin->dynamic_preview_exclude_archive_template_ids($blog_id) || '');
    my %exclude_ids = map { $_ => 1 } @exclude_ids;

    foreach (@tmpl) {
        next if exists $exclude_ids{$_->id};
        push @tmpl_params, {
            id => $_->id,
            name => $_->name,
            blog_id => $_->blog_id,
            type => $_->type,
        };
    }

    # Load index templates
    my @ids = _str_to_ids($plugin->dynamic_preview_index_template_ids($blog_id) || '');
    if(@ids > 0) {
        # NOTE: page のプレビューではarchiveは表示しない
        my $tmpl_type = $type eq 'entry' ? [ 'index', 'archive' ] : 'index';
        my @index_tmpl = MT::Template->load({
            id => \@ids,
            type => $tmpl_type,
        },
        {
            'join' => MT::TemplateMap->join_on(
                undef,
                {},
                {
                    unique => 1,
                    type => 'left',
                    condition => {
                        build_type => \'!= 0 AND template_build_type != 0',
                    },
                },
            ),
            sort => 'name',
            direction => 'ascend',
        }
        );

        # sort by specified id
        my $idx = 0;
        my %order = map { $_ => $idx++ } @ids;
        @index_tmpl = sort { $order{$a->id} <=> $order{$b->id} } @index_tmpl;

        # load archive_type
        require MT::TemplateMap;
        foreach (@index_tmpl) {
            my $params = {
                id => $_->id,
                name => $_->name,
                blog_id => $_->blog_id,
                type => $_->type,
            };
            if($_->type eq 'archive') {
                my $tmpl_map = MT::TemplateMap->load({ 
                    template_id => $_->id, 
                    build_type => { op => '!=', value => 0 },
                    archive_type => 'Category',
                });
                if($tmpl_map) {
                    $params->{archive_type} = lc $tmpl_map->archive_type;       
                }
            }
            push @tmpl_params, $params;
        }
    }

    if(@tmpl_params > 0) {
        unshift @tmpl_params, { 
            id => 0, 
            name => $plugin->translate('All'),
        };
    }
    $param->{template_loop} = \@tmpl_params;
    return 1;
}

sub edit_template_source {
    my ($cb, $app, $tmpl_ref) = @_;

    my $blog_id = $app->param('blog_id')
        or return 1;
    my $plugin = $app->component('DynamicPreview')
        or die 'Plugin not found';
    my $enabled = $plugin->dynamic_preview_enabled($blog_id)
        or return 1;

    my $pattern = quotemeta(<<'TMPL');
<mt:include name="layout/default.tmpl">
TMPL
    my $replacement = <<TMPL;
<mt:setvarblock name="jq_js_include" append="1">
    jQuery('button.preview').off('click');
    jQuery('button.preview').click(function(event) {
        event.preventDefault();
        event.stopPropagation();
        var form = jQuery('#template-listing-form');
        if(form.length == 0) {
            return false;
        }
        form.attr('target', 'dynamic_preview');
        var type = form.find('input[name=type]').val();
        var mode = (type == 'ct_archive' || type == 'ct')
            ? 'dynamic_preview_cd_template'
            : 'dynamic_preview_template';
        form.find('input[name="__mode"]').val(mode);
        if (syncEditor && typeof syncEditor == 'function') {
            syncEditor();
        }
        form.submit();
        return true;
    });
</mt:setvarblock>
TMPL
    $$tmpl_ref =~ s!($pattern)!$replacement$1!;

    return 1;
}

sub edit_category_param {
    my ($cb, $app, $param, $tmpl) = @_;

    my $blog_id = $app->param('blog_id')
        or return 1;
    my $plugin = $app->component('DynamicPreview')
        or die 'Plugin not found';
    my $enabled = $plugin->dynamic_preview_enabled($blog_id)
        or return 1;

    return $plugin->has_extra
        ? DynamicPreview::Extra::edit_category_param(@_)
        : 1;
}

sub edit_content_data_source {
    my ($cb, $app, $tmpl_ref) = @_;

    my $blog_id = $app->param('blog_id')
        or return 1;
    my $plugin = $app->component('DynamicPreview')
        or die 'Plugin not found';
    my $enabled = $plugin->dynamic_preview_enabled($blog_id)
        or return 1;

    my $pattern = quotemeta(<<'TMPL');
<mt:include name="layout/default.tmpl">
TMPL
    my $replacement = <<'TMPL';
<mt:setvarblock name="jq_js_include" append="1">
    var docCookies = {
      getItem: function (sKey) {
        if (!sKey || !this.hasItem(sKey)) { return null; }
        return unescape(document.cookie.replace(new RegExp("(?:^|.*;\\s*)" + escape(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*((?:[^;](?!;))*[^;]?).*"), "$1"));
      },
      setItem: function (sKey, sValue, vEnd, sPath, sDomain, bSecure) {
        if (!sKey || /^(?:expires|max\-age|path|domain|secure)$/i.test(sKey)) { return; }
        var sExpires = "";
        if (vEnd) {
          switch (vEnd.constructor) {
            case Number:
              sExpires = vEnd === Infinity ? "; expires=Tue, 19 Jan 2038 03:14:07 GMT" : "; max-age=" + vEnd;
              break;
            case String:
              sExpires = "; expires=" + vEnd;
              break;
            case Date:
              sExpires = "; expires=" + vEnd.toGMTString();
              break;
          }
        }
        document.cookie = escape(sKey) + "=" + escape(sValue) + sExpires + (sDomain ? "; domain=" + sDomain : "") + (sPath ? "; path=" + sPath : "") + (bSecure ? "; secure" : "");
      },
      removeItem: function (sKey, sPath) {
        if (!sKey || !this.hasItem(sKey)) { return; }
        document.cookie = escape(sKey) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT" + (sPath ? "; path=" + sPath : "");
      },
      hasItem: function (sKey) {
        return (new RegExp("(?:^|;\\s*)" + escape(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=")).test(document.cookie);
      },
      keys: /* optional method: you can safely remove it! */ function () {
        var aKeys = document.cookie.replace(/((?:^|\s*;)[^\=]+)(?=;|$)|^\s*|\s*(?:\=[^;]*)?(?:\1|$)/g, "").split(/\s*(?:\=[^;]*)?;\s*/);
        for (var nIdx = 0; nIdx < aKeys.length; nIdx++) { aKeys[nIdx] = unescape(aKeys[nIdx]); }
        return aKeys;
      }
    };
    window.dynamicPreview = {
        queue: [],
        sortOrder: {},
        previewWindow: null,
        defaultPreview: <mt:var name="default_preview" default="1" />,
        queueItems: <mt:if name="item_loop"><mt:var name="item_loop" to_json="1" /><mt:else>[]</mt:if>
    };
    jQuery('button.preview').click(function(event) {
        if(dynamicPreview.defaultPreview) {
            return true;
        }
        event.preventDefault();
        var form = jQuery('#edit-content-type-data-form');
        if(form.length == 0) {
            return false;
        }

        // Handle multiline text fields
        resetMultiLineTextData(form);
        setMultiLineTextData(form);

        dynamicPreview.queue = [];
        form.find('input[name="__mode"]').val('dynamic_preview_content_data');
        var select = form.find('#tmpl_id');
        
        if (App && App.singleton && typeof App.singleton.saveHTML == 'function') {
            App.singleton.saveHTML();
        }

        dynamicPreview.queueItems.forEach(function(item) {
            if(item.type == 'ct') {
                item.query = form.serialize().replace(/(&?)(tmpl_id)=(\d+)/, '$1$2=' + item.id);
            }
        });

        if(form.find('#tmpl_id').val() == 0) {
            form.find('#tmpl_id option').each(function() {
                var id = jQuery(this).val();
                if(id != 0) {
                    var _items = dynamicPreview.queueItems.filter(function(item) {
                        return item.id == id;
                    });
                    if(_items.length > 0) {
                        dynamicPreview.queue.push(..._items);
                    }
                }
            });
        }
        else {
            var id = form.find('#tmpl_id').val();
            var _items = dynamicPreview.queueItems.filter(function(item) {
                return item.id == id;
            });
            if(_items.length > 0) {
                dynamicPreview.queue.push(..._items);
            }
        }

        form.removeAttr('target');
        form.find('input[name="__mode"]').val('save');
       
        var query = '?__mode=dynamic_preview_cd&blog_id=<mt:var name="blog_id" escape="js" />&id=<mt:var name="id" escape="js" />&_type=<mt:var name="object_type" escape="js" /><mt:if name="rev_number">&r=<mt:var name="rev_number" escape="js" /></mt:if>'; 
        if(jQuery('input[name="dirty"]').val()) {
            query += '&dirty=1'
        }
        var w = window.open(form.attr('action') + query, 'dynamic_preview');
        if(w) {
            dynamicPreview.previewWindow = w;
        }
        return false;
    });
    jQuery('button.publish').click(function(event) {
        if(dynamicPreview.previewWindow != null && !dynamicPreview.previewWindow.closed) {
            docCookies.setItem('reloadPreview', true);
        }
        else {
            docCookies.setItem('reloadPreview', false);
        }
    });
    jQuery(function() {
        var reloadPreview = docCookies.getItem('reloadPreview');
        if(reloadPreview === 'true') {
            jQuery('button.preview').click();
            docCookies.setItem('reloadPreview', false);
        }
    });
</mt:setvarblock>
TMPL
    $$tmpl_ref =~ s!($pattern)!$replacement$1!;

    return 1;
}

sub edit_content_data_param {
    my ($cb, $app, $param, $tmpl) = @_;

    my $blog_id = $app->param('blog_id')
        or return 1;
    my $plugin = $app->component('DynamicPreview')
        or die 'Plugin not found';
    my $enabled = $plugin->dynamic_preview_enabled($blog_id)
        or return 1;

    require MT::TemplateMap;
    require MT::Template;

    my @tmpl_params = ();
    my $type = $app->param('_type') || 'content_data';
    my $at = 'ContentType'; # $type eq 'page' ? 'Page' : 'Individual';
    my @tmpl = MT::Template->load(
        undef,
        {
            'join' => MT::TemplateMap->join_on('template_id',
                {
                    archive_type => $at,
                    blog_id      => $blog_id,
                    build_type   => { not => MT::PublishOption::DISABLED() },
                },
            ),
            sort => 'name',
            direction => 'ascend',
        },
    );

    # Exclude specified templates
    my @exclude_ids = _str_to_ids($plugin->dynamic_preview_exclude_archive_template_ids_for_cd($blog_id) || '');
    my %exclude_ids = map { $_ => 1 } @exclude_ids;
    my $content_type_id = $app->param('content_type_id');

    foreach (@tmpl) {
        next if exists $exclude_ids{$_->id};

        # exclude if template is not associated with content type
        next if $_->content_type_id && $content_type_id
            && $_->content_type_id != $content_type_id;

        push @tmpl_params, {
            id => $_->id,
            name => $_->name,
            blog_id => $_->blog_id,
            type => $_->type,
        };
    }

    # Load index templates
    my @ids = _str_to_ids($plugin->dynamic_preview_index_template_ids_for_cd($blog_id) || '');
    if(@ids > 0) {
        # NOTE: page のプレビューではarchiveは表示しない
        # my $tmpl_type = $type eq 'entry' ? [ 'index', 'archive' ] : 'index';
        my $tmpl_type = ['index', 'ct_archive'];
        my @index_tmpl = MT::Template->load({
            id => \@ids,
            type => $tmpl_type,
        },
        {
            'join' => MT::TemplateMap->join_on(
                undef,
                {},
                {
                    unique => 1,
                    type => 'left',
                    condition => {
                        build_type => \'!= 0 AND template_build_type != 0',
                    },
                },
            ),
            sort => 'name',
            direction => 'ascend',
        }
        );

        # sort by specified id
        my $idx = 0;
        my %order = map { $_ => $idx++ } @ids;
        @index_tmpl = sort { $order{$a->id} <=> $order{$b->id} } @index_tmpl;

        # load archive_type
        require MT::TemplateMap;
        foreach (@index_tmpl) {
            # exclude if template is not associated with content type
            next if $_->content_type_id && $content_type_id
                && $_->content_type_id != $content_type_id;

            my $params = {
                id => $_->id,
                name => $_->name,
                blog_id => $_->blog_id,
                type => $_->type,
            };
            if($_->type eq 'ct_archive') {
                # NOTE: template mapはpreviewの時に取得するので、ここでは取得しない
            }
            push @tmpl_params, $params;
        }
    }

    $param->{default_preview} = 1;

    if(@tmpl_params > 0) {
        unshift @tmpl_params, { 
            id => 0, 
            name => $plugin->translate('All'),
        };

        if(DynamicPreview::MULTI_PREVIEW()) {
            my $node = $tmpl->createElement('app:setting', {
                id => 'template-seletor',
                label => $plugin->translate('Preview Template'),
                label_class => 'top-label',
            });
            $node->innerHTML(q{
            <select name="tmpl_id" id="tmpl_id" class="custom-select form-control" style="width:100%;"><mt:loop name="template_loop">
                <option value="<mt:var name="id" escape="html">" data-blog_id="<mt:var name="blog_id" escape="html">" data-type="<mt:var name="type" escape="html">"<mt:if name="archive_type"> data-archive_type="<mt:var name="archive_type" escape="html">"</mt:if>><mt:var name="name" escape="html"></option></mt:loop>
            </select>
            });
            my $rev_note_node = $tmpl->getElementById('revision-note');
            if($rev_note_node) {
                my $if_use_revision_node = $rev_note_node->parentNode;
                if($if_use_revision_node) {
                    if(!$tmpl->insertAfter($node, $if_use_revision_node)) {
                        warn "failed to insert template selector.";
                    }
                }
            }
        }

        $param->{default_preview} = 0;
    }

    $param->{template_loop} = \@tmpl_params;

    my $cd_id = $app->param('id');
    my @tmpl_ids = grep { $_ > 0 } map { $_->{id} } @tmpl_params;
    require DynamicPreview::CMS;
    my @items = DynamicPreview::CMS::_get_preview_cd_items($app, $cd_id, $blog_id, \@tmpl_ids, 0);
    $param->{item_loop} = \@items;

    return 1;
}

sub cms_pre_preview_template {
    my ($cb, $app, $preview_tmpl, $data) = @_;

    my $archive_category = $app->request('dynamic_preview_archive_category')
        or return 1;
    my $ctx = $preview_tmpl->context;
    $ctx->stash('archive_category', $archive_category);

    return 1;
}

1;
__END__


