<commit_before>const _ = require('lodash'),
  moment = require('moment'),
  dom = require('@nymag/dom'),
  ds = require('dollar-slice'),
  state = require('../page-state'),
  site = require('../site'),
  tpl = require('../tpl'),
  db = require('../edit/db'),
  pane = require('./'),
  datepicker = require('../field-helpers/datepicker'),
  publishPaneController = require('../../controllers/publish-pane'),
  kilnHideClass = 'kiln-hide';

/**
 * create actions for undoing scheduling and publishing
 * @param {object} res
 * @returns {Element}
 */
function createUndoActions(res) {
  const undo = tpl.get('.publish-undo-template');

  let unpublish, unschedule;

  // show undo actions if either are available
  if (res.scheduled || res.published) {
    dom.find(undo, '.undo').classList.remove(kilnHideClass);
  }

  // unscheduling
  if (res.scheduled) {
    state.toggleButton('scheduled', true); // just in case someone else scheduled this page
    unschedule = dom.find(undo, '.unschedule');
    if (unschedule) {
      unschedule.classList.remove(kilnHideClass);
    }
  }

  // unpublish (only affects page)
  if (res.published) {
    state.toggleButton('published', true); // just in case someone else published this page
    unpublish = dom.find(undo, '.unpublish');
    if (unpublish) {
      unpublish.classList.remove(kilnHideClass);
    }
  }

  return undo;
}

/**
 * format and assemble error messages
 * @param {Object[]} errors (or warnings)
 * @param {string} [modifier] modifier class for warnings, info, etc
 * @returns {Element}
 */
function addErrorsOrWarnings(errors, modifier) {
  return _.reduce(errors, function (el, error) {
    var errorEl = tpl.get('.publish-errors-template'),
      errorLabel = dom.find(errorEl, '.label'),
      labelVal = _.get(error, 'rule.label'),
      descVal = _.get(error, 'rule.description'),
      errorDescription = dom.find(errorEl, '.description'),
      list = dom.find(errorEl, '.errors');

    // add rule label if it exists
    if (errorLabel) {
      errorLabel.innerHTML = labelVal ? `${labelVal}:` : 'There was a problem:';
      // note the colon after the label, hence using a ternary operator
    }

    // add rule description if it exists
    if (errorDescription) {
      errorDescription.innerHTML = descVal || 'Please see below for details';
    }

    // add each place where the error/warning occurs
    _.each(error.errors, function (item) {
      var itemEl = dom.create(`<li><span class="error-label">${item.label}</span>${addPreview(item.preview)}</li>`);

      list.appendChild(itemEl);
    });

    el.appendChild(errorEl);
    // add modifier class if it exists
    if (modifier) {
      dom.find(el, '.publish-error').classList.add(modifier);
    }
    return el;
  }, document.createDocumentFragment());
}

/**
 * create validation messages
 * @param {object} validation
 * @returns {Element}
 */
function createPublishValidation(validation) {
  var errors = validation.errors,
    warnings = validation.warnings;

  if (errors.length) {
    let el = document.createDocumentFragment(),
      messagesEl = tpl.get('.publish-error-message-template'),
      errorsEl = addErrorsOrWarnings(errors),
      warningsEl = addErrorsOrWarnings(warnings, 'publish-warning');

    el.appendChild(messagesEl);
    el.appendChild(errorsEl);
    el.appendChild(warningsEl);

    return el;
  } else if (warnings.length) {
    let el = document.createDocumentFragment(),
      messageEl = tpl.get('.publish-warning-message-template'),
      // same way the error pane does it
      errorsEl = addErrorsOrWarnings(warnings, 'publish-warning');

    el.appendChild(messageEl);
    el.appendChild(errorsEl);

    return el;
  } else {
    return tpl.get('.publish-valid-template');
  }
}

/**
 * create the health dynamic pane header
 * @param {object} validation
 * @returns {string}
 */
function createHealthHeader(validation) {
  var header = tpl.get('.health-header-template'),
    errors = validation.errors,
    warnings = validation.warnings;

  if (errors.length) {
    dom.find(header, '.errors').classList.remove(kilnHideClass);
  } else if (warnings.length) {
    dom.find(header, '.warnings').classList.remove(kilnHideClass);
  } else {
    dom.find(header, '.valid').classList.remove(kilnHideClass);
  }

  return header.firstElementChild.innerHTML;
}

/**
 * create messages for the publish pane, depending on the state
 * @param {object} res
 * @returns {Element}
 */
function createPublishMessages(res) {
  var messages = tpl.get('.publish-messages-template'),
    scheduleMessage, stateMessage;

  if (res.published) {
    stateMessage = dom.find(messages, '.publish-state-message');
    if (stateMessage && res.publishedAt) {
      stateMessage.innerHTML = `Published ${state.formatTime(res.publishedAt)}.`;
    } else if (stateMessage) {
      stateMessage.innerHTML = 'Page is currently published.';
    }
  }

  if (res.scheduled) {
    scheduleMessage = dom.find(messages, '.publish-schedule-message');
    if (scheduleMessage) {
      scheduleMessage.innerHTML = `Scheduled to publish ${state.formatTime(res.scheduledAt)}.`;
      scheduleMessage.classList.remove(kilnHideClass);
    }
  }

  return messages;
}

/**
 * get scheduled date
 * @param {object} res
 * @param {boolean} [res.scheduled]
 * @param {string} [res.scheduledAt]
 * @returns {object}
 */
function getScheduledValues(res) {
  var at = res.scheduled ? moment(res.scheduledAt) : moment(),
    date = at.format('YYYY-MM-DD'),
    time = at.format('HH:mm');

  return { date, time };
}

/**
 * create actions for the publish pane, depending on the state
 * @param {object} res
 * @returns {Element}
 */
function createPublishActions(res) {
  const actions = tpl.get('.publish-actions-template'),
    val = getScheduledValues(res);

  let scheduleDate, scheduleTime;

  // set date and time
  if (actions) {
    scheduleDate = dom.find(actions, '#schedule-date');
    scheduleTime = dom.find(actions, '#schedule-time');

    if (scheduleDate) {
      scheduleDate.setAttribute('min', val.date);
      scheduleDate.setAttribute('value', val.date);
      scheduleDate.setAttribute('placeholder', val.date);
    }

    if (scheduleTime) {
      scheduleTime.setAttribute('value', val.time);
      scheduleTime.setAttribute('placeholder', val.time);
    }

    // init datepicker for non-native browsers
    datepicker.init(scheduleDate, {static: true});
    datepicker.init(scheduleTime, {static: true});
  }

  return actions;
}

/**
 * create the form to set a custom url
 * @param {object} res which may contain customUrl
 * @returns {Element}
 */
function createLocationForm(res) {
  const form = tpl.get('.custom-url-form-template'),
    val = res.customUrl;

  if (val) {
    // remove the site prefix when displaying the url in the form
    // it'll be added when saving the form
    dom.find(form, '.custom-url-input').value = val.replace(db.uriToUrl(site.get('prefix')), '');
  }

  return form;
}

/**
 * open publish pane
 * @param {object} validation
 * @param {Object[]} validation.errors
 * @param {Object[]} validation.warnings
 * @returns {Promise}
 */
function openPublish(validation) {
  var pubContent = document.createDocumentFragment(),
    locationContent = document.createDocumentFragment(),
    healthContent = document.createDocumentFragment();

  return state.get().then(function (res) {
    var tabs, dynamicTab, el;

    // append publish message and actions to the doc fragment
    pubContent.appendChild(createUndoActions(res));
    pubContent.appendChild(createPublishMessages(res));
    pubContent.appendChild(createPublishActions(res));

    locationContent.appendChild(createLocationForm(res));

    // add dynamic pane with validation
    healthContent.appendChild(createPublishValidation(validation));

    // if there are errors, make the health tab active when the pane opens
    // and disable the publish tab
    if (_.get(validation, 'errors.length')) {
      // publish is disabled
      tabs = [{
        header: 'Publish',
        content: pubContent,
        disabled: true
      }, {
        header: 'Location',
        content: locationContent
      }];
      dynamicTab = {
        header: createHealthHeader(validation),
        content: healthContent,
        active: true // health tab is active when pane opens
      };
    } else {
      tabs = [{
        header: 'Publish',
        content: pubContent,
        active: true // publish tab is active when pane opens
      }, {
        header: 'Location',
        content: locationContent
      }];
      dynamicTab = {
        header: createHealthHeader(validation),
        content: healthContent
      };
    }

    // create pane!
    el = pane.open(tabs, dynamicTab);

    // init controller for publish pane
    ds.controller('publish-pane', publishPaneController);
    ds.get('publish-pane', el);
  });
}

module.exports = openPublish;
_.set(window, 'kiln.services.panes.openPublish', module.exports);
<commit_msg>hotfix addpreview<commit_after>const _ = require('lodash'),
  moment = require('moment'),
  dom = require('@nymag/dom'),
  ds = require('dollar-slice'),
  state = require('../page-state'),
  site = require('../site'),
  tpl = require('../tpl'),
  db = require('../edit/db'),
  pane = require('./'),
  datepicker = require('../field-helpers/datepicker'),
  publishPaneController = require('../../controllers/publish-pane'),
  kilnHideClass = 'kiln-hide';

/**
 * create actions for undoing scheduling and publishing
 * @param {object} res
 * @returns {Element}
 */
function createUndoActions(res) {
  const undo = tpl.get('.publish-undo-template');

  let unpublish, unschedule;

  // show undo actions if either are available
  if (res.scheduled || res.published) {
    dom.find(undo, '.undo').classList.remove(kilnHideClass);
  }

  // unscheduling
  if (res.scheduled) {
    state.toggleButton('scheduled', true); // just in case someone else scheduled this page
    unschedule = dom.find(undo, '.unschedule');
    if (unschedule) {
      unschedule.classList.remove(kilnHideClass);
    }
  }

  // unpublish (only affects page)
  if (res.published) {
    state.toggleButton('published', true); // just in case someone else published this page
    unpublish = dom.find(undo, '.unpublish');
    if (unpublish) {
      unpublish.classList.remove(kilnHideClass);
    }
  }

  return undo;
}

