Skip to content
Snippets Groups Projects
ticket.js 18 KiB
Newer Older
Jared Hancock's avatar
Jared Hancock committed
/*********************************************************************
    ticket.js

    Ticket utility loaded on ticket view!

    Useful for UI config settings, ticket locking ...etc

    Peter Rotich <peter@osticket.com>
    Copyright (c) 2006-2013 osTicket
Jared Hancock's avatar
Jared Hancock committed
    http://www.osticket.com

    Released under the GNU General Public License WITHOUT ANY WARRANTY.

    vim: expandtab sw=4 ts=4 sts=4:
**********************************************************************/
var autoLock = {
Jared Hancock's avatar
Jared Hancock committed
    addEvent: function(elm, evType, fn, useCapture) {
        if(elm.addEventListener) {
            elm.addEventListener(evType, fn, useCapture);
            return true;
        }else if(elm.attachEvent) {
            return elm.attachEvent('on' + evType, fn);
        }else{
            elm['on' + evType] = fn;
        }
    },

    removeEvent: function(elm, evType, fn, useCapture) {
        if(elm.removeEventListener) {
            elm.removeEventListener(evType, fn, useCapture);
            return true;
        }else if(elm.detachEvent) {
            return elm.detachEvent('on' + evType, fn);
        }else {
            elm['on' + evType] = null;
        }
    },

    //Incoming event...
    handleEvent: function(e) {
        if(!autoLock.lockId) {
            var now = new Date().getTime();
            //Retry every 5 seconds??
            if(autoLock.retry && (!autoLock.lastattemptTime || (now-autoLock.lastattemptTime)>5000)) {
                autoLock.acquireLock(e,autoLock.warn);
                autoLock.lastattemptTime=new Date().getTime();
            }
        }else{
            autoLock.renewLock(e);
        }

        if(!autoLock.lasteventTime) { //I hate nav away warnings..but
            $(window).bind('beforeunload', function(e) {
                return "Any changes or info you've entered will be discarded!";
             });
        }
Jared Hancock's avatar
Jared Hancock committed
        autoLock.lasteventTime=new Date().getTime();
    },

    //Watch activity on individual form.
    watchForm: function(fObj,fn) {
        if(!fObj || !fObj.length)
            return;

        //Watch onSubmit event on the form.
        autoLock.addEvent(fObj,'submit',autoLock.onSubmit,true);
        //Watch activity on text + textareas + select fields.
        for (var i=0; i<fObj.length; i++) {
            switch(fObj[i].type) {
                case 'textarea':
                case 'text':
                    autoLock.addEvent(fObj[i],'keyup',autoLock.handleEvent,true);
                    break;
                case 'select-one':
                case 'select-multiple':
                    if(fObj.name!='reply') //Bug on double ajax call since select make it's own ajax call. TODO: fix it
Jared Hancock's avatar
Jared Hancock committed
                        autoLock.addEvent(fObj[i],'change',autoLock.handleEvent,true);
                    break;
                default:
            }
        }
    },

    //Watch all the forms on the document.
    watchDocument: function() {

        //Watch forms of interest only.
        for (var i=0; i<document.forms.length; i++) {
            if(!document.forms[i].id.value || parseInt(document.forms[i].id.value)!=autoLock.tid)
                continue;
            autoLock.watchForm(document.forms[i],autoLock.checkLock);
        }
    },

    Init: function(config) {

        //make sure we are on ticket view page & locking is enabled!
        var fObj=$('form#note');
        if(!fObj
                || !$(':input[name=id]',fObj).length
                || !$(':input[name=locktime]',fObj).length
                || $(':input[name=locktime]',fObj).val()==0) {
            return;
        void(autoLock.tid=parseInt($(':input[name=id]',fObj).val()));
        void(autoLock.lockTime=parseInt($(':input[name=locktime]',fObj).val()));

Jared Hancock's avatar
Jared Hancock committed
        autoLock.lockId=0;
        autoLock.timerId=0;
        autoLock.lasteventTime=0;
        autoLock.lastattemptTime=0;
        autoLock.acquireTime=0;
        autoLock.renewTime=0;
        autoLock.renewFreq=0; //renewal frequency in seconds...based on returned lock time.
        autoLock.time=0;
        autoLock.lockAttempts=0; //Consecutive lock attempt errors
        autoLock.maxattempts=2; //Maximum failed lock attempts before giving up.
        autoLock.warn=true;
        autoLock.retry=true;
        autoLock.watchDocument();
        autoLock.resetTimer();
        autoLock.addEvent(window,'unload',autoLock.releaseLock,true); //Release lock regardless of any activity.
    },
Jared Hancock's avatar
Jared Hancock committed

    onSubmit: function(e) {
        if(e.type=='submit') { //Submit. double check!
            //remove nav away warning if any.
            $(window).unbind('beforeunload');
Jared Hancock's avatar
Jared Hancock committed
            //Only warn if we had a failed lock attempt.
            if(autoLock.warn && !autoLock.lockId && autoLock.lasteventTime) {
                var answer=confirm('Unable to acquire a lock on the ticket. Someone else could be working on the same ticket. \
                    Please confirm if you wish to continue anyways.');
                if(!answer) {
                    e.returnValue=false;
                    e.cancelBubble=true;
                    if(e.preventDefault) {
                        e.preventDefault();
                    }
                    return false;
                }
            }
        }
        return true;
    },

    acquireLock: function(e,warn) {
Jared Hancock's avatar
Jared Hancock committed

        if(!autoLock.tid) { return false; }

        var warn = warn || false;

        if(autoLock.lockId) {
            autoLock.renewLock(e);
        } else {
            $.ajax({
                type: "POST",
                url: 'ajax.php/tickets/'+autoLock.tid+'/lock',
Jared Hancock's avatar
Jared Hancock committed
                dataType: 'json',
                cache: false,
                success: function(lock){
                    autoLock.setLock(lock,'acquire',warn);
                }
            })
            .done(function() { })
            .fail(function() { });
        }
Jared Hancock's avatar
Jared Hancock committed
        return autoLock.lockId;
    },

    //Renewal only happens on form activity..
Jared Hancock's avatar
Jared Hancock committed
    renewLock: function(e) {
Jared Hancock's avatar
Jared Hancock committed
        if(!autoLock.lockId) { return false; }

        var now= new Date().getTime();
Jared Hancock's avatar
Jared Hancock committed
        if(!autoLock.lastcheckTime || (now-autoLock.lastcheckTime)>=(autoLock.renewFreq*1000)){
            $.ajax({
                type: 'POST',
                url: 'ajax.php/tickets/'+autoLock.tid+'/lock/'+autoLock.lockId+'/renew',
Jared Hancock's avatar
Jared Hancock committed
                dataType: 'json',
                cache: false,
                success: function(lock){
                    autoLock.setLock(lock,'renew',autoLock.warn);
                }
            })
            .done(function() {  })
            .fail(function() { });
        }
Jared Hancock's avatar
Jared Hancock committed
    releaseLock: function(e) {
        if(!autoLock.tid) { return false; }

        $.ajax({
            type: 'POST',
            url: 'ajax.php/tickets/'+autoLock.tid+'/lock/'+autoLock.lockId+'/release',
Jared Hancock's avatar
Jared Hancock committed
            data: 'delete',
Jared Hancock's avatar
Jared Hancock committed
            cache: false,
            success: function(){
Jared Hancock's avatar
Jared Hancock committed
            }
        })
        .done(function() { })
        .fail(function() { });
    },

    setLock: function(lock, action, warn) {
        var warn = warn || false;
Jared Hancock's avatar
Jared Hancock committed
        if(!lock) return false;

        if(lock.id) {
            autoLock.renewFreq=lock.time?(lock.time/2):30;
            autoLock.lastcheckTime=new Date().getTime();
        }
        autoLock.lockId=lock.id; //override the lockid.

Jared Hancock's avatar
Jared Hancock committed
        switch(action){
            case 'renew':
                if(!lock.id && lock.retry) {
                    autoLock.lockAttempts=1; //reset retries.
                    autoLock.acquireLock(e,false); //We lost the lock?? ..try to re acquire now.
                }
                break;
            case 'acquire':
                if(!lock.id) {
                    autoLock.lockAttempts++;
                    if(warn && (!lock.retry || autoLock.lockAttempts>=autoLock.maxattempts)) {
                        autoLock.retry=false;
                        alert('Unable to lock the ticket. Someone else could be working on the same ticket.');
Jared Hancock's avatar
Jared Hancock committed
    discardWarning: function(e) {
        e.returnValue="Any changes or info you've entered will be discarded!";
    },

    //TODO: Monitor events and elapsed time and warn user when the lock is about to expire.
Jared Hancock's avatar
Jared Hancock committed
    monitorEvents: function() {
       // warn user when lock is about to expire??;
        //autoLock.resetTimer();
    },

    clearTimer: function() {
        clearTimeout(autoLock.timerId);
    },
Jared Hancock's avatar
Jared Hancock committed
    resetTimer: function() {
        clearTimeout(autoLock.timerId);
        autoLock.timerId=setTimeout(function () { autoLock.monitorEvents() },30000);
    }
};
$.autoLock = autoLock;
Jared Hancock's avatar
Jared Hancock committed

/*
   UI & form events
*/

jQuery(function($) {
    $('#response_options form').hide();
    $('#ticket_notes').hide();
    if(location.hash != "" && $('#response_options '+location.hash).length) {
        $('#response_options '+location.hash+'_tab').addClass('active');
        $('#response_options '+location.hash).show();
Peter Rotich's avatar
Peter Rotich committed
    } else if(location.hash == "#notes" && $('#ticket_notes').length) {
Jared Hancock's avatar
Jared Hancock committed
        $('#response_options #note_tab').addClass('active');
        $('#response_options form').hide();
        $('#response_options #note').show();
        $('#ticket_thread').hide();
        $('#ticket_notes').show();
        $('#toggle_ticket_thread').removeClass('active');
        $('#toggle_notes').addClass('active');
    } else {
        $('#response_options ul.tabs li:first a').addClass('active');
        $('#response_options '+$('#response_options ul.tabs li:first a').attr('href')).show();
Jared Hancock's avatar
Jared Hancock committed
    }

    $('#reply_tab').click(function() {
       $(this).removeClass('tell');
Jared Hancock's avatar
Jared Hancock committed
     });

    $('#note_tab').click(function() {
        if($('#response').val() != '') {
            $('#reply_tab').addClass('tell');
        }
     });

    $('#response_options ul.tabs li a').click(function(e) {
Jared Hancock's avatar
Jared Hancock committed
        e.preventDefault();
        $('#response_options ul.tabs li a').removeClass('active');
Jared Hancock's avatar
Jared Hancock committed
        $(this).addClass('active');
        $('#response_options form').hide();
        //window.location.hash = this.hash;
        $('#response_options '+$(this).attr('href')).show();
        $("#msg_error, #msg_notice, #msg_warning").fadeOut();
     });

    $('#toggle_ticket_thread, #toggle_notes, .show_notes').click(function(e) {
        e.preventDefault();
        $('#threads a').removeClass('active');

        if($(this).attr('id') == 'toggle_ticket_thread') {
            $('#ticket_notes').hide();
            $('#ticket_thread').show();
            $('#toggle_ticket_thread').addClass('active');
            $('#reply_tab').removeClass('tell').click();
        } else {
            $('#ticket_thread').hide();
            $('#ticket_notes').show();
            $('#toggle_notes').addClass('active');
            $('#note_tab').click();
            if($('#response').val() != '') {
                $('#reply_tab').addClass('tell');
            }
        }
     });
Peter Rotich's avatar
Peter Rotich committed
    //Start watching the form for activity.
    autoLock.Init();

    /*** Ticket Actions **/
    //print options
    $('a#ticket-print').click(function(e) {
        e.preventDefault();
        $('#overlay').show();
Peter Rotich's avatar
Peter Rotich committed
        $('.dialog#print-options').show();
Peter Rotich's avatar
Peter Rotich committed
    //ticket status (close & reopen)
    $('a#ticket-close, a#ticket-reopen').click(function(e) {
        e.preventDefault();
        $('#overlay').show();
        $('.dialog#ticket-status').show();
        return false;
    });
Peter Rotich's avatar
Peter Rotich committed
    //ticket actions confirmation - Delete + more
    $('a#ticket-delete, a#ticket-claim, #action-dropdown-more li a').click(function(e) {
        e.preventDefault();
        if($('.dialog#confirm-action '+$(this).attr('href')+'-confirm').length) {
            var action = $(this).attr('href').substr(1, $(this).attr('href').length);
            $('.dialog#confirm-action #action').val(action);
            $('#overlay').show();
            $('.dialog#confirm-action .confirm-action').hide();
            $('.dialog#confirm-action p'+$(this).attr('href')+'-confirm')
            .show()
            .parent('div').show().trigger('click');

        } else {
            alert('Unknown action '+$(this).attr('href')+'- get technical help.');
        }

        return false;
    });

    //Collaborators
    $(document).on('click', 'a#managecollaborators, a#addcollaborators, a.editcollaborator', function(e) {
        e.preventDefault();
        var target = $(this).attr('href').substr(1);
        $('.dialog.collaborators .body').load('ajax.php/'+target, function () {
            $('#overlay').show();
            $('.dialog.collaborators').show();
         });

        return false;
     });

    $(document).on('click', 'form.collaborators a#addcollaborator', function (e) {
        e.preventDefault();
        $('div#manage_collaborators').hide();
        $('div#add_collaborator').fadeIn();
        return false;
     });

    $(document).on('click', 'form.collaborators a.remove', function (e) {
        e.preventDefault();
        var fObj = $(this).closest('form');
        $('input'+$(this).attr('href'))
            .val($(this).attr('href').substr(2))
            .trigger('change');
        $(this).closest('tr').addClass('strike');

        return false;
     });

    $(document).on('change', 'form.collaborators input:checkbox, input[name="del[]"]', function (e) {
       var fObj = $(this).closest('form');
       $('div#savewarning', fObj).fadeIn();
       $('input:submit', fObj).css('color', 'red');
     });

    $(document).on('click', 'form.collaborators input:reset', function(e) {
        var fObj = $(this).closest('form');
        fObj.find('input[name="del[]"]').val('');
        fObj.find('tr').removeClass('strike');
        $('div#savewarning', fObj).hide();
        $('input:submit', fObj).removeAttr('style');
    });


    $(document).on('click', 'form.collaborators input.cancel', function (e) {
        e.preventDefault();
        var $elem = $(this);

        if($elem.attr('data-href')) {
            var href = $elem.data('href').substr(1);
            $('.dialog.collaborators .body').load('ajax.php/'+href, function () {
                });
        } else {

            $('div#manage_collaborators').show();
            $('div#add_collaborator').hide();
        }
        return false;
    });

    $(document).on('change', 'form#reply select#emailreply', function(e) {
         var $cc = $('form#reply tbody#cc_sec');
        if($(this).val() == 0)
            $cc.hide();
        else
            $cc.show();
     });

    $(document).on('submit', 'form.collaborators', function(e) {
        e.preventDefault();
        var fObj = $(this);
        $.ajax({
                type: "POST",
                url: 'ajax.php/'+fObj.attr('action').substr(1),
                data: fObj.serialize(),
                cache: false,
                success: function(resp){
                    $('.dialog.collaborators .body').html(resp);
                    $('#msg_notice, #msg_error').delay(5000).fadeOut();
                }
            })
            .done(function() { })
            .fail(function() { });
        return false;
    });

    var showNonLocalImage = function(div) {
        var $div = $(div),
            $img = $div.append($('<img>')
              .attr('src', $div.data('src'))
              .attr('alt', $div.attr('alt'))
              .attr('title', $div.attr('title'))
              .attr('style', $div.data('style'))
            );
        if ($div.attr('width'))
            $img.width($div.attr('width'));
        if ($div.attr('height'))
            $img.height($div.attr('height'));
    };

    // Optionally show external images
    $('.thread-entry').each(function(i, te) {
        var extra = $(te).find('.textra'),
            imgs = $(te).find('div.non-local-image[data-src]');
        if (!extra) return;
        if (!imgs.length) return;
        extra.append($('<a>')
          .addClass("action-button show-images")
          .css({'font-weight':'normal'})
          .text(' Show Images')
          .click(function(ev) {
            imgs.each(function(i, img) {
              showNonLocalImage(img);
              $(img).removeClass('non-local-image')
                // Remove placeholder sizing
                .css({'display':'inline-block'})
                .width('auto')
                .height('auto')
                .removeAttr('width')
                .removeAttr('height');
              extra.find('.show-images').hide();
            });
          })
          .prepend($('<i>')
            .addClass('icon-picture')
          )
        );
        imgs.each(function(i, img) {
            var $img = $(img);
            // Save a copy of the original styling
            $img.data('style', $img.attr('style'));
            $img.removeAttr('style');
            // If the image has a 'height' attribute, use it, otherwise, use
            // 40px
            $img.height(($img.attr('height') || '40') + 'px');
            // Ensure the image placeholder is visible width-wise
            if (!$img.width())
                $img.width(($img.attr('width') || '80') + 'px');
            // TODO: Add a hover-button to show just one image
        });
    });
Jared Hancock's avatar
Jared Hancock committed
});

showImagesInline = function(urls, thread_id) {
    var selector = (thread_id == undefined)
        ? '.thread-body img[data-cid]'
        : '.thread-body#thread-id-'+thread_id+' img[data-cid]';
    $(selector).each(function(i, el) {
        var cid = $(el).data('cid'),
            info = urls[cid],
            e = $(el);
        if (info) {
            // Add a hover effect with the filename
Jared Hancock's avatar
Jared Hancock committed
            var timeout, caption = $('<div class="image-hover">');
            e.wrap(caption).parent()
                    function() {
                        var self = this;
                        timeout = setTimeout(
                            function() { $(self).find('.caption').slideDown(250); },
                            500);
                    },
                    function() {
                        clearTimeout(timeout);
                        $(this).find('.caption').slideUp(250);
                    }
                ).append($('<div class="caption">')
                    .append('<span class="filename">'+info.filename+'</span>')
                    .append('<a href="'+info.download_url+'" class="action-button"><i class="icon-download-alt"></i> Download</a>')
Jared Hancock's avatar
Jared Hancock committed
                );