<commit_before>var Gallery = (function() {
  const PRE_PATH = 'chrome://ffosassistant/content/';

  function getListContainer() {
    return $id('picture-list-container');
  }

  function init() {
    getListContainer().innerHTML = '';
    for (var uname in storageInfoList) {
      if(storageInfoList[uname].totalSpace && storageInfoList[uname].totalSpace > 0) {
        $id('empty-picture-container').hidden = true;
        customEventElement.removeEventListener('dataChange', onMessage);
        customEventElement.addEventListener('dataChange', onMessage);
        getAllPictures();
        return;
      }
    }
  }

  function onMessage(e) {
    if (e.detail.type != 'picture') {
      return;
    }
    var msg = e.detail.data;
    switch (msg.callbackID) {
      case 'ondeleted':
        updateRemovedPictures(msg.detail);
        updateUI();
        break;
      case 'enumerate':
        addPicture(msg.detail);
        updateUI();
        break;
      default:
        break;
    }
  }

  function getAllPictures() {
    var getPicturesIndex = 0;
    var picturesCount = 0;
    var loadingGroupId = animationLoading.start();
    CMD.Pictures.getOldPicturesInfo(function(oldPicture) {
      var picture = JSON.parse(oldPicture.data);
      if (picture.callbackID == 'enumerate') {
        getPicturesIndex++;
        addPicture(picture.detail);
      }
      else if (picture.callbackID == 'enumerate-done') {
        picturesCount = picture.detail;
      }
      if (getPicturesIndex == picturesCount) {
        updateChangedPictures();
        updateUI();
        updateControls();
        animationLoading.stop(loadingGroupId);
      }
    }, function onerror() {
      updateControls();
      animationLoading.stop(loadingGroupId);
    });
  }

  function updateChangedPictures() {
    CMD.Pictures.getChangedPicturesInfo();
  }

  function addPicture(picture) {
    if (!picture) {
      return;
    }

    var threadId = formatDate(parseInt(picture.date));
    var threadContainer = $id('pic-' + threadId);
    var container = getListContainer();

    if (threadContainer) {
      var threadBody = threadContainer.getElementsByTagName('ul')[0];
      threadBody.appendChild(createPictureListItem(picture));
      threadContainer.dataset.length = 1 + parseInt(threadContainer.dataset.length);
      var title = threadContainer.getElementsByTagName('label')[0];
      var templateData = {
        threadId: threadId,
        isToday: isToday(new Date(threadId)),
        length: threadContainer.dataset.length
      };
      title.innerHTML = tmpl('tmpl_thread_title', templateData);
      navigator.mozL10n.translate(title);
      return;
    }
    var templateData = {
      id: 'pic-' + threadId,
      isToday: isToday(new Date(threadId)),
      threadId: threadId
    };

    var div = document.createElement('div');
    div.innerHTML = tmpl('tmpl_pic_thread_container', templateData);
    navigator.mozL10n.translate(div);
    var threads = $expr('.picture-thread', container);
    if (threads.length == 0) {
      container.appendChild(div);
    } else {
      var dt = new Date(threadId);
      var index = 0;
      for (; index < threads.length; index++) {
        var date = new Date(threads[index].dataset.threadId);
        if (dt > date) {
          container.insertBefore(div, threads[index].parentNode);
          break;
        }
      }
      if (index == threads.length) {
        container.appendChild(div);
      }
    }
    var title = $expr('label', div)[0];

    title.onclick = function onSelectThread(e) {
      var target = e.target;

      if (!(target instanceof HTMLLabelElement)) {
        return;
      }

      var threadContainer = this.parentNode.parentNode;
      var picItems = $expr('li', threadContainer);

      var bChecked = threadContainer.dataset.checked == 'true';
      threadContainer.dataset.checked = !bChecked;

      picItems.forEach(function(item) {
        item.dataset.checked = !bChecked;
      });

      updateControls();
    };

    var threadBody = $expr('ul', div)[0];
    threadBody.appendChild(createPictureListItem(picture));
  }

  function updateRemovedPictures(pictures) {
    if (!pictures || !pictures.length) {
      return;
    }
    for (var i = 0; i < pictures.length; i++) {
      var pic = $expr('li[data-pic-url="' + pictures[i] + '"]')[0];
      if (!pic) {
        continue;
      }
      var threadBody = pic.parentNode;
      threadBody.removeChild(pic);
      var thread = threadBody.parentNode.parentNode;
      if ($expr('li', threadBody).length == 0) {
        getListContainer().removeChild(thread);
      } else {
        var headerNode = threadBody.parentNode;
        headerNode.dataset.length = parseInt(headerNode.dataset.length) - 1;
        var numberNode = headerNode.getElementsByTagName('span')[1];
        numberNode.textContent = ' (' + headerNode.dataset.length + ')';
      }
    }
  }

  function createPictureListItem(picture) {
    var listItem = document.createElement('li');
    listItem.dataset.checked = 'false';
    listItem.dataset.picUrl = picture.name;
    listItem.dataset.title = getFileName(picture.name);
    listItem.dataset.date = picture.date;
    listItem.dataset.size = picture.size;

    var templateData = {
      thumbnail: picture.metadata.thumbnail
    };

    listItem.innerHTML = tmpl('tmpl_pic_item', templateData);

    listItem.onclick = function item_click(e) {
      this.dataset.checked = !(this.dataset.checked == 'true');
      var threadBody = this.parentNode;
      var threadContainer = threadBody.parentNode;
      threadContainer.dataset.checked = $expr('li[data-checked=true]', threadBody).length == threadContainer.dataset.length;
      updateControls();
    };

    listItem.ondblclick = function(e) {
      $id('tip').hidden = true;
      if (isWifiConnected) {
        return;
      }
      var picList = $expr('li', getListContainer());
      var currentIndex = 0;
      for (; currentIndex < picList.length; currentIndex++) {
        if (picList[currentIndex].dataset.picUrl == this.dataset.picUrl) {
          break;
        }
      }

      new ImageViewer({
        count: picList.length,
        currentIndex: currentIndex,
        getPictureAt: getPictureAt
      });
    };

    templateData = {
      name: listItem.dataset.title,
      date: formatDate(parseInt(listItem.dataset.date)),
      size: toSizeInMB(listItem.dataset.size)
    };

    new Tip({
      element: listItem,
      innerHTML: tmpl('tmpl_tip', templateData),
      container: $id('picture-list-container')
    });

    return listItem;
  }

  function getPictureAt(index, callback) {
    var picList = $expr('li', getListContainer());
    if (picList[index]) {
      // TODO: check if picture has been cached already
      var name = picList[index].dataset.picUrl.substr(picList[index].dataset.picUrl.lastIndexOf('/'));

      var path = getCachedDir(['extensions', 'ffosassistant@mozillaonline.com', 'content', CACHE_FOLDER]);
      if (!device) {
        return;
      }
      var cachedUrl = PRE_PATH + CACHE_FOLDER + name;
      var aFrom = picList[index].dataset.picUrl;
      var reg = /^\/([a-z0-9]+)\//;
      var result = aFrom.match(reg);
      var storage = result[1];
      if (!storageInfoList[storage] || !storageInfoList[storage].path) {
        return;
      }
      aFrom = aFrom.replace(reg, storageInfoList[storage].path);
      device.pull(aFrom, path + name).then(function() {
        callback(true, cachedUrl);
        animationLoading.stop(loadingGroupId);
      }, function() {
        new AlertDialog({
          message: _('operation-failed'),
          showCancelButton: false
        });
        animationLoading.stop(loadingGroupId);
      });
    }
  }

  function updateControls() {
    if ($expr('#picture-list-container .picture-thread').length == 0) {
      $id('selectAll-pictures').dataset.checked = false;
      $id('selectAll-pictures').dataset.disabled = true;
    } else {
      $id('selectAll-pictures').dataset.checked =
        $expr('#picture-list-container li').length === $expr('#picture-list-container li[data-checked="true"]').length;
      $id('selectAll-pictures').dataset.disabled = false;
    }

    $id('remove-pictures').dataset.disabled =
      $expr('#picture-list-container li[data-checked="true"]').length === 0;

    //$id('remove-pictures').dataset.disabled = isWifiConnected;
    $id('import-pictures-btn').hidden =
    $id('import-pictures').dataset.disabled = isWifiConnected ||
                                              !adbHelperInstalled ||
                                              needUpdateAdbHelper;
    $id('export-pictures').dataset.disabled = isWifiConnected ||
                                              !adbHelperInstalled ||
                                              needUpdateAdbHelper ||
                                              ($expr('#picture-list-container li[data-checked="true"]').length === 0);
  }

  function updateUI() {
    $id('empty-picture-container').hidden = !!$expr('#picture-list-container li').length;
    selectAllPictures(false);
  }

  function selectAllPictures(selected) {
    $expr('#picture-list-container .picture-thread').forEach(function(thread) {
      thread.dataset.checked = selected;
      $expr('li', thread).forEach(function(item) {
        item.dataset.checked = selected;
      });
    });

    updateControls();
  }

  function removePictures(files) {
    var items = files || [];
    if (items.length <= 0) {
      // TODO: prompt selecting pictures to remove...
      return;
    }

    var dialog = new FilesOPDialog({
      title_l10n_id: 'remove-pictures-dialog-header',
      processbar_l10n_id: 'processbar-remove-pictures-prompt',
      type: 7,
      files: items,
      alert_prompt: 'files-cannot-be-removed',
      maxSteps: 50
    });

    dialog.start();
  }

  function importPictures() {
    if (this.dataset.disabled == 'true') {
      return;
    }

    selectMultiFilesFromDisk(function(data) {
      if (!data) {
        return;
      }
      data = data.substr(0, data.length - 1);
      var pictures = data.split(';');

      if (pictures.length == 0) {
        return;
      }

      var dialog = new FilesOPDialog({
        title_l10n_id: 'import-pictures-dialog-header',
        processbar_l10n_id: 'processbar-import-pictures-prompt',
        type: 6,
        files: pictures,
        callback: updateChangedPictures,
        alert_prompt: 'files-cannot-be-imported',
        maxSteps: 50
      });

      dialog.start();
    }, {
      title: _('import-picture-title'),
      fileType: 'Image'
    });
  }

  window.addEventListener('load', function wnd_onload(event) {
    $id('selectAll-pictures').onclick = function(event) {
      if (this.dataset.disabled == "true") {
        return;
      }
      selectAllPictures(this.dataset.checked == "false");
    };

    $id('remove-pictures').onclick = function onclick_removePictures(event) {
      if (this.dataset.disabled == 'true') {
        return;
      }

      var files = [];
      $expr('#picture-list-container li[data-checked="true"]').forEach(function(item) {
        files.push(item.dataset.picUrl);
      });

      new AlertDialog({
        message: _('delete-pictures-confirm', {
          n: files.length
        }),
        showCancelButton: true,
        okCallback: function() {
          removePictures(files);
        }
      });
    };

    $id('refresh-pictures').onclick = function onclick_refreshPictures(event) {
      updateChangedPictures();
    };

    $id('import-pictures-btn').onclick = $id('import-pictures').onclick = importPictures;

    $id('export-pictures').onclick = function onclick_exportPictures(event) {
      if (this.dataset.disabled == 'true') {
        return;
      }

      var pictures = $expr('#picture-list-container li[data-checked="true"]');

      if (pictures.length == 0) {
        return;
      }

      selectDirectory(function(dir) {
        var dialog = new FilesOPDialog({
          title_l10n_id: 'export-pictures-dialog-header',
          processbar_l10n_id: 'processbar-export-pictures-prompt',
          dir: dir,
          type: 5,
          files: pictures,
          alert_prompt: 'files-cannot-be-exported',
          maxSteps: 50
        });

        dialog.start();
      }, {
        title: _('export-picture-title'),
        fileType: 'Image'
      });
    };
  });

  return {
    init: init
  };
})();
<commit_msg>modify display picture<commit_after>var Gallery = (function() {
  const PRE_PATH = 'chrome://ffosassistant/content/';

  function getListContainer() {
    return $id('picture-list-container');
  }

  function init() {
    getListContainer().innerHTML = '';
    for (var uname in storageInfoList) {
      if(storageInfoList[uname].totalSpace && storageInfoList[uname].totalSpace > 0) {
        $id('empty-picture-container').hidden = true;
        customEventElement.removeEventListener('dataChange', onMessage);
        customEventElement.addEventListener('dataChange', onMessage);
        getAllPictures();
        return;
      }
    }
  }

  function onMessage(e) {
    if (e.detail.type != 'picture') {
      return;
    }
    var msg = e.detail.data;
    switch (msg.callbackID) {
      case 'ondeleted':
        updateRemovedPictures(msg.detail);
        updateUI();
        break;
      case 'enumerate':
        addPicture(msg.detail);
        updateUI();
        break;
      default:
        break;
    }
  }

  function getAllPictures() {
    var getPicturesIndex = 0;
    var picturesCount = 0;
    var loadingGroupId = animationLoading.start();
    CMD.Pictures.getOldPicturesInfo(function(oldPicture) {
      var picture = JSON.parse(oldPicture.data);
      if (picture.callbackID == 'enumerate') {
        getPicturesIndex++;
        addPicture(picture.detail);
      }
      else if (picture.callbackID == 'enumerate-done') {
        picturesCount = picture.detail;
      }
      if (getPicturesIndex == picturesCount) {
        updateChangedPictures();
        updateUI();
        updateControls();
        animationLoading.stop(loadingGroupId);
      }
    }, function onerror() {
      updateControls();
      animationLoading.stop(loadingGroupId);
    });
  }

  function updateChangedPictures() {
    CMD.Pictures.getChangedPicturesInfo();
  }

  function addPicture(picture) {
    if (!picture) {
      return;
    }

    var threadId = formatDate(parseInt(picture.date));
    var threadContainer = $id('pic-' + threadId);
    var container = getListContainer();

    if (threadContainer) {
      var threadBody = threadContainer.getElementsByTagName('ul')[0];
      threadBody.appendChild(createPictureListItem(picture));
      threadContainer.dataset.length = 1 + parseInt(threadContainer.dataset.length);
      var title = threadContainer.getElementsByTagName('label')[0];
      var templateData = {
        threadId: threadId,
        isToday: isToday(new Date(threadId)),
        length: threadContainer.dataset.length
      };
      title.innerHTML = tmpl('tmpl_thread_title', templateData);
      navigator.mozL10n.translate(title);
      return;
    }
    var templateData = {
      id: 'pic-' + threadId,
      isToday: isToday(new Date(threadId)),
      threadId: threadId
    };

    var div = document.createElement('div');
    div.innerHTML = tmpl('tmpl_pic_thread_container', templateData);
    navigator.mozL10n.translate(div);
    var threads = $expr('.picture-thread', container);
    if (threads.length == 0) {
      container.appendChild(div);
    } else {
      var dt = new Date(threadId);
      var index = 0;
      for (; index < threads.length; index++) {
        var date = new Date(threads[index].dataset.threadId);
        if (dt > date) {
          container.insertBefore(div, threads[index].parentNode);
          break;
        }
      }
      if (index == threads.length) {
        container.appendChild(div);
      }
    }
    var title = $expr('label', div)[0];

    title.onclick = function onSelectThread(e) {
      var target = e.target;

      if (!(target instanceof HTMLLabelElement)) {
        return;
      }

      var threadContainer = this.parentNode.parentNode;
      var picItems = $expr('li', threadContainer);

      var bChecked = threadContainer.dataset.checked == 'true';
      threadContainer.dataset.checked = !bChecked;

      picItems.forEach(function(item) {
        item.dataset.checked = !bChecked;
      });

      updateControls();
    };

    var threadBody = $expr('ul', div)[0];
    threadBody.appendChild(createPictureListItem(picture));
  }

  function updateRemovedPictures(pictures) {
    if (!pictures || !pictures.length) {
      return;
    }
    for (var i = 0; i < pictures.length; i++) {
      var pic = $expr('li[data-pic-url="' + pictures[i] + '"]')[0];
      if (!pic) {
        continue;
      }
      var threadBody = pic.parentNode;
      threadBody.removeChild(pic);
      var thread = threadBody.parentNode.parentNode;
      if ($expr('li', threadBody).length == 0) {
        getListContainer().removeChild(thread);
      } else {
        var headerNode = threadBody.parentNode;
        headerNode.dataset.length = parseInt(headerNode.dataset.length) - 1;
        var numberNode = headerNode.getElementsByTagName('span')[1];
        numberNode.textContent = ' (' + headerNode.dataset.length + ')';
      }
    }
  }

  function createPictureListItem(picture) {
    var listItem = document.createElement('li');
    listItem.dataset.checked = 'false';
    listItem.dataset.picUrl = picture.name;
    listItem.dataset.title = getFileName(picture.name);
    listItem.dataset.date = picture.date;
    listItem.dataset.size = picture.size;

    var templateData = {
      thumbnail: picture.metadata.thumbnail
    };

    listItem.innerHTML = tmpl('tmpl_pic_item', templateData);

    listItem.onclick = function item_click(e) {
      this.dataset.checked = !(this.dataset.checked == 'true');
      var threadBody = this.parentNode;
      var threadContainer = threadBody.parentNode;
      threadContainer.dataset.checked = $expr('li[data-checked=true]', threadBody).length == threadContainer.dataset.length;
      updateControls();
    };

    listItem.ondblclick = function(e) {
      $id('tip').hidden = true;
      if (isWifiConnected) {
        return;
      }
      var picList = $expr('li', getListContainer());
      var currentIndex = 0;
      for (; currentIndex < picList.length; currentIndex++) {
        if (picList[currentIndex].dataset.picUrl == this.dataset.picUrl) {
          break;
        }
      }

      new ImageViewer({
        count: picList.length,
        currentIndex: currentIndex,
        getPictureAt: getPictureAt
      });
    };

    templateData = {
      name: listItem.dataset.title,
      date: formatDate(parseInt(listItem.dataset.date)),
      size: toSizeInMB(listItem.dataset.size)
    };

    new Tip({
      element: listItem,
      innerHTML: tmpl('tmpl_tip', templateData),
      container: $id('picture-list-container')
    });

    return listItem;
  }

  function getPictureAt(index, callback) {
    var picList = $expr('li', getListContainer());
    if (picList[index]) {
      // TODO: check if picture has been cached already
      var name = picList[index].dataset.picUrl.substr(picList[index].dataset.picUrl.lastIndexOf('/'));

      var path = getCachedDir(['extensions', 'ffosassistant@mozillaonline.com', 'content', CACHE_FOLDER]);
      if (!device) {
        return;
      }
      name = decodeURIComponent(name);
      var cachedUrl = PRE_PATH + CACHE_FOLDER + name;
      var aFrom = picList[index].dataset.picUrl;
      var reg = /^\/([a-z0-9]+)\//;
      var result = aFrom.match(reg);
      var storage = result[1];
      if (!storageInfoList[storage] || !storageInfoList[storage].path) {
        return;
      }
      aFrom = aFrom.replace(reg, storageInfoList[storage].path);
      device.pull(aFrom, path + name).then(function() {
        callback(true, cachedUrl);
        animationLoading.stop(loadingGroupId);
      }, function() {
        new AlertDialog({
          message: _('operation-failed'),
          showCancelButton: false
        });
        animationLoading.stop(loadingGroupId);
      });
    }
  }

  function updateControls() {
    if ($expr('#picture-list-container .picture-thread').length == 0) {
      $id('selectAll-pictures').dataset.checked = false;
      $id('selectAll-pictures').dataset.disabled = true;
    } else {
      $id('selectAll-pictures').dataset.checked =
        $expr('#picture-list-container li').length === $expr('#picture-list-container li[data-checked="true"]').length;
      $id('selectAll-pictures').dataset.disabled = false;
    }

    $id('remove-pictures').dataset.disabled =
      $expr('#picture-list-container li[data-checked="true"]').length === 0;

    //$id('remove-pictures').dataset.disabled = isWifiConnected;
    $id('import-pictures-btn').hidden =
    $id('import-pictures').dataset.disabled = isWifiConnected ||
                                              !adbHelperInstalled ||
                                              needUpdateAdbHelper;
    $id('export-pictures').dataset.disabled = isWifiConnected ||
                                              !adbHelperInstalled ||
                                              needUpdateAdbHelper ||
                                              ($expr('#picture-list-container li[data-checked="true"]').length === 0);
  }

  function updateUI() {
    $id('empty-picture-container').hidden = !!$expr('#picture-list-container li').length;
    selectAllPictures(false);
  }

  function selectAllPictures(selected) {
    $expr('#picture-list-container .picture-thread').forEach(function(thread) {
      thread.dataset.checked = selected;
      $expr('li', thread).forEach(function(item) {
        item.dataset.checked = selected;
      });
    });

    updateControls();
  }

  function removePictures(files) {
    var items = files || [];
    if (items.length <= 0) {
      // TODO: prompt selecting pictures to remove...
      return;
    }

    var dialog = new FilesOPDialog({
      title_l10n_id: 'remove-pictures-dialog-header',
      processbar_l10n_id: 'processbar-remove-pictures-prompt',
      type: 7,
      files: items,
      alert_prompt: 'files-cannot-be-removed',
      maxSteps: 50
    });

    dialog.start();
  }

  function importPictures() {
    if (this.dataset.disabled == 'true') {
      return;
    }

    selectMultiFilesFromDisk(function(data) {
      if (!data) {
        return;
      }
      data = data.substr(0, data.length - 1);
      var pictures = data.split(';');

      if (pictures.length == 0) {
        return;
      }

      var dialog = new FilesOPDialog({
        title_l10n_id: 'import-pictures-dialog-header',
        processbar_l10n_id: 'processbar-import-pictures-prompt',
        type: 6,
        files: pictures,
        callback: updateChangedPictures,
        alert_prompt: 'files-cannot-be-imported',
        maxSteps: 50
      });

      dialog.start();
    }, {
      title: _('import-picture-title'),
      fileType: 'Image'
    });
  }

  window.addEventListener('load', function wnd_onload(event) {
    $id('selectAll-pictures').onclick = function(event) {
      if (this.dataset.disabled == "true") {
        return;
      }
      selectAllPictures(this.dataset.checked == "false");
    };

    $id('remove-pictures').onclick = function onclick_removePictures(event) {
      if (this.dataset.disabled == 'true') {
        return;
      }

      var files = [];
      $expr('#picture-list-container li[data-checked="true"]').forEach(function(item) {
        files.push(item.dataset.picUrl);
      });

      new AlertDialog({
        message: _('delete-pictures-confirm', {
          n: files.length
        }),
        showCancelButton: true,
        okCallback: function() {
          removePictures(files);
        }
      });
    };

    $id('refresh-pictures').onclick = function onclick_refreshPictures(event) {
      updateChangedPictures();
    };

    $id('import-pictures-btn').onclick = $id('import-pictures').onclick = importPictures;

    $id('export-pictures').onclick = function onclick_exportPictures(event) {
      if (this.dataset.disabled == 'true') {
        return;
      }

      var pictures = $expr('#picture-list-container li[data-checked="true"]');

      if (pictures.length == 0) {
        return;
      }

      selectDirectory(function(dir) {
        var dialog = new FilesOPDialog({
          title_l10n_id: 'export-pictures-dialog-header',
          processbar_l10n_id: 'processbar-export-pictures-prompt',
          dir: dir,
          type: 5,
          files: pictures,
          alert_prompt: 'files-cannot-be-exported',
          maxSteps: 50
        });

        dialog.start();
      }, {
        title: _('export-picture-title'),
        fileType: 'Image'
      });
    };
  });

  return {
    init: init
  };
})();
<|endoftext|>