/**
 * add error preview, e.g. excerpt of the data that contains errors
 * @param {string} preview
 * @returns {string}
 */
function addPreview(preview) {
  if (preview) {
    return `<span class="error-preview">${preview}</span>`;
  } else {
    return '';
  }
}

/**
 * format and assemble error messages
 * @param {Object[]} errors (or warnings)
 * @param {string} [modifier] modifier class for warnings, info, etc
 * @returns {Element}
 */
function addErrorsOrWarnings(errors, modifier) {
  return _.reduce(errors, function (el, error) {
    var errorEl = tpl.get('.publish-errors-template'),
      errorLabel = dom.find(errorEl, '.label'),
      labelVal = _.get(error, 'rule.label'),
      descVal = _.get(error, 'rule.description'),
      errorDescription = dom.find(errorEl, '.description'),
      list = dom.find(errorEl, '.errors');

    // add rule label if it exists
    if (errorLabel) {
      errorLabel.innerHTML = labelVal ? `${labelVal}:` : 'There was a problem:';
      // note the colon after the label, hence using a ternary operator
    }

    // add rule description if it exists
    if (errorDescription) {
      errorDescription.innerHTML = descVal || 'Please see below for details';
    }

    // add each place where the error/warning occurs
    _.each(error.errors, function (item) {
      var itemEl = dom.create(`<li><span class="error-label">${item.label}</span>${addPreview(item.preview)}</li>`);

      list.appendChild(itemEl);
    });

    el.appendChild(errorEl);
    // add modifier class if it exists
    if (modifier) {
      dom.find(el, '.publish-error').classList.add(modifier);
    }
    return el;
  }, document.createDocumentFragment());
}

/**
 * create validation messages
 * @param {object} validation
 * @returns {Element}
 */
function createPublishValidation(validation) {
  var errors = validation.errors,
    warnings = validation.warnings;

  if (errors.length) {
    let el = document.createDocumentFragment(),
      messagesEl = tpl.get('.publish-error-message-template'),
      errorsEl = addErrorsOrWarnings(errors),
      warningsEl = addErrorsOrWarnings(warnings, 'publish-warning');

    el.appendChild(messagesEl);
    el.appendChild(errorsEl);
    el.appendChild(warningsEl);

    return el;
  } else if (warnings.length) {
    let el = document.createDocumentFragment(),
      messageEl = tpl.get('.publish-warning-message-template'),
      // same way the error pane does it
      errorsEl = addErrorsOrWarnings(warnings, 'publish-warning');

    el.appendChild(messageEl);
    el.appendChild(errorsEl);

    return el;
  } else {
    return tpl.get('.publish-valid-template');
  }
}

/**
 * create the health dynamic pane header
 * @param {object} validation
 * @returns {string}
 */
function createHealthHeader(validation) {
  var header = tpl.get('.health-header-template'),
    errors = validation.errors,
    warnings = validation.warnings;

  if (errors.length) {
    dom.find(header, '.errors').classList.remove(kilnHideClass);
  } else if (warnings.length) {
    dom.find(header, '.warnings').classList.remove(kilnHideClass);
  } else {
    dom.find(header, '.valid').classList.remove(kilnHideClass);
  }

  return header.firstElementChild.innerHTML;
}

/**
 * create messages for the publish pane, depending on the state
 * @param {object} res
 * @returns {Element}
 */
function createPublishMessages(res) {
  var messages = tpl.get('.publish-messages-template'),
    scheduleMessage, stateMessage;

  if (res.published) {
    stateMessage = dom.find(messages, '.publish-state-message');
    if (stateMessage && res.publishedAt) {
      stateMessage.innerHTML = `Published ${state.formatTime(res.publishedAt)}.`;
    } else if (stateMessage) {
      stateMessage.innerHTML = 'Page is currently published.';
    }
  }

  if (res.scheduled) {
    scheduleMessage = dom.find(messages, '.publish-schedule-message');
    if (scheduleMessage) {
      scheduleMessage.innerHTML = `Scheduled to publish ${state.formatTime(res.scheduledAt)}.`;
      scheduleMessage.classList.remove(kilnHideClass);
    }
  }

  return messages;
}

/**
 * get scheduled date
 * @param {object} res
 * @param {boolean} [res.scheduled]
 * @param {string} [res.scheduledAt]
 * @returns {object}
 */
function getScheduledValues(res) {
  var at = res.scheduled ? moment(res.scheduledAt) : moment(),
    date = at.format('YYYY-MM-DD'),
    time = at.format('HH:mm');

  return { date, time };
}

/**
 * create actions for the publish pane, depending on the state
 * @param {object} res
 * @returns {Element}
 */
function createPublishActions(res) {
  const actions = tpl.get('.publish-actions-template'),
    val = getScheduledValues(res);

  let scheduleDate, scheduleTime;

  // set date and time
  if (actions) {
    scheduleDate = dom.find(actions, '#schedule-date');
    scheduleTime = dom.find(actions, '#schedule-time');

    if (scheduleDate) {
      scheduleDate.setAttribute('min', val.date);
      scheduleDate.setAttribute('value', val.date);
      scheduleDate.setAttribute('placeholder', val.date);
    }

    if (scheduleTime) {
      scheduleTime.setAttribute('value', val.time);
      scheduleTime.setAttribute('placeholder', val.time);
    }

    // init datepicker for non-native browsers
    datepicker.init(scheduleDate, {static: true});
    datepicker.init(scheduleTime, {static: true});
  }

  return actions;
}

/**
 * create the form to set a custom url
 * @param {object} res which may contain customUrl
 * @returns {Element}
 */
function createLocationForm(res) {
  const form = tpl.get('.custom-url-form-template'),
    val = res.customUrl;

  if (val) {
    // remove the site prefix when displaying the url in the form
    // it'll be added when saving the form
    dom.find(form, '.custom-url-input').value = val.replace(db.uriToUrl(site.get('prefix')), '');
  }

  return form;
}

/**
 * open publish pane
 * @param {object} validation
 * @param {Object[]} validation.errors
 * @param {Object[]} validation.warnings
 * @returns {Promise}
 */
function openPublish(validation) {
  var pubContent = document.createDocumentFragment(),
    locationContent = document.createDocumentFragment(),
    healthContent = document.createDocumentFragment();

  return state.get().then(function (res) {
    var tabs, dynamicTab, el;

    // append publish message and actions to the doc fragment
    pubContent.appendChild(createUndoActions(res));
    pubContent.appendChild(createPublishMessages(res));
    pubContent.appendChild(createPublishActions(res));

    locationContent.appendChild(createLocationForm(res));

    // add dynamic pane with validation
    healthContent.appendChild(createPublishValidation(validation));

    // if there are errors, make the health tab active when the pane opens
    // and disable the publish tab
    if (_.get(validation, 'errors.length')) {
      // publish is disabled
      tabs = [{
        header: 'Publish',
        content: pubContent,
        disabled: true
      }, {
        header: 'Location',
        content: locationContent
      }];
      dynamicTab = {
        header: createHealthHeader(validation),
        content: healthContent,
        active: true // health tab is active when pane opens
      };
    } else {
      tabs = [{
        header: 'Publish',
        content: pubContent,
        active: true // publish tab is active when pane opens
      }, {
        header: 'Location',
        content: locationContent
      }];
      dynamicTab = {
        header: createHealthHeader(validation),
        content: healthContent
      };
    }

    // create pane!
    el = pane.open(tabs, dynamicTab);

    // init controller for publish pane
    ds.controller('publish-pane', publishPaneController);
    ds.get('publish-pane', el);
  });
}

module.exports = openPublish;
_.set(window, 'kiln.services.panes.openPublish', module.exports);
<|endoftext|>


<commit_before>(function(){

	"use strict";

	angular

		.module('postServices', [])
		.factory('PostBlog', function($http) {
    var postFactory = {}; // Create the userFactory object

    // Register users in database

    postFactory.createBlog = function(p) {
        return $http.post('/api/blogss', p);
    };

     postFactory.displayBlog = function() {
        return $http.get('/api/blogss');
    };

     postFactory.deleteBlog = function(h) {
            return $http.delete('/api/blogss/' + h);
        };

			return postFactory;
		});
})();<commit_msg>Delete postServices.js<commit_after><|endoftext|>


<commit_before>
const EXPORT = ['StatusBar'];

// local utility
this.__defineGetter__('aWin', function() Application.activeWindow);
this.__defineGetter__('aDoc', function() {
    // chrome スキームなどで、 fuel がエラー
    try {
        return aWin.activeTab.document;
    } catch(e) { return null; }
});
this.__defineGetter__('locationURL', function() {
    return aDoc ? aDoc.location.href : null;
});
this.__defineGetter__('isHttp', function() aDoc && aDoc.location.protocol.indexOf('http') == 0);

elementGetter(this, 'addButton', 'hBookmarkAddButton', document);
elementGetter(this, 'statusCount', 'hBookmark-status-count', document);
elementGetter(this, 'statusComment', 'hBookmark-status-comment', document);

let countCache = new ExpireCache('uCount', 60 * 60); // 一時間キャッシュ

