The FogBugz user interface, including global javascript objects and DOM elements, can and does change without warning. If you have trouble with FogBugz or your customization after applying a customization or a new release, please confirm that your customization is working as expected. Note: this article is for FogBugz On Demand and On Site only. FogBugz For Your Server users, see this guide instead.

The FogBugz case and list pages are composed of a single-page app. When you go to a case or filter/search URL on your fogbugz site, your browser is sent a mostly-blank HTML document and a javascript package. If you want javascript code to execute when case content is actually on the screen, you need to hook into the correct navigation events and look for some parameters. We made this template to give you a jump-start in doing this on the case page:

name: Case Page Things
description: Do things on the case page in ocelot and oldbugz
author: You
version: 1.0.0.0

js:

$(function(){
  var isOcelot = function() {
    return (typeof fb.config != 'undefined');
  };
  // if this isn't Ocelot, and we're not on the oldbugz case page, don't run
  if (!isOcelot() && !$('#bugviewContainer').length) {
    return;
  }

  // this needs to be idempotent. in ocelot, we don't know on a full page
  // load if it was run on the /nav/end event so we run it again after subscribing
  var runThisWhenCasePageModeChanges = function(sCommand, fIsOcelot) {
  // the bug JS object is goBug in the old UI and fb.cases.current.bug in Ocelot
  var bug;
  // tags differ in the two UIs: bug.tags in Ocelot and goBug.ListTagsAsArray() in the old UI
  var rgTags;
  // when using url parameters to populate case form fields on page-load, Ocelot and the
  // old UI differ in one or more. In this instance, the tag list is "tags" in Ocelot
  // and "sTags" in the old UI
  if (fIsOcelot) {
    bug = goBug;
    rgTags = bug.tags;
  }
  else {
    bug = fb.cases.current.bug;
    rgTags = goBug.ListTagsAsArray();
  }
  if (sCommand == 'load' || sCommand == 'view') {
    // do something on case view
  }
  else {
    // do something on case edit in one of the various modes: edit,
    // assign, resolve, close, reactivate, reopen, open, email, reply,
    // forward, and new
    }
  };
  
  // ------ set up to call your code when the case view changes -------------------
  
  // Customizations are run only on full page-load. In ocelot, this only
  // happens when you go direclty to a URL or when you refresh the whole page.
  // Therefore, we just want to subscribe to the navigation event that fires whenever
  // the view is changed, as well as subscribe to any other events we care about
  // and run our code one initial time.
  if (isOcelot()) {
    // /nav/end fires at the end of every single-page-app navigation, e.g. when
    // the list page is done displaying or when the case page is done changing from
    // view to edit mode. the event param contains some useful info e.g.
    // event.route is something like '/cases/234/case-title-here' and event.url
    // has the entire url of the page
    // on the case page, fb.cases.current.sAction is the mode of the page just like
    // in the old UI: view, edit, assign, resolve, close, reactivate, reopen,
    // open, email, reply, forward and new
    fb.pubsub.subscribe({
      '/nav/end': function(event) {
        //console.log("navigated to url " + event.url);
        //console.log("route: " + event.route);
        // if it's the case page, add the onkeyup for the title field and
        // run an initial check of the fields
        if (typeof fb.cases.current.sAction != 'undefined') {
          runThisWhenCasePageModeChanges(fb.cases.current.sAction, true);
        }
        else {
          //console.log('ocelot page but not the case page');
        }
      }
    });
    // depending on timing, if you go directly to a case page, the pubsub might not
    // finish before /nav/end is called the first time, so run the function once
    // after a delay. remember your code should be idempotent
    setTimeout(function() { runThisWhenCasePageModeChanges(fb.cases.current.sAction, true) }, 100);
  }
  else {
    // the old UI case page is a full page load in many situations, but does use ajax
    // transitions for certain things, like clicking edit while viewing a case or
    // canceling an edit in progress.
    
    // this runs on full page load and determines which action is occurring based on
    // what case-page html elements are present
    if ($('#bugviewContainerEdit textarea').length > 0) {
      if ($('div.ixBug a').length > 0) {
        if ($('#sTo').length > 0) {
          // warning: not localized:
          if ($('#bugviewContainerEdit div.bugevent div.summary span.action:contains("Replied by")').length > 0) {
            runThisWhenCasePageModeChanges('reply', false);
          }
          // warning: not localized:
          else if ($('#bugviewContainerEdit div.bugevent div.summary span.action:contains("Forwarded by")').length > 0) {
            runThisWhenCasePageModeChanges('forward', false);
          }
          else {
            runThisWhenCasePageModeChanges('email', false);
          }
        }
        // warning: not localized:
        else if ($('#bugviewContainerEdit div.bugevent div.summary span.action:contains("Assigned by")').length > 0) {
          runThisWhenCasePageModeChanges('assign', false);
        }
        // warning: not localized:
        else if ($('#bugviewContainerEdit div.bugevent div.summary span.action:contains("Closed by")').length > 0) {
          runThisWhenCasePageModeChanges('close', false);
        }
        // warning: not localized:
        else if ($('#bugviewContainerEdit div.bugevent div.summary span.action:contains("Reactivated by")').length > 0) {
          runThisWhenCasePageModeChanges('reactivate');
        }
        else if ($('#Button_Resolve').length > 0) {
          runThisWhenCasePageModeChanges('resolve', false);
        }
        else {
          runThisWhenCasePageModeChanges('edit', false);
        }
        // add to this: direct link could be to edit or reply or...
      }
      else if ($('#bulkBugItems').length > 0) {
        // bulk action
        runThisWhenCasePageModeChanges('edit', false);
        // add to this: direct link could be to edit or reply or...
      }
      else {
        runThisWhenCasePageModeChanges('new', false);
      }
    }
    else {
      // this is the case view page on a full page-load. The sCommand value
      // on an ajax load is 'view' If you do not need to distinguish
      // the two, change the string 'load' here to 'view'.
      // Note that in Ocelot, the sAction is always 'view' and never 'load'
      runThisWhenCasePageModeChanges('load', false);
    }
    // full page loads are handled above. To handle the ajax
    // transitions, hook into the BugViewChange event:
    $(window).on('BugViewChange', function(e, data) {
      runThisWhenCasePageModeChanges(data.sCommand, false);
    });
  }
});


css:

/ * your css here */