<commit_before>/* This software is licensed under a BSD license; see the LICENSE file for details. */

//
// TODO: Replace this controller with something that's not such a horrible mess!
//

(function () {

var __Question_callback__ = null;
var __Questions_answers__ = null;

define_ibex_controller({
name: "PictureAccept",

jqueryWidget: {
    _init: function () {
        this.cssPrefix = this.options._cssPrefix;
        this.utils = this.options._utils;
        this.finishedCallback = this.options._finishedCallback;

        var questionField = "Sentence";
        var answerField = "Chosen picture (Starting from 1)";
        var correctField = "Whether or not answer was correct (NULL if N/A)";
        var timeField = "Time taken to answer.";

        this.question = dget(this.options, "s");
        this.answers = this.options.as;
        //assert(this.answers.length == 2, "PictureAccept only works with 2 pictures!");

        this.hasCorrect = dget(this.options, "hasCorrect", false);
        // hasCorrect is either false, indicating that there is no correct answer,
        // true, indicating that the first answer is correct, or an integer giving
        // the index of the correct answer, OR a string giving the correct answer.
        // Now we change it to either false or an index.
        if (this.hasCorrect === true)
            this.hasCorrect = 0;
        if (typeof(this.hasCorrect) == "string") {
            var foundIt = false;
            for (var i = 0; i < this.answers.length; ++i) {
                if (this.answers[i].toLowerCase() == this.hasCorrect.toLowerCase()) {
                    this.hasCorrect = i;
                    foundIt = true;
                    break;
                }
            }
            assert(foundIt, "Value of 'hasCorrect' option not recognized in PictureAccept");
        }
        this.showNumbers = dget(this.options, "showNumbers", true);
        this.presentAsScale = true;
        this.randomOrder = dget(this.options, "randomOrder", false);
        this.timeout = dget(this.options, "timeout", null);
        this.instructions = dget(this.options, "instructions");
        this.leftComment = dget(this.options, "leftComment");
        this.rightComment = dget(this.options, "rightComment");
        this.autoFirstChar = dget(this.options, "autoFirstChar", false);
        this.presentHorizontally = dget(this.options, "presentHorizontally", false);

        if (! (this.hasCorrect === false))
            assert(typeof(this.hasCorrect) == "number" && this.hasCorrect < this.answers.length,
                   "Bad index for correct answer in PictureAccept");

        if (this.randomOrder) {
            assert(typeof(this.answers[0]) != "object",
                  "Cannot set 'randomOrder' option to a list of keys when keys are included with the 'as' option.");
            assert(typeof(this.answers[0]) != "object" || this.answers.length == this.randomOrder.length,
                   "Length of 'randomOrder' doesn't match length of 'as'.");

            this.orderedAnswers = new Array(this.answers.length);
            for (var i = 0; i < this.answers.length; ++i)
                this.orderedAnswers[i] = this.answers[i];
            fisherYates(this.orderedAnswers);
        }
        else {
            this.orderedAnswers = this.answers;
        }

        this.setFlag = function(correct) {
            if (! correct) {
                this.utils.setValueForNextElement("failed", true);
            }
        }

        if (this.question) {
            this.qp = $(document.createElement("p"))
            .addClass(this.cssPrefix + "sentence")
            .css('text-align', conf_centerItems ? 'center' : 'left')
            .append(this.question);
        }
        this.xl = $(document.createElement(((!this.presentAsScale && !this.presentHorizontally) && this.showNumbers) ? "ol" : "ul"))
            .css('margin-left', "2em").css('padding-left', 0);
        __Question_answers__ = new Array(this.answers.length);

        if ((this.presentAsScale || this.presentHorizontally) && this.leftComment) {
            var lcd = $(document.createElement("li"))
                      .addClass(this.cssPrefix + "scale-comment-box")
                      .append(this.leftComment);
            this.xl.append(lcd);
        }
        for (var i = 0; i < this.orderedAnswers.length; ++i) {
            var li;
            li = $(document.createElement("li"));
            if (this.presentAsScale || this.presentHorizontally) {
                li.addClass(this.cssPrefix + "scale-box");
                var t = this;
                 // IE doesn't support :hover for anything other than links, so we
                 // have to use JS.
                 (function (li) {
                     li.mouseover(function () {
                         li.css('border-color', "white")
                           .css('cursor', 'pointer');
                     });
                     li.mouseout(function () {
                         li.css('border-color', "white")
                           .css('cursor', "default");
                     });
                 })(li);
            }
            else {
                li.addClass(this.cssPrefix + "normal-answer");
            }
            (function(i) {
                li.click(function () { __Question_callback__(i); });
            })(i);
            var ans = typeof(this.orderedAnswers[i]) == "string" ? this.orderedAnswers[i] : this.orderedAnswers[i][1];
            ans = "";
            var t = this; // 'this' doesn't behave as a lexically scoped variable so can't be
                          // captured in the closure defined below.
            var a = $(document.createElement("span")).addClass(this.cssPrefix + "fake-link");
            __Question_answers__[i] = ans;
            __Question_callback__ = function (i) {
                var answerTime = new Date().getTime();
                var ans = __Question_answers__[i];
                var resultsAns = (i+1).toString();
                var correct = "NULL";
                if (! (t.hasCorrect === false)) {
                    var correct_ans = typeof(t.answers[t.hasCorrect]) == "string" ? t.answers[t.hasCorrect] : t.answers[t.hasCorrect][1];
                    correct = (ans == correct_ans ? 1 : 0);
                    t.setFlag(correct);
                }
                t.finishedCallback([[[questionField, t.question ? csv_url_encode(t.question) : "NULL"],
                                     [answerField, csv_url_encode(resultsAns)],
                                     [correctField, correct],
                                     [timeField, answerTime - t.creationTime]]]);
            };
            this.xl.append(li.append(a.append(ans)));
        }
        if ((this.presentAsScale || this.presentHorizontally) && this.rightComment) {
            this.xl.append($(document.createElement("li"))
                           .addClass(this.cssPrefix + 'scale-comment-box')
                           .append(this.rightComment));
        }

        if (! (this.qp === undefined))
            this.element.append(this.qp);

        // Again, using tables to center because IE sucks.
        var table = $("
");
        var tr = $(document.createElement("tr"));
        var td = $("")
        if (conf_centerItems)
            td.attr('align', 'center');
        this.element.append(table.append(tr.append(td.append(this.xl))));

        if (this.instructions) {
            this.element.append($(document.createElement("p"))
                                .addClass(this.cssPrefix + "instructions-text")
                                .css('text-align', conf_centerItems ? 'center' : 'left')
                                .text(this.instructions));
        }

        if (this.timeout) {
            var t = this;
            this.utils.setTimeout(function () {
                var answerTime = new Date().getTime();
                t.setFlag(false);
                t.finishedCallback([[[questionField, t.question ? csv_url_encode(t.question) : "NULL"],
                                     [answerField, "NULL"], [correctField, "NULL"],
                                     [timeField, answerTime - t.creationTime]]]);
            }, this.timeout);
        }

        // TODO: A bit of code duplication in this function.
        var t = this;
        this.safeBind($(document), 'keydown', function(e) {
            var code = e.keyCode;
            var time = new Date().getTime();

            var answerTime = new Date().getTime();
            if ((! t.presentAsScale && !t.presentHorizontally) && t.showNumbers &&
                ((code >= 48 && code <= 57) || (code >= 96 && code <= 105))) {
                // Convert numeric keypad codes to ordinary keypad codes.
                var n = code >= 96 ? code - 96 : code - 48;
                if (n > 0 && n <= t.orderedAnswers.length) {
                    var ans = typeof(t.orderedAnswers[n-1]) == "string" ? t.orderedAnswers[n-1] : t.orderedAnswers[n-1][1];
                    var resultsAns = n.toString();
                    var correct = "NULL";
                    if (! (t.hasCorrect === false)) {
                        var correct_ans = typeof(t.answers[t.hasCorrect]) == "string" ? t.answers[t.hasCorrect] : t.answers[t.hasCorrect][1];
                        correct = (correct_ans == ans ? 1 : 0);
                        t.setFlag(correct);
                    }
                    t.finishedCallback([[[questionField, t.question ? csv_url_encode(t.question) : "NULL"],
                                         [answerField, csv_url_encode(resultsAns)],
                                         [correctField, correct],
                                         [timeField, answerTime - t.creationTime]]]);

                    return false;
                }
                else {
                    return true;
                }
            }
            // Letters (and numbers in the case of scales).
            else if ((code >= 65 && code <= 90) || (t.presentAsScale && ((code >= 48 && code <= 57) || (code >= 96 && code <= 105)))) {
                // Convert numeric keypad codes to ordinary keypad codes.
                code = (code >= 96 && code <= 105) ? code - 48 : code;
                var ans = null;
                var resultsAns = null;
                if (typeof(t.randomOrder) != "object") {
                    for (var i = 0; i < t.answers.length; ++i) {
                        if (t.autoFirstChar && typeof(t.answers[i]) == "string" && code == t.answers[i].toUpperCase().charCodeAt(0)) {
                            ans = t.answers[i];
                            resultsAns = (i+1).toString();
                            break;
                        }
                        else if ($.isArray(t.answers[i]) && code == t.answers[i][0].toUpperCase().charCodeAt(0)) {
                            ans = t.answers[i][1];
                            resultsAns = (i+1).toString();
                            break;
                        }
                    }
                }
                else {
                    for (var i = 0; i < t.randomOrder.length; ++i) {
                        if (code == t.randomOrder[i].toUpperCase().charCodeAt(0)) {
                            ans = t.orderedAnswers[i];
                            resultsAns = (i+1).toString();
                            break;
                        }
                    }
                }

                if (ans) {
                    var correct = "NULL";
                    if (! (t.hasCorrect === false)) {
                        var correct_ans = typeof(t.answers[t.hasCorrect]) == "string" ? t.answers[t.hasCorrect] : t.answers[t.hasCorrect][1];
                        correct = (correct_ans == ans ? 1 : 0);
                        t.setFlag(correct);
                    }
                    t.finishedCallback([[[questionField, t.question ? csv_url_encode(t.question) : "NULL"],
                                         [answerField, csv_url_encode(resultsAns)],
                                         [correctField, correct],
                                         [timeField, answerTime - t.creationTime]]]);

                    return false;
                }
            }

            return true;
        });

        // Store the time when this was first displayed.
        this.creationTime = new Date().getTime();
    }
},

properties: {
    obligatory: ["as"],
    htmlDescription: function(opts) {
        return $(document.createElement("div")).text(opts.s || "");
    }
}
});

})();

<commit_msg>update PictureAccept to use performance.now where available<commit_after>/* This software is licensed under a BSD license; see the LICENSE file for details. */

//
// TODO: Replace this controller with something that's not such a horrible mess!
//

(function () {

var __Question_callback__ = null;
var __Questions_answers__ = null;

define_ibex_controller({
name: "PictureAccept",

jqueryWidget: {
    _init: function () {
        this.cssPrefix = this.options._cssPrefix;
        this.utils = this.options._utils;
        this.finishedCallback = this.options._finishedCallback;

        if (typeof window.performance == 'object' && typeof performance.now == 'function')
            this.now = function () { return window.performance.now(); };
        else
            this.now = function () { return new Date().getTime(); };

        var questionField = "Sentence";
        var answerField = "Chosen picture (Starting from 1)";
        var correctField = "Whether or not answer was correct (NULL if N/A)";
        var timeField = "Time taken to answer.";

        this.question = dget(this.options, "s");
        this.answers = this.options.as;
        //assert(this.answers.length == 2, "PictureAccept only works with 2 pictures!");

        this.hasCorrect = dget(this.options, "hasCorrect", false);
        // hasCorrect is either false, indicating that there is no correct answer,
        // true, indicating that the first answer is correct, or an integer giving
        // the index of the correct answer, OR a string giving the correct answer.
        // Now we change it to either false or an index.
        if (this.hasCorrect === true)
            this.hasCorrect = 0;
        if (typeof(this.hasCorrect) == "string") {
            var foundIt = false;
            for (var i = 0; i < this.answers.length; ++i) {
                if (this.answers[i].toLowerCase() == this.hasCorrect.toLowerCase()) {
                    this.hasCorrect = i;
                    foundIt = true;
                    break;
                }
            }
            assert(foundIt, "Value of 'hasCorrect' option not recognized in PictureAccept");
        }
        this.showNumbers = dget(this.options, "showNumbers", true);
        this.presentAsScale = true;
        this.randomOrder = dget(this.options, "randomOrder", false);
        this.timeout = dget(this.options, "timeout", null);
        this.instructions = dget(this.options, "instructions");
        this.leftComment = dget(this.options, "leftComment");
        this.rightComment = dget(this.options, "rightComment");
        this.autoFirstChar = dget(this.options, "autoFirstChar", false);
        this.presentHorizontally = dget(this.options, "presentHorizontally", false);

        if (! (this.hasCorrect === false))
            assert(typeof(this.hasCorrect) == "number" && this.hasCorrect < this.answers.length,
                   "Bad index for correct answer in PictureAccept");

        if (this.randomOrder) {
            assert(typeof(this.answers[0]) != "object",
                  "Cannot set 'randomOrder' option to a list of keys when keys are included with the 'as' option.");
            assert(typeof(this.answers[0]) != "object" || this.answers.length == this.randomOrder.length,
                   "Length of 'randomOrder' doesn't match length of 'as'.");

            this.orderedAnswers = new Array(this.answers.length);
            for (var i = 0; i < this.answers.length; ++i)
                this.orderedAnswers[i] = this.answers[i];
            fisherYates(this.orderedAnswers);
        }
        else {
            this.orderedAnswers = this.answers;
        }

        this.setFlag = function(correct) {
            if (! correct) {
                this.utils.setValueForNextElement("failed", true);
            }
        }

        if (this.question) {
            this.qp = $(document.createElement("p"))
            .addClass(this.cssPrefix + "sentence")
            .css('text-align', conf_centerItems ? 'center' : 'left')
            .append(this.question);
        }
        this.xl = $(document.createElement(((!this.presentAsScale && !this.presentHorizontally) && this.showNumbers) ? "ol" : "ul"))
            .css('margin-left', "2em").css('padding-left', 0);
        __Question_answers__ = new Array(this.answers.length);

        if ((this.presentAsScale || this.presentHorizontally) && this.leftComment) {
            var lcd = $(document.createElement("li"))
                      .addClass(this.cssPrefix + "scale-comment-box")
                      .append(this.leftComment);
            this.xl.append(lcd);
        }
        for (var i = 0; i < this.orderedAnswers.length; ++i) {
            var li;
            li = $(document.createElement("li"));
            if (this.presentAsScale || this.presentHorizontally) {
                li.addClass(this.cssPrefix + "scale-box");
                var t = this;
                 // IE doesn't support :hover for anything other than links, so we
                 // have to use JS.
                 (function (li) {
                     li.mouseover(function () {
                         li.css('border-color', "white")
                           .css('cursor', 'pointer');
                     });
                     li.mouseout(function () {
                         li.css('border-color', "white")
                           .css('cursor', "default");
                     });
                 })(li);
            }
            else {
                li.addClass(this.cssPrefix + "normal-answer");
            }
            (function(i) {
                li.click(function () { __Question_callback__(i); });
            })(i);
            var ans = typeof(this.orderedAnswers[i]) == "string" ? this.orderedAnswers[i] : this.orderedAnswers[i][1];
            ans = "";
            var t = this; // 'this' doesn't behave as a lexically scoped variable so can't be
                          // captured in the closure defined below.
            var a = $(document.createElement("span")).addClass(this.cssPrefix + "fake-link");
            __Question_answers__[i] = ans;
            __Question_callback__ = function (i) {
                var answerTime = t.now();
                var ans = __Question_answers__[i];
                var resultsAns = (i+1).toString();
                var correct = "NULL";
                if (! (t.hasCorrect === false)) {
                    var correct_ans = typeof(t.answers[t.hasCorrect]) == "string" ? t.answers[t.hasCorrect] : t.answers[t.hasCorrect][1];
                    correct = (ans == correct_ans ? 1 : 0);
                    t.setFlag(correct);
                }
                t.finishedCallback([[[questionField, t.question ? csv_url_encode(t.question) : "NULL"],
                                     [answerField, csv_url_encode(resultsAns)],
                                     [correctField, correct],
                                     [timeField, answerTime - t.creationTime]]]);
            };
            this.xl.append(li.append(a.append(ans)));
        }
        if ((this.presentAsScale || this.presentHorizontally) && this.rightComment) {
            this.xl.append($(document.createElement("li"))
                           .addClass(this.cssPrefix + 'scale-comment-box')
                           .append(this.rightComment));
        }

        if (! (this.qp === undefined))
            this.element.append(this.qp);

        // Again, using tables to center because IE sucks.
        var table = $("
");
        var tr = $(document.createElement("tr"));
        var td = $("")
        if (conf_centerItems)
            td.attr('align', 'center');
        this.element.append(table.append(tr.append(td.append(this.xl))));

        if (this.instructions) {
            this.element.append($(document.createElement("p"))
                                .addClass(this.cssPrefix + "instructions-text")
                                .css('text-align', conf_centerItems ? 'center' : 'left')
                                .text(this.instructions));
        }

        if (this.timeout) {
            var t = this;
            this.utils.setTimeout(function () {
                var answerTime = t.now();
                t.setFlag(false);
                t.finishedCallback([[[questionField, t.question ? csv_url_encode(t.question) : "NULL"],
                                     [answerField, "NULL"], [correctField, "NULL"],
                                     [timeField, answerTime - t.creationTime]]]);
            }, this.timeout);
        }

        // TODO: A bit of code duplication in this function.
        var t = this;
        this.safeBind($(document), 'keydown', function(e) {
            var code = e.keyCode;

            var answerTime = t.now();
            if ((! t.presentAsScale && !t.presentHorizontally) && t.showNumbers &&
                ((code >= 48 && code <= 57) || (code >= 96 && code <= 105))) {
                // Convert numeric keypad codes to ordinary keypad codes.
                var n = code >= 96 ? code - 96 : code - 48;
                if (n > 0 && n <= t.orderedAnswers.length) {
                    var ans = typeof(t.orderedAnswers[n-1]) == "string" ? t.orderedAnswers[n-1] : t.orderedAnswers[n-1][1];
                    var resultsAns = n.toString();
                    var correct = "NULL";
                    if (! (t.hasCorrect === false)) {
                        var correct_ans = typeof(t.answers[t.hasCorrect]) == "string" ? t.answers[t.hasCorrect] : t.answers[t.hasCorrect][1];
                        correct = (correct_ans == ans ? 1 : 0);
                        t.setFlag(correct);
                    }
                    t.finishedCallback([[[questionField, t.question ? csv_url_encode(t.question) : "NULL"],
                                         [answerField, csv_url_encode(resultsAns)],
                                         [correctField, correct],
                                         [timeField, answerTime - t.creationTime]]]);

                    return false;
                }
                else {
                    return true;
                }
            }
            // Letters (and numbers in the case of scales).
            else if ((code >= 65 && code <= 90) || (t.presentAsScale && ((code >= 48 && code <= 57) || (code >= 96 && code <= 105)))) {
                // Convert numeric keypad codes to ordinary keypad codes.
                code = (code >= 96 && code <= 105) ? code - 48 : code;
                var ans = null;
                var resultsAns = null;
                if (typeof(t.randomOrder) != "object") {
                    for (var i = 0; i < t.answers.length; ++i) {
                        if (t.autoFirstChar && typeof(t.answers[i]) == "string" && code == t.answers[i].toUpperCase().charCodeAt(0)) {
                            ans = t.answers[i];
                            resultsAns = (i+1).toString();
                            break;
                        }
                        else if ($.isArray(t.answers[i]) && code == t.answers[i][0].toUpperCase().charCodeAt(0)) {
                            ans = t.answers[i][1];
                            resultsAns = (i+1).toString();
                            break;
                        }
                    }
                }
                else {
                    for (var i = 0; i < t.randomOrder.length; ++i) {
                        if (code == t.randomOrder[i].toUpperCase().charCodeAt(0)) {
                            ans = t.orderedAnswers[i];
                            resultsAns = (i+1).toString();
                            break;
                        }
                    }
                }

                if (ans) {
                    var correct = "NULL";
                    if (! (t.hasCorrect === false)) {
                        var correct_ans = typeof(t.answers[t.hasCorrect]) == "string" ? t.answers[t.hasCorrect] : t.answers[t.hasCorrect][1];
                        correct = (correct_ans == ans ? 1 : 0);
                        t.setFlag(correct);
                    }
                    t.finishedCallback([[[questionField, t.question ? csv_url_encode(t.question) : "NULL"],
                                         [answerField, csv_url_encode(resultsAns)],
                                         [correctField, correct],
                                         [timeField, answerTime - t.creationTime]]]);

                    return false;
                }
            }

            return true;
        });

        // Store the time when this was first displayed.
        this.creationTime = t.now();
    }
},

properties: {
    obligatory: ["as"],
    htmlDescription: function(opts) {
        return $(document.createElement("div")).text(opts.s || "");
    }
}
});

})();

<|endoftext|>


<commit_before>'use strict';
/*globals describe,it*/

var assert = require('chai').assert;
var Detector = require('../src/Detector');
var request = require('supertest');
var express = require('express');

// Mock Server so we can create real NodeJS request objects for testing (helps version testing over different NodeJS versions)
var app = express();
app.get('/getReqObj', function (req, res) {
    res.json(JSON.parse(toString(req)));
});
app.post('/getReqObj', function (req, res) {
    res.json(JSON.parse(toString(req)));
});

describe('Detector', function () {

    describe('#detect()', function () {

        it('normal browser request should not be intercepted', function (done) {

            request(app)
                .get('/getReqObj')
                .set('Accept', 'application/json')
                .set('user-Agent', 'Mozilla/5.0 (Windows NT 6.2; WOW64; rv:25.0) Gecko/20100101 Firefox/25.0')
                .expect(200)
                .end(function (err, res) {
                    if (err) return done(err);

                    var request = res.body;

                    var detector = new Detector();
                    detector.setRequest(request);

                    assert.notOk(detector.detect(), 'should have returned false since its a normal browser request');

                    done();
                });

        });

        it('bot request should be intercepted', function (done) {

            request(app)
                .get('/getReqObj')
                .set('Accept', 'application/json')
                .set('User-Agent', 'AdsBot-Google ( http://www.google.com/adsbot.html)')
                .expect(200)
                .end(function (err, res) {
                    if (err) return done(err);

                    var request = res.body;

                    var detector = new Detector();
                    detector.setRequest(request);

                    assert.ok(detector.detect(), 'should have returned true since its a search bot request');

                    done();
                });

        });

        it('SnapSearch bot request should not be intercepted', function (done) {

            request(app)
                .get('/getReqObj')
                .set('Accept', 'application/json')
                .set('user-Agent', 'SnapSearch')
                .expect(200)
                .end(function (err, res) {
                    if (err) return done(err);

                    var request = res.body;

                    var detector = new Detector();
                    detector.setRequest(request);

                    assert.notOk(detector.detect(), 'should have returned false since its a SnapSearch bot request');

                    done();
                });

        });

        it('POST request should not be intercepted', function (done) {

            request(app)
                .post('/getReqObj')
                .set('Accept', 'application/json')
                .set('user-Agent', 'AdsBot-Google ( http://www.google.com/adsbot.html)')
                .expect(200)
                .end(function (err, res) {
                    if (err) return done(err);

                    var request = res.body;

                    var detector = new Detector();
                    detector.setRequest(request);

                    assert.notOk(detector.detect(), 'should have returned false since its a POST request');

                    done();
                });

        });

        it('Ignored User Agent request should not be intercepted', function (done) {

            request(app)
                .get('/getReqObj')
                .set('Accept', 'application/json')
                .set('user-Agent', 'Googlebot-Video/1.0')
                .expect(200)
                .end(function (err, res) {
                    if (err) return done(err);

                    var request = res.body;

                    var detector = new Detector(false, ['^\/getReqObj']);
                    detector.setRequest(request);

                    assert.notOk(detector.detect(), 'should have returned false since its a ignored User Agent request');

                    done();
                });

        });

        it('Non Matching User Agent request should not be intercepted', function (done) {

            request(app)
                .get('/getReqObj')
                .set('Accept', 'application/json')
                .set('user-Agent', 'msnbot/1.1 ( http://search.msn.com/msnbot.htm)')
                .expect(200)
                .end(function (err, res) {
                    if (err) return done(err);

                    var request = res.body;

                    var detector = new Detector(false, null, ['^\/non_matched_route']);
                    detector.setRequest(request);

                    assert.notOk(detector.detect(), 'should have returned false since its a Non Matching User Agent request');

                    done();
                });

        });

        it('Matching User Agent request should be intercepted', function (done) {

            request(app)
                .get('/getReqObj')
                .set('Accept', 'application/json')
                .set('user-Agent', 'msnbot/1.1 ( http://search.msn.com/msnbot.htm)')
                .expect(200)
                .end(function (err, res) {
                    if (err) return done(err);

                    var request = res.body;

                    var detector = new Detector(false, null, ['^\/getReqObj']);
                    detector.setRequest(request);

                    assert.ok(detector.detect(), 'should have returned true since its a Matching User Agent request');

                    done();
                });

        });

        it('Requests with _escape_fragment_ parameter should be intercepted', function (done) {

            request(app)
                .get('/getReqObj?blah=yay&_escaped_fragment_=key1%3Dlol')
                .set('Accept', 'application/json')
                .set('user-Agent', 'AdsBot-Google ( http://www.google.com/adsbot.html)')
                .expect(200)
                .end(function (err, res) {
                    if (err) return done(err);

                    var request = res.body;

                    var detector = new Detector();
                    detector.setRequest(request);

                    assert.ok(detector.detect(), 'should have returned true since its a request with _escape_fragment_ paramter');

                    done();
                });

        });

    });

    describe('#getEncodedUrl()', function () {

        it('_escape_fragment_ parameter should be properly encoded', function (done) {

            var a = request(app);
            a.get('/getReqObj?blah=yay&_escaped_fragment_=key1%3Dlol')
                .set('Accept', 'application/json')
                .set('user-Agent', 'Mozilla/5.0 (Windows NT 6.2; WOW64; rv:25.0) Gecko/20100101 Firefox/25.0')
                .expect(200)
                .end(function (err, res) {
                    if (err) return done(err);

                    var request = res.body;

                    var detector = new Detector();
                    detector.setRequest(request);

                    // hack to change the custom ip and port since we use a mock server to generate the request object
                    var url = detector.getEncodedUrl();
                    var end = url.split(':')[2].split('/');
                    end.splice(0, 1);
                    url = url.split('://')[0] + '://localhost/' + end.join('/');

                    assert.equal(url, 'http://localhost/getReqObj?blah=yay#!key1=lol', 'should have encoded _escape_fragment_ parameter properly');

                    done();
                });

        });

    });

});

/**
 * Helper to serialize the circular request object.
 *
 * @param  object obj   Javascript object to be serialised
 *
 * @return string       Serialised JSON String
 */
function toString(obj) {

    var cache = [];

    function serializer(key, value) {
        if (typeof value === 'object' && value !== null) {
            if (cache.indexOf(value) !== -1) {
                // Circular reference found, discard key
                return;
            }
            // Store value in our collection
            if (cache.indexOf(value) == -1) {
                cache.push(value);
            }
        }
        return value;
    }

    var string = JSON.stringify(obj, serializer);

    cache = null; // Enable garbage collection
    return string;

}<commit_msg>another typo in DetectorTest<commit_after>'use strict';
/*globals describe,it*/

var assert = require('chai').assert;
var Detector = require('../src/Detector');
var request = require('supertest');
var express = require('express');

// Mock Server so we can create real NodeJS request objects for testing (helps version testing over different NodeJS versions)
var app = express();
app.get('/getReqObj', function (req, res) {
    res.json(JSON.parse(toString(req)));
});
app.post('/getReqObj', function (req, res) {
    res.json(JSON.parse(toString(req)));
});

describe('Detector', function () {

    describe('#detect()', function () {

        it('normal browser request should not be intercepted', function (done) {

            request(app)
                .get('/getReqObj')
                .set('Accept', 'application/json')
                .set('user-Agent', 'Mozilla/5.0 (Windows NT 6.2; WOW64; rv:25.0) Gecko/20100101 Firefox/25.0')
                .expect(200)
                .end(function (err, res) {
                    if (err) return done(err);

                    var request = res.body;

                    var detector = new Detector();
                    detector.setRequest(request);

                    assert.notOk(detector.detect(), 'should have returned false since its a normal browser request');

                    done();
                });

        });

        it('bot request should be intercepted', function (done) {

            request(app)
                .get('/getReqObj')
                .set('Accept', 'application/json')
                .set('User-Agent', 'AdsBot-Google ( http://www.google.com/adsbot.html)')
                .expect(200)
                .end(function (err, res) {
                    if (err) return done(err);

                    var request = res.body;

                    var detector = new Detector();
                    detector.setRequest(request);

                    assert.ok(detector.detect(), 'should have returned true since its a search bot request');

                    done();
                });

        });

        it('SnapSearch bot request should not be intercepted', function (done) {

            request(app)
                .get('/getReqObj')
                .set('Accept', 'application/json')
                .set('user-Agent', 'SnapSearch')
                .expect(200)
                .end(function (err, res) {
                    if (err) return done(err);

                    var request = res.body;

                    var detector = new Detector();
                    detector.setRequest(request);

                    assert.notOk(detector.detect(), 'should have returned false since its a SnapSearch bot request');

                    done();
                });

        });

        it('POST request should not be intercepted', function (done) {

            request(app)
                .post('/getReqObj')
                .set('Accept', 'application/json')
                .set('user-Agent', 'AdsBot-Google ( http://www.google.com/adsbot.html)')
                .expect(200)
                .end(function (err, res) {
                    if (err) return done(err);

                    var request = res.body;

                    var detector = new Detector();
                    detector.setRequest(request);

                    assert.notOk(detector.detect(), 'should have returned false since its a POST request');

                    done();
                });

        });

        it('Ignored User Agent request should not be intercepted', function (done) {

            request(app)
                .get('/getReqObj')
                .set('Accept', 'application/json')
                .set('user-Agent', 'Googlebot-Video/1.0')
                .expect(200)
                .end(function (err, res) {
                    if (err) return done(err);

                    var request = res.body;

                    var detector = new Detector(false, ['^\/getReqObj']);
                    detector.setRequest(request);

                    assert.notOk(detector.detect(), 'should have returned false since its a ignored User Agent request');

                    done();
                });

        });

        it('Non Matching User Agent request should not be intercepted', function (done) {

            request(app)
                .get('/getReqObj')
                .set('Accept', 'application/json')
                .set('user-Agent', 'msnbot/1.1 ( http://search.msn.com/msnbot.htm)')
                .expect(200)
                .end(function (err, res) {
                    if (err) return done(err);

                    var request = res.body;

                    var detector = new Detector(false, null, ['^\/non_matched_route']);
                    detector.setRequest(request);

                    assert.notOk(detector.detect(), 'should have returned false since its a Non Matching User Agent request');

                    done();
                });

        });

        it('Matching User Agent request should be intercepted', function (done) {

            request(app)
                .get('/getReqObj')
                .set('Accept', 'application/json')
                .set('user-Agent', 'msnbot/1.1 ( http://search.msn.com/msnbot.htm)')
                .expect(200)
                .end(function (err, res) {
                    if (err) return done(err);

                    var request = res.body;

                    var detector = new Detector(false, null, ['^\/getReqObj']);
                    detector.setRequest(request);

                    assert.ok(detector.detect(), 'should have returned true since its a Matching User Agent request');

                    done();
                });

        });

        it('Requests with _escape_fragment_ parameter should be intercepted', function (done) {

            request(app)
                .get('/getReqObj?blah=yay&_escaped_fragment_=key1%3Dlol')
                .set('Accept', 'application/json')
                .set('user-Agent', 'AdsBot-Google ( http://www.google.com/adsbot.html)')
                .expect(200)
                .end(function (err, res) {
                    if (err) return done(err);

                    var request = res.body;

                    var detector = new Detector();
                    detector.setRequest(request);

                    assert.ok(detector.detect(), 'should have returned true since its a request with _escape_fragment_ parameter');

                    done();
                });

        });

    });

    describe('#getEncodedUrl()', function () {

        it('_escape_fragment_ parameter should be properly encoded', function (done) {

            var a = request(app);
            a.get('/getReqObj?blah=yay&_escaped_fragment_=key1%3Dlol')
                .set('Accept', 'application/json')
                .set('user-Agent', 'Mozilla/5.0 (Windows NT 6.2; WOW64; rv:25.0) Gecko/20100101 Firefox/25.0')
                .expect(200)
                .end(function (err, res) {
                    if (err) return done(err);

                    var request = res.body;

                    var detector = new Detector();
                    detector.setRequest(request);

                    // hack to change the custom ip and port since we use a mock server to generate the request object
                    var url = detector.getEncodedUrl();
                    var end = url.split(':')[2].split('/');
                    end.splice(0, 1);
                    url = url.split('://')[0] + '://localhost/' + end.join('/');

                    assert.equal(url, 'http://localhost/getReqObj?blah=yay#!key1=lol', 'should have encoded _escape_fragment_ parameter properly');

                    done();
                });

        });

    });

});

/**
 * Helper to serialize the circular request object.
 *
 * @param  object obj   Javascript object to be serialised
 *
 * @return string       Serialised JSON String
 */
function toString(obj) {

    var cache = [];

    function serializer(key, value) {
        if (typeof value === 'object' && value !== null) {
            if (cache.indexOf(value) !== -1) {
                // Circular reference found, discard key
                return;
            }
            // Store value in our collection
            if (cache.indexOf(value) == -1) {
                cache.push(value);
            }
        }
        return value;
    }

    var string = JSON.stringify(obj, serializer);

    cache = null; // Enable garbage collection
    return string;

}<|endoftext|>


<commit_before>'use strict';
/*globals describe,it*/

var assert = require( 'chai' ).assert;
var Detector = require( '../src/Detector' );
var request = require( 'supertest' );
var express = require( 'express' );

// Mock Server so we can create real NodeJS request objects for testing (helps version testing over different NodeJS versions)
var app = express();
app.get( '/getReqObj', function ( req, res ) {
    res.json( JSON.parse( toString( req )));
});
app.post( '/getReqObj', function ( req, res ) {
    res.json( JSON.parse( toString( req )));
});

describe( 'Detector', function () {

    describe( '#detect()', function () {

        it( 'normal browser request should not be intercepted', function ( done ) {

            request( app )
            .get( '/getReqObj' )
            .set( 'Accept', 'application/json' )
            .set( 'user-Agent', 'Mozilla/5.0 (Windows NT 6.2; WOW64; rv:25.0) Gecko/20100101 Firefox/25.0' )
            .expect( 200 )
            .end(function ( err, res ) {

                if ( err ) return done( err );

                var request = res.body;

                var detector = new Detector();
                detector.setRequest( request );

                assert.notOk( detector.detect(), 'should have returned false since its a normal browser request' );

                done();

            });

        });

        it( 'bot request should be intercepted', function ( done ) {

            request( app )
            .get( '/getReqObj' )
            .set( 'Accept', 'application/json' )
            .set( 'User-Agent', 'AdsBot-Google ( http://www.google.com/adsbot.html)' )
            .expect( 200 )
            .end(function ( err, res ) {

                if ( err ) return done( err );

                var request = res.body;

                var detector = new Detector();
                detector.setRequest( request );

                assert.ok( detector.detect(), 'should have returned true since its a search bot request' );

                done();

            });

        });

        it( 'SnapSearch bot request should not be intercepted', function ( done ) {

            request( app )
            .get( '/getReqObj' )
            .set( 'Accept', 'application/json' )
            .set( 'user-Agent', 'SnapSearch' )
            .expect( 200 )
            .end(function ( err, res ) {

                if ( err ) return done( err );

                var request = res.body;

                var detector = new Detector();
                detector.setRequest( request );

                assert.notOk( detector.detect(), 'should have returned false since its a SnapSearch bot request' );

                done();

            });

        });

        it( 'POST request should not be intercepted', function ( done ) {

            request( app )
            .post( '/getReqObj' )
            .set( 'Accept', 'application/json' )
            .set( 'user-Agent', 'AdsBot-Google ( http://www.google.com/adsbot.html)' )
            .expect( 200 )
            .end(function ( err, res ) {

                if ( err ) return done( err );

                var request = res.body;

                var detector = new Detector();
                detector.setRequest( request );

                assert.notOk( detector.detect(), 'should have returned false since its a POST request' );

                done();

            });

        });

        it( 'Ignored User Agent request should not be intercepted', function ( done ) {

            request( app )
            .get( '/getReqObj' )
            .set( 'Accept', 'application/json' )
            .set( 'user-Agent', 'Googlebot-Video/1.0' )
            .expect( 200 )
            .end(function ( err, res ) {

                if ( err ) return done( err );

                var request = res.body;

                var detector = new Detector( [ '^\/getReqObj' ]);
                detector.setRequest( request );

                assert.notOk( detector.detect(), 'should have returned false since its a ignored User Agent request' );

                done();

            });

        });

        it( 'Non Matching User Agent request should not be intercepted', function ( done ) {

            request( app )
            .get( '/getReqObj' )
            .set( 'Accept', 'application/json' )
            .set( 'user-Agent', 'msnbot/1.1 ( http://search.msn.com/msnbot.htm)' )
            .expect( 200 )
            .end(function ( err, res ) {

                if ( err ) return done( err );

                var request = res.body;

                var detector = new Detector( null, [ '^\/non_matched_route' ]);
                detector.setRequest( request );

                assert.notOk( detector.detect(), 'should have returned false since its a Non Matching User Agent request' );

                done();

            });

        });

        it( 'Matching User Agent request should be intercepted', function ( done ) {

            request( app )
            .get( '/getReqObj' )
            .set( 'Accept', 'application/json' )
            .set( 'user-Agent', 'msnbot/1.1 ( http://search.msn.com/msnbot.htm)' )
            .expect( 200 )
            .end(function ( err, res ) {

                if ( err ) return done( err );

                var request = res.body;

                var detector = new Detector( null, [ '^\/getReqObj' ]);
                detector.setRequest( request );

                assert.ok( detector.detect(), 'should have returned true since its a Matching User Agent request' );

                done();

            });

        });

        it( 'Requests with _escaped_fragment_ parameter should be intercepted', function ( done ) {

            //this should be working with an empty _escaped_fragment_
            request( app )
            .get( '/getReqObj?key1=value1&_escaped_fragment_=' )
            .set( 'Accept', 'application/json' )
            .set( 'user-Agent', 'Mozilla/5.0 (Windows NT 6.2; WOW64; rv:25.0) Gecko/20100101 Firefox/25.0' )
            .expect( 200 )
            .end(function ( err, res ) {

                if ( err ) return done( err );

                var request = res.body;

                var detector = new Detector();
                detector.setRequest( request );

                assert.ok( detector.detect(), 'should have returned true since its a request with _escaped_fragment_ parameter' );

                done();

            });

        });

    });

    describe( '#getEncodedUrl()', function () {

        it( '_escape_fragment_ parameter should be properly encoded', function ( done ) {

            var a = request( app );

            a.get( '/getReqObj?key1=value1&_escaped_fragment_=%2Fhashpath%3Fkey2=value2' )
            .set( 'Accept', 'application/json' )
            .set( 'user-Agent', 'Mozilla/5.0 (Windows NT 6.2; WOW64; rv:25.0) Gecko/20100101 Firefox/25.0' )
            .expect( 200 )
            .end(function ( err, res ) {

                if ( err ) return done( err );

                var request = res.body;

                var detector = new Detector();
                detector.setRequest( request );

                // hack to change the custom ip and port since we use a mock server to generate the request object
                var url = detector.getEncodedUrl();
                var end = url.split( ':' )[ 2 ].split( '/' );
                end.splice( 0, 1 );
                url = url.split( '://' )[ 0 ] + '://localhost/' + end.join( '/' );

                assert.equal( url, 'http://localhost/getReqObj?key1=value1#!/hashpath?key2=value2', 'should have encoded _escaped_fragment_ parameter properly' );

                done();

            });

        });

    });

    describe( '#robots', function () {

        it( 'robots array should be modifyable programically', function ( done ) {

            request( app )
            .get( '/getReqObj' )
            .set( 'Accept', 'application/json' )
            .set( 'user-Agent', 'Adsbot-Google' )
            .expect( 200 )
            .end(function ( err, res ) {

                if ( err ) return done( err );

                var request = res.body;

                var detector = new Detector();
                detector.setRequest( request );

                detector.robots.ignore.push('Adsbot-Google');

                assert.notOk( detector.detect(), 'should have returned false sinde UserAgent is in ignore list' );

                done();

            });

        });

    });

});

/**
 * Helper to serialize the circular request object.
 *
 * @param  object obj   Javascript object to be serialised
 *
 * @return string       Serialised JSON String
 */
function toString( obj ) {

    var cache = [];

    function serializer( key, value ) {
        if ( typeof value === 'object' && value !== null ) {
            if ( cache.indexOf( value ) !== -1 ) {
                // Circular reference found, discard key
                return;
            }
            // Store value in our collection
            if ( cache.indexOf( value ) == -1 ) {
                cache.push( value );
            }
        }
        return value;
    }

    var string = JSON.stringify( obj, serializer );

    cache = null; // Enable garbage collection
    return string;

}<commit_msg>typo in DetectorTest<commit_after>'use strict';
/*globals describe,it*/

var assert = require( 'chai' ).assert;
var Detector = require( '../src/Detector' );
var request = require( 'supertest' );
var express = require( 'express' );

// Mock Server so we can create real NodeJS request objects for testing (helps version testing over different NodeJS versions)
var app = express();
app.get( '/getReqObj', function ( req, res ) {
    res.json( JSON.parse( toString( req )));
});
app.post( '/getReqObj', function ( req, res ) {
    res.json( JSON.parse( toString( req )));
});

describe( 'Detector', function () {

    describe( '#detect()', function () {

        it( 'normal browser request should not be intercepted', function ( done ) {

            request( app )
            .get( '/getReqObj' )
            .set( 'Accept', 'application/json' )
            .set( 'user-Agent', 'Mozilla/5.0 (Windows NT 6.2; WOW64; rv:25.0) Gecko/20100101 Firefox/25.0' )
            .expect( 200 )
            .end(function ( err, res ) {

                if ( err ) return done( err );

                var request = res.body;

                var detector = new Detector();
                detector.setRequest( request );

                assert.notOk( detector.detect(), 'should have returned false since its a normal browser request' );

                done();

            });

        });

        it( 'bot request should be intercepted', function ( done ) {

            request( app )
            .get( '/getReqObj' )
            .set( 'Accept', 'application/json' )
            .set( 'User-Agent', 'AdsBot-Google ( http://www.google.com/adsbot.html)' )
            .expect( 200 )
            .end(function ( err, res ) {

                if ( err ) return done( err );

                var request = res.body;

                var detector = new Detector();
                detector.setRequest( request );

                assert.ok( detector.detect(), 'should have returned true since its a search bot request' );

                done();

            });

        });

        it( 'SnapSearch bot request should not be intercepted', function ( done ) {

            request( app )
            .get( '/getReqObj' )
            .set( 'Accept', 'application/json' )
            .set( 'user-Agent', 'SnapSearch' )
            .expect( 200 )
            .end(function ( err, res ) {

                if ( err ) return done( err );

                var request = res.body;

                var detector = new Detector();
                detector.setRequest( request );

                assert.notOk( detector.detect(), 'should have returned false since its a SnapSearch bot request' );

                done();

            });

        });

        it( 'POST request should not be intercepted', function ( done ) {

            request( app )
            .post( '/getReqObj' )
            .set( 'Accept', 'application/json' )
            .set( 'user-Agent', 'AdsBot-Google ( http://www.google.com/adsbot.html)' )
            .expect( 200 )
            .end(function ( err, res ) {

                if ( err ) return done( err );

                var request = res.body;

                var detector = new Detector();
                detector.setRequest( request );

                assert.notOk( detector.detect(), 'should have returned false since its a POST request' );

                done();

            });

        });

        it( 'Ignored User Agent request should not be intercepted', function ( done ) {

            request( app )
            .get( '/getReqObj' )
            .set( 'Accept', 'application/json' )
            .set( 'user-Agent', 'Googlebot-Video/1.0' )
            .expect( 200 )
            .end(function ( err, res ) {

                if ( err ) return done( err );

                var request = res.body;

                var detector = new Detector( [ '^\/getReqObj' ]);
                detector.setRequest( request );

                assert.notOk( detector.detect(), 'should have returned false since its a ignored User Agent request' );

                done();

            });

        });

        it( 'Non Matching User Agent request should not be intercepted', function ( done ) {

            request( app )
            .get( '/getReqObj' )
            .set( 'Accept', 'application/json' )
            .set( 'user-Agent', 'msnbot/1.1 ( http://search.msn.com/msnbot.htm)' )
            .expect( 200 )
            .end(function ( err, res ) {

                if ( err ) return done( err );

                var request = res.body;

                var detector = new Detector( null, [ '^\/non_matched_route' ]);
                detector.setRequest( request );

                assert.notOk( detector.detect(), 'should have returned false since its a Non Matching User Agent request' );

                done();

            });

        });

        it( 'Matching User Agent request should be intercepted', function ( done ) {

            request( app )
            .get( '/getReqObj' )
            .set( 'Accept', 'application/json' )
            .set( 'user-Agent', 'msnbot/1.1 ( http://search.msn.com/msnbot.htm)' )
            .expect( 200 )
            .end(function ( err, res ) {

                if ( err ) return done( err );

                var request = res.body;

                var detector = new Detector( null, [ '^\/getReqObj' ]);
                detector.setRequest( request );

                assert.ok( detector.detect(), 'should have returned true since its a Matching User Agent request' );

                done();

            });

        });

        it( 'Requests with _escaped_fragment_ parameter should be intercepted', function ( done ) {

            //this should be working with an empty _escaped_fragment_
            request( app )
            .get( '/getReqObj?key1=value1&_escaped_fragment_=' )
            .set( 'Accept', 'application/json' )
            .set( 'user-Agent', 'Mozilla/5.0 (Windows NT 6.2; WOW64; rv:25.0) Gecko/20100101 Firefox/25.0' )
            .expect( 200 )
            .end(function ( err, res ) {

                if ( err ) return done( err );

                var request = res.body;

                var detector = new Detector();
                detector.setRequest( request );

                assert.ok( detector.detect(), 'should have returned true since its a request with _escaped_fragment_ parameter' );

                done();

            });

        });

    });

    describe( '#getEncodedUrl()', function () {

        it( '_escape_fragment_ parameter should be properly encoded', function ( done ) {

            var a = request( app );

            a.get( '/getReqObj?key1=value1&_escaped_fragment_=%2Fhashpath%3Fkey2=value2' )
            .set( 'Accept', 'application/json' )
            .set( 'user-Agent', 'Mozilla/5.0 (Windows NT 6.2; WOW64; rv:25.0) Gecko/20100101 Firefox/25.0' )
            .expect( 200 )
            .end(function ( err, res ) {

                if ( err ) return done( err );

                var request = res.body;

                var detector = new Detector();
                detector.setRequest( request );

                // hack to change the custom ip and port since we use a mock server to generate the request object
                var url = detector.getEncodedUrl();
                var end = url.split( ':' )[ 2 ].split( '/' );
                end.splice( 0, 1 );
                url = url.split( '://' )[ 0 ] + '://localhost/' + end.join( '/' );

                assert.equal( url, 'http://localhost/getReqObj?key1=value1#!/hashpath?key2=value2', 'should have encoded _escaped_fragment_ parameter properly' );

                done();

            });

        });

    });

    describe( '#robots', function () {

        it( 'robots array should be modifyable programically', function ( done ) {

            request( app )
            .get( '/getReqObj' )
            .set( 'Accept', 'application/json' )
            .set( 'user-Agent', 'Adsbot-Google' )
            .expect( 200 )
            .end(function ( err, res ) {

                if ( err ) return done( err );

                var request = res.body;

                var detector = new Detector();
                detector.setRequest( request );

                detector.robots.ignore.push('Adsbot-Google');

                assert.notOk( detector.detect(), 'should have returned false since UserAgent is in ignore list' );

                done();

            });

        });

    });

});

/**
 * Helper to serialize the circular request object.
 *
 * @param  object obj   Javascript object to be serialised
 *
 * @return string       Serialised JSON String
 */
function toString( obj ) {

    var cache = [];

    function serializer( key, value ) {
        if ( typeof value === 'object' && value !== null ) {
            if ( cache.indexOf( value ) !== -1 ) {
                // Circular reference found, discard key
                return;
            }
            // Store value in our collection
            if ( cache.indexOf( value ) == -1 ) {
                cache.push( value );
            }
        }
        return value;
    }

    var string = JSON.stringify( obj, serializer );

    cache = null; // Enable garbage collection
    return string;

}<|endoftext|>