var StatusBar = {
    goHome: function StatusBar_goHome() {
        // browser.loadURIWithFlags(url, Ci.nsIWebNavigation.LOAD_FLAGS_NONE, null, null, null);
        // XXX: 別タブで開きたい
        aWin.open(newURI('chrome://hatenabookmark/content/index.html'));
    },
    goEntry: function StatusBar_goEntry(event) {
        if (isHttp) {
            let url = locationURL;

            //openUILink(newURI('http://b.hatena.ne.jp/entry/' + url.replace('#', '%23')));
            openUILink('http://b.hatena.ne.jp/entry/' + url.replace('#', '%23'), event);
        }
    },
    checkBookmarked: function StatusBar_checkBookmarked() {
        if (isHttp && User.user && User.user.hasBookmark(locationURL)) {
            addButton.setAttribute('added', true);
        } else {
            addButton.setAttribute('added', false);
        }
    },
    lastURL: null,
    update: function StatsuBar_update() {
        let lURL = locationURL;
        if (StatusBar.lastURL == lURL) return;

        StatusBar.lastURL = lURL;
        if (StatusBar.prefs.get('addButton'))
            StatusBar.checkBookmarked();
        if (StatusBar.prefs.get('counter'))
            StatusBar.checkCount();
    },
    get prefs() {
        if (!StatusBar._prefs) {
            StatusBar._prefs = new Prefs('extensions.hatenabookmark.statusbar.');
        }
        return StatusBar._prefs;
    },
    lastCountValue: null,
    checkCount: function StatusBar_checkCount() {
        statusCount.value = (isHttp ? HTTPCache.counter.get(locationURL) : '0') || '0';
        if (StatusBar.lastCountValue == statusCount.value) return;
        StatusBar.lastCountValue = statusCount.value;
        while (statusCount.firstChild) statusCount.removeChild(statusCount.firstChild);
        let counts = statusCount.value.toString().split('');
        counts.forEach(function(i) {
            let image = document.createElement('image');
            image.setAttribute('width', '6px');
            image.setAttribute('height', '7px');
            image.width = '6px';
            image.height = '7px';
            image.setAttribute('src', 'chrome://hatenabookmark/skin/images/counter_' + i + '.png');
            statusCount.appendChild(image);
        });

        if (statusCount.value >= 5) {
            statusCount.setAttribute('users', 'many');
            statusComment.setAttribute('comment', true);
        } else if (statusCount.value >= 1) {
            statusComment.setAttribute('comment', true);
            statusCount.setAttribute('users', 'few');
        } else {
            statusCount.removeAttribute('comment');
            statusCount.setAttribute('users', 'none');
        }
    },
    registerPrefsListeners: function () {
        StatusBar.prefs.createListener('addButton', StatusBar.prefsAddButtonHandler);
        StatusBar.prefs.createListener('counter', StatusBar.prefsCounterHandler);
        StatusBar.prefsAddButtonHandler();
        StatusBar.prefsCounterHandler();
    },
    prefsAddButtonHandler: function(e) {
        if (StatusBar.prefs.get('addButton')) {
            addButton.removeAttribute('hidden');
        } else {
            addButton.setAttribute('hidden', true);
        }
    },
    prefsCounterHandler: function(e) {
        if (StatusBar.prefs.get('counter')) {
            statusCount.removeAttribute('hidden');
            statusComment.removeAttribute('hidden');
        } else {
            statusCount.setAttribute('hidden', true);
            statusComment.setAttribute('hidden', true);
        }
    },
    loadHandler: function() {
        StatusBar.registerPrefsListeners();
        StatusBar.update();
        getBrowser().addEventListener('DOMContentLoaded', StatusBar.update, false);
    }
}

window.addEventListener('load', StatusBar.loadHandler, false);

Application.activeWindow.events.addListener('TabSelect', function() {
    StatusBar.update();
});

/*
let ProgressListenr = {
    QueryInterface: XPCOMUtils.generateQI([
        Ci.nsIWebProgressListener,
        Ci.nsIXULBrowserWindow
    ]),
    onStateChange: function (webProgress, request, flags, status)
    {
         if (flags & (Ci.nsIWebProgressListener.STATE_IS_DOCUMENT | Ci.nsIWebProgressListener.STATE_IS_WINDOW)) {
             if (flags & Ci.nsIWebProgressListener.STATE_START) {
                StatusBar.update();
             }
         }
    },
    onSecurityChange: function (webProgress, aRequest, aState) {},
    onStatusChange: function (webProgress, request, status, message) {},
    onProgressChange: function (webProgress, request, curSelfProgress, maxSelfProgress, curTotalProgress, maxTotalProgress) {},
    onLocationChange: function () {},
}
*/

// XXX: うーん
// getBrowser().addProgressListener(ProgressListenr, Ci.nsIWebProgress.NOTIFY_ALL);

/*
Application.activeWindow.events.addListener('TabOpen', function() {
    //getBrowser().addProgressListener(ProgressListenr, Ci.nsIWebProgress.NOTIFY_ALL);
    getBrowser().addEventListener('DOMContentLoaded', StatusBar.update, false);
});
*/

EventService.createListener('UserChange', function() {
    StatusBar.update();
}, User);

<commit_msg>disable カウントを有効に、コメントアイコンをちゃんとdisable<commit_after>
const EXPORT = ['StatusBar'];

// local utility
this.__defineGetter__('aWin', function() Application.activeWindow);
this.__defineGetter__('aDoc', function() {
    // chrome スキームなどで、 fuel がエラー
    try {
        return aWin.activeTab.document;
    } catch(e) { return null; }
});
this.__defineGetter__('locationURL', function() {
    return aDoc ? aDoc.location.href : null;
});
this.__defineGetter__('isHttp', function() aDoc && aDoc.location.protocol.indexOf('http') == 0);

elementGetter(this, 'addButton', 'hBookmarkAddButton', document);
elementGetter(this, 'statusCount', 'hBookmark-status-count', document);
elementGetter(this, 'statusComment', 'hBookmark-status-comment', document);

let countCache = new ExpireCache('uCount', 60 * 60); // 一時間キャッシュ

var StatusBar = {
    goHome: function StatusBar_goHome() {
        // browser.loadURIWithFlags(url, Ci.nsIWebNavigation.LOAD_FLAGS_NONE, null, null, null);
        // XXX: 別タブで開きたい
        aWin.open(newURI('chrome://hatenabookmark/content/index.html'));
    },
    goEntry: function StatusBar_goEntry(event) {
        if (isHttp) {
            let url = locationURL;

            //openUILink(newURI('http://b.hatena.ne.jp/entry/' + url.replace('#', '%23')));
            openUILink('http://b.hatena.ne.jp/entry/' + url.replace('#', '%23'), event);
        }
    },
    checkBookmarked: function StatusBar_checkBookmarked() {
        if (isHttp && User.user && User.user.hasBookmark(locationURL)) {
            addButton.setAttribute('added', true);
        } else {
            addButton.setAttribute('added', false);
        }
    },
    lastURL: null,
    update: function StatsuBar_update() {
        let lURL = locationURL;
        if (StatusBar.lastURL == lURL) return;

        StatusBar.lastURL = lURL;
        if (StatusBar.prefs.get('addButton'))
            StatusBar.checkBookmarked();
        if (StatusBar.prefs.get('counter'))
            StatusBar.checkCount();
    },
    get prefs() {
        if (!StatusBar._prefs) {
            StatusBar._prefs = new Prefs('extensions.hatenabookmark.statusbar.');
        }
        return StatusBar._prefs;
    },
    lastCountValue: null,
    appendCountImage: function StatusBar_appendCountImage(url) {
        let image = document.createElement('image');
        image.setAttribute('width', '6px');
        image.setAttribute('height', '7px');
        image.width = '6px';
        image.height = '7px';
        image.setAttribute('src', url);
        statusCount.appendChild(image);
    },
    checkCount: function StatusBar_checkCount() {
        statusCount.value = (isHttp ? HTTPCache.counter.get(locationURL) : '0') || '0';
        if (StatusBar.lastCountValue == statusCount.value) return;
        StatusBar.lastCountValue = statusCount.value;
        while (statusCount.firstChild) statusCount.removeChild(statusCount.firstChild);
        if (statusCount.value > 0) {
            let counts = statusCount.value.toString().split('');
            counts.forEach(function(i) {
                StatusBar.appendCountImage('chrome://hatenabookmark/skin/images/counter_' + i + '.png');
            });
        } else {
             StatusBar.appendCountImage('chrome://hatenabookmark/skin/images/counter_disable.png');
        }

        if (statusCount.value >= 5) {
            statusCount.setAttribute('users', 'many');
            statusComment.setAttribute('comment', 'true');
        } else if (statusCount.value >= 1) {
            statusComment.setAttribute('comment', 'true');
            statusCount.setAttribute('users', 'few');
        } else {
            statusComment.setAttribute('comment', 'false');
            statusCount.setAttribute('users', 'none');
        }
    },
    registerPrefsListeners: function () {
        StatusBar.prefs.createListener('addButton', StatusBar.prefsAddButtonHandler);
        StatusBar.prefs.createListener('counter', StatusBar.prefsCounterHandler);
        StatusBar.prefsAddButtonHandler();
        StatusBar.prefsCounterHandler();
    },
    prefsAddButtonHandler: function(e) {
        if (StatusBar.prefs.get('addButton')) {
            addButton.removeAttribute('hidden');
        } else {
            addButton.setAttribute('hidden', true);
        }
    },
    prefsCounterHandler: function(e) {
        if (StatusBar.prefs.get('counter')) {
            statusCount.removeAttribute('hidden');
            statusComment.removeAttribute('hidden');
        } else {
            statusCount.setAttribute('hidden', true);
            statusComment.setAttribute('hidden', true);
        }
    },
    loadHandler: function() {
        StatusBar.registerPrefsListeners();
        StatusBar.update();
        getBrowser().addEventListener('DOMContentLoaded', StatusBar.update, false);
    }
}

window.addEventListener('load', StatusBar.loadHandler, false);

Application.activeWindow.events.addListener('TabSelect', function() {
    StatusBar.update();
});

/*
let ProgressListenr = {
    QueryInterface: XPCOMUtils.generateQI([
        Ci.nsIWebProgressListener,
        Ci.nsIXULBrowserWindow
    ]),
    onStateChange: function (webProgress, request, flags, status)
    {
         if (flags & (Ci.nsIWebProgressListener.STATE_IS_DOCUMENT | Ci.nsIWebProgressListener.STATE_IS_WINDOW)) {
             if (flags & Ci.nsIWebProgressListener.STATE_START) {
                StatusBar.update();
             }
         }
    },
    onSecurityChange: function (webProgress, aRequest, aState) {},
    onStatusChange: function (webProgress, request, status, message) {},
    onProgressChange: function (webProgress, request, curSelfProgress, maxSelfProgress, curTotalProgress, maxTotalProgress) {},
    onLocationChange: function () {},
}
*/

// XXX: うーん
// getBrowser().addProgressListener(ProgressListenr, Ci.nsIWebProgress.NOTIFY_ALL);

/*
Application.activeWindow.events.addListener('TabOpen', function() {
    //getBrowser().addProgressListener(ProgressListenr, Ci.nsIWebProgress.NOTIFY_ALL);
    getBrowser().addEventListener('DOMContentLoaded', StatusBar.update, false);
});
*/

EventService.createListener('UserChange', function() {
    StatusBar.update();
}, User);

<|endoftext|>


<commit_before>/**
 * @class Elementer
 * @namespace Hupper
 * @description Class to create and manipulate DOM elements
 * @constructor
 */
Hupper.Elementer = function(doc) {
  this.doc = doc || HUP.w;
  this.li = this.doc.createElement('li');
  this.ul = this.doc.createElement('ul');
  this.div = this.doc.createElement('div');
  this.span = this.doc.createElement('span');
  this.a = this.doc.createElement('a');
  this.img = this.doc.createElement('img');
  this.button = this.doc.createElement('button');
  this.GetBody();
};
Hupper.Elementer.prototype = {
  /**
   * Creates an 'li' element
   * @return Li element
   * @type Element
   */
  Li: function() {
    return this.li.cloneNode(true);
  },
  /**
   * Creates an 'ul' element
   * @return Ul element
   * @type Element
   */
  Ul: function() {
    return this.ul.cloneNode(true);
  },
  /**
   * Creates an 'div' element
   * @return Div element
   * @type Element
   */
  Div: function() {
    return this.div.cloneNode(true);
  },
  /**
   * Creates an 'span' element
   * @return Span element
   * @type Element
   */
  Span: function() {
    return this.span.cloneNode(true);
  },
  /**
   * Creates an 'a' element
   * @return A element
   * @type Element
   */
  A: function() {
    return this.a.cloneNode(true);
  },
  /**
   * Creates an 'img' element
   * @param {String} src source of the image
   * @param {String} alt image alternate text
   * @return Img element
   * @type Element
   */
  Img: function(src, alt) {
    var img = this.img.cloneNode(true);
    img.setAttribute('src', src);
    img.setAttribute('alt', alt);
    return img;
  },
  /**
   * Creates a 'button' element
   * @return button element
   * @type Element
   */
  Btn: function(title, className) {
    var button = this.button.cloneNode(true);
    button.setAttribute('title', title);
    this.AddClass(button, className);
    return button;
  },
  /**
   * Creates a specified element
   * @param {String} el type of element
   * @return Li element
   * @type Element
   */
  El: function(el) {
    return this.doc.createElement(el);
  },
  /**
   * Creates a text element
   * @return Text element
   * @type Element
   */
  Txt: function(text) {
    return this.doc.createTextNode(text);
  },
  /**
   * Adds a child element
   * @param {Element} elem addable element
   * @param {Element} parent Element, where the new element will appended
   * @return Returns the element
   * @type {Element,Null}
   */
  Add: function(elem, parent) {
    if(parent && elem) {
      parent.appendChild(elem);
      return elem;
    }
    return null;
  },
  /**
    * Inserts an element before another element
    * @param {Element} elem insertable element
    * @param {Element} before element before the new elem will inserted
    * @return Returns the elem
    * @type Element
    */
  Insert: function(elem, before) {
    if(!before || !elem) {
      Hupper.Log('huha', elem);
      return;
    }
    before.parentNode.insertBefore(elem, before);
    return elem;
  },
  /**
    * Removes the specified element
    * @param {Element} elem removable childnode
    * @param {Element} parent
    */
  Remove: function(elem, parent) {
    if(!elem || !elem.parentNode) return;
    var el = elem.parentNode.removeChild(elem);
    el = null;
  },
  /**
  * Removes all childnode of the element
  * @param {Element} element
  */
  RemoveAll: function(element) {
    while(element && element.firstChild) {
      this.Remove(element.firstChild);
    }
  },
  /**
    * @param {Element} inner the new content element
    * @param {Element} obj updatable element
    */
  Update: function(inner, obj) {
    this.RemoveAll(obj);
    this.Add(inner, obj);
  },
  /**
    * Collects the elements by their tag name
    * @param {String} tag the elements tag name
    * @param {Element} [parent] parent element
    * @return An array which contains the elements with the given tagname
    * @type {Array}
    */
  GetTag: function(tag, parent) {
    if(parent && typeof parent == 'object') {
      return parent.getElementsByTagName(tag);
    }
    return this.doc.getElementsByTagName(tag);
  },
  /**
    * Returns the first matching tag
    * @see #GetTag
    * @param {String} tag the elements tag name
    * @param {Objecŧ} [parent] parent element
    * @return First element node or null
    * @type Element
    */
  GetFirstTag: function(tag, parent) {
    var tags = this.GetTag(tag, parent);
    return (tags.length) ? tags[0] : null;
  },
  /**
    * Returns the document body
    * @type Element
    */
  GetBody: function() {
    if(this.body) {
      return this.body;
    }
    this.body = document.body;
    return this.body;
  },
  /**
    * Returns an element by it's id
    * @param {String} id Id of the element
    * @param {Element} [parent] parent element
    * @type Element
    */
  GetId: function(id, parent) {
    // if(!this.elements) {
    //   this.elements = new Object();
    // }
    var output;
    // if(!this.elements[id]) {
      if(typeof parent == 'object' && parent.nodeType === 1) {
        output = parent.getElementById(id);
      } else if(typeof(parent) == 'undefined') {
        output = this.doc.getElementById(id);
      }
    // }
    return output;
  },
  /**
  * Adds the specified class to the element
  * @param {Element} el DOM element
  * @param {String} c Class name
  */
  AddClass: function(el, c) {
    if(!el || !c || this.HasClass(el, c)) return false;
    Components.utils.import('resource://huppermodules/hupstringer.jsm');
    var curClass = el.getAttribute('class');
    (curClass === null || HupStringer.empty(curClass)) ? el.setAttribute('class', c) : el.setAttribute('class', curClass + ' ' + c);
  },
  /**
  * Removes the specified class from the element
  * @param {Element} el DOM element
  * @param {String} c Class name
  */
  RemoveClass: function(el, c) {
    if(!el || !c) return false;
    var cl =  new RegExp('\\b' + c + '\\b');
    el.setAttribute('class', el.getAttribute('class').replace(cl, ''));
  },
  /**
  * Checks that the element has the specified class or not
  * @param {Element} el Element
  * @param {String} c Class name
  * @type {Boolean}
  */
  HasClass: function(el, c) {
    if(!el || !c) return false;
    var cl = new RegExp('\\b' + c + '\\b');
    return cl.test(el.getAttribute('class'));
  },
  /**
  * Adds or removes the specified class from the element
  * @param {Element} el DOM element
  * @param {String} c Class name
  */
  ToggleClass: function(el, c) {
   this.HasClass(el, c) ? this.RemoveClass(el, c) : this.AddClass(el, c);
  },
  /**
  * Collects the elements, which has the specified className (cn) and childNodes of the specified node (par)
  * @param {Element} par parent element node
  * @param {String} cn The className
  * @param {String} el element type
  * @param {Boolean} [force] if the par attribute is false|undefined change the parent element to the body if the value of the variable is true
  * @return the elements which are childnodes of the parent and has the specified classname
  * @type {Array}
  */
  GetByClass: function(par, cn, el, force) {
    el = el ? el.toUpperCase() : '*';
    if(!par) {
      if(force == true) {
        par = this.GetBody();
      } else {
        return new Array();
      }
    }
    var out = new Array();
    // try to use the native getElementsByClassName method
    if(document.getElementsByClassName) {
      var ts = Array.prototype.slice.call(par.getElementsByClassName(cn));
      return el == '*' ? ts : ts.filter(function(elem) {
        return elem.nodeName == el;
      });
    } else {
      var ts = this.GetTag(el, par);
      for(var i = 0, tsl = ts.length; i < tsl; i++) {
        if(this.HasClass(ts[i], cn)) {
          out.push(ts[i]);
        }
      }
    }
    return out;
  },
  /**
   * @param {Element} par parent element node
   * @param {String} attr attribitue name
   * @param {String} [val] value of the attribute
   * @param {String} el element type
   * @param {Boolean} [force] if the par attribute is false|undefined change the parent element to the body if the value of the variable is true
   * @return the elements which are childnodes of the parent and has the specified attribute
   * @type Array
   */
  GetByAttrib: function(par, el, attr, val, force) {
    el = el ? el.toUpperCase() : el = '*';
    if(!par) {
      if(force == true) {
        par = this.GetBody();
      } else {
        return new Array();
      }
    }
    var out = new Array();
    var ts = this.GetTag(el, par);
    for(var i = 0, tsl = ts.length; i < tsl; i++) {
      if(this.HasAttr(ts[i], attr, val)) {
        out.push(ts[i]);
      }
    }
    return out;
  },
  /**
   * @param {Element} el an element
   * @param {String} attr name of the attribute
   * @param {String} [val] value of the attribute
   * @returns true if the element has the attribute (if value specified the attribute value also checked)
   * @type {Boolean}
   */
  HasAttr: function(el, attr, val) {
    var a = el.getAttribute(attr);
    if(typeof val == 'string') {
      return (typeof a != 'undefined' && a == val);
    } else {
      return (typeof a != 'undefined');
    }
  },
  /**
   * @param {String} text link content
   * @param {String} [href] url of the link
   * @return link object
   * @type Element
   */
  CreateLink: function(text, href) {
    var l = this.A();
    if(href) {
      l.setAttribute('href', href);
    }
    this.Add(this.Txt(text), l)
    return l;
  },
  Hide: function(el) {
    if(el && typeof el == 'object' && el.nodeName != 'BUTTON') {
      // HUP.L.log('HIDE', el);
    }
    this.AddClass(el, 'hup-hidden');
  },
  Show: function(el) {
    if(el && el == 'object' && el.nodeName != 'BUTTON') {
      // HUP.L.log('SHOW', el);
    }
    this.RemoveClass(el, 'hup-hidden');
  }
};

<commit_msg>Make possible to create documentFragment<commit_after>/**
 * @class Elementer
 * @namespace Hupper
 * @description Class to create and manipulate DOM elements
 * @constructor
 */
Hupper.Elementer = function(doc) {
  this.doc = doc || HUP.w;
  this.li = this.doc.createElement('li');
  this.ul = this.doc.createElement('ul');
  this.div = this.doc.createElement('div');
  this.span = this.doc.createElement('span');
  this.a = this.doc.createElement('a');
  this.img = this.doc.createElement('img');
  this.button = this.doc.createElement('button');
  this.GetBody();
};
Hupper.Elementer.prototype = {
  /**
   * Creates an 'li' element
   * @return Li element
   * @type Element
   */
  Li: function() {
    return this.li.cloneNode(true);
  },
  /**
   * Creates an 'ul' element
   * @return Ul element
   * @type Element
   */
  Ul: function() {
    return this.ul.cloneNode(true);
  },
  /**
   * Creates an 'div' element
   * @return Div element
   * @type Element
   */
  Div: function() {
    return this.div.cloneNode(true);
  },
  /**
   * Creates an 'span' element
   * @return Span element
   * @type Element
   */
  Span: function() {
    return this.span.cloneNode(true);
  },
  /**
   * Creates an 'a' element
   * @return A element
   * @type Element
   */
  A: function() {
    return this.a.cloneNode(true);
  },
  /**
   * Creates an 'img' element
   * @param {String} src source of the image
   * @param {String} alt image alternate text
   * @return Img element
   * @type Element
   */
  Img: function(src, alt) {
    var img = this.img.cloneNode(true);
    img.setAttribute('src', src);
    img.setAttribute('alt', alt);
    return img;
  },
  /**
   * Creates a 'button' element
   * @return button element
   * @type Element
   */
  Btn: function(title, className) {
    var button = this.button.cloneNode(true);
    button.setAttribute('title', title);
    this.AddClass(button, className);
    return button;
  },
  /**
   * Creates a specified element
   * @param {String} el type of element
   * @return Li element
   * @type Element
   */
  El: function(el) {
    return this.doc.createElement(el);
  },
  /**
   * Creates a text element
   * @return Text element
   * @type Element
   */
  Txt: function(text) {
    return this.doc.createTextNode(text);
  },
  /**
   * Adds a child element
   * @param {Element} elem addable element
   * @param {Element} parent Element, where the new element will appended
   * @return Returns the element
   * @type {Element,Null}
   */
  Add: function(elem, parent) {
    if(parent && elem) {
      parent.appendChild(elem);
      return elem;
    }
    return null;
  },
  /**
    * Inserts an element before another element
    * @param {Element} elem insertable element
    * @param {Element} before element before the new elem will inserted
    * @return Returns the elem
    * @type Element
    */
  Insert: function(elem, before) {
    if(!before || !elem) {
      Hupper.Log('huha', elem);
      return;
    }
    before.parentNode.insertBefore(elem, before);
    return elem;
  },
  /**
    * Removes the specified element
    * @param {Element} elem removable childnode
    * @param {Element} parent
    */
  Remove: function(elem, parent) {
    if(!elem || !elem.parentNode) return;
    var el = elem.parentNode.removeChild(elem);
    el = null;
  },
  /**
  * Removes all childnode of the element
  * @param {Element} element
  */
  RemoveAll: function(element) {
    while(element && element.firstChild) {
      this.Remove(element.firstChild);
    }
  },
  Fragment: function () {
      return this.doc.createDocumentFragment();
  },
  /**
    * @param {Element} inner the new content element
    * @param {Element} obj updatable element
    */
  Update: function(inner, obj) {
    this.RemoveAll(obj);
    this.Add(inner, obj);
  },
  /**
    * Collects the elements by their tag name
    * @param {String} tag the elements tag name
    * @param {Element} [parent] parent element
    * @return An array which contains the elements with the given tagname
    * @type {Array}
    */
  GetTag: function(tag, parent) {
    if(parent && typeof parent == 'object') {
      return parent.getElementsByTagName(tag);
    }
    return this.doc.getElementsByTagName(tag);
  },
  /**
    * Returns the first matching tag
    * @see #GetTag
    * @param {String} tag the elements tag name
    * @param {Objecŧ} [parent] parent element
    * @return First element node or null
    * @type Element
    */
  GetFirstTag: function(tag, parent) {
    var tags = this.GetTag(tag, parent);
    return (tags.length) ? tags[0] : null;
  },
  /**
    * Returns the document body
    * @type Element
    */
  GetBody: function() {
    if(this.body) {
      return this.body;
    }
    this.body = document.body;
    return this.body;
  },
  /**
    * Returns an element by it's id
    * @param {String} id Id of the element
    * @param {Element} [parent] parent element
    * @type Element
    */
  GetId: function(id, parent) {
    // if(!this.elements) {
    //   this.elements = new Object();
    // }
    var output;
    // if(!this.elements[id]) {
      if(typeof parent == 'object' && parent.nodeType === 1) {
        output = parent.getElementById(id);
      } else if(typeof(parent) == 'undefined') {
        output = this.doc.getElementById(id);
      }
    // }
    return output;
  },
  /**
  * Adds the specified class to the element
  * @param {Element} el DOM element
  * @param {String} c Class name
  */
  AddClass: function(el, c) {
    if(!el || !c || this.HasClass(el, c)) return false;
    Components.utils.import('resource://huppermodules/hupstringer.jsm');
    var curClass = el.getAttribute('class');
    (curClass === null || HupStringer.empty(curClass)) ? el.setAttribute('class', c) : el.setAttribute('class', curClass + ' ' + c);
  },
  /**
  * Removes the specified class from the element
  * @param {Element} el DOM element
  * @param {String} c Class name
  */
  RemoveClass: function(el, c) {
    if(!el || !c) return false;
    var cl =  new RegExp('\\b' + c + '\\b');
    el.setAttribute('class', el.getAttribute('class').replace(cl, ''));
  },
  /**
  * Checks that the element has the specified class or not
  * @param {Element} el Element
  * @param {String} c Class name
  * @type {Boolean}
  */
  HasClass: function(el, c) {
    if(!el || !c) return false;
    var cl = new RegExp('\\b' + c + '\\b');
    return cl.test(el.getAttribute('class'));
  },
  /**
  * Adds or removes the specified class from the element
  * @param {Element} el DOM element
  * @param {String} c Class name
  */
  ToggleClass: function(el, c) {
   this.HasClass(el, c) ? this.RemoveClass(el, c) : this.AddClass(el, c);
  },
  /**
  * Collects the elements, which has the specified className (cn) and childNodes of the specified node (par)
  * @param {Element} par parent element node
  * @param {String} cn The className
  * @param {String} el element type
  * @param {Boolean} [force] if the par attribute is false|undefined change the parent element to the body if the value of the variable is true
  * @return the elements which are childnodes of the parent and has the specified classname
  * @type {Array}
  */
  GetByClass: function(par, cn, el, force) {
    el = el ? el.toUpperCase() : '*';
    if(!par) {
      if(force == true) {
        par = this.GetBody();
      } else {
        return new Array();
      }
    }
    var out = new Array();
    // try to use the native getElementsByClassName method
    if(document.getElementsByClassName) {
      var ts = Array.prototype.slice.call(par.getElementsByClassName(cn));
      return el == '*' ? ts : ts.filter(function(elem) {
        return elem.nodeName == el;
      });
    } else {
      var ts = this.GetTag(el, par);
      for(var i = 0, tsl = ts.length; i < tsl; i++) {
        if(this.HasClass(ts[i], cn)) {
          out.push(ts[i]);
        }
      }
    }
    return out;
  },
  /**
   * @param {Element} par parent element node
   * @param {String} attr attribitue name
   * @param {String} [val] value of the attribute
   * @param {String} el element type
   * @param {Boolean} [force] if the par attribute is false|undefined change the parent element to the body if the value of the variable is true
   * @return the elements which are childnodes of the parent and has the specified attribute
   * @type Array
   */
  GetByAttrib: function(par, el, attr, val, force) {
    el = el ? el.toUpperCase() : el = '*';
    if(!par) {
      if(force == true) {
        par = this.GetBody();
      } else {
        return new Array();
      }
    }
    var out = new Array();
    var ts = this.GetTag(el, par);
    for(var i = 0, tsl = ts.length; i < tsl; i++) {
      if(this.HasAttr(ts[i], attr, val)) {
        out.push(ts[i]);
      }
    }
    return out;
  },
  /**
   * @param {Element} el an element
   * @param {String} attr name of the attribute
   * @param {String} [val] value of the attribute
   * @returns true if the element has the attribute (if value specified the attribute value also checked)
   * @type {Boolean}
   */
  HasAttr: function(el, attr, val) {
    var a = el.getAttribute(attr);
    if(typeof val == 'string') {
      return (typeof a != 'undefined' && a == val);
    } else {
      return (typeof a != 'undefined');
    }
  },
  /**
   * @param {String} text link content
   * @param {String} [href] url of the link
   * @return link object
   * @type Element
   */
  CreateLink: function(text, href) {
    var l = this.A();
    if(href) {
      l.setAttribute('href', href);
    }
    this.Add(this.Txt(text), l)
    return l;
  },
  Hide: function(el) {
    if(el && typeof el == 'object' && el.nodeName != 'BUTTON') {
      // HUP.L.log('HIDE', el);
    }
    this.AddClass(el, 'hup-hidden');
  },
  Show: function(el) {
    if(el && el == 'object' && el.nodeName != 'BUTTON') {
      // HUP.L.log('SHOW', el);
    }
    this.RemoveClass(el, 'hup-hidden');
  }
};

<|endoftext|>


<commit_before>/* See license.txt for terms of usage */

FBL.ns(function() { with (FBL) {

const Cc = Components.classes;
const Ci = Components.interfaces;

const appInfo = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo);

const harVersion = "1.1";
var prefDomain = "extensions.firebug.netexport";

// ************************************************************************************************
// HAR builder implementation

Firebug.NetExport.HARBuilder = function()
{
    this.pageMap = [];
}

Firebug.NetExport.HARBuilder.prototype =
{
    build: function(context)
    {
        this.context = context;

        var panel = context.getPanel("net");

        // Build basic structure for data.
        var log = this.buildLog();

        // Before enumerating requests, we need to call layout methods that renders
        // content of the panel. This ensures export even if the panel is not visible
        // (since e.g. Firebug UI is minimized).
        panel.layout();

        // If set to true, requests coming from BFCache will also be exported.
        var exportFromBFCache = Firebug.getPref(prefDomain, "exportFromBFCache");

        // Build entries.
        var self = this;
        panel.enumerateRequests(function(file)
        {
            // Don't export BFCache responses. These don't represent network activity.
            // Do export if the pref says so.
            if (file.fromBFCache && !exportFromBFCache)
                return;

            if (file.loaded)
                log.entries.push(self.buildEntry(log, file));
        })

        return {log:log};
    },

    buildLog: function()
    {
        var log = {};
        log.version = harVersion;
        log.creator = {name: "Firebug", version: Firebug.version};
        log.browser = {name: appInfo.name, version: appInfo.version};
        log.pages = [];
        log.entries = [];
        return log;
    },

    buildPage: function(file)
    {
        var page = {};

        // Page start time is set when the first request is processed (see buildEntry)
        page.startedDateTime = 0;

        // Page title and ID comes from a document object that is shared by
        // all requests executed by the same page (since Firebug 1.5b1).
        var doc = this.getTopDocument(file);
        var pageId = doc.id;
        var title = doc.title;

        page.id = "page_" + (pageId ? pageId : "0");
        page.title = title ? title : this.context.getTitle();
        return page;
    },

    getTopDocument: function(file)
    {
        // Always get ID of the top document, not a document that comes from
        // and iframe. Otherwise, inner frames would be exported as separated
        // pages, see Issue 5153
        var doc = file.document;
        while (doc.parent)
            doc = doc.parent;

        return doc;
    },

    getPage: function(log, file)
    {
        var doc = this.getTopDocument(file);

        var page = this.pageMap[doc.id];
        if (page)
            return page;

        if (FBTrace.DBG_NETEXPORT)
            FBTrace.sysout("netexport.getPage; " + doc.id);

        this.pageMap[doc.id] = page = this.buildPage(file);
        log.pages.push(page);

        return page;
    },

    buildEntry: function(log, file)
    {
        var page = this.getPage(log, file);

        var entry = {};
        entry.pageref = page.id;
        entry.startedDateTime = dateToJSON(new Date(file.startTime));
        entry.time = file.endTime - file.startTime;
        entry.request = this.buildRequest(file);
        entry.response = this.buildResponse(file);
        entry.cache = this.buildCache(file);
        entry.timings = this.buildTimings(file);

        // Remote IP address and port number are accessible in Firefox 5.
        if (file.remoteAddress)
            entry.serverIPAddress = file.remoteAddress;

        if (file.remotePort)
            entry.connection = file.remotePort + ""; // must be a string

        // Compute page load start time according to the first request start time.
        if (!page.startedDateTime)
            page.startedDateTime = entry.startedDateTime;

        page.pageTimings = this.buildPageTimings(page, file);

        return entry;
    },

    buildPageTimings: function(page, file)
    {
        var timings = page.pageTimings;

        // Put page timings into the page object when we have the first entry.
        if (!timings)
        {
            // The default value -1 (not available) according to the spec.
            timings = {onContentLoad: -1, onLoad: -1};

            if (file.phase.contentLoadTime)
                timings.onContentLoad = file.phase.contentLoadTime - file.startTime;

            if (file.phase.windowLoadTime)
                timings.onLoad = file.phase.windowLoadTime - file.startTime;

            // Remember start time of the first request for time stamps below. Time stamps
            // are always computed against the page start time.
            this.startedDateTime = file.startTime;
        }

        var phase = file.phase;

        // Compatibility with older versions of Firebug (no time stamp).
        if (!phase.timeStamps)
            return timings;

        if (!this.phases)
            this.phases = [];

        // Check whether time-stamps from this phase has been already exported.
        if (this.phases.indexOf(phase) != -1)
            return timings;

        // Timings produced by console.timeStamp. This can be part of other phases too.
        var stamps = [];
        for (var i=0; i<phase.timeStamps.length; i++)
        {
            var stamp = phase.timeStamps[i];
            if (!stamp.time)
                continue;

            // Ignore standard timings, they are already exported.
            var label = stamp.label;
            if (label == "load" || label == "DOMContentLoaded")
                continue;

            // Time stamps from all phases are inserted into one list (HAR doesn't know about
            // phases). It's up to the client how to deal with it.
            // xxxHonza: This field is non standard so far.
            if (!timings._timeStamps)
            {
                timings.comment = "_timeStamps field contains timing data generated using " +
                    "console.timeStamp() method. See Firebug documentation: " +
                    "http://getfirebug.com/wiki/index.php/Console_API";
                timings._timeStamps = [];
            }

            timings._timeStamps.push({
                time: stamp.time - this.startedDateTime,
                label: label
            });
        }

        // Time stamps from this phase has been exported.
        this.phases.push(phase);

        return timings;
    },

    buildRequest: function(file)
    {
        var request = {};

        request.method = file.method;
        request.url = file.request.URI.spec;
        request.httpVersion = this.getHttpVersion(file.request, true);

        request.cookies = this.buildRequestCookies(file);
        request.headers = this.buildHeaders(file.requestHeaders);

        request.queryString = file.urlParams;
        request.postData = this.buildPostData(file);

        request.headersSize = file.requestHeadersText ? file.requestHeadersText.length : -1;
        request.bodySize = file.postText ? file.postText.length : -1;

        return request;
    },

    buildPostData: function(file)
    {
        if (!file.postText)
            return;

        var postData = {mimeType: "", params: [], text: ""};

        var text = file.postText;
        if (isURLEncodedFile(file, text))
        {
            var lines = text.split("\n");
            postData.mimeType = "application/x-www-form-urlencoded";
            postData.params = parseURLEncodedText(lines[lines.length-1]);
        }
        else
        {
            postData.text = text;
        }

        if (FBTrace.DBG_NETEXPORT)
            FBTrace.sysout("netexport.buildPostData; ", postData);

        return postData;
    },

    buildRequestCookies: function(file)
    {
        var header = findHeader(file.requestHeaders, "cookie");

        var result = [];
        var cookies = header ? header.split("; ") : [];
        for (var i=0; i<cookies.length; i++)
        {
            var data = cookies[i];
            var index = data.indexOf("=");
            var cookie = {};
            cookie.name = data.substr(0, index);
            cookie.value = data.substr(index+1);
            result.push(cookie);
        }

        return result;
    },

    buildResponseCookies: function(file)
    {
        var header = findHeader(file.responseHeaders, "set-cookie");

        var result = [];
        var cookies = header ? header.split("\n") : [];
        for (var i=0; i<cookies.length; i++)
        {
            var cookie = this.parseCookieFromResponse(cookies[i]);
            result.push(cookie);
        }

        return result;
    },

    parseCookieFromResponse: function(string)
    {
        var cookie = new Object();
        var pairs = string.split("; ");

        for (var i=0; i<pairs.length; i++)
        {
            var option = pairs[i].split("=");
            if (i == 0)
            {
                cookie.name = option[0];
                cookie.value = option[1];
            }
            else
            {
                var name = option[0].toLowerCase();
                if (name == "httponly")
                {
                    cookie.httpOnly = true;
                }
                else if (name == "expires")
                {
                    var value = option[1];
                    value = value.replace(/-/g, " ");
                    cookie[name] = dateToJSON(new Date(value.replace(/-/g, " ")));
                }
                else
                {
                    cookie[name] = option[1];
                }
            }
        }

        return cookie;
    },

    buildHeaders: function(headers)
    {
        var result = [];
        for (var i=0; headers && i<headers.length; i++)
            result.push({name: headers[i].name, value: headers[i].value});
        return result;
    },

    buildResponse: function(file)
    {
        var response = {status: 0};

        // Arbitary value if it's aborted to make sure status has a number
        if (file.responseStatus)
            response.status = parseInt(file.responseStatus);

        response.statusText = file.responseStatusText;
        response.httpVersion = this.getHttpVersion(file.request, false);

        response.cookies = this.buildResponseCookies(file);
        response.headers = this.buildHeaders(file.responseHeaders);
        response.content = this.buildContent(file);

        response.redirectURL = findHeader(file.responseHeaders, "Location");

        response.headersSize = file.responseHeadersText ? file.responseHeadersText.length : -1;
        response.bodySize = file.size;

        return response;
    },

    buildContent: function(file)
    {
        var content = {mimeType: ""};
        content.size = file.responseText ? file.responseText.length :
            (file.size >= 0 ? file.size : -1);

        try
        {
            content.mimeType = file.request.contentType;
        }
        catch (e)
        {
            if (FBTrace.DBG_NETEXPORT || FBTrace.DBG_ERRORS)
                FBTrace.sysout("netexport.buildContent EXCEPTION", e);
        }

        if (file.responseText)
            content.text = file.responseText;

        return content;
    },

    buildCache: function(file)
    {
        var cache = {};

        if (!file.fromCache)
            return cache;

        //cache.beforeRequest = {}; //xxxHonza: There is no such info yet in the Net panel.

        if (file.cacheEntry)
            cache.afterRequest = this.buildCacheEntry(file.cacheEntry);
        else
            cache.afterRequest = null;

        return cache;
    },

    buildCacheEntry: function(cacheEntry)
    {
        var cache = {};
        cache.expires = findHeader(cacheEntry, "Expires");
        cache.lastAccess = findHeader(cacheEntry, "Last Fetched");
        cache.eTag = ""; //xxxHonza
        cache.hitCount = findHeader(cacheEntry, "Fetch Count");
        return cache;
    },

    buildTimings: function(file)
    {
        var startTime = file.startTime;
        var resolvingTime = file.resolvingTime;
        var connectingTime = file.connectingTime;
        var connectedTime = file.connectedTime;
        var sendingTime = file.sendingTime;
        var waitingForTime = file.waitingForTime;
        var respondedTime = file.respondedTime;
        var endTime = file.endTime;

        // Fix problem where some net events wasn't sent and some timing info
        // wasn't updated to the last received event.
        // This problem should be fixed in Firebug 1.8b5
        if (connectingTime < resolvingTime)
            connectingTime = resolvingTime;

        if (connectedTime < connectingTime)
            connectedTime = connectingTime;

        if (sendingTime < connectedTime)
            sendingTime = connectedTime;

        if (waitingForTime < sendingTime)
            waitingForTime = sendingTime;

        var blockingEnd = this.getBlockingEndTime(file);

        var timings = {};
        timings.blocked = blockingEnd - startTime;
        timings.dns = connectingTime - resolvingTime;
        timings.connect = file.connectStarted ? sendingTime - connectingTime : 0;
        timings.send = waitingForTime - sendingTime;
        timings.wait = respondedTime - waitingForTime;
        timings.receive = endTime - respondedTime;

        return timings;
    },

    // xxxHonza: duplicated in NetUtils
    getBlockingEndTime: function(file)
    {
        if (file.resolveStarted && file.connectStarted)
            return file.resolvingTime;

        if (file.connectStarted)
            return file.connectingTime;

        if (file.sendStarted)
            return file.sendingTime;

        // This is how blocking end was computed before Firebug 1.8b6
        return (file.sendingTime > file.startTime) ? file.sendingTime : file.waitingForTime;
    },

    getHttpVersion: function(request, forRequest)
    {
        if (request instanceof Ci.nsIHttpChannelInternal)
        {
            try
            {
                var major = {}, minor = {};

                if (forRequest)
                    request.getRequestVersion(major, minor);
                else
                    request.getResponseVersion(major, minor);

                return "HTTP/" + major.value + "." + minor.value;
            }
            catch(err)
            {
                if (FBTrace.DBG_NETEXPORT || FBTrace.DBG_ERRORS)
                    FBTrace.sysout("netexport.getHttpVersion EXCEPTION", err);
            }
        }

        return "";
    },
}

// ************************************************************************************************
// Helpers

// xxxHonza: duplicated in net.js
function isURLEncodedFile(file, text)
{
    if (text && text.toLowerCase().indexOf("content-type: application/x-www-form-urlencoded") != -1)
        return true;

    // The header value doesn't have to be alway exactly "application/x-www-form-urlencoded",
    // there can be even charset specified. So, use indexOf rather than just "==".
    var headerValue = findHeader(file.requestHeaders, "content-type");
    if (headerValue && headerValue.indexOf("application/x-www-form-urlencoded") == 0)
        return true;

    return false;
}

function findHeader(headers, name)
{
    name = name.toLowerCase();
    for (var i = 0; headers && i < headers.length; ++i)
    {
        if (headers[i].name.toLowerCase() == name)
            return headers[i].value;
    }

    return "";
}

function safeGetName(request)
{
    try
    {
        return request.name;
    }
    catch (exc) { }

    return null;
}

function dateToJSON(date)
{
    function f(n, c) {
        if (!c) c = 2;
        var s = new String(n);
        while (s.length < c) s = "0" + s;
        return s;
    }

    var result = date.getFullYear() + '-' +
        f(date.getMonth() + 1) + '-' +
        f(date.getDate()) + 'T' +
        f(date.getHours()) + ':' +
        f(date.getMinutes()) + ':' +
        f(date.getSeconds()) + '.' +
        f(date.getMilliseconds(), 3);

    var offset = date.getTimezoneOffset();
    var positive = offset > 0;

    // Convert to positive number before using Math.floor (see issue 5512)
    offset = Math.abs(offset);
    var offsetHours = Math.floor(offset / 60);
    var offsetMinutes = Math.floor(offset % 60);
    var prettyOffset = (positive > 0 ? "-" : "+") + f(offsetHours) + ":" + f(offsetMinutes);

    return result + prettyOffset;
}

// ************************************************************************************************
}});
<commit_msg>Issue 5662 (NetExport captures only certain domain values) http://code.google.com/p/fbug/issues/detail?id=5662<commit_after>/* See license.txt for terms of usage */

FBL.ns(function() { with (FBL) {

const Cc = Components.classes;
const Ci = Components.interfaces;

const appInfo = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo);

const harVersion = "1.1";
var prefDomain = "extensions.firebug.netexport";

// ************************************************************************************************
// HAR builder implementation

Firebug.NetExport.HARBuilder = function()
{
    this.pageMap = [];
}

Firebug.NetExport.HARBuilder.prototype =
{
    build: function(context)
    {
        this.context = context;

        var panel = context.getPanel("net");

        // Build basic structure for data.
        var log = this.buildLog();

        // Before enumerating requests, we need to call layout methods that renders
        // content of the panel. This ensures export even if the panel is not visible
        // (since e.g. Firebug UI is minimized).
        panel.layout();

        // If set to true, requests coming from BFCache will also be exported.
        var exportFromBFCache = Firebug.getPref(prefDomain, "exportFromBFCache");

        // Build entries.
        var self = this;
        panel.enumerateRequests(function(file)
        {
            // Don't export BFCache responses. These don't represent network activity.
            // Do export if the pref says so.
            if (file.fromBFCache && !exportFromBFCache)
                return;

            if (file.loaded)
                log.entries.push(self.buildEntry(log, file));
        })

        return {log:log};
    },

    buildLog: function()
    {
        var log = {};
        log.version = harVersion;
        log.creator = {name: "Firebug", version: Firebug.version};
        log.browser = {name: appInfo.name, version: appInfo.version};
        log.pages = [];
        log.entries = [];
        return log;
    },

    buildPage: function(file)
    {
        var page = {};

        // Page start time is set when the first request is processed (see buildEntry)
        page.startedDateTime = 0;

        // Page title and ID comes from a document object that is shared by
        // all requests executed by the same page (since Firebug 1.5b1).
        var doc = this.getTopDocument(file);
        var pageId = doc.id;
        var title = doc.title;

        page.id = "page_" + (pageId ? pageId : "0");
        page.title = title ? title : this.context.getTitle();
        return page;
    },

    getTopDocument: function(file)
    {
        // Always get ID of the top document, not a document that comes from
        // and iframe. Otherwise, inner frames would be exported as separated
        // pages, see Issue 5153
        var doc = file.document;
        while (doc.parent)
            doc = doc.parent;

        return doc;
    },

    getPage: function(log, file)
    {
        var doc = this.getTopDocument(file);

        var page = this.pageMap[doc.id];
        if (page)
            return page;

        if (FBTrace.DBG_NETEXPORT)
            FBTrace.sysout("netexport.getPage; " + doc.id);

        this.pageMap[doc.id] = page = this.buildPage(file);
        log.pages.push(page);

        return page;
    },

    buildEntry: function(log, file)
    {
        var page = this.getPage(log, file);

        var entry = {};
        entry.pageref = page.id;
        entry.startedDateTime = dateToJSON(new Date(file.startTime));
        entry.time = file.endTime - file.startTime;
        entry.request = this.buildRequest(file);
        entry.response = this.buildResponse(file);
        entry.cache = this.buildCache(file);
        entry.timings = this.buildTimings(file);

        // Remote IP address and port number are accessible in Firefox 5.
        if (file.remoteAddress)
            entry.serverIPAddress = file.remoteAddress;

        if (file.remotePort)
            entry.connection = file.remotePort + ""; // must be a string

        // Compute page load start time according to the first request start time.
        if (!page.startedDateTime)
            page.startedDateTime = entry.startedDateTime;

        page.pageTimings = this.buildPageTimings(page, file);

        return entry;
    },

    buildPageTimings: function(page, file)
    {
        var timings = page.pageTimings;

        // Put page timings into the page object when we have the first entry.
        if (!timings)
        {
            // The default value -1 (not available) according to the spec.
            timings = {onContentLoad: -1, onLoad: -1};

            if (file.phase.contentLoadTime)
                timings.onContentLoad = file.phase.contentLoadTime - file.startTime;

            if (file.phase.windowLoadTime)
                timings.onLoad = file.phase.windowLoadTime - file.startTime;

            // Remember start time of the first request for time stamps below. Time stamps
            // are always computed against the page start time.
            this.startedDateTime = file.startTime;
        }

        var phase = file.phase;

        // Compatibility with older versions of Firebug (no time stamp).
        if (!phase.timeStamps)
            return timings;

        if (!this.phases)
            this.phases = [];

        // Check whether time-stamps from this phase has been already exported.
        if (this.phases.indexOf(phase) != -1)
            return timings;

        // Timings produced by console.timeStamp. This can be part of other phases too.
        var stamps = [];
        for (var i=0; i<phase.timeStamps.length; i++)
        {
            var stamp = phase.timeStamps[i];
            if (!stamp.time)
                continue;

            // Ignore standard timings, they are already exported.
            var label = stamp.label;
            if (label == "load" || label == "DOMContentLoaded")
                continue;

            // Time stamps from all phases are inserted into one list (HAR doesn't know about
            // phases). It's up to the client how to deal with it.
            // xxxHonza: This field is non standard so far.
            if (!timings._timeStamps)
            {
                timings.comment = "_timeStamps field contains timing data generated using " +
                    "console.timeStamp() method. See Firebug documentation: " +
                    "http://getfirebug.com/wiki/index.php/Console_API";
                timings._timeStamps = [];
            }

            timings._timeStamps.push({
                time: stamp.time - this.startedDateTime,
                label: label
            });
        }

        // Time stamps from this phase has been exported.
        this.phases.push(phase);

        return timings;
    },

    buildRequest: function(file)
    {
        var request = {};

        request.method = file.method;
        request.url = file.request.URI.spec;
        request.httpVersion = this.getHttpVersion(file.request, true);

        request.cookies = this.buildRequestCookies(file);
        request.headers = this.buildHeaders(file.requestHeaders);

        request.queryString = file.urlParams;
        request.postData = this.buildPostData(file);

        request.headersSize = file.requestHeadersText ? file.requestHeadersText.length : -1;
        request.bodySize = file.postText ? file.postText.length : -1;

        return request;
    },

    buildPostData: function(file)
    {
        if (!file.postText)
            return;

        var postData = {mimeType: "", params: [], text: ""};

        var text = file.postText;
        if (isURLEncodedFile(file, text))
        {
            var lines = text.split("\n");
            postData.mimeType = "application/x-www-form-urlencoded";
            postData.params = parseURLEncodedText(lines[lines.length-1]);
        }
        else
        {
            postData.text = text;
        }

        if (FBTrace.DBG_NETEXPORT)
            FBTrace.sysout("netexport.buildPostData; ", postData);

        return postData;
    },

    buildRequestCookies: function(file)
    {
        var header = findHeader(file.requestHeaders, "cookie");

        var result = [];
        var cookies = header ? header.split("; ") : [];
        for (var i=0; i<cookies.length; i++)
        {
            var data = cookies[i];
            var index = data.indexOf("=");
            var cookie = {};
            cookie.name = data.substr(0, index);
            cookie.value = data.substr(index+1);
            result.push(cookie);
        }

        return result;
    },

    buildResponseCookies: function(file)
    {
        var header = findHeader(file.responseHeaders, "set-cookie");

        var result = [];
        var cookies = header ? header.split("\n") : [];
        for (var i=0; i<cookies.length; i++)
        {
            var cookie = this.parseCookieFromResponse(cookies[i]);
            if (!cookie.domain)
                cookie.domain = file.request.URI.host;
            result.push(cookie);
        }

        return result;
    },

    parseCookieFromResponse: function(string)
    {
        var cookie = new Object();
        var pairs = string.split(";");

        for (var i=0; i<pairs.length; i++)
        {
            var pair = FBL.trim(pairs[i]);
            var option = pair.split("=");
            if (i == 0)
            {
                cookie.name = option[0];
                cookie.value = option[1];
            }
            else
            {
                var name = option[0].toLowerCase();
                if (name == "httponly")
                {
                    cookie.httpOnly = true;
                }
                else if (name == "expires")
                {
                    var value = option[1];
                    value = value.replace(/-/g, " ");
                    cookie[name] = dateToJSON(new Date(value.replace(/-/g, " ")));
                }
                else if (name == "secure")
                {
                    cookie.secure = true;
                }
                else
                {
                    cookie[name] = option[1];
                }
            }
        }

        return cookie;
    },

    buildHeaders: function(headers)
    {
        var result = [];
        for (var i=0; headers && i<headers.length; i++)
            result.push({name: headers[i].name, value: headers[i].value});
        return result;
    },

    buildResponse: function(file)
    {
        var response = {status: 0};

        // Arbitary value if it's aborted to make sure status has a number
        if (file.responseStatus)
            response.status = parseInt(file.responseStatus);

        response.statusText = file.responseStatusText;
        response.httpVersion = this.getHttpVersion(file.request, false);

        response.cookies = this.buildResponseCookies(file);
        response.headers = this.buildHeaders(file.responseHeaders);
        response.content = this.buildContent(file);

        response.redirectURL = findHeader(file.responseHeaders, "Location");

        response.headersSize = file.responseHeadersText ? file.responseHeadersText.length : -1;
        response.bodySize = file.size;

        return response;
    },

    buildContent: function(file)
    {
        var content = {mimeType: ""};
        content.size = file.responseText ? file.responseText.length :
            (file.size >= 0 ? file.size : -1);

        try
        {
            content.mimeType = file.request.contentType;
        }
        catch (e)
        {
            if (FBTrace.DBG_NETEXPORT || FBTrace.DBG_ERRORS)
                FBTrace.sysout("netexport.buildContent EXCEPTION", e);
        }

        if (file.responseText)
            content.text = file.responseText;

        return content;
    },

    buildCache: function(file)
    {
        var cache = {};

        if (!file.fromCache)
            return cache;

        //cache.beforeRequest = {}; //xxxHonza: There is no such info yet in the Net panel.

        if (file.cacheEntry)
            cache.afterRequest = this.buildCacheEntry(file.cacheEntry);
        else
            cache.afterRequest = null;

        return cache;
    },

    buildCacheEntry: function(cacheEntry)
    {
        var cache = {};
        cache.expires = findHeader(cacheEntry, "Expires");
        cache.lastAccess = findHeader(cacheEntry, "Last Fetched");
        cache.eTag = ""; //xxxHonza
        cache.hitCount = findHeader(cacheEntry, "Fetch Count");
        return cache;
    },

    buildTimings: function(file)
    {
        var startTime = file.startTime;
        var resolvingTime = file.resolvingTime;
        var connectingTime = file.connectingTime;
        var connectedTime = file.connectedTime;
        var sendingTime = file.sendingTime;
        var waitingForTime = file.waitingForTime;
        var respondedTime = file.respondedTime;
        var endTime = file.endTime;

        // Fix problem where some net events wasn't sent and some timing info
        // wasn't updated to the last received event.
        // This problem should be fixed in Firebug 1.8b5
        if (connectingTime < resolvingTime)
            connectingTime = resolvingTime;

        if (connectedTime < connectingTime)
            connectedTime = connectingTime;

        if (sendingTime < connectedTime)
            sendingTime = connectedTime;

        if (waitingForTime < sendingTime)
            waitingForTime = sendingTime;

        var blockingEnd = this.getBlockingEndTime(file);

        var timings = {};
        timings.blocked = blockingEnd - startTime;
        timings.dns = connectingTime - resolvingTime;
        timings.connect = file.connectStarted ? sendingTime - connectingTime : 0;
        timings.send = waitingForTime - sendingTime;
        timings.wait = respondedTime - waitingForTime;
        timings.receive = endTime - respondedTime;

        return timings;
    },

    // xxxHonza: duplicated in NetUtils
    getBlockingEndTime: function(file)
    {
        if (file.resolveStarted && file.connectStarted)
            return file.resolvingTime;

        if (file.connectStarted)
            return file.connectingTime;

        if (file.sendStarted)
            return file.sendingTime;

        // This is how blocking end was computed before Firebug 1.8b6
        return (file.sendingTime > file.startTime) ? file.sendingTime : file.waitingForTime;
    },

    getHttpVersion: function(request, forRequest)
    {
        if (request instanceof Ci.nsIHttpChannelInternal)
        {
            try
            {
                var major = {}, minor = {};

                if (forRequest)
                    request.getRequestVersion(major, minor);
                else
                    request.getResponseVersion(major, minor);

                return "HTTP/" + major.value + "." + minor.value;
            }
            catch(err)
            {
                if (FBTrace.DBG_NETEXPORT || FBTrace.DBG_ERRORS)
                    FBTrace.sysout("netexport.getHttpVersion EXCEPTION", err);
            }
        }

        return "";
    },
}

// ************************************************************************************************
// Helpers

// xxxHonza: duplicated in net.js
function isURLEncodedFile(file, text)
{
    if (text && text.toLowerCase().indexOf("content-type: application/x-www-form-urlencoded") != -1)
        return true;

    // The header value doesn't have to be alway exactly "application/x-www-form-urlencoded",
    // there can be even charset specified. So, use indexOf rather than just "==".
    var headerValue = findHeader(file.requestHeaders, "content-type");
    if (headerValue && headerValue.indexOf("application/x-www-form-urlencoded") == 0)
        return true;

    return false;
}

function findHeader(headers, name)
{
    name = name.toLowerCase();
    for (var i = 0; headers && i < headers.length; ++i)
    {
        if (headers[i].name.toLowerCase() == name)
            return headers[i].value;
    }

    return "";
}

function safeGetName(request)
{
    try
    {
        return request.name;
    }
    catch (exc) { }

    return null;
}

function dateToJSON(date)
{
    function f(n, c) {
        if (!c) c = 2;
        var s = new String(n);
        while (s.length < c) s = "0" + s;
        return s;
    }

    var result = date.getFullYear() + '-' +
        f(date.getMonth() + 1) + '-' +
        f(date.getDate()) + 'T' +
        f(date.getHours()) + ':' +
        f(date.getMinutes()) + ':' +
        f(date.getSeconds()) + '.' +
        f(date.getMilliseconds(), 3);

    var offset = date.getTimezoneOffset();
    var positive = offset > 0;

    // Convert to positive number before using Math.floor (see issue 5512)
    offset = Math.abs(offset);
    var offsetHours = Math.floor(offset / 60);
    var offsetMinutes = Math.floor(offset % 60);
    var prettyOffset = (positive > 0 ? "-" : "+") + f(offsetHours) + ":" + f(offsetMinutes);

    return result + prettyOffset;
}

// ************************************************************************************************
}});
<|endoftext|>